功能更新
This commit is contained in:
310
init.sql
310
init.sql
@@ -1,17 +1,239 @@
|
||||
-- ============================================================
|
||||
-- nl-pms-api 初始化脚本(MySQL 5.7+ / 8.x)
|
||||
-- ============================================================
|
||||
-- nl-pms-api / code-count 共用库初始化脚本(MySQL 5.7+ / 8.x)
|
||||
-- 用法:mysql -u root -p < init.sql
|
||||
-- 合并自 view/init.sql(业务同步表)+ pms_files(文件元数据)。
|
||||
-- 生产环境服务启动不执行任何建表/迁移(DDL),部署前必须先执行本脚本;
|
||||
-- 仅 dev 环境(config.yaml 里 env: dev)启动时才 AutoMigrate。
|
||||
-- 与 view 同步服务共用 code_count 库(该库由 view/init.sql 创建,这里兜底建库)。
|
||||
-- 默认账号:liqi / qiqi991012(bcrypt 哈希存储,可在应用内注册新账号)
|
||||
-- ============================================================
|
||||
|
||||
CREATE DATABASE IF NOT EXISTS code_count DEFAULT CHARSET utf8mb4;
|
||||
USE code_count;
|
||||
|
||||
-- 应用账号(密码为 bcrypt 哈希)
|
||||
CREATE TABLE IF NOT EXISTS users(
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '用户ID',
|
||||
username VARCHAR(64) NOT NULL UNIQUE COMMENT '登录名',
|
||||
password_hash VARCHAR(100) NOT NULL COMMENT 'bcrypt 密码哈希',
|
||||
totp_secret VARCHAR(64) NOT NULL DEFAULT '' COMMENT 'TOTP Base32 密钥(仅管理员)',
|
||||
totp_enabled TINYINT NOT NULL DEFAULT 0 COMMENT '是否已启用 TOTP',
|
||||
last_login_ip VARCHAR(64) NOT NULL DEFAULT '' COMMENT '最近登录 IP',
|
||||
last_seen_at VARCHAR(32) NOT NULL DEFAULT '' COMMENT '最近活跃时间 RFC3339',
|
||||
ai_banned TINYINT NOT NULL DEFAULT 0 COMMENT '禁止使用 AI',
|
||||
disabled TINYINT NOT NULL DEFAULT 0 COMMENT '禁用账号',
|
||||
created_at VARCHAR(32) NOT NULL COMMENT '注册时间(RFC3339)'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='应用账号';
|
||||
|
||||
-- Todo 同步表(按 user_id 隔离,LWW 以 updated_at 判定)
|
||||
-- history 为生命周期轨迹 JSON:[{"status":"open","at":"..."},...],记录每次进入某状态的时间
|
||||
-- team_id>0 表示该条已共享给对应团队(团队管理员可见),0 为私密
|
||||
CREATE TABLE IF NOT EXISTS sync_todos(
|
||||
user_id BIGINT NOT NULL COMMENT '所属用户ID',
|
||||
uuid CHAR(36) NOT NULL COMMENT '客户端生成的全局唯一ID',
|
||||
title TEXT NOT NULL COMMENT '标题',
|
||||
content MEDIUMTEXT NOT NULL COMMENT '正文(Markdown,可含内嵌图片)',
|
||||
project_name VARCHAR(255) NOT NULL DEFAULT '' COMMENT '关联项目名(展示用)',
|
||||
due_at VARCHAR(32) NOT NULL DEFAULT '' COMMENT '截止时间',
|
||||
priority VARCHAR(16) NOT NULL DEFAULT 'medium' COMMENT '优先级:low/medium/high',
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'open' COMMENT '状态:open/doing/done/cancelled',
|
||||
history MEDIUMTEXT NOT NULL COMMENT '生命周期轨迹 JSON',
|
||||
team_id BIGINT NOT NULL DEFAULT 0 COMMENT '共享团队ID,0=私密',
|
||||
created_at VARCHAR(32) NOT NULL DEFAULT '' COMMENT '创建时间',
|
||||
updated_at VARCHAR(32) NOT NULL COMMENT '最后更新时间(LWW)',
|
||||
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '软删除标记:1=已删',
|
||||
PRIMARY KEY(user_id, uuid),
|
||||
KEY idx_sync_todos_updated(user_id, updated_at),
|
||||
KEY idx_sync_todos_team(team_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='待办同步表';
|
||||
|
||||
-- 工单同步表
|
||||
CREATE TABLE IF NOT EXISTS sync_tickets(
|
||||
user_id BIGINT NOT NULL COMMENT '所属用户ID',
|
||||
uuid CHAR(36) NOT NULL COMMENT '客户端生成的全局唯一ID',
|
||||
title TEXT NOT NULL COMMENT '标题',
|
||||
description MEDIUMTEXT NOT NULL COMMENT '描述(Markdown)',
|
||||
type VARCHAR(16) NOT NULL DEFAULT 'task' COMMENT '类型:task/bug/feature 等',
|
||||
project_name VARCHAR(255) NOT NULL DEFAULT '' COMMENT '关联项目名',
|
||||
start_at VARCHAR(32) NOT NULL DEFAULT '' COMMENT '开始时间',
|
||||
due_at VARCHAR(32) NOT NULL DEFAULT '' COMMENT '截止时间',
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'open' COMMENT '状态',
|
||||
priority VARCHAR(16) NOT NULL DEFAULT 'medium' COMMENT '优先级',
|
||||
history MEDIUMTEXT NOT NULL COMMENT '生命周期轨迹 JSON',
|
||||
team_id BIGINT NOT NULL DEFAULT 0 COMMENT '共享团队ID,0=私密',
|
||||
created_at VARCHAR(32) NOT NULL DEFAULT '' COMMENT '创建时间',
|
||||
updated_at VARCHAR(32) NOT NULL COMMENT '最后更新时间(LWW)',
|
||||
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '软删除标记',
|
||||
PRIMARY KEY(user_id, uuid),
|
||||
KEY idx_sync_tickets_updated(user_id, updated_at),
|
||||
KEY idx_sync_tickets_team(team_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='工单同步表';
|
||||
|
||||
-- 记事本同步表
|
||||
CREATE TABLE IF NOT EXISTS sync_notes(
|
||||
user_id BIGINT NOT NULL COMMENT '所属用户ID',
|
||||
uuid CHAR(36) NOT NULL COMMENT '客户端生成的全局唯一ID',
|
||||
content MEDIUMTEXT NOT NULL COMMENT '记事本正文',
|
||||
updated_at VARCHAR(32) NOT NULL COMMENT '最后更新时间(LWW)',
|
||||
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '软删除标记',
|
||||
PRIMARY KEY(user_id, uuid),
|
||||
KEY idx_sync_notes_updated(user_id, updated_at)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='记事本同步表';
|
||||
|
||||
-- 按用户存储的同步设置(如加密盐 enc_salt、加密后的 AI API Key api_keys)。
|
||||
-- api_keys 的值为 AES-256-GCM 密文(密钥由登录密码派生),服务器无法解密。
|
||||
-- 另有全局资源行:日历节日背景图存为 fest_img:<节日名>,统一挂在管理员账号
|
||||
-- (id=1)名下 —— 管理员在应用内上传推送,所有账号登录后拉取展示。
|
||||
CREATE TABLE IF NOT EXISTS sync_settings(
|
||||
user_id BIGINT NOT NULL COMMENT '所属用户ID(全局资源挂在管理员 id=1)',
|
||||
name VARCHAR(64) NOT NULL COMMENT '设置键名',
|
||||
value MEDIUMTEXT NOT NULL COMMENT '设置值(明文或密文)',
|
||||
updated_at VARCHAR(32) NOT NULL COMMENT '最后更新时间',
|
||||
PRIMARY KEY(user_id, name)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户同步设置键值表';
|
||||
|
||||
-- 旧版本升级:早期脚本建的 content/description 为 TEXT(64KB),
|
||||
-- 待办/工单内容支持 Markdown 内嵌图片后需要 MEDIUMTEXT;重复执行无副作用。
|
||||
ALTER TABLE sync_todos MODIFY content MEDIUMTEXT NOT NULL COMMENT '正文(Markdown,可含内嵌图片)';
|
||||
ALTER TABLE sync_tickets MODIFY description MEDIUMTEXT NOT NULL COMMENT '描述(Markdown)';
|
||||
|
||||
-- 旧版本升级:补 history 生命周期列(不存在时才添加,重复执行无副作用)。
|
||||
SET @sql = IF((SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA='code_count' AND TABLE_NAME='sync_todos' AND COLUMN_NAME='history')=0,
|
||||
'ALTER TABLE sync_todos ADD COLUMN history MEDIUMTEXT NOT NULL COMMENT ''生命周期轨迹 JSON'' AFTER status', 'SELECT 1');
|
||||
PREPARE st FROM @sql; EXECUTE st; DEALLOCATE PREPARE st;
|
||||
SET @sql = IF((SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA='code_count' AND TABLE_NAME='sync_tickets' AND COLUMN_NAME='history')=0,
|
||||
'ALTER TABLE sync_tickets ADD COLUMN history MEDIUMTEXT NOT NULL COMMENT ''生命周期轨迹 JSON'' AFTER priority', 'SELECT 1');
|
||||
PREPARE st FROM @sql; EXECUTE st; DEALLOCATE PREPARE st;
|
||||
|
||||
-- ============================================================
|
||||
-- 团队协作(v2.1):资料 / 团队 / 团队任务 / 日报 / 通知
|
||||
-- ============================================================
|
||||
|
||||
-- 用户公开资料(同服务器用户互相可见:昵称/头衔/技术栈标签/头像缩略图)
|
||||
CREATE TABLE IF NOT EXISTS user_profiles(
|
||||
user_id BIGINT PRIMARY KEY COMMENT '对应用户ID',
|
||||
nickname VARCHAR(64) NOT NULL DEFAULT '' COMMENT '昵称',
|
||||
title VARCHAR(64) NOT NULL DEFAULT '' COMMENT '头衔/职位',
|
||||
email VARCHAR(128) NOT NULL DEFAULT '' COMMENT '公开邮箱',
|
||||
bio VARCHAR(500) NOT NULL DEFAULT '' COMMENT '个人简介',
|
||||
tech_tags VARCHAR(1000) NOT NULL DEFAULT '[]' COMMENT '技术栈标签 JSON 数组',
|
||||
avatar_thumb MEDIUMTEXT NOT NULL COMMENT '头像缩略图(dataURL 或 URL)',
|
||||
updated_at VARCHAR(32) NOT NULL COMMENT '最后更新时间'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户公开资料';
|
||||
|
||||
-- 用户头像历史(与账号关联,多端可回选;每人保留最近若干条)
|
||||
CREATE TABLE IF NOT EXISTS user_avatar_history(
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '历史记录ID',
|
||||
user_id BIGINT NOT NULL COMMENT '所属用户ID',
|
||||
mode VARCHAR(16) NOT NULL DEFAULT 'url' COMMENT '存储模式:url | base64',
|
||||
value MEDIUMTEXT NOT NULL COMMENT '头像内容(URL 或 dataURL)',
|
||||
created_at VARCHAR(32) NOT NULL COMMENT '写入时间(RFC3339)',
|
||||
KEY idx_uah_user_id(user_id, id DESC)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户头像历史';
|
||||
|
||||
-- 团队(digest_time:日报 AI 摘要自动生成时间 HH:MM)
|
||||
CREATE TABLE IF NOT EXISTS teams(
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '团队ID',
|
||||
name VARCHAR(64) NOT NULL COMMENT '团队名称',
|
||||
owner_id BIGINT NOT NULL COMMENT '创建者用户ID',
|
||||
digest_time VARCHAR(8) NOT NULL DEFAULT '21:00' COMMENT '日报 AI 摘要自动生成时间 HH:MM',
|
||||
ai_banned TINYINT NOT NULL DEFAULT 0 COMMENT '禁止团队使用 AI',
|
||||
created_at VARCHAR(32) NOT NULL COMMENT '创建时间'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='团队';
|
||||
|
||||
-- 团队成员(role: owner | admin | member)
|
||||
CREATE TABLE IF NOT EXISTS team_members(
|
||||
team_id BIGINT NOT NULL COMMENT '团队ID',
|
||||
user_id BIGINT NOT NULL COMMENT '成员用户ID',
|
||||
role VARCHAR(16) NOT NULL DEFAULT 'member' COMMENT '角色:owner/admin/member',
|
||||
joined_at VARCHAR(32) NOT NULL COMMENT '加入时间',
|
||||
PRIMARY KEY(team_id, user_id),
|
||||
KEY idx_team_members_user(user_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='团队成员';
|
||||
|
||||
-- 团队任务/工单(服务器唯一真相,在线操作;kind: todo | ticket)
|
||||
CREATE TABLE IF NOT EXISTS team_tasks(
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '任务ID',
|
||||
team_id BIGINT NOT NULL COMMENT '所属团队ID',
|
||||
kind VARCHAR(16) NOT NULL DEFAULT 'todo' COMMENT '种类:todo/ticket',
|
||||
title TEXT NOT NULL COMMENT '标题',
|
||||
description MEDIUMTEXT NOT NULL COMMENT '描述',
|
||||
priority VARCHAR(16) NOT NULL DEFAULT 'medium' COMMENT '优先级',
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'open' COMMENT '状态',
|
||||
creator_id BIGINT NOT NULL COMMENT '创建者用户ID',
|
||||
assignee_id BIGINT NOT NULL DEFAULT 0 COMMENT '指派人用户ID,0=未指派',
|
||||
start_at VARCHAR(32) NOT NULL DEFAULT '' COMMENT '开始时间',
|
||||
due_at VARCHAR(32) NOT NULL DEFAULT '' COMMENT '截止时间',
|
||||
urged_at VARCHAR(32) NOT NULL DEFAULT '' COMMENT '最近催办时间',
|
||||
history MEDIUMTEXT NOT NULL COMMENT '生命周期轨迹 JSON',
|
||||
updated_at VARCHAR(32) NOT NULL COMMENT '最后更新时间',
|
||||
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '软删除标记',
|
||||
KEY idx_team_tasks_team(team_id, deleted),
|
||||
KEY idx_team_tasks_assignee(assignee_id, deleted)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='团队任务/工单';
|
||||
|
||||
-- 团队日报(date 为本地日期 YYYY-MM-DD)
|
||||
CREATE TABLE IF NOT EXISTS team_reports(
|
||||
team_id BIGINT NOT NULL COMMENT '团队ID',
|
||||
user_id BIGINT NOT NULL COMMENT '提交人用户ID',
|
||||
date CHAR(10) NOT NULL COMMENT '日报日期 YYYY-MM-DD',
|
||||
content MEDIUMTEXT NOT NULL COMMENT '日报正文(Markdown)',
|
||||
submitted_at VARCHAR(32) NOT NULL COMMENT '提交/更新时间',
|
||||
PRIMARY KEY(team_id, user_id, date)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='团队日报';
|
||||
|
||||
-- 团队日报 AI 摘要(管理员客户端生成,全员可见)
|
||||
CREATE TABLE IF NOT EXISTS team_digests(
|
||||
team_id BIGINT NOT NULL COMMENT '团队ID',
|
||||
date CHAR(10) NOT NULL COMMENT '摘要对应日期 YYYY-MM-DD',
|
||||
content MEDIUMTEXT NOT NULL COMMENT 'AI 摘要正文(Markdown)',
|
||||
provider VARCHAR(32) NOT NULL DEFAULT '' COMMENT '生成所用 AI 提供商',
|
||||
generated_at VARCHAR(32) NOT NULL COMMENT '生成时间',
|
||||
PRIMARY KEY(team_id, date)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='团队日报 AI 摘要';
|
||||
|
||||
-- 团队通知(指派/催办/成员变动等;客户端同步时按 to_user 增量拉取转本地消息)
|
||||
CREATE TABLE IF NOT EXISTS team_notices(
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '通知ID',
|
||||
team_id BIGINT NOT NULL COMMENT '团队ID',
|
||||
to_user BIGINT NOT NULL COMMENT '接收用户ID',
|
||||
from_user BIGINT NOT NULL COMMENT '发送用户ID',
|
||||
kind VARCHAR(16) NOT NULL COMMENT '通知类型',
|
||||
ref_id VARCHAR(64) NOT NULL DEFAULT '' COMMENT '关联对象ID',
|
||||
content TEXT NOT NULL COMMENT '通知正文',
|
||||
created_at VARCHAR(32) NOT NULL COMMENT '创建时间',
|
||||
KEY idx_team_notices_to(to_user, id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='团队通知';
|
||||
|
||||
-- 旧版本升级:个人待办/工单支持按条共享到团队(team_id=0 私密;重复执行无副作用)。
|
||||
SET @sql = IF((SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA='code_count' AND TABLE_NAME='sync_todos' AND COLUMN_NAME='team_id')=0,
|
||||
'ALTER TABLE sync_todos ADD COLUMN team_id BIGINT NOT NULL DEFAULT 0 COMMENT ''共享团队ID,0=私密'' AFTER history', 'SELECT 1');
|
||||
PREPARE st FROM @sql; EXECUTE st; DEALLOCATE PREPARE st;
|
||||
SET @sql = IF((SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA='code_count' AND TABLE_NAME='sync_tickets' AND COLUMN_NAME='team_id')=0,
|
||||
'ALTER TABLE sync_tickets ADD COLUMN team_id BIGINT NOT NULL DEFAULT 0 COMMENT ''共享团队ID,0=私密'' AFTER history', 'SELECT 1');
|
||||
PREPARE st FROM @sql; EXECUTE st; DEALLOCATE PREPARE st;
|
||||
SET @sql = IF((SELECT COUNT(*) FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA='code_count' AND TABLE_NAME='sync_todos' AND INDEX_NAME='idx_sync_todos_team')=0,
|
||||
'ALTER TABLE sync_todos ADD KEY idx_sync_todos_team(team_id)', 'SELECT 1');
|
||||
PREPARE st FROM @sql; EXECUTE st; DEALLOCATE PREPARE st;
|
||||
SET @sql = IF((SELECT COUNT(*) FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA='code_count' AND TABLE_NAME='sync_tickets' AND INDEX_NAME='idx_sync_tickets_team')=0,
|
||||
'ALTER TABLE sync_tickets ADD KEY idx_sync_tickets_team(team_id)', 'SELECT 1');
|
||||
PREPARE st FROM @sql; EXECUTE st; DEALLOCATE PREPARE st;
|
||||
|
||||
-- 默认账号 liqi(密码 qiqi991012 的 bcrypt 哈希);已存在则跳过,不会覆盖改过的密码
|
||||
INSERT IGNORE INTO users(username, password_hash, created_at)
|
||||
VALUES ('liqi', '$2a$10$XWBtGPu9xYRyr8diFEfvBeEHRkO6pa3CDduE09OurJztuXVcJZOB2', '2026-08-11T13:20:00Z');
|
||||
|
||||
-- ============================================================
|
||||
-- 文件存储元数据(nl-pms-api)
|
||||
-- ============================================================
|
||||
|
||||
-- 已上传文件元数据(文件内容落盘在 storage_dir,本表只存相对路径与摘要)
|
||||
-- 归属按 (user_id, team_id) 记录:素材库据此划定"本人 / 团队管理员 / 超管 id=1"的管理范围。
|
||||
-- 索引名与 gorm AutoMigrate 的默认命名保持一致,避免 dev/prod 两套 schema 漂移。
|
||||
CREATE TABLE IF NOT EXISTS pms_files(
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
name VARCHAR(191) NOT NULL, -- 存储相对路径,如 2026/08/13/<随机hex>.jpg
|
||||
@@ -19,7 +241,7 @@ CREATE TABLE IF NOT EXISTS pms_files(
|
||||
mime VARCHAR(64) NOT NULL DEFAULT '',
|
||||
size BIGINT NOT NULL DEFAULT 0,
|
||||
sha256 CHAR(64) NOT NULL DEFAULT '', -- 内容摘要,同归属秒传去重
|
||||
user_id BIGINT NOT NULL DEFAULT 0, -- 上传者(code_count 库 users 表 id,0 为未知)
|
||||
user_id BIGINT NOT NULL DEFAULT 0, -- 上传者(users 表 id,0 为未知)
|
||||
team_id BIGINT NOT NULL DEFAULT 0, -- 上传时所在团队(teams 表 id,0 为个人)
|
||||
kind VARCHAR(16) NOT NULL DEFAULT '', -- avatar | content
|
||||
created_at VARCHAR(32) NOT NULL DEFAULT '',
|
||||
@@ -27,7 +249,7 @@ CREATE TABLE IF NOT EXISTS pms_files(
|
||||
KEY idx_pms_files_sha256(sha256),
|
||||
KEY idx_pms_files_user_id(user_id),
|
||||
KEY idx_pms_files_team_id(team_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='已上传文件元数据';
|
||||
|
||||
-- 旧版本升级:补 team_id 归属列与 user_id/team_id 索引(不存在才添加,重复执行无副作用)。
|
||||
SET @sql = IF((SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
@@ -42,3 +264,79 @@ SET @sql = IF((SELECT COUNT(*) FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA='code_count' AND TABLE_NAME='pms_files' AND INDEX_NAME='idx_pms_files_team_id')=0,
|
||||
'ALTER TABLE pms_files ADD KEY idx_pms_files_team_id(team_id)', 'SELECT 1');
|
||||
PREPARE st FROM @sql; EXECUTE st; DEALLOCATE PREPARE st;
|
||||
|
||||
-- ============================================================
|
||||
-- 管理员后台 / TOTP / 日活 / AI 用量 / 发版(v2.2)
|
||||
-- ============================================================
|
||||
|
||||
SET @sql = IF((SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA='code_count' AND TABLE_NAME='users' AND COLUMN_NAME='totp_secret')=0,
|
||||
'ALTER TABLE users ADD COLUMN totp_secret VARCHAR(64) NOT NULL DEFAULT '''' COMMENT ''TOTP Base32 密钥(仅管理员)'' AFTER password_hash', 'SELECT 1');
|
||||
PREPARE st FROM @sql; EXECUTE st; DEALLOCATE PREPARE st;
|
||||
SET @sql = IF((SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA='code_count' AND TABLE_NAME='users' AND COLUMN_NAME='totp_enabled')=0,
|
||||
'ALTER TABLE users ADD COLUMN totp_enabled TINYINT NOT NULL DEFAULT 0 COMMENT ''是否已启用 TOTP'' AFTER totp_secret', 'SELECT 1');
|
||||
PREPARE st FROM @sql; EXECUTE st; DEALLOCATE PREPARE st;
|
||||
SET @sql = IF((SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA='code_count' AND TABLE_NAME='users' AND COLUMN_NAME='last_login_ip')=0,
|
||||
'ALTER TABLE users ADD COLUMN last_login_ip VARCHAR(64) NOT NULL DEFAULT '''' COMMENT ''最近登录 IP'' AFTER totp_enabled', 'SELECT 1');
|
||||
PREPARE st FROM @sql; EXECUTE st; DEALLOCATE PREPARE st;
|
||||
SET @sql = IF((SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA='code_count' AND TABLE_NAME='users' AND COLUMN_NAME='last_seen_at')=0,
|
||||
'ALTER TABLE users ADD COLUMN last_seen_at VARCHAR(32) NOT NULL DEFAULT '''' COMMENT ''最近活跃时间 RFC3339'' AFTER last_login_ip', 'SELECT 1');
|
||||
PREPARE st FROM @sql; EXECUTE st; DEALLOCATE PREPARE st;
|
||||
SET @sql = IF((SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA='code_count' AND TABLE_NAME='users' AND COLUMN_NAME='ai_banned')=0,
|
||||
'ALTER TABLE users ADD COLUMN ai_banned TINYINT NOT NULL DEFAULT 0 COMMENT ''禁止使用 AI'' AFTER last_seen_at', 'SELECT 1');
|
||||
PREPARE st FROM @sql; EXECUTE st; DEALLOCATE PREPARE st;
|
||||
SET @sql = IF((SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA='code_count' AND TABLE_NAME='users' AND COLUMN_NAME='disabled')=0,
|
||||
'ALTER TABLE users ADD COLUMN disabled TINYINT NOT NULL DEFAULT 0 COMMENT ''禁用账号'' AFTER ai_banned', 'SELECT 1');
|
||||
PREPARE st FROM @sql; EXECUTE st; DEALLOCATE PREPARE st;
|
||||
SET @sql = IF((SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA='code_count' AND TABLE_NAME='teams' AND COLUMN_NAME='ai_banned')=0,
|
||||
'ALTER TABLE teams ADD COLUMN ai_banned TINYINT NOT NULL DEFAULT 0 COMMENT ''禁止团队使用 AI'' AFTER digest_time', 'SELECT 1');
|
||||
PREPARE st FROM @sql; EXECUTE st; DEALLOCATE PREPARE st;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS user_daily_active(
|
||||
user_id BIGINT NOT NULL COMMENT '用户ID',
|
||||
active_date CHAR(10) NOT NULL COMMENT '活跃日期 YYYY-MM-DD',
|
||||
last_ip VARCHAR(64) NOT NULL DEFAULT '' COMMENT '当日最近 IP',
|
||||
last_seen_at VARCHAR(32) NOT NULL DEFAULT '' COMMENT '当日最近活跃时间',
|
||||
PRIMARY KEY(user_id, active_date),
|
||||
KEY idx_uda_date(active_date)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户日活';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ai_usage_daily(
|
||||
user_id BIGINT NOT NULL COMMENT '用户ID',
|
||||
team_id BIGINT NOT NULL DEFAULT 0 COMMENT '团队ID,0=个人',
|
||||
usage_date CHAR(10) NOT NULL COMMENT '用量日期 YYYY-MM-DD',
|
||||
provider VARCHAR(32) NOT NULL DEFAULT '' COMMENT 'AI 提供商',
|
||||
prompt_tokens BIGINT NOT NULL DEFAULT 0 COMMENT '提示 token',
|
||||
completion_tokens BIGINT NOT NULL DEFAULT 0 COMMENT '补全 token',
|
||||
calls INT NOT NULL DEFAULT 0 COMMENT '调用次数',
|
||||
estimated TINYINT NOT NULL DEFAULT 0 COMMENT '是否含估算数据',
|
||||
PRIMARY KEY(user_id, team_id, usage_date, provider),
|
||||
KEY idx_aud_date(usage_date)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='AI Token 日汇总';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS app_releases(
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '发版ID',
|
||||
version VARCHAR(32) NOT NULL COMMENT '版本号 semver',
|
||||
channel VARCHAR(16) NOT NULL DEFAULT 'stable' COMMENT '渠道',
|
||||
filename VARCHAR(255) NOT NULL COMMENT '存储相对路径',
|
||||
sha256 CHAR(64) NOT NULL DEFAULT '' COMMENT '文件 SHA256',
|
||||
size_bytes BIGINT NOT NULL DEFAULT 0 COMMENT '字节大小',
|
||||
changelog MEDIUMTEXT NOT NULL COMMENT '更新说明',
|
||||
created_at VARCHAR(32) NOT NULL COMMENT '上传时间',
|
||||
is_latest TINYINT NOT NULL DEFAULT 0 COMMENT '是否当前最新',
|
||||
UNIQUE KEY idx_app_releases_ver_ch(version, channel),
|
||||
KEY idx_app_releases_latest(channel, is_latest)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='客户端发版';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS admin_stepup(
|
||||
user_id BIGINT PRIMARY KEY COMMENT '管理员用户ID',
|
||||
token_hash CHAR(64) NOT NULL COMMENT 'stepup token SHA256',
|
||||
client_ip VARCHAR(64) NOT NULL DEFAULT '' COMMENT '签发时 IP',
|
||||
expires_at VARCHAR(32) NOT NULL COMMENT '过期时间 RFC3339'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='管理员敏感操作二次验证';
|
||||
|
||||
Reference in New Issue
Block a user