# 数据库迁移与清理独立复核 复核日期:2026-09-05。本附录只读取迁移文件、全量 DDL、`tools/review-db.py` 和执行 JSON,并做静态比较;没有再次连接数据库、执行迁移或修改业务数据。实际执行证据来自主审查生成的 [database-migration.json](database-migration.json) 和 [cleanup.json](cleanup.json)。 ## 结论与原始缺陷 原 034 为运行、运行成员和事件建立独立唯一索引,再以组合外键引用。主执行者在目标 KingbaseES `V008R006C009B0014` 的隔离 schema 验证发现,该版本要求外键目标为显式 `UNIQUE` 约束,仅有独立唯一索引时创建步骤表失败,报外键没有匹配唯一约束(SQLSTATE `42830`)。这会阻断新步骤事实表部署。 修复新增 `20260905_035_teaching_step_run_kingbase_constraints.sql`:在 005 基础结构和 033 后执行,以 035 替代 034;历史 034 文件保持原样。035 使用系统目录判断并创建三个 `_aggregate_fk` 显式唯一约束,再幂等建立步骤表和查询索引。它不包含业务行更新、删除或历史事实回填。 部署文档已同步为当前开发库执行 033、035;其他环境必须独立核对迁移状态。035 含 `DO $$...$$`,执行客户端必须正确识别 dollar quote,不能简单按分号切分。 ## 035 与全量 DDL 一致性 独立比较 `035` 与 `database/unreal_tran_full.sql` 的步骤表定义,除 `IF NOT EXISTS` 和空白格式外完全一致。 | 对照项 | 结果 | |---|---| | 父表聚合 UNIQUE 约束 | 3 个;名称与列顺序一致:运行 `(id, assignment_id)`、运行成员 `(id, run_id)`、事件 `(id, run_id, assignment_id)` | | 步骤表外键 | 4 个:任务、运行归属、运行成员归属、事件归属;均为 `RESTRICT` | | 步骤活动唯一键 | 2 个:`(run_id, step_code, delete_token)` 和 `(run_id, step_order, delete_token)` | | CHECK 约束 | 11 个,覆盖顺序、目标进度、状态、尝试/失败次数、证据来源、时间、回填标记、版本和软删除字段 | | 步骤查询索引 | 3 个,按任务状态、运行当前步骤和最近事件查询 | 父表新增组合唯一键都包含已有主键 `id`,不改变既有行的业务唯一性。步骤表字段和四个外键引用列一致,未发现本次修复会破坏既有业务行或放松运行归属校验的问题。 ## 实际执行与恢复验证边界 [database-migration.json](database-migration.json) 记录的目标为开发环境 `unreal_tran/public`,迁移前记录时间为 `2026-09-05T11:22:17.562488`。执行文件指纹与复核时工作区文件一致: | 文件 | SHA-256 | |---|---| | `20260904_033_teaching_command_receipt.sql` | `3b62137942c54d2fc50f6ee9975d20c44f8d2b240d8fff0c153970052e5b8d43` | | `20260905_035_teaching_step_run_kingbase_constraints.sql` | `53d0baae9b90e37172277dd5e346542cce3d95b02595dc94e742c4ecaa8e3c31` | 执行工具先读取本地开发配置并核对数据库/schema,再执行以下路径: 1. 在专用隔离 schema 建立最小父表,首次完整执行 033、035,再完整重跑。 2. 在另一专用隔离 schema 模拟“033 回执表已提交、035 首个父表唯一约束已提交”的中断状态,再完整执行和重跑。 3. 隔离检查成功后,切回 `public` 执行 033、035。 JSON 中上述各路径的无效记录和孤儿记录检查均为 0。当前执行工具也会在迁移后检查出现非零结果时抛错,避免继续报告成功。隔离验证证明的是这两个增量在最小父表上的首次、重复和指定中断点恢复;它不是全量建库脚本、任意中断点或历史数据回填的验收。 此执行记录只证明所列开发环境已迁移,不表示其他开发实例、测试库或生产库已执行。 ## 备份与测试数据清理 执行前的 `database-before.json` 位于工作区 `.codex-tmp/review-20260905/`,复核确认文件存在。它保存任务、运行、运行成员、事件四张相关表的既有数据以及列、索引元数据,是本次扩展式迁移的本地对照快照;不是整库物理备份,也未据此宣称完成整库恢复演练。报告不复制快照中的业务明细或连接凭据。 清理工具按 `UTE2E-REVIEW-` 测试任务前缀选取记录,并排除迁移前快照已有任务 ID。删除在同一事务中按外键依赖顺序执行:先步骤事实、命令回执及运行附属测试数据,再事件、干预/故障关联、运行成员、运行、教学成员和任务。`ut_teaching_run.member_id` 引用的教学成员在运行删除之后处理。 测试用户按本轮时间及测试标识筛选,临时管理员另按本地记录的精确 ID、用户名和非保护身份确认。清理撤销其会话,并软删除角色关系、凭证与用户,同时停用用户、递增令牌版本;并非物理删除所有审计记录。临时管理员明文凭据文件在清理后移除。 复核读取的清理记录中,四张快照表既有行的 `originalRowsUnchanged` 均为 `true`,`remainingActiveTestUsers` 为 0。该比较只覆盖这四张表的快照既有行,不代表逐字比较了整库全部数据。重复截图测试后的最终清理时间、任务列表和数量以最新 [cleanup.json](cleanup.json) 为准,本附录不固定清理数量。 未发现当前执行记录反映出误删既有业务数据或约束损坏。复核提出的清理验收建议已落实:工具写出记录后,若任一既有行对照不一致或仍有活动测试用户,就以错误退出。主任务负责最终清理和主报告汇总。