@@ -0,0 +1,16 @@
|
||||
-- ============================================================
|
||||
-- notice 表补 sch_id 列(多租户隔离)
|
||||
-- 决策 D3:发布时从 BoaUser.currentRole.schId 填充,查询时按校过滤
|
||||
--
|
||||
-- 对应实体:Notice.schId / NoticeRepository 多租户统计方法
|
||||
--
|
||||
-- 执行方式:由 DBA 手动在对应环境执行,AI 不代跑
|
||||
-- MySQL 5.7+ 兼容
|
||||
-- ============================================================
|
||||
|
||||
-- 1) 加列
|
||||
ALTER TABLE `notice`
|
||||
ADD COLUMN `sch_id` INT NULL COMMENT '学校ID(多租户隔离:发布时从当前用户角色填充)';
|
||||
|
||||
-- 2) 加索引(用于按学校过滤查询)
|
||||
CREATE INDEX `idx_notice_sch_id` ON `notice` (`sch_id`);
|
||||
@@ -0,0 +1,159 @@
|
||||
-- ============================================================
|
||||
-- 存量数据回填脚本
|
||||
-- 1. notice.sch_id:按发布人所属学校回填(从 sys_user_role_rel 取 main=1 的 sch_id)
|
||||
-- 2. notice_receiver:从 notice.receive_list JSON 字符串展开为关联表行
|
||||
--
|
||||
-- 幂等设计:可重复执行,已存在的数据不会重复插入
|
||||
-- 执行方式:由 DBA 手动在对应环境执行,AI 不代跑
|
||||
-- MySQL 5.7+ 兼容
|
||||
-- ============================================================
|
||||
|
||||
-- ============================================================
|
||||
-- Part 1:回填 notice.sch_id
|
||||
-- ============================================================
|
||||
|
||||
-- 方式一:如果 base_user / sys_user 表有 sch_id 字段,直接关联回填
|
||||
-- 注意:需根据实际平台库表结构调整关联条件
|
||||
UPDATE `notice` n
|
||||
INNER JOIN (
|
||||
SELECT DISTINCT u.login_name, u.sch_id
|
||||
FROM base_user u
|
||||
WHERE u.sch_id IS NOT NULL
|
||||
) t ON n.publisher_account = t.login_name
|
||||
SET n.sch_id = t.sch_id
|
||||
WHERE n.sch_id IS NULL
|
||||
AND n.publisher_account IS NOT NULL
|
||||
AND n.publisher_account != '';
|
||||
|
||||
-- 方式二(备用):如果用户学校关系在 sys_user_role_rel 表
|
||||
-- 取每个用户的主身份学校(is_main=1),回填 sch_id
|
||||
-- 取消注释后执行(视实际库表结构选用)
|
||||
--
|
||||
-- UPDATE `notice` n
|
||||
-- INNER JOIN (
|
||||
-- SELECT r.account, r.sch_id
|
||||
-- FROM sys_user_role_rel r
|
||||
-- WHERE r.is_main = 1
|
||||
-- AND r.sch_id IS NOT NULL
|
||||
-- ) t ON n.publisher_account = t.account
|
||||
-- SET n.sch_id = t.sch_id
|
||||
-- WHERE n.sch_id IS NULL
|
||||
-- AND n.publisher_account IS NOT NULL
|
||||
-- AND n.publisher_account != '';
|
||||
|
||||
|
||||
-- ============================================================
|
||||
-- Part 2:从 notice.receive_list 展开到 notice_receiver 关联表
|
||||
-- ============================================================
|
||||
|
||||
-- 说明:
|
||||
-- - notice.receive_list 存储格式有两种:
|
||||
-- 1) "ALL"(全员通知)—— 不写明细行,跳过
|
||||
-- 2) JSON 数组 ["account1","account2",...] 或逗号分隔串
|
||||
-- - 本脚本使用 MySQL JSON 函数解析(MySQL 5.7+ 支持 JSON_EXTRACT)
|
||||
-- - 对于非标准 JSON 格式(逗号分隔串),使用循环逐条插入
|
||||
-- - 幂等:每次插入前先删除该通知的旧接收人记录
|
||||
|
||||
-- 注意:MySQL 5.7 的 JSON_TABLE 不可用,这里用存储过程逐条解析
|
||||
-- 如果 receive_list 是标准 JSON 数组格式,可以直接用 JSON_EXTRACT
|
||||
|
||||
DELIMITER $$
|
||||
|
||||
-- 存储过程:展开单个通知的接收人列表
|
||||
DROP PROCEDURE IF EXISTS `expand_notice_receivers`$$
|
||||
CREATE PROCEDURE `expand_notice_receivers`()
|
||||
BEGIN
|
||||
DECLARE v_done INT DEFAULT 0;
|
||||
DECLARE v_notice_id INT;
|
||||
DECLARE v_receive_list TEXT;
|
||||
DECLARE cur CURSOR FOR
|
||||
SELECT id, receive_list
|
||||
FROM notice
|
||||
WHERE receive_list IS NOT NULL
|
||||
AND TRIM(receive_list) != ''
|
||||
AND UPPER(TRIM(receive_list)) != 'ALL'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM notice_receiver WHERE notice_id = notice.id LIMIT 1
|
||||
);
|
||||
DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_done = 1;
|
||||
|
||||
OPEN cur;
|
||||
read_loop: LOOP
|
||||
FETCH cur INTO v_notice_id, v_receive_list;
|
||||
IF v_done = 1 THEN
|
||||
LEAVE read_loop;
|
||||
END IF;
|
||||
|
||||
-- 清理旧数据(幂等)
|
||||
DELETE FROM notice_receiver WHERE notice_id = v_notice_id;
|
||||
|
||||
-- 尝试 JSON 数组解析
|
||||
-- receive_list 格式: ["account1","account2"] 或 account1,account2
|
||||
SET v_receive_list = TRIM(v_receive_list);
|
||||
|
||||
-- 去除首尾方括号(如有)
|
||||
IF LEFT(v_receive_list, 1) = '[' AND RIGHT(v_receive_list, 1) = ']' THEN
|
||||
SET v_receive_list = SUBSTRING(v_receive_list, 2, CHAR_LENGTH(v_receive_list) - 2);
|
||||
END IF;
|
||||
|
||||
-- 替换引号为空,按逗号分隔
|
||||
SET v_receive_list = REPLACE(REPLACE(v_receive_list, '"', ''), '''', '');
|
||||
|
||||
-- 逐条插入
|
||||
-- 简单实现:用 FIND_IN_SET 不适用于变量,改用临时表
|
||||
BEGIN
|
||||
DECLARE v_pos INT DEFAULT 1;
|
||||
DECLARE v_account VARCHAR(64);
|
||||
DECLARE v_remaining TEXT;
|
||||
SET v_remaining = v_receive_list;
|
||||
|
||||
WHILE v_remaining IS NOT NULL AND TRIM(v_remaining) != '' DO
|
||||
IF LOCATE(',', v_remaining) > 0 THEN
|
||||
SET v_account = TRIM(SUBSTRING_INDEX(v_remaining, ',', 1));
|
||||
SET v_remaining = SUBSTRING(v_remaining, LOCATE(',', v_remaining) + 1);
|
||||
ELSE
|
||||
SET v_account = TRIM(v_remaining);
|
||||
SET v_remaining = NULL;
|
||||
END IF;
|
||||
|
||||
IF v_account != '' AND v_account IS NOT NULL THEN
|
||||
INSERT INTO notice_receiver (notice_id, account, receive_type, create_time)
|
||||
VALUES (v_notice_id, v_account, 1, NOW());
|
||||
END IF;
|
||||
END WHILE;
|
||||
END;
|
||||
END LOOP;
|
||||
|
||||
CLOSE cur;
|
||||
END$$
|
||||
|
||||
DELIMITER ;
|
||||
|
||||
-- 执行存储过程
|
||||
CALL expand_notice_receivers();
|
||||
|
||||
-- 清理临时存储过程
|
||||
DROP PROCEDURE IF EXISTS `expand_notice_receivers`;
|
||||
|
||||
-- ============================================================
|
||||
-- 回填结果验证查询(执行后手动检查)
|
||||
-- ============================================================
|
||||
|
||||
-- 检查1:sch_id 回填覆盖率
|
||||
-- SELECT
|
||||
-- COUNT(*) AS total,
|
||||
-- SUM(CASE WHEN sch_id IS NOT NULL THEN 1 ELSE 0 END) AS sch_id_filled,
|
||||
-- SUM(CASE WHEN sch_id IS NULL THEN 1 ELSE 0 END) AS sch_id_null
|
||||
-- FROM notice;
|
||||
|
||||
-- 检查2:notice_receiver 展开行数
|
||||
-- SELECT COUNT(*) FROM notice_receiver;
|
||||
|
||||
-- 检查3:未展开的通知(receive_list 非空非 ALL 但 receiver 表无记录)
|
||||
-- SELECT n.id, n.receive_list
|
||||
-- FROM notice n
|
||||
-- LEFT JOIN notice_receiver r ON r.notice_id = n.id
|
||||
-- WHERE n.receive_list IS NOT NULL
|
||||
-- AND TRIM(n.receive_list) != ''
|
||||
-- AND UPPER(TRIM(n.receive_list)) != 'ALL'
|
||||
-- AND r.id IS NULL;
|
||||
@@ -0,0 +1,24 @@
|
||||
-- ============================================================
|
||||
-- notice_receiver 建表(替代 notice.receive_list JSON 字符串存储)
|
||||
-- 决策 D2-B:拆关联表,支撑阅读情况/催收/统计 SQL 化
|
||||
--
|
||||
-- 对应实体:com.yunhai.notice.entity.NoticeReceiver
|
||||
-- 索引:idx_notice_id(notice_id) + idx_account(account)
|
||||
--
|
||||
-- 执行方式:由 DBA 手动在对应环境执行,AI 不代跑
|
||||
-- MySQL 5.7+ 兼容
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `notice_receiver` (
|
||||
`id` INT NOT NULL AUTO_INCREMENT COMMENT '自增主键',
|
||||
`notice_id` INT NOT NULL COMMENT '通知ID(关联 notice.id)',
|
||||
`account` VARCHAR(64) NOT NULL COMMENT '接收人登录账号',
|
||||
`receive_type` INT NOT NULL DEFAULT 1 COMMENT '接收类型:1 接收 / 2 抄送',
|
||||
`create_by` VARCHAR(64) NULL COMMENT '创建人登录账号(审计填充)',
|
||||
`create_time` DATETIME NULL COMMENT '创建时间(审计填充)',
|
||||
`update_by` VARCHAR(64) NULL COMMENT '更新人登录账号(审计填充)',
|
||||
`update_time` DATETIME NULL COMMENT '更新时间(审计填充)',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_notice_id` (`notice_id`),
|
||||
KEY `idx_account` (`account`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='通知接收人关联表';
|
||||
@@ -0,0 +1,92 @@
|
||||
-- =====================================================================
|
||||
-- 活动信息发布模块 · 建表脚本(fa_activity / fa_activity_read / fa_activity_signup / operation_log)
|
||||
-- 说明:应用配置 ddl-auto=update 会自动建表,本脚本供 DBA 手工建库/核对字段使用。
|
||||
-- 库:family_activity(本地开发;建库:CREATE DATABASE family_activity DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;)
|
||||
-- =====================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `fa_activity` (
|
||||
`id` INT NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`sch_id` INT NULL COMMENT '学校ID(数据隔离)',
|
||||
`title` VARCHAR(200) NULL COMMENT '标题',
|
||||
`content` TEXT NULL COMMENT '活动内容(富文本)',
|
||||
`category` VARCHAR(50) NULL COMMENT '活动分类',
|
||||
`venue` VARCHAR(200) NULL COMMENT '活动地点',
|
||||
`start_time` DATETIME NULL COMMENT '活动开始时间',
|
||||
`end_time` DATETIME NULL COMMENT '活动结束时间',
|
||||
`signup_deadline` DATETIME NULL COMMENT '报名截止时间',
|
||||
`receipt_mode` VARCHAR(16) NOT NULL DEFAULT 'none' COMMENT '回执方式:none/signup/confirm',
|
||||
`quota` INT NOT NULL DEFAULT 0 COMMENT '名额上限(0 不限)',
|
||||
`allow_accompany` INT NOT NULL DEFAULT 0 COMMENT '是否允许家长随行:0/1',
|
||||
`cost` DECIMAL(10,2) NULL COMMENT '活动费用(元)',
|
||||
`dept_id` VARCHAR(50) NULL COMMENT '主办部门ID(sys_dict.dict_key)',
|
||||
`dept_name` VARCHAR(100) NULL COMMENT '主办部门名称(冗余)',
|
||||
`publisher_id` INT NULL COMMENT '发布人ID(平台用户ID;家长端=学生ID)',
|
||||
`publisher_name` VARCHAR(50) NULL COMMENT '发布人姓名',
|
||||
`status` INT NOT NULL DEFAULT 0 COMMENT '发送状态:0 草稿 / 1 已发布',
|
||||
`topmost` INT NOT NULL DEFAULT 0 COMMENT '置顶:0/1',
|
||||
`is_pulled` INT NOT NULL DEFAULT 0 COMMENT '撤回:0/1(家长端不可见)',
|
||||
`timing_send_time` DATETIME NULL COMMENT '定时发送时间',
|
||||
`sms_flag` INT NOT NULL DEFAULT 0 COMMENT '短信发送标记:0/1(仅存储)',
|
||||
`receive_type` VARCHAR(16) NOT NULL DEFAULT 'class' COMMENT '接收对象类型:class=班级',
|
||||
`receive_list` TEXT NULL COMMENT '接收列表:"ALL" 或班级id逗号串',
|
||||
`source_id` INT NULL COMMENT '转发/复用源活动ID',
|
||||
`create_by` VARCHAR(50) NULL COMMENT '创建人',
|
||||
`create_time` DATETIME NULL COMMENT '创建时间',
|
||||
`update_by` VARCHAR(50) NULL COMMENT '更新人',
|
||||
`update_time` DATETIME NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_fa_activity_sch_status` (`sch_id`, `status`, `is_pulled`),
|
||||
KEY `idx_fa_activity_publisher` (`sch_id`, `publisher_id`)
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '活动主表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `fa_activity_read` (
|
||||
`id` INT NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`activity_id` INT NOT NULL COMMENT '活动ID',
|
||||
`class_id` VARCHAR(50) NOT NULL COMMENT '班级ID(已读按班级粒度)',
|
||||
`student_id` INT NULL COMMENT '首读学生ID(家长=学生)',
|
||||
`read_time` DATETIME NULL COMMENT '阅读时间',
|
||||
`create_by` VARCHAR(50) NULL COMMENT '创建人',
|
||||
`create_time` DATETIME NULL COMMENT '创建时间',
|
||||
`update_by` VARCHAR(50) NULL COMMENT '更新人',
|
||||
`update_time` DATETIME NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_fa_read_activity_class` (`activity_id`, `class_id`)
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '活动已读记录(班级粒度)';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `fa_activity_signup` (
|
||||
`id` INT NOT NULL AUTO_INCREMENT COMMENT '主键(前端 signupId)',
|
||||
`activity_id` INT NOT NULL COMMENT '活动ID',
|
||||
`sch_id` INT NULL COMMENT '学校ID(数据隔离)',
|
||||
`student_id` INT NULL COMMENT '学生ID',
|
||||
`student_name` VARCHAR(50) NULL COMMENT '学生姓名',
|
||||
`class_id` VARCHAR(50) NULL COMMENT '班级ID',
|
||||
`class_name` VARCHAR(100) NULL COMMENT '班级名称',
|
||||
`guardian_name` VARCHAR(50) NULL COMMENT '家长姓名',
|
||||
`guardian_phone` VARCHAR(20) NULL COMMENT '联系电话',
|
||||
`join_count` INT NOT NULL DEFAULT 1 COMMENT '参加人数(含学生本人)',
|
||||
`status` VARCHAR(16) NOT NULL DEFAULT 'none' COMMENT 'none/signed/confirmed/canceled',
|
||||
`remark` VARCHAR(500) NULL COMMENT '备注(过敏史、随行说明等)',
|
||||
`signed_at` DATETIME NULL COMMENT '回执时间',
|
||||
`create_by` VARCHAR(50) NULL COMMENT '创建人',
|
||||
`create_time` DATETIME NULL COMMENT '创建时间',
|
||||
`update_by` VARCHAR(50) NULL COMMENT '更新人',
|
||||
`update_time` DATETIME NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_fa_signup_activity_student` (`activity_id`, `student_id`),
|
||||
KEY `idx_fa_signup_activity_status` (`activity_id`, `status`)
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '活动回执/报名记录';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `operation_log` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`operator_id` INT NULL COMMENT '操作人平台用户ID',
|
||||
`operate_time` DATETIME NULL COMMENT '操作时间',
|
||||
`operator` VARCHAR(64) NULL COMMENT '操作人姓名',
|
||||
`ip` VARCHAR(64) NULL COMMENT '客户端IP',
|
||||
`module` VARCHAR(64) NULL COMMENT '模块名称',
|
||||
`content` VARCHAR(3000) NULL COMMENT '操作内容',
|
||||
`sch_id` INT NULL COMMENT '学校ID(超管=0)',
|
||||
`request_uri` VARCHAR(255) NULL COMMENT '请求URI',
|
||||
`request_method` VARCHAR(16) NULL COMMENT '请求方法',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_oplog_sch_time` (`sch_id`, `operate_time`)
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '业务操作日志';
|
||||
@@ -0,0 +1,46 @@
|
||||
-- =====================================================================
|
||||
-- 活动信息发布模块 · 账号改ID迁移脚本
|
||||
-- 变更:实体不再保存教师/学生的登录账号(publisher_account / account /
|
||||
-- guardian_account / user_account / creator_account / operation_log.account),
|
||||
-- 统一改存平台用户ID / 学生ID。业务回退查询见各 Repository。
|
||||
-- 说明:
|
||||
-- 1. 新增 ID 列后,历史行无法在本库内把账号映射为平台用户ID(用户表在基础平台库),
|
||||
-- publisher_id / creator_id 历史行为 NULL;fa_activity_read.student_id、
|
||||
-- fa_activity_signup.student_id 本就落了 ID,无需回填。
|
||||
-- 2. fa_activity_signup 唯一键由 (activity_id, guardian_account) 改为
|
||||
-- (activity_id, student_id)。如库中该唯一键索引名由 Hibernate 自动生成(UK*),
|
||||
-- 请先 SHOW CREATE TABLE fa_activity_signup 核对名称后替换。
|
||||
-- 3. 本脚本可重复执行前请先核对列是否仍存在(MySQL DROP COLUMN 不存在会报错)。
|
||||
-- =====================================================================
|
||||
USE `family_activity`;
|
||||
|
||||
-- 1) 活动主表:发布人账号 → 发布人ID
|
||||
ALTER TABLE `fa_activity`
|
||||
ADD COLUMN `publisher_id` INT NULL COMMENT '发布人ID(平台用户ID;家长端=学生ID)' AFTER `publisher_name`;
|
||||
ALTER TABLE `fa_activity` DROP COLUMN `publisher_account`;
|
||||
-- 注:实体未声明 @Index 时 Hibernate 不会建 idx_fa_activity_publisher;
|
||||
-- 若目标库手工建过该账号索引,请在此先 DROP 后按新列重建。
|
||||
ALTER TABLE `fa_activity`
|
||||
ADD INDEX `idx_fa_activity_publisher` (`sch_id`, `publisher_id`);
|
||||
|
||||
-- 2) 已读记录:操作账号 → 首读学生ID(已读仍按班级粒度去重,学生ID仅溯源)
|
||||
ALTER TABLE `fa_activity_read`
|
||||
ADD COLUMN `student_id` INT NULL COMMENT '首读学生ID(家长=学生)' AFTER `class_id`;
|
||||
ALTER TABLE `fa_activity_read` DROP COLUMN `account`;
|
||||
|
||||
-- 3) 回执/报名记录:删除家长账号(家长=学生,student_id 即身份),唯一键随之迁移
|
||||
ALTER TABLE `fa_activity_signup` DROP INDEX `UK68upl3jts93doy45ggqwqdf4o`;
|
||||
ALTER TABLE `fa_activity_signup` DROP COLUMN `guardian_account`;
|
||||
ALTER TABLE `fa_activity_signup`
|
||||
ADD UNIQUE KEY `uk_fa_signup_activity_student` (`activity_id`, `student_id`);
|
||||
|
||||
-- 4) 联系组:创建人账号 → 创建人ID
|
||||
ALTER TABLE `contacts_group`
|
||||
ADD COLUMN `creator_id` INT NULL COMMENT '创建人ID(平台用户ID)' AFTER `creator_account`;
|
||||
ALTER TABLE `contacts_group` DROP COLUMN `creator_account`;
|
||||
|
||||
-- 5) 联系组成员:删除用户账号快照(user_id 即身份)
|
||||
ALTER TABLE `contacts_group_user` DROP COLUMN `user_account`;
|
||||
|
||||
-- 6) 操作日志:删除操作人账号(operator_id 即身份)
|
||||
ALTER TABLE `operation_log` DROP COLUMN `account`;
|
||||
Reference in New Issue
Block a user