第六阶段开发。

This commit is contained in:
zfjia committed 2026-11-12 13:36:24 +08:00
1 parent 35751b4f00
commit bacdd15f3a
93 files changed
+10190 -22

No files matched your search

+129
View File
@@ -0,0 +1,129 @@
-- =============================================================================
-- SIP 认证(apt_sip_auth)
-- 定位:分机在 Asterisk 上的 auth 账号,与 endpoint 1:1(可先建认证后绑分机)。
-- 多租户/多实例:
-- * 唯一性 = (gyid, instance_code, username) —— 同一实例下用户名唯一;
-- 不同公寓或不同 Asterisk 实例可出现同名用户名,但彼此不可见;
-- * 所有读写必须同时带 gyid + instance_code,禁止只按 username 查(跨公寓串号);
-- * auth_id 采用 AUTH-<instance_code>-<username>,在实例内全局唯一,供 ps_auths.id 使用。
-- 口令策略:
-- * secret_mode='PLAIN':库内口令,默认 psw.voip(内网/联调用);
-- * secret_mode='ENV' :只存环境变量名 secret_ref,取值在渲染/同步时注入,
-- 生产建议用这一档(与 APT_AMI_SECRET 同一原则,不把口令写进库与日志);
-- * 两种模式都保留 secret_version / last_rotated_at,便于轮换与审计。
-- 幂等:建表 IF NOT EXISTS;种子与视图可重复执行。
-- =============================================================================
SET NAMES utf8mb4;
CREATE TABLE IF NOT EXISTS apt_sip_auth (
id BIGINT NOT NULL AUTO_INCREMENT,
gyid VARCHAR(32) NOT NULL COMMENT '公寓标识(租户隔离键)',
instance_code VARCHAR(64) NOT NULL COMMENT 'Asterisk 实例(apt_ast_instance.instance_code)',
username VARCHAR(64) NOT NULL COMMENT 'SIP 用户名/分机号',
auth_id VARCHAR(128) NOT NULL COMMENT 'Asterisk ps_auths.id,约定 AUTH-<instance>-<username>',
endpoint_id VARCHAR(64) NULL COMMENT '关联分机(1:1,可空以便先建认证后绑分机)',
auth_type VARCHAR(20) NOT NULL DEFAULT 'userpass' COMMENT 'Asterisk auth_type',
secret_mode VARCHAR(10) NOT NULL DEFAULT 'PLAIN' COMMENT 'PLAIN=库内口令 / ENV=环境变量',
secret_plain VARCHAR(128) NOT NULL DEFAULT 'psw.voip' COMMENT 'secret_mode=PLAIN 时的口令;空串表示未设置',
secret_ref VARCHAR(64) NULL COMMENT 'secret_mode=ENV 时的环境变量名',
secret_version INT NOT NULL DEFAULT 1 COMMENT '口令版本,轮换时 +1',
realm VARCHAR(64) NOT NULL DEFAULT 'asterisk' COMMENT 'Asterisk auth realm',
state VARCHAR(20) NOT NULL DEFAULT 'ACTIVE' COMMENT 'ACTIVE/DISABLED/ROTATING',
enabled TINYINT NOT NULL DEFAULT 1,
last_rotated_at DATETIME(3) NULL,
sync_state VARCHAR(20) NOT NULL DEFAULT 'PENDING' COMMENT 'PENDING/SYNCED/FAILED/DRIFT',
config_version BIGINT NOT NULL DEFAULT 1,
deleted TINYINT NOT NULL DEFAULT 0,
create_time DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
update_time DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_sipauth_gyid_auth_id (gyid, auth_id),
UNIQUE KEY uk_sipauth_instance_username (gyid, instance_code, username),
UNIQUE KEY uk_sipauth_instance_endpoint (gyid, instance_code, endpoint_id),
KEY idx_sipauth_gyid_state (gyid, instance_code, state, enabled),
KEY idx_sipauth_gyid_endpoint (gyid, endpoint_id),
CONSTRAINT ck_sipauth_secret_mode CHECK (secret_mode IN ('PLAIN','ENV')),
CONSTRAINT ck_sipauth_state CHECK (state IN ('ACTIVE','DISABLED','ROTATING')),
CONSTRAINT ck_sipauth_auth_type CHECK (auth_type IN ('userpass','md5','userpass,md5')),
CONSTRAINT ck_sipauth_secret_ref CHECK (secret_mode = 'PLAIN' OR secret_ref IS NOT NULL)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
COMMENT='SIP 认证账号(按公寓 + Asterisk 实例隔离;默认口令 psw.voip 仅限内网/联调)';
-- -----------------------------------------------------------------------------
-- 种子:为已登记分机补认证账号(每个公寓取第一个启用的实例;已存在则跳过)
-- -----------------------------------------------------------------------------
INSERT INTO apt_sip_auth
(gyid, instance_code, username, auth_id, endpoint_id, secret_plain)
SELECT e.gyid,
i.instance_code,
e.extension,
CONCAT('AUTH-', i.instance_code, '-', e.extension),
e.endpoint_id,
'psw.voip'
FROM apt_sip_endpoint e
JOIN apt_ast_instance i
ON i.id = (SELECT i2.id FROM apt_ast_instance i2
WHERE i2.gyid = e.gyid AND i2.deleted = 0 AND i2.enabled = 1
ORDER BY i2.id LIMIT 1)
LEFT JOIN apt_sip_auth a
ON a.gyid = e.gyid AND a.instance_code = i.instance_code AND a.username = e.extension
WHERE e.deleted = 0
AND a.id IS NULL;
-- -----------------------------------------------------------------------------
-- Asterisk realtime 视图:ps_auths / ps_endpoints(多实例按 gyid + instance_code 过滤)
-- ENV 模式在视图中不回显口令(password 为 NULL),由同步器渲染后再下发。
-- -----------------------------------------------------------------------------
CREATE OR REPLACE VIEW ps_auths AS
SELECT auth_id AS id,
auth_type,
username,
CASE WHEN secret_mode = 'PLAIN' THEN secret_plain END AS password,
realm
FROM apt_sip_auth
WHERE deleted = 0 AND enabled = 1 AND state = 'ACTIVE';
CREATE OR REPLACE VIEW ps_endpoints AS
SELECT e.endpoint_id AS id,
'transport-udp' AS transport,
a.aor_id AS aors,
au.auth_id AS auth,
'from-internal' AS context,
'ulaw' AS allow,
'all' AS disallow,
'no' AS direct_media,
e.extension AS callerid,
'yes' AS rewrite_contact
FROM apt_sip_endpoint e
JOIN apt_sip_aor a
ON a.gyid = e.gyid AND a.endpoint_id = e.endpoint_id AND a.deleted = 0
LEFT JOIN apt_sip_auth au
ON au.gyid = e.gyid AND au.endpoint_id = e.endpoint_id
AND au.deleted = 0 AND au.enabled = 1 AND au.state = 'ACTIVE'
WHERE e.deleted = 0 AND e.enabled = 1;
-- -----------------------------------------------------------------------------
-- 校验
-- -----------------------------------------------------------------------------
SELECT '=== 36. apt_sip_auth 列数(应 20)===' AS section;
SELECT COUNT(*) AS auth_columns
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'apt_sip_auth';
SELECT '=== 37. 唯一键(应 3:auth_id / instance+username / instance+endpoint)===' AS section;
SELECT INDEX_NAME, GROUP_CONCAT(COLUMN_NAME ORDER BY SEQ_IN_INDEX) AS cols
FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'apt_sip_auth' AND NON_UNIQUE = 0
GROUP BY INDEX_NAME;
SELECT '=== 38. 默认口令 ===' AS section;
SELECT COLUMN_NAME, COLUMN_DEFAULT
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'apt_sip_auth'
AND COLUMN_NAME IN ('secret_plain','secret_mode');
SELECT '=== 39. 现有认证账号(按实例分布)===' AS section;
SELECT gyid, instance_code, COUNT(*) AS accounts, SUM(secret_mode = 'PLAIN') AS plain_accounts
FROM apt_sip_auth
WHERE deleted = 0
GROUP BY gyid, instance_code;