| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364 |
- -- =====================================================================================
- -- 1.0.496 S8-SYSUSER-ONLY-1 · 人员身份统一为系统账号 + 旧运行数据 CLEAN RESET
- --
- -- 背景(真库取证,只读):
- -- · ado_s8_exception.assignee_id 34 个非空值里只有 20 个能解析到 EmployeeMaster.RecID,
- -- 0 个能解析到 SysUser.Id,另有 14 行是 assignee_id = 1(两边都不存在的脏值)。
- -- · ado_s8_exception_timeline.operator_id 同一列混装两套 ID 空间:
- -- CLAIM 写 EmployeeMaster.RecID(19 行),START_PROGRESS / VERIFY_* / CREATE 写 SysUser.Id。
- -- · EmployeeMaster 1105 行只有 8 行绑定了 sys_user_id(0.7%),
- -- 所有「经 EmployeeMaster.SysUserId 换算」的通知与审批流转办路径实际上长期静默失败。
- --
- -- 本次动作:物理列改名为显式 *_user_id,并清空旧运行数据(不做逐条身份迁移 —— 业务已确认
- -- 旧 S8 历史数据无保留要求;混装两套 ID 空间的历史值也没有可靠的迁移依据)。
- --
- -- 幂等:六个阶段全部可重复执行。第二次运行时 PHASE 3 清 0 行、PHASE 4 全部跳过。
- --
- -- 安全边界:
- -- · RESET 表为**硬编码白名单**,绝不使用 LIKE 'ado_s8_%'(否则将来新增的表会被无声删掉)。
- -- · 只清 S8 自己的运行数据。SysUser / SysRole / SysUserRole / SysOrg /
- -- DepartmentMaster / EmployeeMaster 一行不动。
- -- · 配置态表(exception_type / scene_config / notification_layer / role_permission_config /
- -- dashboard_cell_config / watch_rule 的参数列)一行不动。
- -- · 审批流表按 **BizType 白名单**定点删除 S8 自己发起的实例与任务,
- -- 绝不整表清空(ApprovalFlowInstance / Task 由 S5/S6/S7 共用)。
- -- =====================================================================================
- -- ─────────────────────────────────────────────────────────────────────────────────────
- -- PHASE 1 · PREFLIGHT
- -- 目的:把「执行前长什么样」原样记下来。出问题时这张表是唯一的现场。
- -- ─────────────────────────────────────────────────────────────────────────────────────
- CREATE TABLE IF NOT EXISTS `ado_s8_sysuser_only_reset_1_0_496` (
- `id` BIGINT NOT NULL AUTO_INCREMENT,
- `phase` VARCHAR(16) NOT NULL COMMENT 'BEFORE / AFTER',
- `table_name` VARCHAR(96) NOT NULL,
- `table_class` VARCHAR(32) NOT NULL COMMENT 'RUNTIME / FLOW_RUNTIME / POLICY_RUNTIME_STATE',
- `row_count` BIGINT NOT NULL,
- `noted_at` DATETIME NOT NULL,
- PRIMARY KEY (`id`),
- KEY `idx_reset_1_0_496_phase` (`phase`, `table_name`)
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='1.0.496 CLEAN RESET 前后行数留证';
- -- 重复执行时先清掉上一轮的留证,避免同一 phase 出现多份互相矛盾的计数。
- DELETE FROM `ado_s8_sysuser_only_reset_1_0_496`;
- -- ── RESET 白名单(就是下面这 7 张,没有第 8 张)──
- INSERT INTO `ado_s8_sysuser_only_reset_1_0_496` (`phase`,`table_name`,`table_class`,`row_count`,`noted_at`)
- SELECT 'BEFORE','ado_s8_exception','RUNTIME',COUNT(*),NOW() FROM `ado_s8_exception`
- UNION ALL SELECT 'BEFORE','ado_s8_exception_timeline','RUNTIME',COUNT(*),NOW() FROM `ado_s8_exception_timeline`
- UNION ALL SELECT 'BEFORE','ado_s8_detection_log','RUNTIME',COUNT(*),NOW() FROM `ado_s8_detection_log`
- UNION ALL SELECT 'BEFORE','ado_s8_rule_detection_state','RUNTIME',COUNT(*),NOW() FROM `ado_s8_rule_detection_state`
- UNION ALL SELECT 'BEFORE','ado_s8_notification_log','RUNTIME',COUNT(*),NOW() FROM `ado_s8_notification_log`
- UNION ALL SELECT 'BEFORE','ado_s8_evidence','RUNTIME',COUNT(*),NOW() FROM `ado_s8_evidence`
- UNION ALL SELECT 'BEFORE','ado_s8_decision_record','RUNTIME',COUNT(*),NOW() FROM `ado_s8_decision_record`;
- -- 审批流:只统计 S8 自己的三种 BizType,别的模块的行不在本次视野里。
- INSERT INTO `ado_s8_sysuser_only_reset_1_0_496` (`phase`,`table_name`,`table_class`,`row_count`,`noted_at`)
- SELECT 'BEFORE','ApprovalFlowInstance(S8 BizType)','FLOW_RUNTIME',COUNT(*),NOW()
- FROM `ApprovalFlowInstance`
- WHERE `BizType` IN ('EXCEPTION_REPORT','EXCEPTION_ESCALATION','EXCEPTION_CLOSURE')
- UNION ALL
- SELECT 'BEFORE','ApprovalFlowTask(S8 BizType)','FLOW_RUNTIME',COUNT(*),NOW()
- FROM `ApprovalFlowTask` t
- JOIN `ApprovalFlowInstance` i ON i.`Id` = t.`InstanceId`
- WHERE i.`BizType` IN ('EXCEPTION_REPORT','EXCEPTION_ESCALATION','EXCEPTION_CLOSURE');
- -- 规则:只清运行态列,参数列(含租户已配的默认部门)保持不动,见 PHASE 3 说明。
- INSERT INTO `ado_s8_sysuser_only_reset_1_0_496` (`phase`,`table_name`,`table_class`,`row_count`,`noted_at`)
- SELECT 'BEFORE','ado_s8_watch_rule(last_run_at NOT NULL)','POLICY_RUNTIME_STATE',COUNT(*),NOW()
- FROM `ado_s8_watch_rule` WHERE `last_run_at` IS NOT NULL;
- -- ─────────────────────────────────────────────────────────────────────────────────────
- -- PHASE 2 · ARCHIVE / DIAGNOSTICS
- -- 不做整表备份(业务已确认旧数据无保留要求,全量备份只会把混装 ID 的脏数据换个地方留着)。
- -- 只留一份**证据摘要**:这次到底删掉了什么形状的身份数据。
- -- ─────────────────────────────────────────────────────────────────────────────────────
- CREATE TABLE IF NOT EXISTS `ado_s8_idspace_evidence_1_0_496` (
- `id` BIGINT NOT NULL AUTO_INCREMENT,
- `metric` VARCHAR(96) NOT NULL,
- `metric_value` BIGINT NOT NULL,
- `noted_at` DATETIME NOT NULL,
- PRIMARY KEY (`id`)
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='1.0.496 清理前的 ID 空间混用取证';
- DELETE FROM `ado_s8_idspace_evidence_1_0_496`;
- -- 只在旧列还在时取证;改过名之后这些指标已无处可取,跳过即可(幂等)。
- SET @has_old_assignee := (SELECT COUNT(*) FROM information_schema.COLUMNS
- WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ado_s8_exception' AND COLUMN_NAME = 'assignee_id');
- SET @sql := IF(@has_old_assignee = 1, '
- INSERT INTO `ado_s8_idspace_evidence_1_0_496` (`metric`,`metric_value`,`noted_at`)
- SELECT ''assignee_notnull'', COUNT(*), NOW() FROM `ado_s8_exception` WHERE `assignee_id` IS NOT NULL
- UNION ALL SELECT ''assignee_resolves_employee'', COUNT(*), NOW()
- FROM `ado_s8_exception` e JOIN `EmployeeMaster` m ON m.`RecID` = e.`assignee_id`
- UNION ALL SELECT ''assignee_resolves_sysuser'', COUNT(*), NOW()
- FROM `ado_s8_exception` e JOIN `SysUser` u ON u.`Id` = e.`assignee_id`
- UNION ALL SELECT ''assignee_dangling'', COUNT(*), NOW()
- FROM `ado_s8_exception` e
- WHERE e.`assignee_id` IS NOT NULL
- AND NOT EXISTS (SELECT 1 FROM `EmployeeMaster` m WHERE m.`RecID` = e.`assignee_id`)
- AND NOT EXISTS (SELECT 1 FROM `SysUser` u WHERE u.`Id` = e.`assignee_id`)
- ', 'DO 0');
- PREPARE s FROM @sql; EXECUTE s; DEALLOCATE PREPARE s;
- -- ─────────────────────────────────────────────────────────────────────────────────────
- -- PHASE 3 · RUNTIME DATA RESET
- --
- -- 删除顺序:明细 → 主表 → 审批流。全部按白名单点名,没有一条通配。
- -- ─────────────────────────────────────────────────────────────────────────────────────
- -- ① S8 异常运行数据
- DELETE FROM `ado_s8_exception_timeline`;
- DELETE FROM `ado_s8_evidence`;
- DELETE FROM `ado_s8_decision_record`;
- DELETE FROM `ado_s8_exception`;
- -- ② 规则检测运行数据(不清会让规则以为"这些对象我已经报过了",重启后一条都建不出来)
- DELETE FROM `ado_s8_detection_log`;
- DELETE FROM `ado_s8_rule_detection_state`;
- -- ③ 通知投递日志(全部指向已删除的异常)
- DELETE FROM `ado_s8_notification_log`;
- -- ④ 审批流:**只删 S8 自己的三种 BizType**。
- -- ApprovalFlowInstance / ApprovalFlowTask 由 S5/S6/S7 共用,整表清空会毁掉别的模块。
- -- 先删子表(Task)再删父表(Instance),否则 Task 会成为查不到来源的孤儿。
- DELETE t FROM `ApprovalFlowTask` t
- JOIN `ApprovalFlowInstance` i ON i.`Id` = t.`InstanceId`
- WHERE i.`BizType` IN ('EXCEPTION_REPORT','EXCEPTION_ESCALATION','EXCEPTION_CLOSURE');
- DELETE FROM `ApprovalFlowInstance`
- WHERE `BizType` IN ('EXCEPTION_REPORT','EXCEPTION_ESCALATION','EXCEPTION_CLOSURE');
- -- ⑤ 规则的**运行态列**清零。
- -- 刻意只清运行态、不动参数列,也不删行:
- -- · watch_rule 上没有任何人员身份列,本次改造不要求重建它;
- -- · 删行会连带删掉租户已配的默认部门(实测 2 个租户已配 245 / 253),
- -- 而 Provisioning 只能把它们恢复成 NULL —— 那是纯粹的损失,换不来任何东西;
- -- · 也会删掉无代码定义的孤儿行,与 S8RuleProvisioningService「孤儿不删、不改、不启用」相矛盾。
- -- enabled 一列同样不碰:真库 11 行当前全部为 0,Provisioning 新建行也恒为 false,
- -- 因此 reset 之后 Rule 01 仍然是停用状态 —— §十八 要的就是这个结果。
- UPDATE `ado_s8_watch_rule`
- SET `last_run_at` = NULL,
- `last_status` = NULL,
- `last_error` = NULL,
- `last_duration_ms` = NULL,
- `last_run_id` = NULL,
- `next_run_at` = NULL,
- `lock_token` = NULL,
- `locked_by` = NULL,
- `lock_until` = NULL,
- `running_started_at` = NULL,
- `consecutive_failure_count` = 0
- WHERE `last_run_at` IS NOT NULL
- OR `last_status` IS NOT NULL
- OR `last_run_id` IS NOT NULL
- OR `lock_token` IS NOT NULL
- OR `running_started_at` IS NOT NULL
- OR `consecutive_failure_count` <> 0;
- -- ─────────────────────────────────────────────────────────────────────────────────────
- -- PHASE 4 · SCHEMA IDENTITY CHANGE
- --
- -- 三种到场状态都要能收敛(幂等):
- -- (a) 只有旧列 → CHANGE COLUMN 改名
- -- (b) 新旧列同时存在 → CodeFirst 已经把新列加进来了;此时数据已在 PHASE 3 清空,
- -- 直接丢掉旧列即可,不需要也不应该做数据搬运
- -- (c) 只有新列 → 已经改过,跳过
- -- ─────────────────────────────────────────────────────────────────────────────────────
- -- ── ado_s8_exception.assignee_id → assignee_user_id ──
- SET @old := (SELECT COUNT(*) FROM information_schema.COLUMNS
- WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME='ado_s8_exception' AND COLUMN_NAME='assignee_id');
- SET @new := (SELECT COUNT(*) FROM information_schema.COLUMNS
- WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME='ado_s8_exception' AND COLUMN_NAME='assignee_user_id');
- SET @sql := CASE
- WHEN @old = 1 AND @new = 0 THEN 'ALTER TABLE `ado_s8_exception` CHANGE COLUMN `assignee_id` `assignee_user_id` BIGINT NULL COMMENT ''处理人账号Id(SysUser.Id)'''
- WHEN @old = 1 AND @new = 1 THEN 'ALTER TABLE `ado_s8_exception` DROP COLUMN `assignee_id`'
- ELSE 'DO 0' END;
- PREPARE s FROM @sql; EXECUTE s; DEALLOCATE PREPARE s;
- -- ── ado_s8_exception.verifier_id → verifier_user_id ──
- SET @old := (SELECT COUNT(*) FROM information_schema.COLUMNS
- WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME='ado_s8_exception' AND COLUMN_NAME='verifier_id');
- SET @new := (SELECT COUNT(*) FROM information_schema.COLUMNS
- WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME='ado_s8_exception' AND COLUMN_NAME='verifier_user_id');
- SET @sql := CASE
- WHEN @old = 1 AND @new = 0 THEN 'ALTER TABLE `ado_s8_exception` CHANGE COLUMN `verifier_id` `verifier_user_id` BIGINT NULL COMMENT ''检验人账号Id(SysUser.Id)'''
- WHEN @old = 1 AND @new = 1 THEN 'ALTER TABLE `ado_s8_exception` DROP COLUMN `verifier_id`'
- ELSE 'DO 0' END;
- PREPARE s FROM @sql; EXECUTE s; DEALLOCATE PREPARE s;
- -- ── ado_s8_exception.reporter_id → reporter_user_id ──
- SET @old := (SELECT COUNT(*) FROM information_schema.COLUMNS
- WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME='ado_s8_exception' AND COLUMN_NAME='reporter_id');
- SET @new := (SELECT COUNT(*) FROM information_schema.COLUMNS
- WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME='ado_s8_exception' AND COLUMN_NAME='reporter_user_id');
- SET @sql := CASE
- WHEN @old = 1 AND @new = 0 THEN 'ALTER TABLE `ado_s8_exception` CHANGE COLUMN `reporter_id` `reporter_user_id` BIGINT NULL COMMENT ''提报人账号Id(SysUser.Id)'''
- WHEN @old = 1 AND @new = 1 THEN 'ALTER TABLE `ado_s8_exception` DROP COLUMN `reporter_id`'
- ELSE 'DO 0' END;
- PREPARE s FROM @sql; EXECUTE s; DEALLOCATE PREPARE s;
- -- ── ado_s8_exception_timeline.operator_id → operator_user_id ──
- SET @old := (SELECT COUNT(*) FROM information_schema.COLUMNS
- WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME='ado_s8_exception_timeline' AND COLUMN_NAME='operator_id');
- SET @new := (SELECT COUNT(*) FROM information_schema.COLUMNS
- WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME='ado_s8_exception_timeline' AND COLUMN_NAME='operator_user_id');
- SET @sql := CASE
- WHEN @old = 1 AND @new = 0 THEN 'ALTER TABLE `ado_s8_exception_timeline` CHANGE COLUMN `operator_id` `operator_user_id` BIGINT NULL COMMENT ''操作人账号Id(SysUser.Id);系统动作为 NULL'''
- WHEN @old = 1 AND @new = 1 THEN 'ALTER TABLE `ado_s8_exception_timeline` DROP COLUMN `operator_id`'
- ELSE 'DO 0' END;
- PREPARE s FROM @sql; EXECUTE s; DEALLOCATE PREPARE s;
- -- ── 索引重建 ──
- --
- -- 这里有一个**实测踩到过的坑**,不要简化成「索引名不存在才建」:
- -- CodeFirst 若先于本脚本把 assignee_user_id 建了出来,PHASE 4 会走 (b) DROP COLUMN 分支,
- -- 而 MySQL 在 DROP COLUMN 时会把该列**从复合索引里摘掉、索引本身保留**。
- -- 结果是 idx_s8_exception_assignee_status 退化成 (tenant_id, factory_id, status, is_deleted) ——
- -- 索引名还在,覆盖的列却少了最关键的那一个,「我的待办」从此走不到索引,
- -- 而且没有任何报错。因此判据必须是「索引里有没有那一列」,不是「索引名在不在」。
- SET @has_col := (SELECT COUNT(*) FROM information_schema.STATISTICS
- WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME='ado_s8_exception'
- AND INDEX_NAME='idx_s8_exception_assignee_status' AND COLUMN_NAME='assignee_user_id');
- SET @has_idx := (SELECT COUNT(DISTINCT INDEX_NAME) FROM information_schema.STATISTICS
- WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME='ado_s8_exception'
- AND INDEX_NAME='idx_s8_exception_assignee_status');
- SET @sql := IF(@has_idx = 1 AND @has_col = 0,
- 'ALTER TABLE `ado_s8_exception` DROP INDEX `idx_s8_exception_assignee_status`', '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_exception'
- AND INDEX_NAME='idx_s8_exception_assignee_status');
- SET @sql := IF(@has_idx = 0,
- 'ALTER TABLE `ado_s8_exception` ADD INDEX `idx_s8_exception_assignee_status` (`tenant_id`,`factory_id`,`assignee_user_id`,`status`,`is_deleted`)',
- 'DO 0');
- PREPARE s FROM @sql; EXECUTE s; DEALLOCATE PREPARE s;
- SET @has_col := (SELECT COUNT(*) FROM information_schema.STATISTICS
- WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME='ado_s8_exception'
- AND INDEX_NAME='idx_s8_exception_verifier_status' AND COLUMN_NAME='verifier_user_id');
- SET @has_idx := (SELECT COUNT(DISTINCT INDEX_NAME) FROM information_schema.STATISTICS
- WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME='ado_s8_exception'
- AND INDEX_NAME='idx_s8_exception_verifier_status');
- SET @sql := IF(@has_idx = 1 AND @has_col = 0,
- 'ALTER TABLE `ado_s8_exception` DROP INDEX `idx_s8_exception_verifier_status`', '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_exception'
- AND INDEX_NAME='idx_s8_exception_verifier_status');
- SET @sql := IF(@has_idx = 0,
- 'ALTER TABLE `ado_s8_exception` ADD INDEX `idx_s8_exception_verifier_status` (`tenant_id`,`factory_id`,`verifier_user_id`,`status`,`is_deleted`)',
- 'DO 0');
- PREPARE s FROM @sql; EXECUTE s; DEALLOCATE PREPARE s;
- -- ─────────────────────────────────────────────────────────────────────────────────────
- -- PHASE 5 · POSTCHECK
- -- 任何一条不成立就抛错中止,不让"半成功"通过。
- -- ─────────────────────────────────────────────────────────────────────────────────────
- INSERT INTO `ado_s8_sysuser_only_reset_1_0_496` (`phase`,`table_name`,`table_class`,`row_count`,`noted_at`)
- SELECT 'AFTER','ado_s8_exception','RUNTIME',COUNT(*),NOW() FROM `ado_s8_exception`
- UNION ALL SELECT 'AFTER','ado_s8_exception_timeline','RUNTIME',COUNT(*),NOW() FROM `ado_s8_exception_timeline`
- UNION ALL SELECT 'AFTER','ado_s8_detection_log','RUNTIME',COUNT(*),NOW() FROM `ado_s8_detection_log`
- UNION ALL SELECT 'AFTER','ado_s8_rule_detection_state','RUNTIME',COUNT(*),NOW() FROM `ado_s8_rule_detection_state`
- UNION ALL SELECT 'AFTER','ado_s8_notification_log','RUNTIME',COUNT(*),NOW() FROM `ado_s8_notification_log`
- UNION ALL SELECT 'AFTER','ado_s8_evidence','RUNTIME',COUNT(*),NOW() FROM `ado_s8_evidence`
- UNION ALL SELECT 'AFTER','ado_s8_decision_record','RUNTIME',COUNT(*),NOW() FROM `ado_s8_decision_record`;
- INSERT INTO `ado_s8_sysuser_only_reset_1_0_496` (`phase`,`table_name`,`table_class`,`row_count`,`noted_at`)
- SELECT 'AFTER','ApprovalFlowInstance(S8 BizType)','FLOW_RUNTIME',COUNT(*),NOW()
- FROM `ApprovalFlowInstance`
- WHERE `BizType` IN ('EXCEPTION_REPORT','EXCEPTION_ESCALATION','EXCEPTION_CLOSURE')
- UNION ALL
- SELECT 'AFTER','ApprovalFlowTask(S8 BizType)','FLOW_RUNTIME',COUNT(*),NOW()
- FROM `ApprovalFlowTask` t
- JOIN `ApprovalFlowInstance` i ON i.`Id` = t.`InstanceId`
- WHERE i.`BizType` IN ('EXCEPTION_REPORT','EXCEPTION_ESCALATION','EXCEPTION_CLOSURE')
- UNION ALL
- SELECT 'AFTER','ado_s8_watch_rule(last_run_at NOT NULL)','POLICY_RUNTIME_STATE',COUNT(*),NOW()
- FROM `ado_s8_watch_rule` WHERE `last_run_at` IS NOT NULL;
- -- ① 白名单表必须为 0
- SET @left := (SELECT COALESCE(SUM(`row_count`),0) FROM `ado_s8_sysuser_only_reset_1_0_496` WHERE `phase`='AFTER');
- DROP TABLE IF EXISTS `tmp_s8_496_guard`;
- CREATE TABLE `tmp_s8_496_guard` (
- probe BIGINT NOT NULL,
- CONSTRAINT s8_496_BLOCKED_reset_tables_not_empty CHECK (probe = 0)
- ) COMMENT '1.0.496 断言门禁;通过后自动删除';
- INSERT INTO `tmp_s8_496_guard` (probe) VALUES (@left);
- DROP TABLE IF EXISTS `tmp_s8_496_guard`;
- -- ② 四个身份列必须已改名到位(新列在、旧列不在)
- SET @bad := (SELECT COUNT(*) FROM information_schema.COLUMNS
- WHERE TABLE_SCHEMA = DATABASE()
- AND ((TABLE_NAME='ado_s8_exception' AND COLUMN_NAME IN ('assignee_id','verifier_id','reporter_id'))
- OR (TABLE_NAME='ado_s8_exception_timeline' AND COLUMN_NAME='operator_id')));
- DROP TABLE IF EXISTS `tmp_s8_496_guard`;
- CREATE TABLE `tmp_s8_496_guard` (
- probe BIGINT NOT NULL,
- CONSTRAINT s8_496_BLOCKED_old_identity_column_still_present CHECK (probe = 0)
- ) COMMENT '1.0.496 断言门禁;通过后自动删除';
- INSERT INTO `tmp_s8_496_guard` (probe) VALUES (@bad);
- DROP TABLE IF EXISTS `tmp_s8_496_guard`;
- SET @ok := (SELECT COUNT(*) FROM information_schema.COLUMNS
- WHERE TABLE_SCHEMA = DATABASE()
- AND ((TABLE_NAME='ado_s8_exception' AND COLUMN_NAME IN ('assignee_user_id','verifier_user_id','reporter_user_id'))
- OR (TABLE_NAME='ado_s8_exception_timeline' AND COLUMN_NAME='operator_user_id')));
- DROP TABLE IF EXISTS `tmp_s8_496_guard`;
- CREATE TABLE `tmp_s8_496_guard` (
- probe BIGINT NOT NULL,
- CONSTRAINT s8_496_BLOCKED_new_identity_column_missing CHECK (probe = 0)
- ) COMMENT '1.0.496 断言门禁;通过后自动删除';
- INSERT INTO `tmp_s8_496_guard` (probe) VALUES ((4 - @ok));
- DROP TABLE IF EXISTS `tmp_s8_496_guard`;
- -- ③ 两个身份索引必须真的覆盖身份列(不是「索引名还在」就算数,见 PHASE 4 的坑)
- SET @idx_ok := (SELECT COUNT(*) FROM information_schema.STATISTICS
- WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME='ado_s8_exception'
- AND ((INDEX_NAME='idx_s8_exception_assignee_status' AND COLUMN_NAME='assignee_user_id')
- OR (INDEX_NAME='idx_s8_exception_verifier_status' AND COLUMN_NAME='verifier_user_id')));
- DROP TABLE IF EXISTS `tmp_s8_496_guard`;
- CREATE TABLE `tmp_s8_496_guard` (
- probe BIGINT NOT NULL,
- CONSTRAINT s8_496_BLOCKED_identity_index_lost_its_column CHECK (probe = 0)
- ) COMMENT '1.0.496 断言门禁;通过后自动删除';
- INSERT INTO `tmp_s8_496_guard` (probe) VALUES ((2 - @idx_ok));
- DROP TABLE IF EXISTS `tmp_s8_496_guard`;
- -- ④ 平台主数据与配置态一行都不能少 —— 这是本脚本最重要的一条断言。
- -- RESET 允许清运行数据,但只要下面任何一张表被清空,就说明白名单被写错了。
- SET @masters := (SELECT (SELECT COUNT(*) FROM `SysUser`)
- + (SELECT COUNT(*) FROM `SysRole`)
- + (SELECT COUNT(*) FROM `EmployeeMaster`)
- + (SELECT COUNT(*) FROM `DepartmentMaster`));
- DROP TABLE IF EXISTS `tmp_s8_496_guard`;
- CREATE TABLE `tmp_s8_496_guard` (
- probe BIGINT NOT NULL,
- CONSTRAINT s8_496_BLOCKED_platform_master_wiped CHECK (probe = 0)
- ) COMMENT '1.0.496 断言门禁;通过后自动删除';
- INSERT INTO `tmp_s8_496_guard` (probe) VALUES (IF(@masters = 0, 1, 0));
- DROP TABLE IF EXISTS `tmp_s8_496_guard`;
- SET @configs := (SELECT (SELECT COUNT(*) FROM `ado_s8_exception_type`)
- + (SELECT COUNT(*) FROM `ado_s8_scene_config`)
- + (SELECT COUNT(*) FROM `ado_s8_notification_layer`)
- + (SELECT COUNT(*) FROM `ado_s8_role_permission_config`)
- + (SELECT COUNT(*) FROM `ado_s8_watch_rule`));
- DROP TABLE IF EXISTS `tmp_s8_496_guard`;
- CREATE TABLE `tmp_s8_496_guard` (
- probe BIGINT NOT NULL,
- CONSTRAINT s8_496_BLOCKED_product_config_wiped CHECK (probe = 0)
- ) COMMENT '1.0.496 断言门禁;通过后自动删除';
- INSERT INTO `tmp_s8_496_guard` (probe) VALUES (IF(@configs = 0, 1, 0));
- DROP TABLE IF EXISTS `tmp_s8_496_guard`;
- -- ─────────────────────────────────────────────────────────────────────────────────────
- -- PHASE 6 · IDEMPOTENT RERUN GUARD
- -- 第二次执行时:PHASE 3 的 DELETE 命中 0 行、PHASE 4 全部落到 'DO 0'、
- -- PHASE 5 的断言依旧成立。下面这条只是把「本脚本已收敛」这件事也记进留证表。
- -- ─────────────────────────────────────────────────────────────────────────────────────
- INSERT INTO `ado_s8_sysuser_only_reset_1_0_496` (`phase`,`table_name`,`table_class`,`row_count`,`noted_at`)
- VALUES ('AFTER','__migration_converged__','GUARD',1,NOW());
|