Commit 08ccdb1c3812278396b1aa5c3a9a8fe945d28423
1 parent
c64a0269
docs(module_mod): add module completion report
Showing
1 changed file
with
140 additions
and
0 deletions
docs/superpowers/module-reports/2026-05-06-module_mod.md
0 → 100644
| 1 | +--- | |
| 2 | +module_id: module_mod | |
| 3 | +date: 2026-05-06 | |
| 4 | +git_range: ace5841..c64a026 (24 commits) | |
| 5 | +--- | |
| 6 | + | |
| 7 | +# 模块完成报告 — module_mod 模块管理 | |
| 8 | + | |
| 9 | +## ① 模块信息 | |
| 10 | +- 模块 ID: module_mod | |
| 11 | +- 模块名: 模块管理(业务模块定义 / 树形菜单 / 权限分组基础单位) | |
| 12 | +- 开发区间: ace5841(master, plan phase done)→ c64a026(test-gate evidence),共 24 个 commits | |
| 13 | + | |
| 14 | +## ② REQ 完成清单 | |
| 15 | + | |
| 16 | +- [x] REQ-MOD-001 — 模块新增(`POST /api/modules`) | |
| 17 | + - spec: docs/superpowers/specs/2026-05-06-REQ-MOD-001.md | |
| 18 | + - plan: docs/superpowers/plans/2026-05-06-REQ-MOD-001.md | |
| 19 | + - review: docs/superpowers/reviews/2026-05-06-REQ-MOD-001.md | |
| 20 | +- [x] REQ-MOD-002 — 模块修改(`PUT /api/modules/{id}`) | |
| 21 | + - spec: docs/superpowers/specs/2026-05-06-REQ-MOD-002.md | |
| 22 | + - plan: docs/superpowers/plans/2026-05-06-REQ-MOD-002.md | |
| 23 | + - review: docs/superpowers/reviews/2026-05-06-REQ-MOD-002.md | |
| 24 | +- [x] REQ-MOD-003 — 模块软删除(`DELETE /api/modules/{id}`) | |
| 25 | + - spec: docs/superpowers/specs/2026-05-06-REQ-MOD-003.md | |
| 26 | + - plan: docs/superpowers/plans/2026-05-06-REQ-MOD-003.md | |
| 27 | + - review: docs/superpowers/reviews/2026-05-06-REQ-MOD-003.md | |
| 28 | +- [x] REQ-MOD-004 — 模块树查询(`GET /api/modules?keyword=`) | |
| 29 | + - spec: docs/superpowers/specs/2026-05-06-REQ-MOD-004.md | |
| 30 | + - plan: docs/superpowers/plans/2026-05-06-REQ-MOD-004.md | |
| 31 | + - review: docs/superpowers/reviews/2026-05-06-REQ-MOD-004.md | |
| 32 | + | |
| 33 | +## ③ 文件变更表 | |
| 34 | + | |
| 35 | +| 文件 | 操作 | 说明 | | |
| 36 | +|---|---|---| | |
| 37 | +| backend/pom.xml | A | Spring Boot 3.2.5 父 pom + 依赖(mybatis-plus 3.5.7、flyway 10、hutool、mapstruct、test)+ surefire includes 修复(让 *IT.java 跑入 mvn test) | | |
| 38 | +| backend/src/main/java/com/xly/erp/ErpApplication.java | A | Spring Boot 启动类 + @MapperScan | | |
| 39 | +| backend/src/main/resources/application.yml | A | DB / Flyway / MyBatis-Plus 配置;从 .env.local 注入凭据 | | |
| 40 | +| backend/src/main/resources/application-test.yml | A | test profile:Flyway baseline-on-migrate=true / baseline-version=1 | | |
| 41 | +| backend/src/main/java/com/xly/erp/common/response/ApiResponse.java | A | 统一响应 `{code, message, data, timestamp}` + 静态工厂 | | |
| 42 | +| backend/src/main/java/com/xly/erp/common/response/ErrorCode.java | A | 错误码枚举:SUCCESS / PARAM_INVALID / MOD_PARENT_NOT_FOUND / MOD_NOT_FOUND / MOD_PROC_NAME_DUP / MOD_HAS_REFERENCES / MOD_PARENT_LOOP / INTERNAL_ERROR | | |
| 43 | +| backend/src/main/java/com/xly/erp/common/exception/BizException.java | A | 业务异常(带 ErrorCode) | | |
| 44 | +| backend/src/main/java/com/xly/erp/common/exception/GlobalExceptionHandler.java | A | @RestControllerAdvice:BizException → ApiResponse.fail(code),MethodArgumentNotValidException → 40010,Exception → 50000,**不回显堆栈** | | |
| 45 | +| backend/src/main/java/com/xly/erp/config/SecurityConfig.java | A | SecurityFilterChain permitAll(占位,REQ-USR-004 时收紧为 JWT) | | |
| 46 | +| backend/src/main/java/com/xly/erp/config/JacksonConfig.java | A | Jackson 字段访问可见性配置(解决 Lombok getter `iIncrement` 被 Introspector 解析为 `IIncrement` 导致 JSON key 错位) | | |
| 47 | +| backend/src/main/java/com/xly/erp/module/mod/entity/ModuleEntity.java | A | tModule 实体;保留匈牙利前缀;iParentId 用 `FieldStrategy.IGNORED` 让 update 时显式 NULL 写入 | | |
| 48 | +| backend/src/main/java/com/xly/erp/module/mod/mapper/ModuleMapper.java | A | extends BaseMapper<ModuleEntity>;只用 BaseMapper 默认 SQL | | |
| 49 | +| backend/src/main/resources/mapper/mod/ModuleMapper.xml | A | 空骨架(预留扩展) | | |
| 50 | +| backend/src/main/java/com/xly/erp/module/mod/dto/ModuleCreateDTO.java | A | POST 入参 + Bean Validation(@NotBlank / @Pattern 枚举 / @Size / @Min) | | |
| 51 | +| backend/src/main/java/com/xly/erp/module/mod/dto/ModuleUpdateDTO.java | A | PUT 入参;剥除 sProcedureName(不可改) | | |
| 52 | +| backend/src/main/java/com/xly/erp/module/mod/dto/ModuleQueryDTO.java | A | GET 入参 keyword(@Size max=50) | | |
| 53 | +| backend/src/main/java/com/xly/erp/module/mod/vo/ModuleVO.java | A | 创建/修改返回 VO(11 字段)+ 静态 from(ModuleEntity) | | |
| 54 | +| backend/src/main/java/com/xly/erp/module/mod/vo/ModuleDeleteResultVO.java | A | 删除返回精简 VO(iIncrement + bDeleted) | | |
| 55 | +| backend/src/main/java/com/xly/erp/module/mod/vo/ModuleTreeNodeVO.java | A | 树查询节点 VO(7 字段 + children=[]) | | |
| 56 | +| backend/src/main/java/com/xly/erp/module/mod/service/ModuleService.java | A | 接口:create / update / delete / tree | | |
| 57 | +| backend/src/main/java/com/xly/erp/module/mod/service/impl/ModuleServiceImpl.java | A | 4 业务方法:create(唯一性预检 + DuplicateKey 兜底)/ update(父 FK 校验 + 环路 walk-up depth=5)/ delete(子模块引用检查 + LambdaUpdateWrapper.set 显式 SET 列)/ tree(内存树构造 + keyword 模糊 + 祖先链 walk-up) | | |
| 58 | +| backend/src/main/java/com/xly/erp/module/mod/controller/ModuleController.java | A | 4 端点:POST / PUT / DELETE / GET | | |
| 59 | +| backend/src/test/java/com/xly/erp/ErpApplicationTest.java | A | contextLoads + Flyway baseline | | |
| 60 | +| backend/src/test/java/com/xly/erp/common/response/ApiResponseTest.java | A | 5 单测(含 7 个错误码常量断言) | | |
| 61 | +| backend/src/test/java/com/xly/erp/common/exception/GlobalExceptionHandlerTest.java | A | 4 单测(standaloneSetup) | | |
| 62 | +| backend/src/test/java/com/xly/erp/config/SecurityConfigTest.java | A | 1 集成(permitAll 验证) | | |
| 63 | +| backend/src/test/java/com/xly/erp/module/mod/dto/ModuleCreateDTOValidationTest.java | A | 5 单测(Bean Validation) | | |
| 64 | +| backend/src/test/java/com/xly/erp/module/mod/dto/ModuleUpdateDTOValidationTest.java | A | 4 单测(Bean Validation) | | |
| 65 | +| backend/src/test/java/com/xly/erp/module/mod/mapper/ModuleMapperIT.java | A | 2 集成(@SpringBootTest + insert/select) | | |
| 66 | +| backend/src/test/java/com/xly/erp/module/mod/service/ModuleServiceImplTest.java | A | 26 单测(Mockito + ArgumentCaptor):6 create / 8 update / 6 delete / 6 tree | | |
| 67 | +| backend/src/test/java/com/xly/erp/module/mod/controller/ModuleControllerIT.java | A | 28 集成(MockMvc + @Transactional rollback):6 POST / 8 PUT / 7 DELETE / 7 GET | | |
| 68 | +| backend/src/test/resources/application-test.yml | A | test profile 副本 | | |
| 69 | +| docs/08-模块任务管理.md | M | § 二 module_mod 4 个 REQ 全部勾选 | | |
| 70 | +| docs/superpowers/specs/2026-05-06-REQ-MOD-00{1..4}.md | A | 4 份功能规格 | | |
| 71 | +| docs/superpowers/plans/2026-05-06-REQ-MOD-00{1..4}.md | A | 4 份任务级实现计划 | | |
| 72 | +| docs/superpowers/reviews/2026-05-06-REQ-MOD-00{1..4}.md | A | 4 份 AI 审阅报告 | | |
| 73 | +| docs/superpowers/module-reports/module_mod-test-gate.md | A | 本模块 test-gate 闸门证据 | | |
| 74 | + | |
| 75 | +## ④ 数据库使用表 | |
| 76 | + | |
| 77 | +- 读: `tModule`(4 个 REQ 都读:create 唯一性预检 / update 父校验 + 后代环路 / delete 子模块引用检查 + 状态校验 / tree selectList) | |
| 78 | +- 写: `tModule`(create insert / update updateById / delete update bDeleted=1 软删除) | |
| 79 | + | |
| 80 | +本模块不读 / 不写其他表。FK 关系:tModule 自引用 `iParentId → iIncrement`(V1 由 db-init 创建)。 | |
| 81 | + | |
| 82 | +## ⑤ 测试结果 | |
| 83 | + | |
| 84 | +- `scripts/test.sh` 最终:green | |
| 85 | +- 通过: 76 / 失败: 0 / 跳过: 0 | |
| 86 | +- 覆盖率: 未启用 JaCoCo 等覆盖率工具(docs/04 § 零 技术栈表未列入);后续模块若需要可作为软规则 S1 引入 | |
| 87 | + | |
| 88 | +测试分布: | |
| 89 | +- 单元测试 49(ApiResponse 5 + GlobalExceptionHandler 4 + DTO Validation 5+4 + ServiceImpl 26 + 其他 5) | |
| 90 | +- 集成测试 31(ApplicationTest 1 + SecurityConfig 1 + ModuleMapperIT 2 + ModuleControllerIT 28) | |
| 91 | + | |
| 92 | +`./scripts/test.sh` 流程:setup-test-db.sh DROP+CREATE → mvn build → mvn lint(compile) → mvn test → frontend skip → e2e 略 → reset DB。耗时 ~15s。 | |
| 93 | + | |
| 94 | +## ⑥ 本模块新增 Migration | |
| 95 | + | |
| 96 | +—(本模块未引入 schema 改动;tModule 由 V1__initial_schema.sql 在 A4 阶段创建) | |
| 97 | + | |
| 98 | +## ⑦ 跨模块改动清单(软规则 S2) | |
| 99 | + | |
| 100 | +—(本模块为 module_mod 独立工作,未触碰其他模块的代码 / schema;hook `log-cross-module.sh` 未生成存根,docs/superpowers/module-reports/module_mod-cross-module.md 不存在) | |
| 101 | + | |
| 102 | +## ⑧ 偏离 spec 清单 | |
| 103 | + | |
| 104 | +- **REQ-MOD-001** → 顺手新增了计划外的 `JacksonConfig.java`(spec / plan 文件清单未列出)。原因:项目沿用 docs/03 匈牙利前缀命名(`iIncrement`),Lombok 生成的 `getIIncrement()` 经 JavaBeans Introspector 解析为属性 `IIncrement`(首两字符全大写时保留),导致 JSON key 输出 `"IIncrement"` 而非期望的 `"iIncrement"`,破坏 API 契约。配置 Jackson 用字段直接读取属性名修复。已在 review 报告 § Nice-to-have 披露。 | |
| 105 | +- **REQ-MOD-001** → spec 要求 Controller / Service / Mapper / DTO / VO 关键类贴 `// REQ-MOD-001` 标签;实现中只在 Controller / SecurityConfig 上写注释,其他类未贴。原因:实施时疏漏,已在 review § Nice-to-have 记录,不影响功能。 | |
| 106 | +- **REQ-MOD-002** → 在 ModuleEntity#iParentId 上加了 `FieldStrategy.IGNORED` 让 NULL 写入生效(spec 未规划 entity 层改动)。原因:MyBatis-Plus 默认 update 跳过 null,导致 `setIParentId(null)` 不能写入 SQL。这是 entity 全局行为变更,已在 review 报告标注为 Nice-to-have(未来其他 service 走 partial updateById 路径会埋雷)。 | |
| 107 | +- **REQ-MOD-003** → round 1 review 发现 high 隐患:iParentId.IGNORED 副作用导致 delete 时 entity-driven update 静默清空父引用。round 2 fix commit (2419659) 改用 `LambdaUpdateWrapper.set(...)` 显式 SET,并补 IT `delete_preservesOtherFields_onChildModule` 钉死回归。 | |
| 108 | +- **REQ-MOD-004** → 顺手修复了 `backend/pom.xml` 中 maven-surefire-plugin 默认 includes 不含 `*IT.java` 的配置缺陷。原因:之前 REQ-MOD-001/002/003 的 IT 测试在 `mvn test` 全量阶段根本未被发现 / 执行(仅在单独 `-Dtest=xxx` 下运行过),所有 verify approval 都基于不完整覆盖。修复后所有 IT 进入 mvn test,本模块累计 76 测试全部参与全量。已在 review 报告披露。 | |
| 109 | +- **跨 REQ — 鉴权延期**:所有 4 个 REQ 的 Controller 都加了说明性注释 `REQ-USR-004 完成后追加 @PreAuthorize(...)`,contract 要求的 `MOD:CREATE/UPDATE/DELETE/READ` 权限在本模块**未启用**。这是计划性技术债(docs/02 § 三关键说明 + 各 REQ spec § 边界都明确点到),由 REQ-USR-004 首次落地登录上下文时统一收紧 SecurityConfig。 | |
| 110 | +- **跨 REQ — 多租户字段 / sCreatedBy 留 NULL**:所有写入路径中 `sBrandsId / sSubsidiaryId / sCreatedBy` 都落 NULL(spec 明确点到,等 REQ-USR-004 引入登录上下文 / 多租户上下文后回填)。 | |
| 111 | + | |
| 112 | +## ⑨ AI reviewer 报告汇总 | |
| 113 | + | |
| 114 | +- REQ-MOD-001: round 1 — approve(link: docs/superpowers/reviews/2026-05-06-REQ-MOD-001.md) | |
| 115 | +- REQ-MOD-002: round 1 — approve(link: docs/superpowers/reviews/2026-05-06-REQ-MOD-002.md) | |
| 116 | +- REQ-MOD-003: round 1 — request-changes → round 2 — approve(link: docs/superpowers/reviews/2026-05-06-REQ-MOD-003.md,修复 commit 2419659) | |
| 117 | +- REQ-MOD-004: round 1 — approve(link: docs/superpowers/reviews/2026-05-06-REQ-MOD-004.md) | |
| 118 | + | |
| 119 | +## ⑩ 已知问题 | |
| 120 | + | |
| 121 | +1. **鉴权延期**:4 个端点目前 SecurityConfig permitAll;任何客户端无 token 也能调通。**REQ-USR-004 必须回头**给本模块端点加 `@PreAuthorize` + 把 `permitAll()` 改为 `authenticated()`,并补端到端鉴权 IT。 | |
| 122 | +2. **多租户字段 NULL**:tModule 中 `sBrandsId / sSubsidiaryId / sCreatedBy` 列在所有写入路径都为 NULL。REQ-USR-004 后需要:(a) 引入多租户拦截器从 SecurityContext 注入;(b) 写一个 V_n migration 给历史 NULL 行回填默认值;(c) 收紧 schema 把这几列改为 NOT NULL(视业务决策)。 | |
| 123 | +3. **iParentId 的 `FieldStrategy.IGNORED` 全局副作用**:本期所有 update 路径都走 load-then-modify 全量回填模式,所以安全;但未来若新增"只更新单字段"的 partial update 路径需特别注意——直接调 `updateById(patchEntity)` 会把 iParentId 静默清空。建议未来用 `LambdaUpdateWrapper.set(...)` 模式(参考 REQ-MOD-003 delete 实现)。 | |
| 124 | +4. **REQ-MOD-004 同级排序 IT 缺失**:spec § 验收 #7 同级排序仅在单测覆盖,端到端 IT 未直接断言。Jackson 默认保留 List 顺序,风险低,但理论上有序列化层差异的可能。下一模块或 docs sweep 时补一个端到端用例。 | |
| 125 | +5. **keyword 通配符未转义**:spec § 边界已声明本期不处理 `%` / `_`;业务模块名通常不含这些字符。后续若需要严格语义可在 service 层加 `escape \`。 | |
| 126 | +6. **覆盖率工具未引入**:docs/04 § 零 技术栈未列 JaCoCo / SonarQube;本期 76 测试由人工分析覆盖。 | |
| 127 | +7. **docs/05 § REQ-MOD-003 `data.references` 描述与实现不一致**:契约写 40912 响应附 `data.references` 列出阻塞引用清单,但实现只返回错误码 + 文案;BizException 没有 data 通道。后续 docs sweep 或 REQ-USR-004 引入完整 data payload 时统一对齐。 | |
| 128 | + | |
| 129 | +## ⑪ 下一模块预览 | |
| 130 | + | |
| 131 | +按 docs/02 § 二 顺序,下一个模块是 **module_usr 用户管理**(4 个 REQ:USR-001 用户新增 / USR-002 用户修改 / USR-003 用户查询 / USR-004 用户登录)。 | |
| 132 | + | |
| 133 | +关键关注点: | |
| 134 | +- REQ-USR-004 是项目 SecurityConfig + JWT + 登录上下文 + 多租户拦截器**首次落地**点,会同时收紧 module_mod 的 4 个 permitAll 端点。 | |
| 135 | +- REQ-USR-001 写 tUser + tUserPermission 关联表(多对多),需联动 tStaff(员工选)+ tPermissionCategory(权限分类只读字典)。tStaff 与 tPermissionCategory 当前**无增删改接口**,作为只读字典——若实施过程中发现需要增删改,可能要拆出新模块(docs/03 § tStaff / § tPermissionCategory 业务注记已点到此问题)。 | |
| 136 | +- REQ-USR-003 查询接口的列表 VO 包含跨表 JOIN(员工名 / 部门来自 tStaff),需要在 ModuleMapper 模式之外引入 selectJoinList 或 自定义 ResultMap。 | |
| 137 | + | |
| 138 | +## ⑫ MR 链接 | |
| 139 | + | |
| 140 | +待 `mr-create` 推送后回填。 | ... | ... |