1.0.487.sql 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. -- S8-STANDARD-DATASET-HARD-CUTOVER-1 · 步骤 2:删除 Legacy 取数体系的全部残留。
  2. --
  3. -- 最终架构(唯一路径):
  4. -- Data Platform → Governed Dataset → S8 Provider → Canonical Contract
  5. -- → Watch Rule → Evaluator → Detection → Exception
  6. -- S8 不维护物理数据源、不执行任意 SQL、不保留 Legacy Rule。
  7. --
  8. -- 本脚本是**破坏性**的。部署前复核(2026-09-06 实测,非沿用历史数字):
  9. -- ado_s8_watch_rule : 134 行 = Legacy 132(data_access_mode 全为 NULL)+ Standard 2
  10. -- Legacy 中 enabled=1 : 1 行(id=86 RULE_S1_ORDER_DELIVERY_DUE_GRAY30_DATE_DELAY)
  11. -- ado_s8_data_source : 3 行(零 FK / 零触发器 / 零视图 / 零存储过程 / 零事件引用 / 除 PRIMARY 无索引)
  12. -- Legacy 运行产物 : exception 34 · timeline 40 · notification_log 3 · decision 0 · evidence 0
  13. -- detection_log 327 · rule_detection_state 45
  14. --
  15. -- ⚠️ 判定 Legacy 用「非 STANDARD_DATASET」补集写法,且必须显式写 IS NULL。
  16. -- 实测反证:`data_access_mode <> 'STANDARD_DATASET'` 只命中 **0 行**
  17. -- (132 行全为 NULL,三值逻辑求值为 UNKNOWN),正确写法命中 132 行。
  18. -- 补集写法同时覆盖脏值(如 'SQL'),与代码侧「非 STANDARD 即无运行资格」对齐。
  19. --
  20. -- ⚠️⚠️ rule_code 跨租户重名,且 Rule 01 正在其中:
  21. -- RULE_S4_PURCHASE_DELIVERY_DATE_DELAY 同时存在于
  22. -- id=46(租户 797403760988230,Legacy)
  23. -- id=144(租户 838257237606469,Legacy)
  24. -- id=1329909460023(租户 838257186181189,STANDARD_DATASET ← Rule 01,必须保留)
  25. -- 而 ado_s8_rule_detection_state **只有 rule_code、没有 rule_id**。
  26. -- 因此凡按 rule_code 定位的删除,一律附加 (tenant_id, factory_id) 限定,
  27. -- 绝不允许出现裸 `WHERE rule_code = ...`。
  28. --
  29. -- 保留:Rule 01(1329909460023)与诊断规则(1329909460024),
  30. -- 两者均为 STANDARD_DATASET / PURCHASE_DELIVERY / enabled=0,本脚本不触碰。
  31. --
  32. -- ════════════════════════════════════════════════════════════════════════════
  33. -- 部署前复核新增的两项设计(PRE-DEPLOY REVIEW,2026-09-06)
  34. --
  35. -- 【一】先归档、再删除
  36. -- 复核发现待删的 34 条异常单中,有 2 条带**完整人工流转链**且当前仍未闭环:
  37. -- EX-20260904-2F4715F1 / EX-20260904-DA45EB40(均为 PENDING_VERIFICATION)
  38. -- CREATE → CLAIM(3172) → START_PROGRESS(838259724726341) → VERIFY_SUBMITTED
  39. -- 另有 1 条(id=391)带真实 recovered_at(2026-09-02 由调度器 reconcile 写入)。
  40. -- 备注内容为 "test"/"111",判断属 UAT 演练而非生产业务;但「有人认领过、
  41. -- 流程尚未闭环」这一事实本身就不该被静默物理删除 —— 出问题时无从解释。
  42. -- 故本脚本改为**归档后删除**:删除行为不变,但结果可解释、可追、可恢复。
  43. --
  44. -- 归档范围只取「删了就再也拼不回来」的部分:
  45. -- watch_rule —— 不archive 的话 exception.source_rule_id 会指向虚空,归档失去意义
  46. -- exception + 4 张子表 —— 人工痕迹本体
  47. -- 刻意**不归档**:
  48. -- detection_log —— 纯遥测,且本库已有 weekly retention event 在按 30 天滚动删除它
  49. -- rule_detection_state —— 抗抖瞬时状态,无历史解释价值
  50. --
  51. -- 【二】列删除后仍可重跑(部分失败可恢复)
  52. -- 执行器语义(AutoVersionUpdate):只有 Status=Success 才跳过,Failed 会在下次
  53. -- 启动**重跑整个脚本**,且失败会 throw 中断应用启动。而 MySQL 的 DDL 是隐式提交,
  54. -- 执行器逐条 ExecuteNonQuery、无显式事务,故本脚本天然不是原子的。
  55. --
  56. -- 原实现存在**不可恢复**缺陷:第 0 步用 `WHERE data_access_mode IS NULL ...` 建集合。
  57. -- 一旦第 4 步把该列删掉之后脚本才失败,重跑时第 0 步会 ERROR 1054 Unknown column,
  58. -- 于是永远失败 → 应用永远起不来 → 只能人工改库。
  59. --
  60. -- 修法:temp 表改为**显式 schema**(不 CREATE ... AS SELECT,故不依赖源表列),
  61. -- 凡引用 data_access_mode 或对 watch_rule 做 `SELECT *` 的语句,一律由
  62. -- @has_mode 探针守卫,列不存在时退化为 `DO 0`。
  63. -- 不变式:**列还在 = Legacy 清理尚未走完;列没了 = 已经走完**,重跑即空转。
  64. -- ════════════════════════════════════════════════════════════════════════════
  65. --
  66. -- 幂等:DELETE 带条件、DROP 带 IF EXISTS、CREATE 带 IF NOT EXISTS、
  67. -- INSERT 用 IGNORE、ALTER 前查 information_schema。重复执行影响行数为 0。
  68. -- ────────────────────────────────────────────────────────────────────────────
  69. -- 0. 列存在性探针(整个 Legacy 清理块的守卫开关)
  70. -- ────────────────────────────────────────────────────────────────────────────
  71. SET @has_mode := (
  72. SELECT COUNT(*) FROM information_schema.COLUMNS
  73. WHERE TABLE_SCHEMA = DATABASE()
  74. AND TABLE_NAME = 'ado_s8_watch_rule'
  75. AND COLUMN_NAME = 'data_access_mode');
  76. -- ────────────────────────────────────────────────────────────────────────────
  77. -- 1. 固定 Legacy 规则 id 集合(后续步骤全部引用它,避免各步判定漂移)
  78. -- 显式 schema:不用 CREATE ... AS SELECT,从而不依赖 ado_s8_watch_rule 的列。
  79. -- ────────────────────────────────────────────────────────────────────────────
  80. DROP TEMPORARY TABLE IF EXISTS tmp_s8_legacy_rule;
  81. CREATE TEMPORARY TABLE tmp_s8_legacy_rule (
  82. id BIGINT NOT NULL PRIMARY KEY,
  83. tenant_id BIGINT NULL,
  84. factory_id BIGINT NULL,
  85. rule_code VARCHAR(64) NULL
  86. );
  87. SET @sql := IF(@has_mode > 0,
  88. 'INSERT INTO tmp_s8_legacy_rule (id, tenant_id, factory_id, rule_code)
  89. SELECT id, tenant_id, factory_id, rule_code FROM ado_s8_watch_rule
  90. WHERE data_access_mode IS NULL
  91. OR TRIM(data_access_mode) = ''''
  92. OR UPPER(TRIM(data_access_mode)) <> ''STANDARD_DATASET''',
  93. 'DO 0');
  94. PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
  95. -- ────────────────────────────────────────────────────────────────────────────
  96. -- 2. Legacy 规则产生的异常单集合
  97. -- ────────────────────────────────────────────────────────────────────────────
  98. DROP TEMPORARY TABLE IF EXISTS tmp_s8_legacy_exception;
  99. CREATE TEMPORARY TABLE tmp_s8_legacy_exception (id BIGINT NOT NULL PRIMARY KEY);
  100. INSERT INTO tmp_s8_legacy_exception (id)
  101. SELECT e.id FROM ado_s8_exception e JOIN tmp_s8_legacy_rule r ON r.id = e.source_rule_id;
  102. -- ────────────────────────────────────────────────────────────────────────────
  103. -- 3. 归档(先于任何删除)
  104. -- watch_rule 用 SELECT * 且其列会在第 7 步被改,故同样由 @has_mode 守卫;
  105. -- 其余 5 张表结构不受本脚本影响,列数恒定匹配,无需守卫。
  106. -- ────────────────────────────────────────────────────────────────────────────
  107. CREATE TABLE IF NOT EXISTS ado_s8_watch_rule_bak_1_0_487 LIKE ado_s8_watch_rule;
  108. CREATE TABLE IF NOT EXISTS ado_s8_exception_bak_1_0_487 LIKE ado_s8_exception;
  109. CREATE TABLE IF NOT EXISTS ado_s8_exception_timeline_bak_1_0_487 LIKE ado_s8_exception_timeline;
  110. CREATE TABLE IF NOT EXISTS ado_s8_decision_record_bak_1_0_487 LIKE ado_s8_decision_record;
  111. CREATE TABLE IF NOT EXISTS ado_s8_evidence_bak_1_0_487 LIKE ado_s8_evidence;
  112. CREATE TABLE IF NOT EXISTS ado_s8_notification_log_bak_1_0_487 LIKE ado_s8_notification_log;
  113. SET @sql := IF(@has_mode > 0,
  114. 'INSERT IGNORE INTO ado_s8_watch_rule_bak_1_0_487
  115. SELECT w.* FROM ado_s8_watch_rule w JOIN tmp_s8_legacy_rule r ON r.id = w.id',
  116. 'DO 0');
  117. PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
  118. INSERT IGNORE INTO ado_s8_exception_bak_1_0_487
  119. SELECT e.* FROM ado_s8_exception e JOIN tmp_s8_legacy_exception x ON x.id = e.id;
  120. INSERT IGNORE INTO ado_s8_exception_timeline_bak_1_0_487
  121. SELECT t.* FROM ado_s8_exception_timeline t JOIN tmp_s8_legacy_exception x ON x.id = t.exception_id;
  122. INSERT IGNORE INTO ado_s8_decision_record_bak_1_0_487
  123. SELECT d.* FROM ado_s8_decision_record d JOIN tmp_s8_legacy_exception x ON x.id = d.exception_id;
  124. INSERT IGNORE INTO ado_s8_evidence_bak_1_0_487
  125. SELECT v.* FROM ado_s8_evidence v JOIN tmp_s8_legacy_exception x ON x.id = v.exception_id;
  126. INSERT IGNORE INTO ado_s8_notification_log_bak_1_0_487
  127. SELECT n.* FROM ado_s8_notification_log n JOIN tmp_s8_legacy_exception x ON x.id = n.exception_id;
  128. -- ────────────────────────────────────────────────────────────────────────────
  129. -- 4. Legacy 规则产生的异常单及其子表
  130. -- 无 FK(全库外键数 = 0),必须手工按父子顺序删;子表一律经 exception_id 关联。
  131. -- ────────────────────────────────────────────────────────────────────────────
  132. DELETE t FROM ado_s8_exception_timeline t JOIN tmp_s8_legacy_exception x ON x.id = t.exception_id;
  133. DELETE d FROM ado_s8_decision_record d JOIN tmp_s8_legacy_exception x ON x.id = d.exception_id;
  134. DELETE v FROM ado_s8_evidence v JOIN tmp_s8_legacy_exception x ON x.id = v.exception_id;
  135. DELETE n FROM ado_s8_notification_log n JOIN tmp_s8_legacy_exception x ON x.id = n.exception_id;
  136. DELETE e FROM ado_s8_exception e JOIN tmp_s8_legacy_exception x ON x.id = e.id;
  137. -- ────────────────────────────────────────────────────────────────────────────
  138. -- 5. Legacy 规则的检测轨迹与抗抖状态
  139. -- detection_log 有 rule_id,可精确关联;
  140. -- rule_detection_state 只有 rule_code,故必须三键(tenant + factory + code)限定 ——
  141. -- 否则会连带删掉 Rule 01 的抗抖状态(见文件头的重名说明)。
  142. --
  143. -- 复核实测:45 条 state 行全部属于 Legacy 规则,其中 34 条的 active_exception_id
  144. -- 指向第 4 步删掉的异常单 —— 随本步一并删除,不会留下悬挂指针。
  145. -- ────────────────────────────────────────────────────────────────────────────
  146. DELETE d FROM ado_s8_detection_log d JOIN tmp_s8_legacy_rule r ON r.id = d.rule_id;
  147. DELETE s FROM ado_s8_rule_detection_state s
  148. JOIN tmp_s8_legacy_rule r
  149. ON r.tenant_id = s.tenant_id
  150. AND r.factory_id = s.factory_id
  151. AND r.rule_code = s.rule_code;
  152. -- ────────────────────────────────────────────────────────────────────────────
  153. -- 6. Legacy 规则本体
  154. -- ────────────────────────────────────────────────────────────────────────────
  155. DELETE w FROM ado_s8_watch_rule w JOIN tmp_s8_legacy_rule r ON r.id = w.id;
  156. DROP TEMPORARY TABLE IF EXISTS tmp_s8_legacy_exception;
  157. DROP TEMPORARY TABLE IF EXISTS tmp_s8_legacy_rule;
  158. -- ────────────────────────────────────────────────────────────────────────────
  159. -- 7. 规则表的 Legacy 列
  160. -- 必须在第 6 步之后:data_access_mode 正是第 1 步的判定依据。
  161. -- 用 information_schema 预检使脚本可重复执行(MySQL 的 DROP COLUMN 无 IF EXISTS)。
  162. -- data_access_mode 刻意放在三者最后:它是 @has_mode 守卫的开关,
  163. -- 只要它还在,重跑就能从头正常空转。
  164. -- ────────────────────────────────────────────────────────────────────────────
  165. SET @sql := (
  166. SELECT IF(COUNT(*) > 0, 'ALTER TABLE ado_s8_watch_rule DROP COLUMN data_source_id', 'DO 0')
  167. FROM information_schema.COLUMNS
  168. WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ado_s8_watch_rule' AND COLUMN_NAME = 'data_source_id');
  169. PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
  170. SET @sql := (
  171. SELECT IF(COUNT(*) > 0, 'ALTER TABLE ado_s8_watch_rule DROP COLUMN expression', 'DO 0')
  172. FROM information_schema.COLUMNS
  173. WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ado_s8_watch_rule' AND COLUMN_NAME = 'expression');
  174. PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
  175. SET @sql := (
  176. SELECT IF(COUNT(*) > 0, 'ALTER TABLE ado_s8_watch_rule DROP COLUMN data_access_mode', 'DO 0')
  177. FROM information_schema.COLUMNS
  178. WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ado_s8_watch_rule' AND COLUMN_NAME = 'data_access_mode');
  179. PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
  180. -- ────────────────────────────────────────────────────────────────────────────
  181. -- 8. 数据源整表
  182. -- 该表 endpoint 列存的是含凭据的真实连接串;S8 已不再维护物理连接,直接删除。
  183. -- 删前先归档:3 行配置里有可用于排障的历史连接标识(密码段虽是明文,
  184. -- 但归档表与原表同库同权限,不扩大暴露面;如需彻底销毁另行处理)。
  185. -- ────────────────────────────────────────────────────────────────────────────
  186. -- ⚠️ 必须用 @has_tbl 守卫,不能裸写 `CREATE TABLE IF NOT EXISTS x LIKE y`:
  187. -- 实测(MySQL 8)IF NOT EXISTS **不会短路**,源表 y 已被 DROP 时仍报 ERROR 1146。
  188. -- 一旦 DROP 成功后本脚本才失败,裸写法会在重跑时永久失败 → 应用再也起不来。
  189. -- (第 3 步那 6 张归档源表不需要此守卫:本脚本只删它们的行、不删表本身。)
  190. SET @has_tbl := (
  191. SELECT COUNT(*) FROM information_schema.TABLES
  192. WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ado_s8_data_source');
  193. SET @sql := IF(@has_tbl > 0,
  194. 'CREATE TABLE IF NOT EXISTS ado_s8_data_source_bak_1_0_487 LIKE ado_s8_data_source', 'DO 0');
  195. PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
  196. SET @sql := IF(@has_tbl > 0,
  197. 'INSERT IGNORE INTO ado_s8_data_source_bak_1_0_487 SELECT * FROM ado_s8_data_source', 'DO 0');
  198. PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
  199. DROP TABLE IF EXISTS ado_s8_data_source;
  200. -- ────────────────────────────────────────────────────────────────────────────
  201. -- 9. 菜单与授权
  202. -- 先清 SysRoleMenu 授权行,再删菜单,避免留下悬挂授权。
  203. -- 「维护数据源」按钮权限点(s8:config:data-source)一并清除;
  204. -- 目录侧保留一条 Deprecated 墓碑,防止该码被复用。
  205. -- ────────────────────────────────────────────────────────────────────────────
  206. DELETE rm FROM SysRoleMenu rm
  207. JOIN SysMenu m ON m.Id = rm.MenuId
  208. WHERE m.Name = 'aidopS8DataSourceConfig'
  209. OR m.Permission = 's8:config:data-source';
  210. DELETE FROM SysMenu
  211. WHERE Name = 'aidopS8DataSourceConfig'
  212. OR Permission = 's8:config:data-source';