-- ===================================================================================== -- 1.0.500 S8-NOTIFY-RECIPIENT-1 · 通知收件人模型 -- -- 取代 ado_s8_notification_layer 成为「这个事件通知谁」的 CURRENT AUTHORITY。 -- -- 旧表为什么不够用(真库取证): -- · 它按 (scene_code, severity, level_code) 组织,**不按规则** —— 无法回答 -- 「这条规则出事通知谁」,而 P1-C 已经把责任落到了规则级; -- · level_code 的 L1/L2/L3 三档在库里只有 L1_OPERATOR 被真正用过(16 行全是它, -- 另有 1 行 level_code 为空),且「主管 / 总监」在系统中没有对应实体 -- (SysOrg.DirectorId 与 SysUser.ManagerUserId 填充率均为 0); -- · 收件人只支持 Role 一种,无法表达「通知当前处理人」「通知责任池」这类真实诉求。 -- -- 旧表**不删**,降级为 LEGACY fallback:仅当新表对该 (租户, 规则, 事件) 没有任何配置 -- 时才使用。优先级单一、可解释 —— 不是两个平级 authority。这样现网既有通知不会被 -- 这批改动打断,管理员可以按规则逐步迁移。 -- -- 表由 CodeFirst(InitTables) 创建;本脚本只补索引 + postcheck,不写任何收件人数据 -- (收件人是 per-tenant / per-rule 的人工配置,写死既不适配新租户也会覆盖管理员的选择)。 -- ===================================================================================== CREATE TABLE IF NOT EXISTS `ado_s8_notification_recipient` ( `id` BIGINT NOT NULL AUTO_INCREMENT, `tenant_id` BIGINT NOT NULL COMMENT '租户(唯一安全边界)', `rule_code` VARCHAR(128) NOT NULL COMMENT "规则编码;'*' = 租户级默认(无来源规则的异常)", `event_code` VARCHAR(64) NOT NULL COMMENT '事件码,须在 S8NotificationCatalog.Events 内', `recipient_type` VARCHAR(32) NOT NULL COMMENT 'HANDLER_POOL / ASSIGNEE / REVIEWER / SPECIFIC_USER', `recipient_user_ids` VARCHAR(2000) NULL COMMENT '仅 SPECIFIC_USER 使用,逗号分隔 SysUser.Id', `created_at` DATETIME NOT NULL, `created_by` VARCHAR(64) NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='S8 通知收件人配置'; -- 唯一索引:同一 (租户, 规则, 事件, 收件人类型) 只允许一行。 -- 没有它,重复行会让"取消某个收件人类型"变成删一行、剩一行 —— -- 页面显示已取消,实际还在发。 SET @has_uk := (SELECT COUNT(DISTINCT INDEX_NAME) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME='ado_s8_notification_recipient' AND INDEX_NAME='uk_s8_notify_recipient'); SET @sql := IF(@has_uk = 0, ' DELETE a FROM `ado_s8_notification_recipient` a JOIN `ado_s8_notification_recipient` b ON a.`tenant_id` = b.`tenant_id` AND a.`rule_code` = b.`rule_code` AND a.`event_code` = b.`event_code` AND a.`recipient_type` = b.`recipient_type` AND a.`id` > b.`id`', 'DO 0'); PREPARE s FROM @sql; EXECUTE s; DEALLOCATE PREPARE s; SET @sql := IF(@has_uk = 0, 'ALTER TABLE `ado_s8_notification_recipient` ADD UNIQUE INDEX `uk_s8_notify_recipient` (`tenant_id`,`rule_code`,`event_code`,`recipient_type`)', 'DO 0'); PREPARE s FROM @sql; EXECUTE s; DEALLOCATE PREPARE s; -- 旧表打上 LEGACY 标记(只改注释,不动任何数据行)—— -- 让直接看库的人也能立刻知道哪张才是当前 authority。 ALTER TABLE `ado_s8_notification_layer` COMMENT='[LEGACY · 1.0.500 起降级] S8 通知分层配置;CURRENT AUTHORITY = ado_s8_notification_recipient,本表仅在新表无配置时作为 fallback'; -- ── POSTCHECK ── SET @cols := (SELECT COUNT(*) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME='ado_s8_notification_recipient' AND INDEX_NAME='uk_s8_notify_recipient' AND COLUMN_NAME IN ('tenant_id','rule_code','event_code','recipient_type')); DROP TABLE IF EXISTS `tmp_s8_500_guard`; CREATE TABLE `tmp_s8_500_guard` ( probe BIGINT NOT NULL, CONSTRAINT s8_500_BLOCKED_unique_index_missing CHECK (probe = 0) ) COMMENT '1.0.500 断言门禁;通过后自动删除'; INSERT INTO `tmp_s8_500_guard` (probe) VALUES ((4 - @cols)); DROP TABLE IF EXISTS `tmp_s8_500_guard`; -- 旧表一行都不能少:本批只降级、不清理。 SET @legacy := (SELECT COUNT(*) FROM `ado_s8_notification_layer`); DROP TABLE IF EXISTS `tmp_s8_500_guard`; CREATE TABLE `tmp_s8_500_guard` ( probe BIGINT NOT NULL, CONSTRAINT s8_500_BLOCKED_legacy_layer_wiped CHECK (probe = 0) ) COMMENT '1.0.500 断言门禁;通过后自动删除'; INSERT INTO `tmp_s8_500_guard` (probe) VALUES (IF(@legacy = 0, 1, 0)); DROP TABLE IF EXISTS `tmp_s8_500_guard`; -- 幂等:第二次执行建表跳过、索引已存在跳过、注释重复设置无副作用、断言仍成立。