Files
railwaysystem/docs/development/issue-01-04-space.md
T

17 KiB
Raw Blame History

ISSUE-01-04 教学空间资源维护开发验收文档 (Contract V1.0.2 CG-05 闭环版)

1. 任务概述与环境基线

  • 任务 ID: ISSUE-01-04
  • 任务名称: 教学空间资源维护(普通教室与实训场地)
  • 所属 Epic: EPIC-01 基础主数据必要集合
  • 工作区目录: /Users/wzb/Desktop/项目/RailwaySystem-worktrees/issue-01-04-space
  • 当前分支: feat/issue-01-04-space
  • 基线 Commit: 7fcaea29b8810546fd972ba0c9d366c71a357d63 (issues-v1.0)
  • API 契约基准: api-v2-contract-v1.0.2 (a979c02841b7b0f8b00c45a67c07a915fc668413)(Contract Gate 已正式解除)
  • 数据库基线: Database V2 (train_classroom_info, train_training_site)
  • Generator 状态: GENERATOR SLOT = SPACE (受控 Pilot 已完成,PASS)(完成 train_classroom_info 与 train_training_site 双阶段元数据导入与沙箱预览,严禁覆盖源码,零迁移 generated artifact)
  • 核心业务目标: 建立普通教室 (Classroom) 与专业实训场地 (TrainingSite) 的主数据档案,保证物理主键标识(classRoomId, siteId)的稳定性与唯一性,为后续排课冲突检测与课表编排提供可靠实体空间校验支撑。

2. HTTP Endpoint 最终收口与物理映射

严格依照冻结契约 docs/api-v2/02_module_a_base_resource_api.md 4.1 与 4.2 节收口 Controller 公开接口,非冻结接口全部从 Controller 移除,不再对外公开:

2.1 Classroom 最终物理 Endpoint 完整列表(共 4 个)

优先级 HTTP Method URI 路径 Controller 方法 权限字符 适用角色 契约对齐说明
P0 GET /train/classroom/list list(TrainClassroomInfo) train:classroom:list 管理员, 教师 分页查询教室列表
P0 GET /train/classroom/{classRoomId} getInfo(@PathVariable) train:classroom:list 管理员, 教师 获取教室详情(返回 ClassroomDetailVO)
P0 POST /train/classroom add(@Validated @RequestBody) train:classroom:edit 管理员 新增教室(校验 capacity > 0,名称预查重)
P1 PUT /train/classroom/{classRoomId} edit(@PathVariable, @Validated @RequestBody) train:classroom:edit 管理员 修改教室信息

已彻底从 Controller 移除的非公开方法:

  • PUT /train/classroom(无 Path ID 的修改映射已删除)
  • DELETE /train/classroom/{classRoomIds}(已从 Controller 移除,底层逻辑删除能力保留于 Service/Mapper)
  • POST /train/classroom/export(已从 Controller 移除)

2.2 TrainingSite 最终物理 Endpoint 完整列表(共 4 个)

优先级 HTTP Method URI 路径 Controller 方法 权限字符 适用角色 契约对齐说明
P0 GET /train/training-site/list list(TrainTrainingSite) train:site:list 管理员, 教师 分页查询实训场地列表
P0 GET /train/training-site/{siteId} getInfo(@PathVariable) train:site:list 管理员, 教师 获取实训场地详情
P0 POST /train/training-site add(@Validated @RequestBody) train:site:edit 管理员 新增实训场地(校验 capacity > 0,名称预查重)
P1 PUT /train/training-site/{siteId} edit(@PathVariable, @Validated @RequestBody) train:site:edit 管理员 修改实训场地信息

已彻底从 Controller 移除的非公开方法:

  • PUT /train/training-site(无 Path ID 的修改映射已删除)
  • DELETE /train/training-site/{siteIds}(已从 Controller 移除,底层逻辑删除能力保留于 Service/Mapper)
  • POST /train/training-site/export(已从 Controller 移除)

3. CG-05 落地实现与响应模型收口 (API Contract V1.0.2)

3.1 新增轻量班牌摘要视图:ClassroomBoardSummaryVO

依据 API Contract V1.0.2 (06_dto_vo_catalog.md 1.11 节):

  • 仅且只包含 3 个字段:
    • boardId (String, 系统内部稳定班牌ID)
    • boardSn (String, 设备硬件序列号)
    • boardStatus (Integer, 班牌状态:0离线 1在线)
  • 彻底避免向前端泄漏 TrainElectronicBoard 持久化 Domain 实体字段。

