Files
railwaysystem/docs/database-v2/07_review_round2_changes.md
T

143 lines
9.3 KiB
Markdown
Raw Normal View History

# RailwaySystem 数据库 V2 DDL Review Round 2 变更记录 (最终静态收尾)
> **修订阶段**:数据库 V2 DDL Review Round 2(最终静态收尾)
> **最高依据**:`RailwaySystem 数据库 V2 字段设计与 Kingbase DDL 规范 V1.0.md`
> **修订目标**:解决高级技术负责人 Final Static Review 提出的 7 个核心静态问题(R2-01 ~ R2-07),使数据库 V2 DDL 达到“允许进入测试迁移”状态。
---
## 变更明细清单 (R2-01 ~ R2-07)
### R2-01: 修正住宿状态默认值
- **问题**:`train_accommodation.accommodation_status` 原默认值为 `1`(已入住)。而正式业务流程为:名单确定 -> 报到前宿舍预分配 -> 学员报到核验 -> 展示已分配宿舍 -> 实际入住。若默认直接为已入住,与真实业务全生命周期相悖。
- **原实现**:
```sql
accommodation_status SMALLINT NOT NULL DEFAULT 1
```
- **修改文件**:
- [`002_create_new_tables.sql`](../../RuoYi-Vue/sql/train/v2/002_create_new_tables.sql#L142)
- [`010_verify.sql`](../../RuoYi-Vue/sql/train/v2/010_verify.sql)
- **修改后实现**:
```sql
accommodation_status SMALLINT NOT NULL DEFAULT 0
```
字段注释保持为:`住宿状态 (0:预分配, 1:已入住, 2:已退宿)`;`010_verify.sql` 中新增对 `accommodation_status` 默认值精确为 0 的强断言。
- **验证方式**:静态文本检索与 `010_verify.sql` 自动化断言规则核验。
- **是否解决**:**已彻底解决**。
---
### R2-02: 删除“业务选择型默认值”
- **问题**:原 SQL 中对部分必须由调用方主动选择的业务输入字段设置了 `DEFAULT 0` 或 `DEFAULT 1`,特别是 `verify_result DEFAULT 1`(核验通过),会导致应用层若漏传该参数,数据库自动判定通过,掩盖业务逻辑缺陷;同样,考勤方式、场地类型、住宿需求、来源类型等必须由业务明确提供,不允许数据库代替调用方猜测。
- **原实现**:
- `train_enrollment.source_type`: `DEFAULT 1`
- `train_enrollment.accommodation_required`: `DEFAULT 0`
- `train_lesson.venue_type`: `DEFAULT 1`
- `train_student_score.source_type`: `DEFAULT 1`
- `train_student_checkin.checkin_way`: `DEFAULT 1`
- `train_class_report.verify_result`: `DEFAULT 1`
- **修改文件**:
- [`002_create_new_tables.sql`](../../RuoYi-Vue/sql/train/v2/002_create_new_tables.sql)
- [`004_rebuild_business_tables.sql`](../../RuoYi-Vue/sql/train/v2/004_rebuild_business_tables.sql)
- [`010_verify.sql`](../../RuoYi-Vue/sql/train/v2/010_verify.sql)
- **修改后实现**:
上述 6 个字段全部移除 `DEFAULT` 声明,仅保留 `SMALLINT NOT NULL`。`010_verify.sql` 中增加只读断言:上述字段的 `column_default` 必须全部为 `NULL`,若存在默认值当场抛异常。
- **验证方式**:静态 grep 全局检索与 `010_verify.sql` 断言校验。
- **是否解决**:**已彻底解决**。
---
### R2-03: 清除 Enrollment 来源的臆造枚举并解耦双 source_type
- **问题**:`train_enrollment.source_type` 注释中原先硬编码了“1:组织指派, 2:自主报名, 3:单位推荐, 4:统一调训”,这属于未经正式会议冻结的臆造业务流程枚举;且容易与 `train_student.source_type`(学员长期主档来源)发生混淆。
- **原实现**:
```sql
COMMENT ON COLUMN train_enrollment.source_type IS '进入本期培训名单的来源 (1:组织指派, 2:自主报名, 3:单位推荐, 4:统一调训)';
```
- **修改文件**:
- [`002_create_new_tables.sql`](../../RuoYi-Vue/sql/train/v2/002_create_new_tables.sql#L94)
- [`04_risk_review.md`](04_risk_review.md)
- [`01_table_inventory.md`](01_table_inventory.md)
- **修改后实现**:
- 注释修改为:
```sql
COMMENT ON COLUMN train_enrollment.source_type IS '参训名单来源,具体字典值由 train_enrollment_source 定义';
```
- 文档中明确解耦定义:
1. `train_student.source_type`:学员长期主档进入系统的来源(管理员录入/Excel导入/平台同步);
2. `train_enrollment.source_type`:人员进入“本期培训名单”的来源,由独立字典承载,严禁共用同一模糊枚举。
- **验证方式**:审查注释与文档的一致性。
- **是否解决**:**已彻底解决**。
---
### R2-04: Preflight 升级为真正 Phase 0.5 精确骨架校验
- **问题**:原 `000_preflight.sql` 仅做了表存在性、行数和少量代表性约束判断,未能覆盖完整的 Phase 0.5 物理结构骨架,若存在“少一个正确FK + 多一个错误FK”的对冲情况可能假阳性通过。
- **原实现**:部分抽检 + 触发器循环判断。
- **修改文件**:
- [`000_preflight.sql`](../../RuoYi-Vue/sql/train/v2/000_preflight.sql)
- **修改后实现**:
采用 CTE `VALUES(...)` 构造预期基线集合,与系统目录做全集合差集比对:
- 14 个旧表 PK (table_name + constraint_name + column_name)
- 6 个 Phase 0.5 identity 自增主键 (table_name + column_name + is_identity = 'YES')
- 7 组业务 UNIQUE 约束 (table_name + constraint_name + column_list)
- 24 组业务 FK 约束 (table_name + constraint_name + column_name + foreign_table_name + foreign_column_name)
- 9 个 update_time 触发器 (table_name + trigger_name)
- 行数基线:`train_teacher` 精确 1 行,其余 13 张旧表精确 0 行
- 8 张 V2 表均不得存在,public.sys_* 表零受损
- 任何 missing / unexpected / definition mismatch 立即抛出 `RAISE EXCEPTION`。
- **验证方式**:对比 `rollback/000_phase05_exact_schema.sql` 校验集合完备性。
- **是否解决**:**已彻底解决**。
---
### R2-05: 010_verify 升级为“定义级验收”
- **问题**:原 010 验收脚本主要核验对象名称存在性,对约束内部的具体列排序、外键删除规则、CHECK 表达式逻辑等未做定义级校验。
- **原实现**:检查约束名称与 `count(*) = 26` 等数量判断。
- **修改文件**:
- [`010_verify.sql`](../../RuoYi-Vue/sql/train/v2/010_verify.sql)
- **修改后实现**:
- **UNIQUE 验收**:必须断言 `table_name` + `constraint_name` + `column_list`(如 `train_enrollment(plan_id, student_id)`);
- **FK 验收**:必须断言源表、源列、目标表、目标列及 `delete_rule = 'NO ACTION'`,严禁 CASCADE;
- **CHECK 验收**:按约束名逐项检索表达式,校验包含预期表达式(时间先后、场地二选一、权重0~100、满分>0、得分非负、时长非负、单号系统联动、容量>0);
- **Identity 验收**:逐项确认 12 张业务表的主键列名且 `is_identity = 'YES'`;
- **Index 验收**:确认 3 组排课复合时间列排序;确认 `uq_idx_study_source_event` 的 `(source_system, source_event_id)` 及双非空 WHERE 条件;
- **默认值验收**:强断言 `accommodation_status` 默认为 0,强断言 6 个业务选择型字段无默认值。
- **验证方式**:静态代码比对与逻辑推演。
- **是否解决**:**已彻底解决**。
---
### R2-06: 正式迁移对象删除 IF NOT EXISTS
- **问题**:V2 是一次从已知 Phase 0.5 基线出发的严格版本迁移,已有 Preflight 保证环境纯净。若正式建表或建索引保留 `IF NOT EXISTS`,一旦因历史残留存在同名但结构错误的脏对象,DDL 将静默跳过真正正确的对象创建,掩盖严重隐患。
- **原实现**:
- `CREATE TABLE IF NOT EXISTS train_...` (002)
- `ADD COLUMN IF NOT EXISTS ...` (003)
- `CREATE INDEX IF NOT EXISTS ...` (006)
- **修改文件**:
- [`002_create_new_tables.sql`](../../RuoYi-Vue/sql/train/v2/002_create_new_tables.sql)
- [`003_alter_master_tables.sql`](../../RuoYi-Vue/sql/train/v2/003_alter_master_tables.sql)
- [`006_indexes.sql`](../../RuoYi-Vue/sql/train/v2/006_indexes.sql)
- **修改后实现**:
- 002 中 8 张新增表全部改为严格 `CREATE TABLE`;
- 003 中新增字段全部改为严格 `ADD COLUMN`;
- 006 中所有索引全部改为严格 `CREATE INDEX` 与 `CREATE UNIQUE INDEX`;
- 仅保留防御性卸载旧对象的 `DROP CONSTRAINT IF EXISTS` 和 `DROP TRIGGER IF EXISTS`。
- **验证方式**:全局 grep 检查 `IF NOT EXISTS` 出现位置并逐一确认合理性。
- **是否解决**:**已彻底解决**。
---
### R2-07: 增加 DDL Transaction Probe Gate
- **问题**:迁移执行契约依赖事务的原子性(`BEGIN ... COMMIT / ROLLBACK`)。当前环境为 KingbaseES V009R003C018 (`database_mode=mysql`),需在正式执行 V2 前实证验证当前数据库实例对 DDL 的事务回滚支持能力。
- **原实现**:未设立 DDL 事务能力前置探针。
- **修改文件**:
- 新增:[`RuoYi-Vue/sql/train/v2/000_tx_capability_probe.sql`](../../RuoYi-Vue/sql/train/v2/000_tx_capability_probe.sql)
- [`05_execution_contract.md`](05_execution_contract.md)
- **修改后实现**:
- 创建独立的安全探针脚本(作为 Gate A):
- **Phase A**:`BEGIN -> CREATE TABLE public.__railway_v2_tx_probe -> ALTER TABLE -> ROLLBACK`;
- **Phase B**:只读断言 `__railway_v2_tx_probe` 是否存在。若残留则强行抛出 `RAISE EXCEPTION` 阻断后续所有迁移;
- 绝不读取或修改任何业务表或系统表,仅使用专用临时对象。
- 在执行契约中将 Gate A 设定为执行流水线第一道强制关卡。
- **验证方式**:审查探针脚本的幂等性与零副作用安全性。
- **是否解决**:**已彻底解决**。