Files
railwaysystem/docs/database-v2/04_risk_review.md
T

8.7 KiB
Raw Blame History

RailwaySystem 数据库 V2 关键设计风险深度审查报告 (R-01 ~ R-17)

审查定位:数据库底层引擎兼容性、约束边界、数据一致性与应用层 Service 协同审查
技术基线:KingbaseES V009R003C018 (schema=public, database_mode=mysql)、MyBatis 3.5.x、RuoYi-Vue 基础架构
版本状态:Round 2 审查最终静态收尾版(固化架构决策 A/B,解耦 source_type 语义,清除业务选择型默认值)


风险全景概览

风险编号 风险主题 风险等级 影响范畴 本次 DDL 应对方案 应用层/Service 协同要求 决策状态
R-01 旧外键阻塞 ALTER / DROP COLUMN 高 迁移执行期阻断 在 DDL 步骤中前置显示 DROP CONSTRAINT IF EXISTS 严格按迁移编排顺序执行 已关闭
R-02 identity 主键与 MyBatis 兼容性 中 写入与自增回填 采用标准 GENERATED BY DEFAULT AS IDENTITY Mapper 继续配置 useGeneratedKeys="true" 已闭环
R-03 Kingbase 下 UNIQUE + NULL 语义 中 数据防重有效性 遵循 SQL 标准语义(NULL 不等),关键组合列强制非空 业务关键组合列强制非空或用部分索引 已闭环
R-04 id_card_hash 唯一约束对 NULL 行为 低 学员未采身份证防重 建立单列 UNIQUE,允许多行 NULL 不冲突 应用层空串 "" 必须显式转为 NULL 入库 已闭环
R-05 学习记录幂等唯一约束中 NULL 行为 中 第三方学习数据幂等 采用双非空条件部分唯一索引 + 联动 CHECK 上报接口对具备单号的数据做幂等截流 已闭环
R-06 排课多维时间冲突无法靠 UNIQUE 实现 高 教学资源重叠与冲突 建立高频复合索引支撑区间检索,增加场地互斥 CHECK 架构决策 A:排课 Service 事务校验 + 行锁防重 已冻结决策
R-07 宿舍容量超额无法靠 DB 静态约束 高 宿舍超员分配超售 废除静态 bed_used 字段,加 capacity > 0 CHECK 架构决策 A:住宿 Service 事务内 SELECT FOR UPDATE 已冻结决策
R-08 成绩项目权重总和 100% 无法靠 CHECK 中 成绩评估规则失衡 数据库仅设单行 0~100 标量 CHECK,不跨行聚合 开班与考核规则配置 Service 校验有效和等于 100% 已闭环
R-09 V2 表与 RuoYi Generator / BaseEntity 差异 中 代码生成与 CRUD 数据库采用标准蛇形命名,补齐 del_flag 等审计列 后续重构 Entity 继承 BaseEntity 并重新映射 Mapper 已闭环
R-10 现有 9 个 update_time 触发器安全移除 中 数据更新时间一致性 003 脚本显式前置 DROP TRIGGER IF EXISTS 卸载 应用层 Service 或 MyBatis 拦截器显式维护更新时间 已闭环
R-11 跨培训班核心关系一致性 (串班风险) 高 业务逻辑穿透污染 不在子表反范式冗余 plan_id,保持规范化模型 架构决策 B:Service 强制校验链条一致性 已冻结决策
R-12 班牌设备与课次教室空间一致性 中 考勤打卡位置穿透 班牌绑定教室,签到关联 lesson 与 board 考勤 Service 校验班牌物理教室与课次教室完全匹配 已闭环
R-13 宿舍容量必须考虑培训周期时间重叠 高 跨期住宿容量误判 数据库不保存跨期占用,仅靠 status 会误杀 住宿 Service 必须按学员参训 plan 起止区间算重叠有效人数 已闭环
R-14 报到流水与参训状态变更的原子性 高 状态与报到记录脱节 报到记录表与参训表解耦,依赖业务事务保证 报到操作必须在单个本地事务内执行“插报到+改状态” 已闭环
R-15 成绩记录存在性与真实 0 分语义 中 成绩录入状态失真 移除 DEFAULT 0,加非负 CHECK,无行代表未录成绩 查询综合成绩时通过左连接或外层逻辑区分未评与 0 分 已闭环
R-16 业务选择型字段违规设置数据库默认值 高 业务意图被系统静默猜测 彻底清除核验结果、考勤方式、场地类型等 DEFAULT 调用方必须显式传入真实业务值,严禁自动代填 已闭环
R-17 两处 source_type 语义混淆与臆造枚举 中 字典定义混乱与流程固化 区分主档来源与参训来源,解除未定字典硬编码 独立字典维护,学员主档与名单来源彻底解耦 已闭环