3.2 ClassroomDetailVO 完整字段列表

  • classRoomId (String, 教室ID)
  • roomName (String, 教室名称)
  • roomLocation (String, 教室物理位置)
  • capacity (Long, 核定容纳人数)
  • roomStatus (Integer, 教室状态:0正在使用 1可用空闲 2维护中)
  • remark (String, 备注)
  • createTime (Date, 创建时间)
  • boards (List<ClassroomBoardSummaryVO>, 绑定的班牌摘要列表)
    • 严格遵循 0..N 语义;
    • 无绑定班牌时返回 boards = [],绝不返回 null;
    • 绝不引入 electronicBoard、primaryBoard、preferredBoard,绝不假定“第一条就是默认班牌”。

3.3 详情查询装配逻辑

在 TrainClassroomInfoServiceImpl.selectClassroomDetailByClassRoomId 中:

  1. 根据 classRoomId 查询单表持久化实体 TrainClassroomInfo;
  2. 依据 bind_classroom_id = classRoomId 检索 train_electronic_board 列表;
  3. 将班牌记录转换为轻量 ClassroomBoardSummaryVO 列表;若无记录则初始化为空列表 [];
  4. 组装至 ClassroomDetailVO.boards 返回。

4. Contract Gap 处理结果

4.1 CG-04 (错误码 40008 / 40009 遗漏):已闭环

  • 在 API Contract V1.0.2 中,docs/api-v2/10_permission_and_error_codes.md 已正式补录:
    • 40008:CLASSROOM_NAME_DUPLICATED(教室名称已存在)
    • 40009:SITE_NAME_DUPLICATED(实训场地名称已存在)
  • 代码层 TrainClassroomInfoServiceImpl 与 TrainTrainingSiteServiceImpl 保持现有查重与抛错实现,Contract Gap 正式闭环消除。

4.2 CG-05 (教室详情班牌数据结构与基数未决):已闭环

  • 在 API Contract V1.0.2 中,docs/api-v2/06_dto_vo_catalog.md 明确定义了 ClassroomBoardSummaryVO 与 List<ClassroomBoardSummaryVO> boards(0..N 语义,空返回 [])。
  • 代码层已严格按该定义实现,Contract Gap 正式闭环消除。

4.3 数据库名称唯一性并发边界记录

  • 在 Database V2 物理结构中,train_classroom_info.room_name 与 train_training_site.site_name 均无 UNIQUE 唯一约束。
  • 当前查重为 Service 层的应用级预检查(checkRoomNameUnique / checkSiteNameUnique)。
  • 边界说明: 在高并发写操作下,应用层预检查无法绝对保证无并发幻读重复写入,并发强唯一性当前不保证。本 Issue 遵守红线,未修改 Database V2。

5. 前端同步收口

  • build_ui/src/api/train/classroom.js:
    • 删除了 delClassroom 与 exportClassroom 导出函数;
    • updateClassroom 请求路径收口为带有 Path ID 的 PUT /train/classroom/{classRoomId}。
  • build_ui/src/views/train/classroom/index.vue:
    • 删除了顶部批量删除与导出按钮;
    • 删除了表格操作列的删除按钮;
    • 删除了 handleDelete 与 handleExport 对应方法与未使用的 API 引用;
    • 表单编辑保存精准调用 /train/classroom/{classRoomId}。
  • build_ui/src/api/train/site.js:
    • 删除了 delSite 导出函数;
    • updateSite 请求路径收口为带有 Path ID 的 PUT /train/training-site/{siteId}。
  • build_ui/src/views/train/site/index.vue:
    • 删除了顶部批量删除与导出按钮;
    • 删除了表格操作列的删除按钮;
    • 删除了 handleDelete 与 handleExport 对应方法与未使用的 API 引用;
    • 表单编辑保存精准调用 /train/training-site/{siteId}。

6. 构建与工程验证结果

6.1 后端 Maven 编译验证

  • 命令: mvn compile -DskipTests -f RuoYi-Vue/pom.xml
  • 构建结果: BUILD SUCCESS(8 个模块全部成功,退出码 0)。

6.2 前端 ESLint 严格校验

  • 命令:
    cd build_ui
    /Users/wzb/Desktop/项目/RailwaySystem/build_ui/node_modules/.bin/eslint \
      --resolve-plugins-relative-to /Users/wzb/Desktop/项目/RailwaySystem/build_ui \
      --no-ignore \
      src/api/train/classroom.js \
      src/api/train/site.js \
      src/views/train/classroom/index.vue \
      src/views/train/site/index.vue
    
  • 执行结果: PASS(0 errors, 0 warnings,退出码 0)。

