1.0.498.sql 6.8 KB

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