137 lines
8.1 KiB
SQL
137 lines
8.1 KiB
SQL
SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||
|
||
-- 外管账号与主后台账号、App 用户账号完全隔离;所有唯一键都带 app_code,避免 Fami/Lalu 同名账号互相占用。
|
||
-- 本迁移只创建小型认证/审计表并按唯一索引写权限元数据,不扫描 users、房间或钱包等大业务表。
|
||
SET @now_ms = CAST(UNIX_TIMESTAMP(UTC_TIMESTAMP(3)) * 1000 AS UNSIGNED);
|
||
|
||
CREATE TABLE IF NOT EXISTS external_admin_accounts (
|
||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '外管账号 ID',
|
||
app_code VARCHAR(32) NOT NULL COMMENT '固定 App 租户',
|
||
linked_app_user_id BIGINT NOT NULL COMMENT '绑定的 App 内部用户 ID',
|
||
username VARCHAR(64) NOT NULL COMMENT '外管登录账号,服务端统一转小写',
|
||
password_hash VARCHAR(255) NOT NULL COMMENT 'bcrypt 密码摘要',
|
||
permissions_json JSON NOT NULL COMMENT '允许进入外管白名单路由的权限快照',
|
||
status VARCHAR(24) NOT NULL DEFAULT 'active' COMMENT 'active/disabled',
|
||
password_change_required TINYINT(1) NOT NULL DEFAULT 1 COMMENT '管理员创建或重置后要求首次改密',
|
||
failed_login_count INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '连续登录失败次数',
|
||
locked_until_ms BIGINT NOT NULL DEFAULT 0 COMMENT '失败锁定截止时间,UTC epoch ms',
|
||
last_login_at_ms BIGINT NULL COMMENT '最近成功登录时间,UTC epoch ms',
|
||
created_by_admin_id BIGINT UNSIGNED NOT NULL COMMENT '创建该账号的主后台管理员 ID',
|
||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||
updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_external_admin_accounts_app_username (app_code, username),
|
||
UNIQUE KEY uk_external_admin_accounts_app_linked_user (app_code, linked_app_user_id),
|
||
KEY idx_external_admin_accounts_app_status_updated (app_code, status, updated_at_ms),
|
||
KEY idx_external_admin_accounts_creator (created_by_admin_id)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='独立外管账号';
|
||
|
||
CREATE TABLE IF NOT EXISTS external_admin_sessions (
|
||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '会话 ID',
|
||
account_id BIGINT UNSIGNED NOT NULL COMMENT '外管账号 ID',
|
||
app_code VARCHAR(32) NOT NULL COMMENT '创建时固化的 App 租户',
|
||
token_hash CHAR(64) CHARACTER SET ascii COLLATE ascii_bin NOT NULL COMMENT 'opaque session token SHA-256',
|
||
csrf_token_hash CHAR(64) CHARACTER SET ascii COLLATE ascii_bin NOT NULL COMMENT 'CSRF token SHA-256',
|
||
ip VARCHAR(64) NOT NULL DEFAULT '' COMMENT '登录 IP',
|
||
user_agent VARCHAR(512) NOT NULL DEFAULT '' COMMENT '登录客户端',
|
||
expires_at_ms BIGINT NOT NULL COMMENT '过期时间,UTC epoch ms',
|
||
last_seen_at_ms BIGINT NOT NULL COMMENT '最近访问时间,UTC epoch ms',
|
||
revoked_at_ms BIGINT NOT NULL DEFAULT 0 COMMENT '撤销时间,0 表示有效',
|
||
revoke_reason VARCHAR(64) NOT NULL DEFAULT '' COMMENT '撤销原因',
|
||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_external_admin_sessions_token_hash (token_hash),
|
||
KEY idx_external_admin_sessions_account_active (account_id, revoked_at_ms, expires_at_ms),
|
||
KEY idx_external_admin_sessions_app_expiry (app_code, expires_at_ms),
|
||
CONSTRAINT fk_external_admin_sessions_account FOREIGN KEY (account_id) REFERENCES external_admin_accounts(id)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='外管服务端 opaque 会话';
|
||
|
||
CREATE TABLE IF NOT EXISTS external_admin_login_logs (
|
||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '登录日志 ID',
|
||
account_id BIGINT UNSIGNED NULL COMMENT '命中账号时记录;未知账号保持 NULL 防止伪造关联',
|
||
app_code VARCHAR(32) NOT NULL COMMENT '请求 App',
|
||
username VARCHAR(64) NOT NULL COMMENT '请求账号',
|
||
ip VARCHAR(64) NOT NULL DEFAULT '' COMMENT '请求 IP',
|
||
user_agent VARCHAR(512) NOT NULL DEFAULT '' COMMENT '请求客户端',
|
||
status VARCHAR(24) NOT NULL COMMENT 'success/failed',
|
||
message VARCHAR(128) NOT NULL DEFAULT '' COMMENT '内部审计原因,不回传客户端',
|
||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||
PRIMARY KEY (id),
|
||
KEY idx_external_admin_login_logs_app_username_time (app_code, username, created_at_ms),
|
||
KEY idx_external_admin_login_logs_account_time (account_id, created_at_ms),
|
||
KEY idx_external_admin_login_logs_status_time (status, created_at_ms)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='外管登录日志';
|
||
|
||
CREATE TABLE IF NOT EXISTS external_admin_operation_logs (
|
||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '操作日志 ID',
|
||
account_id BIGINT UNSIGNED NOT NULL COMMENT '外管账号 ID',
|
||
app_code VARCHAR(32) NOT NULL COMMENT '会话固定 App',
|
||
username VARCHAR(64) NOT NULL COMMENT '外管账号',
|
||
request_id VARCHAR(64) NOT NULL DEFAULT '' COMMENT '请求链路 ID',
|
||
action VARCHAR(160) NOT NULL COMMENT 'HTTP 方法与路由模板',
|
||
resource VARCHAR(120) NOT NULL DEFAULT '' COMMENT '资源类型',
|
||
resource_id VARCHAR(128) NOT NULL DEFAULT '' COMMENT '目标资源 ID',
|
||
method VARCHAR(12) NOT NULL COMMENT 'HTTP 方法',
|
||
path VARCHAR(255) NOT NULL COMMENT '实际请求路径',
|
||
ip VARCHAR(64) NOT NULL DEFAULT '' COMMENT '请求 IP',
|
||
user_agent VARCHAR(512) NOT NULL DEFAULT '' COMMENT '请求客户端',
|
||
status VARCHAR(24) NOT NULL COMMENT 'success/failed',
|
||
http_status INT NOT NULL COMMENT 'HTTP 状态码',
|
||
latency_ms BIGINT NOT NULL DEFAULT 0 COMMENT '处理耗时',
|
||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||
PRIMARY KEY (id),
|
||
KEY idx_external_admin_operation_logs_account_time (account_id, created_at_ms),
|
||
KEY idx_external_admin_operation_logs_app_time (app_code, created_at_ms),
|
||
KEY idx_external_admin_operation_logs_request (request_id)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='外管业务操作审计';
|
||
|
||
INSERT INTO admin_permissions (name, code, kind, description, created_at_ms, updated_at_ms) VALUES
|
||
('外管用户查看', 'external-admin-user:view', 'menu', '查看当前 App 的外管用户', @now_ms, @now_ms),
|
||
('外管用户创建', 'external-admin-user:create', 'button', '按当前 App 用户短 ID 创建外管账号', @now_ms, @now_ms),
|
||
('外管用户状态', 'external-admin-user:status', 'button', '启用或停用当前 App 的外管账号', @now_ms, @now_ms),
|
||
('外管用户重置密码', 'external-admin-user:reset-password', 'button', '重置当前 App 的外管账号密码并撤销全部会话', @now_ms, @now_ms)
|
||
ON DUPLICATE KEY UPDATE
|
||
name = VALUES(name),
|
||
kind = VALUES(kind),
|
||
description = VALUES(description),
|
||
updated_at_ms = @now_ms;
|
||
|
||
INSERT INTO admin_menus (parent_id, title, code, path, icon, permission_code, sort, visible, created_at_ms, updated_at_ms)
|
||
SELECT parent.id, '外管用户', 'operation-external-admin-users', '/operations/external-admin-users', 'shield', 'external-admin-user:view', 72, TRUE, @now_ms, @now_ms
|
||
FROM admin_menus parent
|
||
WHERE parent.code = 'operations'
|
||
ON DUPLICATE KEY UPDATE
|
||
parent_id = VALUES(parent_id),
|
||
title = VALUES(title),
|
||
path = VALUES(path),
|
||
icon = VALUES(icon),
|
||
permission_code = VALUES(permission_code),
|
||
sort = VALUES(sort),
|
||
visible = VALUES(visible),
|
||
updated_at_ms = @now_ms;
|
||
|
||
-- 创建或重置密码等同于签发/接管固定的全能力外管账号,只允许平台管理员执行,
|
||
-- 避免运营角色通过委派获得自身权限矩阵中不存在的 VIP 发放等高风险能力。
|
||
INSERT IGNORE INTO admin_role_permissions (role_id, permission_id)
|
||
SELECT role.id, permission.id
|
||
FROM admin_roles role
|
||
JOIN admin_permissions permission
|
||
WHERE role.code = 'platform-admin'
|
||
AND permission.code IN (
|
||
'external-admin-user:view',
|
||
'external-admin-user:create',
|
||
'external-admin-user:status',
|
||
'external-admin-user:reset-password'
|
||
);
|
||
|
||
-- 运营负责人和运营专员负责日常查看、启用和停用,不持有账号凭据签发能力。
|
||
INSERT IGNORE INTO admin_role_permissions (role_id, permission_id)
|
||
SELECT role.id, permission.id
|
||
FROM admin_roles role
|
||
JOIN admin_permissions permission
|
||
WHERE role.code IN ('ops-admin', 'operations-specialist')
|
||
AND permission.code IN (
|
||
'external-admin-user:view',
|
||
'external-admin-user:status'
|
||
);
|