6.3 代码差异与规范检查

  • git diff --check:输出为空,退出码 0,无任何空白字符、CRLF 或冲突标记问题。
  • git status --short:严格收敛于本 Issue 范围内。

7. 受控 Generator Pilot 执行与元数据核验记录

在技术负责人正式释放 GENERATOR SLOT = SPACE 后,严格按照分阶段受控流程执行了双阶段 Pilot:

7.1 第一阶段:train_classroom_info Pilot

  1. 备份表隔离前置检查:
    • 调用 genTableService.selectDbTableList,匹配 bak_v1_* 的表数量为 0,确认历史快照备份表完全隔离不可见,无误选风险。
  2. 单表严格导入:
    • 仅且唯独选择 train_classroom_info 单表导入,成功写入 gen_table(分配 table_id = 4)及 gen_table_column(共 11 列元数据)。
  3. 主键列与状态列元数据核验:
    • 主键 class_room_id: isPk = "1", isIncrement = "0", javaType = "String", columnType = character varying(32) -> PASS(准确识别为非自增 VARCHAR(32) 字符串主键)。
    • 状态 room_status: isPk = "0", isIncrement = "0", javaType = "Long", columnType = smallint。
  4. 沙箱目录隔离输出:
    • 调用 previewCode(4L),文件全部写入临时沙箱目录:/tmp/railwaysystem-generator/issue-01-04-space/classroom/。
    • 输出清单包括:TrainClassroomInfo.java、TrainClassroomInfoMapper.java、ITrainClassroomInfoService.java、TrainClassroomInfoServiceImpl.java、TrainClassroomInfoController.java、TrainClassroomInfoMapper.xml、classroomMenu.sql、classroom.js、index.vue。
    • 禁止覆盖原则: 未向工程源码目录覆盖任何文件,零迁移 generated artifact。

7.2 第二阶段:train_training_site Pilot

  1. 备份表隔离前置检查:
    • 匹配 bak_v1_* 的表数量为 0,无误选风险。
  2. 单表严格导入:
    • 仅选择 train_training_site 单表导入,成功写入 gen_table(分配 table_id = 5)及 gen_table_column(共 12 列元数据)。
  3. 主键列与状态列元数据核验:
    • 主键 site_id: isPk = "1", isIncrement = "0", javaType = "String", columnType = character varying(32) -> PASS(准确识别为非自增 VARCHAR(32) 字符串主键)。
    • 状态 site_status: isPk = "0", isIncrement = "0", javaType = "Long", columnType = smallint。
  4. 沙箱目录隔离输出:
    • 调用 previewCode(5L),文件全部写入临时沙箱目录:/tmp/railwaysystem-generator/issue-01-04-space/site/。
    • 输出清单包括:TrainTrainingSite.java、TrainTrainingSiteMapper.java、ITrainTrainingSiteService.java、TrainTrainingSiteServiceImpl.java、TrainTrainingSiteController.java、TrainTrainingSiteMapper.xml、siteMenu.sql、site.js、index.vue。
    • 禁止覆盖原则: 未向工程源码目录覆盖任何文件,零迁移 generated artifact。

8. 三方比较矩阵 (Generated vs Existing vs API Contract V1.0.2) 与不迁移裁决

8.1 Classroom 三方比对与处理策略

检查维度 Generated (代码生成器输出) Existing (现有实现) API Contract V1.0.2 (正式契约) 最终裁定与处理策略
主键与类型 classRoomId (String), roomStatus (Long), capacity (Long) classRoomId (String), roomStatus (Integer), capacity (Long) classRoomId (String, 32位), roomStatus (Integer), capacity (Long) KEEP Existing
Existing 的 roomStatus 为 Integer,与 Contract 定义的 Integer/Number 契约完全吻合。
Controller 路径与前缀 @RequestMapping("/system/info")(默认系统模块名) @RequestMapping("/train/classroom") /train/classroom/... KEEP Existing
Generated 路径前缀完全偏离业务契约,严禁覆盖。
权限字符定义 @ss.hasPermi('system:info:list') 等默认字符 @ss.hasPermi('train:classroom:list'),train:classroom:edit train:classroom:list,train:classroom:edit KEEP Existing
Generated 权限字符错误,Existing 符合冻结权限表。
DTO / VO 分层 完全无分层(直接裸露持久化实体 TrainClassroomInfo) 入参 ClassroomCreateDTO,出参 ClassroomDetailVO 明确要求响应类型为 ClassroomDetailVO KEEP Existing
架构级契约合规必需品,严禁退化回单实体架构。
班牌摘要聚合 (CG-05) 完全缺失(纯单表生成,无任何关联能力) ClassroomDetailVO.boards: List<ClassroomBoardSummaryVO>(0..N 聚合,无绑定时返回 []) 详情响应必须包含 boards: List<ClassroomBoardSummaryVO> KEEP Existing
核心业务与架构契约指标,Generated 无法提供。
额外端点 (DELETE/export) 生成了未冻结的 POST /export 与 DELETE /{classRoomIds} 以及无 ID 的 PUT 严格收敛为 4 个公开端点(P0: list, getInfo, add; P1: edit with ID),无 DELETE/export 明确仅冻结 4 个端点,无 DELETE 与 export KEEP Existing
坚决拒绝引入契约外暴露的破坏性物理删除与导出端点。
Service 业务防御 纯调用 Mapper,无容量非正数拦截,无名称预查重 容量 <= 0 强校验拦截;checkRoomNameUnique 查重(40008);自动分配 32 位 UUID 容量大于 0;同名校验(CG-04 40008) KEEP Existing
Existing 承载了业务防御规则,保持现有实现。

