| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697 |
- -- =====================================================================================
- -- 1.0.498 S8-HANDLER-POOL-1 · 规则处理账号池
- --
- -- 目标:回答「哪些账号负责这条规则」。与「异常操作权限」(1.0.497,哪个角色能做认领
- -- 这类动作)正交 —— 最终能否认领 = 动作权限 ∩ 规则责任 ∩ 状态机。
- --
- -- 存储选型(基于本仓真实证据,不是偏好):
- -- · 不放 params_json。① 认领资格、列表可见性过滤、通知收件人解析都需要
- -- 「按 user 反查规则」与「与 SysUser 关联校验租户/启用」,JSON 数组做不到;
- -- ② 该字段有事故先例 —— PUT /params {"enabled":false} 曾把整块 JSON 抹成 NULL;
- -- ③ 去重与合法性可由唯一索引在 DB 层保证,JSON 只能靠应用层自觉。
- -- · 身份用 rule_code 而非 rule_id:provisioning 的身份口径就是 (TenantId, RuleCode),
- -- 而 watch_rule 的行是可以被重建的(1.0.496 的 clean reset 之后 id 全部变化)。
- -- 绑 id 会让人员池在那一刻集体失联,而且不报错:规则照常跑,只是谁都不再"负责"它。
- --
- -- 表由 CodeFirst(InitTables) 创建;本脚本只补索引 + postcheck,不写任何成员数据
- -- (成员是 per-tenant 的人工配置,写死在迁移里既不适配新租户、也会覆盖管理员的选择)。
- -- =====================================================================================
- -- ─────────────────────────────────────────────────────────────────────────────────────
- -- PHASE 1 · PREFLIGHT
- -- ─────────────────────────────────────────────────────────────────────────────────────
- CREATE TABLE IF NOT EXISTS `ado_s8_rule_handler_user` (
- `id` BIGINT NOT NULL AUTO_INCREMENT,
- `tenant_id` BIGINT NOT NULL COMMENT '租户(唯一安全边界)',
- `rule_code` VARCHAR(128) NOT NULL COMMENT '规则编码,与 provisioning 身份口径一致',
- `user_id` BIGINT NOT NULL COMMENT '处理账号 SysUser.Id',
- `created_at` DATETIME NOT NULL,
- `created_by` VARCHAR(64) NULL,
- PRIMARY KEY (`id`)
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='S8 规则处理账号池';
- -- ─────────────────────────────────────────────────────────────────────────────────────
- -- PHASE 2 · SCHEMA
- -- 唯一索引:同一 (租户, 规则, 账号) 只允许一行。没有它,重复成员会让"移出池"
- -- 变成删一行、剩一行 —— 界面显示已移出,实际仍然可以认领。
- -- 反查索引:可见性过滤要按 (tenant, user) 反查负责的规则,没有它会全表扫。
- -- ─────────────────────────────────────────────────────────────────────────────────────
- SET @has_uk := (SELECT COUNT(DISTINCT INDEX_NAME) FROM information_schema.STATISTICS
- WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME='ado_s8_rule_handler_user'
- AND INDEX_NAME='uk_s8_rule_handler');
- SET @sql := IF(@has_uk = 0, '
- DELETE a FROM `ado_s8_rule_handler_user` a
- JOIN `ado_s8_rule_handler_user` b
- ON a.`tenant_id` = b.`tenant_id`
- AND a.`rule_code` = b.`rule_code`
- AND a.`user_id` = b.`user_id`
- 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_rule_handler_user` ADD UNIQUE INDEX `uk_s8_rule_handler` (`tenant_id`,`rule_code`,`user_id`)',
- 'DO 0');
- PREPARE s FROM @sql; EXECUTE s; DEALLOCATE PREPARE s;
- SET @has_idx := (SELECT COUNT(DISTINCT INDEX_NAME) FROM information_schema.STATISTICS
- WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME='ado_s8_rule_handler_user'
- AND INDEX_NAME='idx_s8_rule_handler_user');
- SET @sql := IF(@has_idx = 0,
- 'ALTER TABLE `ado_s8_rule_handler_user` ADD INDEX `idx_s8_rule_handler_user` (`tenant_id`,`user_id`)',
- 'DO 0');
- PREPARE s FROM @sql; EXECUTE s; DEALLOCATE PREPARE s;
- -- ─────────────────────────────────────────────────────────────────────────────────────
- -- PHASE 3 · POSTCHECK
- -- ─────────────────────────────────────────────────────────────────────────────────────
- -- ① 唯一索引必须覆盖三列
- SET @cols := (SELECT COUNT(*) FROM information_schema.STATISTICS
- WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME='ado_s8_rule_handler_user'
- AND INDEX_NAME='uk_s8_rule_handler'
- AND COLUMN_NAME IN ('tenant_id','rule_code','user_id'));
- DROP TABLE IF EXISTS `tmp_s8_498_guard`;
- CREATE TABLE `tmp_s8_498_guard` (
- probe BIGINT NOT NULL,
- CONSTRAINT s8_498_BLOCKED_unique_index_missing CHECK (probe = 0)
- ) COMMENT '1.0.498 断言门禁;通过后自动删除';
- INSERT INTO `tmp_s8_498_guard` (probe) VALUES ((3 - @cols));
- DROP TABLE IF EXISTS `tmp_s8_498_guard`;
- -- ② 不允许出现「池成员不属于该行租户」的数据
- SET @bad := (SELECT COUNT(*) FROM `ado_s8_rule_handler_user` h
- JOIN `SysUser` u ON u.`Id` = h.`user_id`
- WHERE u.`TenantId` <> h.`tenant_id`);
- DROP TABLE IF EXISTS `tmp_s8_498_guard`;
- CREATE TABLE `tmp_s8_498_guard` (
- probe BIGINT NOT NULL,
- CONSTRAINT s8_498_BLOCKED_cross_tenant_handler CHECK (probe = 0)
- ) COMMENT '1.0.498 断言门禁;通过后自动删除';
- INSERT INTO `tmp_s8_498_guard` (probe) VALUES (@bad);
- DROP TABLE IF EXISTS `tmp_s8_498_guard`;
- -- ─────────────────────────────────────────────────────────────────────────────────────
- -- PHASE 4 · IDEMPOTENT RERUN GUARD
- -- 第二次执行:建表跳过、索引已存在跳过、断言依旧成立。本脚本不写任何成员数据,
- -- 因此重跑不会改变任何一条责任归属。
- -- ─────────────────────────────────────────────────────────────────────────────────────
|