14 KiB
14 KiB
ISSUE-01-01:师资主数据管理能力开发与验收报告
文档定位: 本文档记录 RailwaySystem P0 阶段第一个正式 Issue(
ISSUE-01-01 师资主数据管理能力)兼 Generator 正式 Pilot 的完整实施与验收结果。 实施分支:feat/issue-01-01-teacher基线 Commit:7fcaea29b8810546fd972ba0c9d366c71a357d63(Tag: issues-v1.0)
1. Issue 目标
- 业务目标: 为排课选师资、计划主讲教师提供完备的教师档案检索、详情查看、建档与维护能力;
- 功能范围: 教师姓名、单位、性别、出生日期、职务/职称、联系电话、邮箱维护;分页检索;手机号系统全局查重;RESTful 详情与更新;逻辑删除;
- 技术目标: 完成 Generator V2 正式业务表 Pilot 验证,通过只读/隔离预览,比对并最小化修改现有实现,坚决落实逻辑删除与 API Contract V1.0.1 契约规范。
2. 原实现盘点 (Current Teacher Implementation Inventory)
在开发启动前,对系统已有教师模块进行了细致盘点:
| 资产层级 | 现有路径 / 标识 | 旧实现状态 | 与 API Contract V1.0.1 / Database V2 差异 |
|---|---|---|---|
| Entity | com.ruoyi.train.domain.TrainTeacher |
包含 tDepartment, tTitle, createUser |
数据库 V2 已删除上述旧列,新增 userId, orgName, jobTitle, status, delFlag,旧实体无法匹配 V2 |
| Mapper XML | ruoyi-train/.../TrainTeacherMapper.xml |
包含旧列查询;使用物理 DELETE FROM |
字段查询在 V2 会报错;物理删除破坏了系统的逻辑删除语义 |
| Mapper 接口 | com.ruoyi.train.mapper.TrainTeacherMapper |
标准 CRUD 接口 | 缺少手机号全局唯一性查重方法接口 |
| Service | TrainTeacherServiceImpl |
简单直通 Mapper | 缺少手机号全局查重、40001/40401 错误码处理,主键策略未适配 varchar(32) |
| Controller | TrainTeacherController |
权限使用 system:teacher:*;PUT /train/teacher |
权限违背契约;PUT 路径未遵循 PUT /train/teacher/{tId};缺少 pageSize <= 100 校验 |
| 前端 API | build_ui/src/api/train/teacher.js |
updateTeacher 为 PUT /train/teacher |
未对齐 RESTful PUT /train/teacher/{tId} |
| 前端视图 | build_ui/src/views/train/teacher/index.vue |
表单使用院系、职称、创建人 | 未对齐单位 orgName、职务 jobTitle、状态 status;缺少手机号格式前端校验 |
3. Generator Pilot 结果
依据授权范围,本轮仅针对 train_teacher 表开放 Generator Import 与 Preview:
- 导入范围约束: 仅且仅导入
train_teacher,其余业务表严禁导入;导入前gen_table为 0,导入后gen_table精确为 1 行; - 表/列元数据审查:
- 候选查询及预览中,历史备份表
bak_v1_*完全不可见; t_id:isPk = 1,isIncrement = 0,javaType = String,columnType = character varying(32)(严格核验不是自增);- 数据类型映射验证:
character varying->Stringcharacter(1)->Stringtimestamp->Datedate->Datesmallint->Long/Integer
- 候选查询及预览中,历史备份表
- 隔离生成与三方 Diff:
- 全部 Generator 输出至隔离临时目录
/tmp/railwaysystem-generator/issue-01-01/,严禁且未直接覆盖任何工程源码; - 三方比对结论:
- Generator 生成的包名为
com.ruoyi.system.*,权限为system:teacher:*,删除为物理DELETE,表单包含迁移注释; - 现有项目包名为
com.ruoyi.train.*,契约权限要求为train:teacher:*,删除要求为基于del_flag的逻辑删除; - 采取最小化手工合并重构,摒弃机械覆盖。
- Generator 生成的包名为
- 全部 Generator 输出至隔离临时目录
4. 保留 / 修改 / 替换文件清单
| 文件路径 | 策略 | 变更重点说明 |
|---|---|---|
RuoYi-Vue/ruoyi-train/src/main/java/com/ruoyi/train/domain/TrainTeacher.java |
MODIFY | 移除旧字段 tDepartment, tTitle, createUser;新增 userId, orgName, jobTitle, status, delFlag 及对应 getter/setter |
RuoYi-Vue/ruoyi-train/src/main/resources/mapper/train/TrainTeacherMapper.xml |
MODIFY | 映射 V2 新列;所有查询增加 del_flag = '0' 过滤;删除改为 UPDATE ... SET del_flag = '2';增加 checkPhoneUnique SQL |
RuoYi-Vue/ruoyi-train/src/main/java/com/ruoyi/train/mapper/TrainTeacherMapper.java |
MODIFY | 增加 checkPhoneUnique(String tPhone) 方法 |
RuoYi-Vue/ruoyi-train/src/main/java/com/ruoyi/train/service/ITrainTeacherService.java |
MODIFY | 增加 checkPhoneUnique(TrainTeacher trainTeacher) 业务契约 |
RuoYi-Vue/ruoyi-train/src/main/java/com/ruoyi/train/service/impl/TrainTeacherServiceImpl.java |
MODIFY | 落实核心业务逻辑:新增/修改手机号系统全局查重抛出 40001;教师不存在抛出 40401;UUID 32位主键生成;默认值与审计赋值;逻辑删除 |
RuoYi-Vue/ruoyi-admin/src/main/java/com/ruoyi/web/controller/train/TrainTeacherController.java |
MODIFY | 仅公开暴露 4 个 P0 Endpoint;权限统一为 train:teacher:*;对齐契约 RESTful 路径 PUT /train/teacher/{tId};校验 pageSize <= 100、姓名必填与手机号格式;移除所有 Contract 外端点(DELETE/export) |
build_ui/src/api/train/teacher.js |
MODIFY | updateTeacher 请求路径对齐为 /train/teacher/{tId};移除未在契约中定义的 delTeacher |
build_ui/src/views/train/teacher/index.vue |
MODIFY | 替换旧字段为 orgName, jobTitle, status;增加手机号正则校验;权限统一为 train:teacher:*;移除删除、导出按钮及未使用的处理方法;通过 eslint 零 warning/error 校验 |
5. API Contract 映射与落实
按 API Contract V1.0.1 冻结契约(docs/api-v2/02_module_a_base_resource_api.md),Teacher 模块正式公开的 HTTP API 严格收敛为 4 个 P0 API:
5.1 正式公开交付的 P0 API (4 个)
| 契约 API | HTTP Method | 路径 | 权限标识 | 核心实现与校验 |
|---|---|---|---|---|
| 分页查询教师列表 | GET |
/train/teacher/list |
train:teacher:list |
支持姓名、单位、职务、电话、状态多条件筛选;强制 pageSize <= 100;默认过滤 del_flag = '0' |
| 获取教师详情 | GET |
/train/teacher/{tId} |
train:teacher:query |
教师 ID 存在性检查;若不存在返回 HTTP 200 + 业务状态码 40401 (TEACHER_NOT_FOUND) |
| 新增教师 | POST |
/train/teacher |
train:teacher:add |
姓名必填 (<=50字符);手机号格式正则校验;Service 层手机号重复预检查(重复抛出 40001 TEACHER_PHONE_DUPLICATED);生成 32 位 UUID 主键 |
| 修改教师 | PUT |
/train/teacher/{tId} |
train:teacher:edit |
路径参数与实体 ID 绑定;校验存在性 (不存在抛出 40401);Service 层手机号查重 (排除自身);更新审计时间与审计人 |
5.2 状态接口说明 (P1 范围)
- 启用/停用教师状态 (
PUT /train/teacher/{tId}/status):属于冻结契约规划的 P1 接口。本 Issue (P0) 不提前公开该端点,教师状态可通过 P0PUT /train/teacher/{tId}进行维护。
5.3 Contract 外 HTTP Endpoint 治理
根据技术负责人审查意见,HTTP API Contract V1.0.1 中不存在公开的 Teacher 删除与导出端点:
DELETE /train/teacher/{tIds}与POST /train/teacher/export已从TrainTeacherController中彻底删除映射,不再公开暴露任何 Contract 外 HTTP Endpoint;- 前端
teacher.js与index.vue同步移除删除与导出调用及界面按钮,消除死代码。
6. 权限设计说明
- 代码层完全落实:
- 后端全部采用
@PreAuthorize("@ss.hasPermi('train:teacher:*')"); - 前端操作按钮全部绑定
v-hasPermi="['train:teacher:*']"; - 彻底废除旧有的
system:teacher:*;
- 后端全部采用
- 零数据库写权限:
- 严格遵循指令规则,本 Issue 绝对不向
sys_menu插入或更新任何菜单与按钮数据; - 菜单数据的入库统一留待
ISSUE-11-01集中处理。
- 严格遵循指令规则,本 Issue 绝对不向
7. 手机号唯一性与持久层能力设计
- 手机号唯一性实现现状与边界:
- 数据库现状: 经只读系统目录核查,当前 Database V2 的
train_teacher表仅包含主键索引train_teacher_pkey以及普通索引idx_teacher_user_id、idx_teacher_name,不存在UNIQUE(t_phone)约束或唯一索引; - 实现机制: 当前仅在 Service层进行手机号重复预检查;
- 边界说明: 并发强唯一性不由当前数据库约束保证。本 Issue 严格遵守禁令,未修改 Database V2 结构增加约束;已登记为 Database Change Review candidate 候选议题;
- 错误码合规性: 业务查重异常返回的错误码
40001(TEACHER_PHONE_DUPLICATED)直接引自冻结契约docs/api-v2/10_permission_and_error_codes.md第 104 行,不存在自行发明。
- 数据库现状: 经只读系统目录核查,当前 Database V2 的
- 底层持久化删除能力设计:
- 架构原则:
HTTP Contract ≠ 内部 Persistence Capability; - 逻辑删除能力保留在 Persistence 层:Mapper 与 Service 层保留基于
del_flag的软删除方法(del_flag = '2'),以支持系统内部业务或级联运维调度使用; - 一期 V1.0.1 未对外暴露 Teacher 删除 HTTP API,Controller 层无公开删除映射。
- 架构原则:
8. 后端验证结果
通过独立的后端测试用例套件 (TestTeacherBackendService),全链路覆盖所有场景:
| 测试用例编号 | 场景描述 | 期望结果 | 实测结果 | 状态 |
|---|---|---|---|---|
| TC-01 | 基线唯一历史教师查询 | 查出 1 行数据,字段正确映射 | 行数精确为 1,orgName 等字段完全一致 |
PASS |
| TC-02 | AC-01: 正常新增教师 | 成功入库,UUID 32位主键,del_flag='0' |
影响行数 1,主键格式正确,查回一致 | PASS |
| TC-03 | AC-02: 手机号重复新增 | 抛出 40001 TEACHER_PHONE_DUPLICATED |
成功捕获 40001 业务异常,阻断新增 | PASS |
| TC-04 | 参数校验 (姓名为空 / 超长) | 拦截非法请求并提示对应错误信息 | 姓名空/超长均被严格拦截 | PASS |
| TC-05 | 查询与更新不存在的 ID | 查询返回空,更新抛出 40401 错误码 | 捕获 40401 TEACHER_NOT_FOUND |
PASS |
| TC-06 | 修改已有教师资料 | 成功更新单位、职务、职称等字段 | 影响行数 1,再次查回字段与修改值完全一致 | PASS |
| TC-07 | 逻辑删除语义与物理恢复 | 软删除置 del_flag='2',恢复基线 1 行 |
数据库物理验证通过,测试数据已自动复原 | PASS |
后端构建结果:
mvn compile -DskipTests -f RuoYi-Vue/pom.xml
[INFO] BUILD SUCCESS
9. 前端验证结果
- 代码规范与 Lint 验证:
- 执行命令:
npx eslint --no-ignore src/api/train/teacher.js src/views/train/teacher/index.vue - 验证结果:
0 problems (0 errors, 0 warnings);
- 执行命令:
- UI 功能与服务验证:
- 前端页面仅保留新增、修改操作及搜索过滤,删除了删除与导出入口,界面整洁规范;
- 表单严格执行姓名必填、手机号正则、邮箱格式等前端校验。
10. Database 影响与事实审计
- 业务数据 DML 事实:
- YES,执行过受控测试业务 DML。测试完成后已清理。
- 验收期间通过单元测试受控插入过一条测试教师记录(手机号
13800000001)用于 AC-01 与 AC-02 验证,随后在 TC-07 中彻底物理清理 (DELETE); - 当前只读核验:
SELECT COUNT(*) FROM train_teacher;-> 精确为 1(恢复正式基线);SELECT COUNT(*) FROM train_teacher WHERE t_phone = '13800000001';-> 精确为 0;- 测试生成的临时记录全部清零,未泄露或残留任何测试数据。
- Database V2 结构影响: 21 张正式业务表结构未发生任何 DDL 变动,保持基线一致;
- Generator 基础设施兼容性处理:
- Generator Pilot 首次真实 Import 暴露了国产 Kingbase 数据库下 Generator 元数据列(
table_id/column_id)自增兼容性问题; - 该问题已确认属于 Generator Infrastructure Patch,已转交独立基础设施补丁任务处理;
- 本 Teacher 分支未包含该 DDL 脚本,未将基础设施改动混入业务 Commit;
- Generator Pilot 首次真实 Import 暴露了国产 Kingbase 数据库下 Generator 元数据列(
- 权限/字典表影响: 未执行
007_dicts.sql,未修改sys_menu与sys_dict。
11. 已知限制与后续演进
- 菜单权限数据: 页面按钮声明了
train:teacher:*权限标识,由于未向sys_menu插入数据,在非 admin 超级管理员角色下需要待ISSUE-11-01菜单数据初始化后方可在生产环境分配角色权限; - 公开 HTTP 接口收敛: 一期 V1.0.1 严格对外暴露 4 个 P0 接口,删除与状态启停用独立端点留待后续版本根据需求统一规划;
- 并发强唯一性: 当前手机号查重依赖 Service 层校验,若后续业务高并发场景需要强唯一性,由 Database Change Review 统一评估增加数据库约束。
12. Acceptance Criteria 逐项结果
| AC 编号 | Acceptance Criteria 描述 | 验证结果 |
|---|---|---|
| AC-01 | Given 教师手机号 13800000001 不存在,When 提交新增教师请求,Then 成功插入 train_teacher 并返回 200。 |
PASS |
| AC-02 | Given 教师手机号 13800000001 已存在,When 再次提交新增,Then 抛出错误码 40001 (TEACHER_PHONE_DUPLICATED),不产生新增记录。 |
PASS |