8.2 TrainingSite 三方比对与处理策略

检查维度 Generated (代码生成器输出) Existing (现有实现) API Contract V1.0.2 (正式契约) 最终裁定与处理策略
主键与类型 siteId (String), siteStatus (Long), capacity (Long) siteId (String), siteStatus (Integer), capacity (Long) siteId (String, 32位), siteStatus (Integer), capacity (Long) KEEP Existing
siteStatus 保持 Integer 对齐契约。
Controller 路径与前缀 @RequestMapping("/system/site") @RequestMapping("/train/training-site") /train/training-site/... KEEP Existing
Generated 路径错误,坚决不覆盖。
权限字符定义 @ss.hasPermi('system:site:list') 等默认字符 @ss.hasPermi('train:site:list'),train:site:edit train:site:list,train:site:edit KEEP Existing
Existing 完全符合冻结契约。
DTO / VO 分层 完全无分层(直接裸露持久化实体 TrainTrainingSite) 入参 SiteCreateDTO,出参 SiteDetailVO 详情出参要求 SiteDetailVO KEEP Existing
保留专用分层对象。
额外端点 (DELETE/export) 生成了未冻结的 POST /export 与 DELETE /{siteIds} 以及无 ID 的 PUT 严格收敛为 4 个公开端点(P0: list, getInfo, add; P1: edit with ID),无 DELETE/export 明确仅冻结 4 个端点,无 DELETE 与 export KEEP Existing
拒绝暴露契约外端点。
Service 业务防御 纯调用 Mapper,无容量校验,无名称查重 容量 <= 0 强校验拦截;checkSiteNameUnique 查重(40009);自动分配 32 位 UUID 容量大于 0;场地名称唯一性校验(CG-04 40009) KEEP Existing
保留核心业务防御逻辑。
  • 综合裁决结论: 本次受控 Pilot 充分证明 Kingbase 代码生成器底层对 train_classroom_info 与 train_training_site 的元数据读取、主键识别(class_room_id 与 site_id 均准确识别为非自增 String 主键)及 Velocity 模板渲染能力 100% 正常。 鉴于现有实现已经严格按照 API Contract V1.0.2(包含 CG-04 与 CG-05)完成了精细化对齐,而 Generator 默认输出包含错误的路径前缀、错误的权限字符、未授权的 DELETE/export 端点且缺失 DTO/VO 分层与班牌聚合,因此坚决不迁移任何 generated artifact,不覆盖现有任何工程源码。生成文件仅作为临时沙箱比对凭证保留于 /tmp/ 目录备查。

9. 数据库影响与写入核查声明

  • Generator 元数据记录:
    • 成功向 gen_table 写入 2 条元数据记录:train_classroom_info (table_id = 4)、train_training_site (table_id = 5)。
    • 成功向 gen_table_column 写入 23 条列元数据(教室 11 列,场地 12 列)。
  • 业务数据库零写入:
    • 未执行任何 ALTER TABLE / CREATE TABLE / DROP TABLE 等 DDL 语句。
    • 未执行任何 IDENTITY 变更或自增序列修正(未调用 nextval / setval)。
    • 未对 train_classroom_info 执行任何业务数据写入(行数保持精确为 0)。
    • 未对 train_training_site 执行任何业务数据写入(行数保持精确为 0)。
    • 未修改 Database V2 既有物理表结构,未修改 API Contract 冻结契约。