# 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 设定为执行流水线第一道强制关卡。 - **验证方式**:审查探针脚本的幂等性与零副作用安全性。 - **是否解决**:**已彻底解决**。