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,并补 IT delete_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)

⑩ 已知问题

  1. 鉴权延期:4 个端点目前 SecurityConfig permitAll;任何客户端无 token 也能调通。REQ-USR-004 必须回头给本模块端点加 @PreAuthorize + 把 permitAll() 改为 authenticated(),并补端到端鉴权 IT。
  2. 多租户字段 NULL:tModule 中 sBrandsId / sSubsidiaryId / sCreatedBy 列在所有写入路径都为 NULL。REQ-USR-004 后需要:(a) 引入多租户拦截器从 SecurityContext 注入;(b) 写一个 V_n migration 给历史 NULL 行回填默认值;(c) 收紧 schema 把这几列改为 NOT NULL(视业务决策)。
  3. iParentId 的 FieldStrategy.IGNORED 全局副作用:本期所有 update 路径都走 load-then-modify 全量回填模式,所以安全;但未来若新增"只更新单字段"的 partial update 路径需特别注意——直接调 updateById(patchEntity) 会把 iParentId 静默清空。建议未来用 LambdaUpdateWrapper.set(...) 模式(参考 REQ-MOD-003 delete 实现)。
  4. REQ-MOD-004 同级排序 IT 缺失:spec § 验收 #7 同级排序仅在单测覆盖,端到端 IT 未直接断言。Jackson 默认保留 List 顺序,风险低,但理论上有序列化层差异的可能。下一模块或 docs sweep 时补一个端到端用例。
  5. keyword 通配符未转义:spec § 边界已声明本期不处理 % / _;业务模块名通常不含这些字符。后续若需要严格语义可在 service 层加 escape \
  6. 覆盖率工具未引入:docs/04 § 零 技术栈未列 JaCoCo / SonarQube;本期 76 测试由人工分析覆盖。
  7. 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 API state=merged,探测默认分支后 git pull --ff-only 同步并推进下一模块

From module-module_mod into master

Merged by 朱子纯

1 participants