核心架构决策正式固化 (技术负责人已拍板)

架构决策 A:排课与住宿并发控制方案 (冻结)

  • 技术负责人拍板结论: 一期坚决不引入外部排课引擎、时间区间调度中间件或重型规则引擎。
  • 实施边界:
    1. 数据库层:提供针对性的多列复合索引(idx_lesson_teacher_time, idx_lesson_classroom_time, idx_lesson_site_time),确保范围查询毫秒级响应;
    2. 应用服务层:排课与调课业务由 TrainLessonService 在 @Transactional 事务内执行区间交叉判断(start_time < :newEnd AND end_time > :newStart);
    3. 并发防护:高并发场景下,使用 SELECT ... FOR UPDATE 悲观锁或分布式锁锁定教师日历/教室资源/宿舍房间,确保零时间冲突、零超售。

架构决策 B:跨培训班一致性控制规范 (防串班,冻结)

  • 技术负责人拍板结论: 坚决不为了防止业务串班而在打卡、成绩、学习记录表中重复冗余增加 plan_id,全面维持数据库 V2 的 3NF 规范化设计,防止数据更新异常与列冗余。
  • 实施边界: 由相关业务 Service 在执行写入前执行强一致性校验链条,任何不匹配必须当场抛出业务异常拒绝写入:
    1. 考勤打卡:
       checkin.enrollment.plan_id == lesson.plan_course.plan_id
    2. 成绩录入:
       student_score.enrollment.plan_id == score_item.plan_id
    3. 考核项关联课程:
       IF score_item.plan_course_id IS NOT NULL:
           score_item.plan_id == plan_course.plan_id
    4. 学习时长上报:
       study_duration.enrollment.plan_id == plan_course.plan_id
    

深度风险分析与防范对策

R-16: 业务选择型默认值删除与责任边界

  • 问题机制: 若数据库为 train_class_report.verify_result 配置 DEFAULT 1(核验通过),一旦前端或接口未传该字段,数据库将自动判定该学员“核验通过”,造成假通过的安全漏洞;同样,venue_type DEFAULT 1、source_type DEFAULT 1、checkin_way DEFAULT 1 等均属于业务必须明确作出的选择。
  • DDL 改造方案: 全面移除以下字段的默认值,强制设为 NOT NULL,迫使应用层显式传参:
    • train_enrollment.source_type SMALLINT NOT NULL
    • train_enrollment.accommodation_required SMALLINT NOT NULL
    • train_lesson.venue_type SMALLINT NOT NULL
    • train_student_score.source_type SMALLINT NOT NULL
    • train_student_checkin.checkin_way SMALLINT NOT NULL
    • train_class_report.verify_result SMALLINT NOT NULL
  • 生命周期状态说明: 生命周期的初始状态仍保留合理默认值,例如:
    • train_class_plan.plan_status: DEFAULT 0 (未开始)
    • train_enrollment.enrollment_status: DEFAULT 0 (待报到)
    • train_accommodation.accommodation_status: DEFAULT 0 (预分配,由 1 修正为 0)
    • train_dorm_auth.face_sync_status: DEFAULT 0 (待下发)
    • train_dorm_auth.retry_count: DEFAULT 0
    • train_student_checkin.manual_flag: DEFAULT 0 (自动)

R-17: 两个 source_type 语义彻底解耦

系统在两张核心表中存在同名字段 source_type,必须从业务与字典层面彻底隔离,杜绝混用:

  1. train_student.source_type (学员长期主档来源):

    • 业务含义:自然人作为学员进入铁路培训系统长期资源库的注册渠道。
    • 典型枚举:
      • 1: 管理员手工录入
      • 2: Excel 花名册批量导入
      • 3: 铁路内部人力/教务业务平台同步
    • 对应字典:train_student_source
  2. train_enrollment.source_type (参训名单进入来源):

    • 业务含义:已在库学员被选拔/加入到具体某一期培训班名单的业务途径。
    • 设计准则: 清除“自主报名/单位推荐/统一调训”等未经正式拍板的臆造流程枚举,DDL 注释明确标记:“参训名单来源,具体字典值由 train_enrollment_source 定义”。
    • 对应字典:后续由独立业务字典 train_enrollment_source 承载,两字段严禁共用同一字典。