Merged
Merge Request #1
·
created by
feat(module_mod): 模块管理
模块完成报告
见 docs/superpowers/module-reports/2026-05-06-module_mod.md(本 MR 仓库内完整贴入下方)。
module_id: module_mod date: 2026-05-06
git_range: ace58411..c64a0269 (24 commits)
模块完成报告 — module_mod 模块管理
① 模块信息
- 模块 ID: module_mod
- 模块名: 模块管理(业务模块定义 / 树形菜单 / 权限分组基础单位)
- 开发区间: ace58411(master, plan phase done)→ c64a0269(test-gate evidence),共 24 个 commits
② REQ 完成清单
- REQ-MOD-001 — 模块新增(
POST /api/modules)- spec: docs/superpowers/specs/2026-05-06-REQ-MOD-001.md
- plan: docs/superpowers/plans/2026-05-06-REQ-MOD-001.md
- review: docs/superpowers/reviews/2026-05-06-REQ-MOD-001.md
- REQ-MOD-002 — 模块修改(
PUT /api/modules/{id})- spec: docs/superpowers/specs/2026-05-06-REQ-MOD-002.md
- plan: docs/superpowers/plans/2026-05-06-REQ-MOD-002.md
- review: docs/superpowers/reviews/2026-05-06-REQ-MOD-002.md
- REQ-MOD-003 — 模块软删除(
DELETE /api/modules/{id})- spec: docs/superpowers/specs/2026-05-06-REQ-MOD-003.md
- plan: docs/superpowers/plans/2026-05-06-REQ-MOD-003.md
- review: docs/superpowers/reviews/2026-05-06-REQ-MOD-003.md
- REQ-MOD-004 — 模块树查询(
GET /api/modules?keyword=)- spec: docs/superpowers/specs/2026-05-06-REQ-MOD-004.md
- plan: docs/superpowers/plans/2026-05-06-REQ-MOD-004.md
- review: docs/superpowers/reviews/2026-05-06-REQ-MOD-004.md
③ 文件变更表
| 文件 | 操作 | 说明 |
|---|---|---|
| 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) |
| backend/src/main/java/com/xly/erp/ErpApplication.java | A | Spring Boot 启动类 + @MapperScan |
| backend/src/main/resources/application.yml | A | DB / Flyway / MyBatis-Plus 配置;从 .env.local 注入凭据 |
| backend/src/main/resources/application-test.yml | A | test profile:Flyway baseline-on-migrate=true / baseline-version=1 |
| backend/src/main/java/com/xly/erp/common/response/ApiResponse.java | A | 统一响应 {code, message, data, timestamp} + 静态工厂 |
| 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 |
| backend/src/main/java/com/xly/erp/common/exception/BizException.java | A | 业务异常(带 ErrorCode) |
| backend/src/main/java/com/xly/erp/common/exception/GlobalExceptionHandler.java | A | @RestControllerAdvice:BizException → ApiResponse.fail(code),MethodArgumentNotValidException → 40010,Exception → 50000,不回显堆栈 |
| backend/src/main/java/com/xly/erp/config/SecurityConfig.java | A | SecurityFilterChain permitAll(占位,REQ-USR-004 时收紧为 JWT) |
| backend/src/main/java/com/xly/erp/config/JacksonConfig.java | A | Jackson 字段访问可见性配置(解决 Lombok getter iIncrement 被 Introspector 解析为 IIncrement 导致 JSON key 错位) |
| backend/src/main/java/com/xly/erp/module/mod/entity/ModuleEntity.java | A | tModule 实体;保留匈牙利前缀;iParentId 用 FieldStrategy.IGNORED 让 update 时显式 NULL 写入 |
| backend/src/main/java/com/xly/erp/module/mod/mapper/ModuleMapper.java | A | extends BaseMapper;只用 BaseMapper 默认 SQL |
| backend/src/main/resources/mapper/mod/ModuleMapper.xml | A | 空骨架(预留扩展) |
| backend/src/main/java/com/xly/erp/module/mod/dto/ModuleCreateDTO.java | A | POST 入参 + Bean Validation(@NotBlank / @Pattern 枚举 / @Size / @Min) |
| backend/src/main/java/com/xly/erp/module/mod/dto/ModuleUpdateDTO.java | A | PUT 入参;剥除 sProcedureName(不可改) |
| backend/src/main/java/com/xly/erp/module/mod/dto/ModuleQueryDTO.java | A | GET 入参 keyword(@Size max=50) |
| backend/src/main/java/com/xly/erp/module/mod/vo/ModuleVO.java | A | 创建/修改返回 VO(11 字段)+ 静态 from(ModuleEntity) |
| backend/src/main/java/com/xly/erp/module/mod/vo/ModuleDeleteResultVO.java | A | 删除返回精简 VO(iIncrement + bDeleted) |
| backend/src/main/java/com/xly/erp/module/mod/vo/ModuleTreeNodeVO.java | A | 树查询节点 VO(7 字段 + children=[]) |
| backend/src/main/java/com/xly/erp/module/mod/service/ModuleService.java | A | 接口:create / update / delete / tree |
| 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) |
| backend/src/main/java/com/xly/erp/module/mod/controller/ModuleController.java | A | 4 端点:POST / PUT / DELETE / GET |
| backend/src/test/java/com/xly/erp/ErpApplicationTest.java | A | contextLoads + Flyway baseline |
| backend/src/test/java/com/xly/erp/common/response/ApiResponseTest.java | A | 5 单测(含 7 个错误码常量断言) |
| backend/src/test/java/com/xly/erp/common/exception/GlobalExceptionHandlerTest.java | A | 4 单测(standaloneSetup) |
| backend/src/test/java/com/xly/erp/config/SecurityConfigTest.java | A | 1 集成(permitAll 验证) |
| backend/src/test/java/com/xly/erp/module/mod/dto/ModuleCreateDTOValidationTest.java | A | 5 单测(Bean Validation) |
| backend/src/test/java/com/xly/erp/module/mod/dto/ModuleUpdateDTOValidationTest.java | A | 4 单测(Bean Validation) |
| backend/src/test/java/com/xly/erp/module/mod/mapper/ModuleMapperIT.java | A | 2 集成(@SpringBootTest + insert/select) |
| backend/src/test/java/com/xly/erp/module/mod/service/ModuleServiceImplTest.java | A | 26 单测(Mockito + ArgumentCaptor):6 create / 8 update / 6 delete / 6 tree |
| 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 |
| backend/src/test/resources/application-test.yml | A | test profile 副本 |
| docs/08-模块任务管理.md | M | § 二 module_mod 4 个 REQ 全部勾选 |
| docs/superpowers/specs/2026-05-06-REQ-MOD-00{1..4}.md | A | 4 份功能规格 |
| docs/superpowers/plans/2026-05-06-REQ-MOD-00{1..4}.md | A | 4 份任务级实现计划 |
| docs/superpowers/reviews/2026-05-06-REQ-MOD-00{1..4}.md | A | 4 份 AI 审阅报告 |
| docs/superpowers/module-reports/module_mod-test-gate.md | A | 本模块 test-gate 闸门证据 |
④ 数据库使用表
- 读:
tModule(4 个 REQ 都读:create 唯一性预检 / update 父校验 + 后代环路 / delete 子模块引用检查 + 状态校验 / tree selectList) - 写:
tModule(create insert / update updateById / delete update bDeleted=1 软删除)
本模块不读 / 不写其他表。FK 关系:tModule 自引用 iParentId → iIncrement(V1 由 db-init 创建)。
⑤ 测试结果
-
scripts/test.sh最终:green - 通过: 76 / 失败: 0 / 跳过: 0
- 覆盖率: 未启用 JaCoCo 等覆盖率工具(docs/04 § 零 技术栈表未列入);后续模块若需要可作为软规则 S1 引入
测试分布:
- 单元测试 49(ApiResponse 5 + GlobalExceptionHandler 4 + DTO Validation 5+4 + ServiceImpl 26 + 其他 5)
- 集成测试 31(ApplicationTest 1 + SecurityConfig 1 + ModuleMapperIT 2 + ModuleControllerIT 28)
./scripts/test.sh 流程:setup-test-db.sh DROP+CREATE → mvn build → mvn lint(compile) → mvn test → frontend skip → e2e 略 → reset DB。耗时 ~15s。
⑥ 本模块新增 Migration
—(本模块未引入 schema 改动;tModule 由 V1__initial_schema.sql 在 A4 阶段创建)
⑦ 跨模块改动清单(软规则 S2)
—(本模块为 module_mod 独立工作,未触碰其他模块的代码 / schema;hook log-cross-module.sh 未生成存根,docs/superpowers/module-reports/module_mod-cross-module.md 不存在)
⑧ 偏离 spec 清单
-
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 披露。 -
REQ-MOD-001 → spec 要求 Controller / Service / Mapper / DTO / VO 关键类贴
// REQ-MOD-001标签;实现中只在 Controller / SecurityConfig 上写注释,其他类未贴。原因:实施时疏漏,已在 review § Nice-to-have 记录,不影响功能。 -
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 路径会埋雷)。 -
REQ-MOD-003 → round 1 review 发现 high 隐患:iParentId.IGNORED 副作用导致 delete 时 entity-driven update 静默清空父引用。round 2 fix commit (24196599) 改用
LambdaUpdateWrapper.set(...)显式 SET,并补 ITdelete_preservesOtherFields_onChildModule钉死回归。 -
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 报告披露。 -
跨 REQ — 鉴权延期:所有 4 个 REQ 的 Controller 都加了说明性注释
REQ-USR-004 完成后追加 @PreAuthorize(...),contract 要求的MOD:CREATE/UPDATE/DELETE/READ权限在本模块未启用。这是计划性技术债(docs/02 § 三关键说明 + 各 REQ spec § 边界都明确点到),由 REQ-USR-004 首次落地登录上下文时统一收紧 SecurityConfig。 -
跨 REQ — 多租户字段 / sCreatedBy 留 NULL:所有写入路径中
sBrandsId / sSubsidiaryId / sCreatedBy都落 NULL(spec 明确点到,等 REQ-USR-004 引入登录上下文 / 多租户上下文后回填)。
⑨ AI reviewer 报告汇总
- REQ-MOD-001: round 1 — approve(link: docs/superpowers/reviews/2026-05-06-REQ-MOD-001.md)
- REQ-MOD-002: round 1 — approve(link: docs/superpowers/reviews/2026-05-06-REQ-MOD-002.md)
- REQ-MOD-003: round 1 — request-changes → round 2 — approve(link: docs/superpowers/reviews/2026-05-06-REQ-MOD-003.md,修复 commit 24196599)
- REQ-MOD-004: round 1 — approve(link: docs/superpowers/reviews/2026-05-06-REQ-MOD-004.md)
⑩ 已知问题
-
鉴权延期:4 个端点目前 SecurityConfig permitAll;任何客户端无 token 也能调通。REQ-USR-004 必须回头给本模块端点加
@PreAuthorize+ 把permitAll()改为authenticated(),并补端到端鉴权 IT。 -
多租户字段 NULL:tModule 中
sBrandsId / sSubsidiaryId / sCreatedBy列在所有写入路径都为 NULL。REQ-USR-004 后需要:(a) 引入多租户拦截器从 SecurityContext 注入;(b) 写一个 V_n migration 给历史 NULL 行回填默认值;(c) 收紧 schema 把这几列改为 NOT NULL(视业务决策)。 -
iParentId 的
FieldStrategy.IGNORED全局副作用:本期所有 update 路径都走 load-then-modify 全量回填模式,所以安全;但未来若新增"只更新单字段"的 partial update 路径需特别注意——直接调updateById(patchEntity)会把 iParentId 静默清空。建议未来用LambdaUpdateWrapper.set(...)模式(参考 REQ-MOD-003 delete 实现)。 - REQ-MOD-004 同级排序 IT 缺失:spec § 验收 #7 同级排序仅在单测覆盖,端到端 IT 未直接断言。Jackson 默认保留 List 顺序,风险低,但理论上有序列化层差异的可能。下一模块或 docs sweep 时补一个端到端用例。
-
keyword 通配符未转义:spec § 边界已声明本期不处理
%/_;业务模块名通常不含这些字符。后续若需要严格语义可在 service 层加escape \。 - 覆盖率工具未引入:docs/04 § 零 技术栈未列 JaCoCo / SonarQube;本期 76 测试由人工分析覆盖。
-
docs/05 § REQ-MOD-003
data.references描述与实现不一致:契约写 40912 响应附data.references列出阻塞引用清单,但实现只返回错误码 + 文案;BizException 没有 data 通道。后续 docs sweep 或 REQ-USR-004 引入完整 data payload 时统一对齐。
⑪ 下一模块预览
按 docs/02 § 二 顺序,下一个模块是 module_usr 用户管理(4 个 REQ:USR-001 用户新增 / USR-002 用户修改 / USR-003 用户查询 / USR-004 用户登录)。
关键关注点:
- REQ-USR-004 是项目 SecurityConfig + JWT + 登录上下文 + 多租户拦截器首次落地点,会同时收紧 module_mod 的 4 个 permitAll 端点。
- REQ-USR-001 写 tUser + tUserPermission 关联表(多对多),需联动 tStaff(员工选)+ tPermissionCategory(权限分类只读字典)。tStaff 与 tPermissionCategory 当前无增删改接口,作为只读字典——若实施过程中发现需要增删改,可能要拆出新模块(docs/03 § tStaff / § tPermissionCategory 业务注记已点到此问题)。
- REQ-USR-003 查询接口的列表 VO 包含跨表 JOIN(员工名 / 部门来自 tStaff),需要在 ModuleMapper 模式之外引入 selectJoinList 或 自定义 ResultMap。
⑫ MR 链接
待 mr-create 推送后回填。
本地闸门证据
- test.sh: green(subagent: aba8704557f137ad2)
审核入口
- 本 MR = 模块
module_mod的唯一人工介入点 - Approve + Merge 后,下次用户运行
/erp-workflow:coding-start时入口会自动扫描到 GitLab APIstate=merged,探测默认分支后git pull --ff-only同步并推进下一模块
From
module-module_mod
into
master