---
module_id: module_mod
date: 2026-04-30
git_range: 642c5f9..b3817b0
---

# 模块完成报告 — module_mod 模块管理

## ① 模块信息
- 模块 ID: module_mod
- 模块名: 模块管理
- 开发区间: `642c5f9..b3817b0`（33 commits，4035 行新增）
- 分支: `module-module_mod`
- 起点：项目首个模块（首个 REQ 同时承担 Spring Boot 工程脚手架建立）

## ② REQ 完成清单

- [x] REQ-MOD-001 — 模块新增
  - spec: docs/superpowers/specs/2026-04-29-REQ-MOD-001.md
  - plan: docs/superpowers/plans/2026-04-29-REQ-MOD-001.md
  - review: docs/superpowers/reviews/2026-04-29-REQ-MOD-001.md
- [x] REQ-MOD-002 — 模块修改
  - spec: docs/superpowers/specs/2026-04-29-REQ-MOD-002.md
  - plan: docs/superpowers/plans/2026-04-29-REQ-MOD-002.md
  - review: docs/superpowers/reviews/2026-04-29-REQ-MOD-002.md
- [x] REQ-MOD-003 — 模块删除
  - spec: docs/superpowers/specs/2026-04-29-REQ-MOD-003.md
  - plan: docs/superpowers/plans/2026-04-29-REQ-MOD-003.md
  - review: docs/superpowers/reviews/2026-04-29-REQ-MOD-003.md
- [x] REQ-MOD-004 — 模块查询
  - spec: docs/superpowers/specs/2026-04-29-REQ-MOD-004.md
  - plan: docs/superpowers/plans/2026-04-29-REQ-MOD-004.md
  - review: docs/superpowers/reviews/2026-04-29-REQ-MOD-004.md

## ③ 文件变更表

| 文件 | 操作 | 说明 |
|---|---|---|
| backend/pom.xml | 新建 | Spring Boot 3.3.5 + MyBatis-Plus 3.5.9 + Flyway + jjwt 0.12.6 |
| backend/.gitignore | 新建 | target / *.iml / .idea |
| backend/src/main/java/com/xly/erp/ErpApplication.java | 新建 | Spring Boot 启动类 + EnableConfigurationProperties |
| backend/src/main/java/com/xly/erp/common/config/MybatisPlusConfig.java | 新建 | @MapperScan |
| backend/src/main/java/com/xly/erp/common/config/TenantProperties.java | 新建 | erp.tenant.* 多租户字段 |
| backend/src/main/java/com/xly/erp/common/config/StubSecurityProperties.java | 新建 | erp.security.stub-user-no / jwt-secret |
| backend/src/main/java/com/xly/erp/common/response/Result.java | 新建 | 统一响应 {code,msg,data} |
| backend/src/main/java/com/xly/erp/common/exception/BizException.java | 新建 | 业务异常 |
| backend/src/main/java/com/xly/erp/common/exception/GlobalExceptionHandler.java | 新建 | @RestControllerAdvice 三类 handler |
| backend/src/main/java/com/xly/erp/common/security/JwtUtil.java | 新建 | HS256 sign/parse |
| backend/src/main/java/com/xly/erp/common/security/JwtAuthenticationFilter.java | 新建 | OncePerRequestFilter 解析 token |
| backend/src/main/java/com/xly/erp/common/security/SecurityConfig.java | 新建 | /api/mod/** permitAll（USR-004 stub） |
| backend/src/main/java/com/xly/erp/common/security/SecurityContextHelper.java | 新建 | currentUserNo 含 anonymous 识别 |
| backend/src/main/java/com/xly/erp/module/mod/entity/Module.java | 新建 | tModule 1:1 PO（17 字段） |
| backend/src/main/java/com/xly/erp/module/mod/dto/CreateModuleDTO.java | 新建 | REQ-MOD-001 入参（Bean Validation） |
| backend/src/main/java/com/xly/erp/module/mod/dto/UpdateModuleDTO.java | 新建 | REQ-MOD-002 入参（剔除 sProcedureName） |
| backend/src/main/java/com/xly/erp/module/mod/vo/ModuleTreeVO.java | 新建 | REQ-MOD-004 出参（递归 children） |
| backend/src/main/java/com/xly/erp/module/mod/mapper/ModuleMapper.java | 新建 | BaseMapper + 4 个自定义 SELECT |
| backend/src/main/java/com/xly/erp/module/mod/service/ModuleService.java | 新建 | create/update/delete/listTree |
| backend/src/main/java/com/xly/erp/module/mod/service/impl/ModuleServiceImpl.java | 新建 | 含枚举/父链/子模块/拼树 全部业务校验 |
| backend/src/main/java/com/xly/erp/module/mod/controller/ModuleController.java | 新建 | POST/PUT/DELETE/GET /api/mod/modules |
| backend/src/main/resources/application.yml | 新建 | DB / Flyway / 端口 / erp.* 配置 |
| backend/src/main/resources/application-test.yml | 新建 | test profile override |
| backend/src/main/resources/logback-spring.xml | 新建 | 默认 console appender |
| backend/src/test/java/com/xly/erp/SmokeTest.java | 新建 | Spring 启动 + Flyway apply 验证 |
| backend/src/test/java/com/xly/erp/common/exception/GlobalExceptionHandlerTest.java | 新建 | 4 用例（standalone MockMvc） |
| backend/src/test/java/com/xly/erp/common/security/JwtUtilTest.java | 新建 | 3 用例 |
| backend/src/test/java/com/xly/erp/common/security/TestJwtHelper.java | 新建 | 测试辅助签发 JWT |
| backend/src/test/java/com/xly/erp/common/security/JwtAuthenticationFilterTest.java | 新建 | 3 用例（filter 三分支） |
| backend/src/test/java/com/xly/erp/module/mod/mapper/ModuleMapperIT.java | 新建 | 5 用例（insert/parent/child queries） |
| backend/src/test/java/com/xly/erp/module/mod/service/ModuleServiceImplTest.java | 新建 | 25 用例（create/update/delete/listTree） |
| backend/src/test/java/com/xly/erp/module/mod/controller/ModuleControllerIT.java | 新建 | 26 用例（4 接口端到端 + JWT 路径） |
| docs/superpowers/specs/2026-04-29-REQ-MOD-{001,002,003,004}.md | 新建 | 4 份功能规格 |
| docs/superpowers/plans/2026-04-29-REQ-MOD-{001,002,003,004}.md | 新建 | 4 份 TDD 计划 |
| docs/superpowers/reviews/2026-04-29-REQ-MOD-{001,002,003,004}.md | 新建 | 4 份 AI 自审报告 |
| docs/superpowers/module-reports/module_mod-test-gate.md | 新建 | 本模块 test-gate 证据 |
| docs/08-模块任务管理.md | 修改 | 4 个 REQ checkbox 勾上 |
| scripts/setup-test-db.sh | 修改 | macOS Homebrew mysql-client 路径自动 prepend |
| scripts/test.sh | 修改 | frontend 段在 frontend/ 缺失时跳过 |

## ④ 数据库使用表

- 读: `tModule`（REQ-MOD-001/002/003/004 均涉及）
- 写: `tModule`
  - INSERT：REQ-MOD-001（新增）
  - UPDATE：REQ-MOD-002（更新可编辑字段，依赖 MyBatis-Plus FieldStrategy.NOT_NULL 跳过 null 不动 sProcedureName/sCreatedBy/标准列）；REQ-MOD-003（软删除：`bDeleted=1` + `tDeletedDate` + `sDeletedBy`）

## ⑤ 测试结果

- `scripts/test.sh` 最终：**GREEN**
- 通过: 67 / 失败: 0 / 跳过: 0（前端段因 `frontend/` 未初始化而 skip 不计入）
- 覆盖率：未配置 jacoco，本期不报告（建议 USR 模块开始前在 pom 引入）
- 详见: `docs/superpowers/module-reports/module_mod-test-gate.md`
- 测试分布：
  - SmokeTest: 1
  - GlobalExceptionHandlerTest: 4
  - JwtUtilTest: 3
  - JwtAuthenticationFilterTest: 3
  - ModuleMapperIT: 5
  - ModuleServiceImplTest: 25
  - ModuleControllerIT: 26

## ⑥ 本模块新增 Migration

—（本模块无 schema 改动；`tModule` 表由 A4 阶段 `V1__initial_schema.sql` 已落地）

## ⑦ 跨模块改动清单（软规则 S2）

无跨模块改动。本模块所有源码改动均位于 `backend/src/main/java/com/xly/erp/module/mod/**` + `backend/src/main/java/com/xly/erp/common/**`（首 REQ 必要的全局基础设施：Result/Exception/Security/Config）+ `backend/src/test/**`。`common/` 下的类（Result / GlobalExceptionHandler / JwtAuthenticationFilter / SecurityConfig 等）属于"项目级共用脚手架"，由首 REQ MOD-001 一次建立，后续模块复用，不视为 S2 跨模块。

## ⑧ 偏离 spec 清单

- **REQ-MOD-002**：错误码段位与 docs/05 偏离——MOD-002 spec 选择复用 MOD-001 的 `40010`（显示类型枚举非法），而 docs/05 § REQ-MOD-002 错误码列表只列 40001/40021/40400。 （原因: 同字段在两个接口语义一致更优；spec § 业务规则 #2 已显式声明该决策，docs/05 后续可补一行）
- **REQ-MOD-002**：spec 与 plan 写"controller 先 trim"，实际实现 trim 在 service。 （原因: service 集中归一化更易测，行为等价；REQ-MOD-004 review 已指出，建议改 spec 文案对齐）
- **REQ-MOD-003**：未实现 docs/05 § REQ-MOD-003 列出的 `40902` 外部引用拦截。 （原因: docs/03 当前 schema 中 tModule 无引用方表；spec § 业务规则 #3 + § 实现范围与边界抉择 #4 双处显式声明本期不实现，待引用方表落地后回填）
- **REQ-MOD-004**：拼树策略不扩展祖先链——子节点命中而父节点未命中时，子节点直接挂为森林 root。 （原因: spec § 实现范围与边界抉择 #1 已声明，REQ 卡仅要求"以树形展示匹配结果"，森林是合法树形；扩展祖先需二次查询或 CTE，复杂度收益不匹配本期需求）
- **REQ-MOD-004**：LIKE 通配符 `%` / `_` 不做转义。 （原因: spec § 边界与约束已声明，业务上不敏感；docs/05 未要求；后续可通过 `replace("%","\\%")` 收口）

## ⑨ AI reviewer 报告汇总

- REQ-MOD-001: round 1 — approve（link: docs/superpowers/reviews/2026-04-29-REQ-MOD-001.md）
- REQ-MOD-002: round 1 — approve（link: docs/superpowers/reviews/2026-04-29-REQ-MOD-002.md）
- REQ-MOD-003: round 1 — approve（link: docs/superpowers/reviews/2026-04-29-REQ-MOD-003.md）
- REQ-MOD-004: round 1 — approve（link: docs/superpowers/reviews/2026-04-29-REQ-MOD-004.md）

全部 round 1 一次性通过；4 份 review 共 25 条 nice-to-have，无 must-fix。

## ⑩ 已知问题

整合自 4 份 review 的非阻塞 nice-to-have，按主题分组：

**鉴权 stub 收尾（USR-004 闭环时一次性处理）**
1. `SecurityConfig` 的 `requestMatchers("/api/mod/**").permitAll()` → 改为 `authenticated()` 或 `hasAuthority('SUPER_ADMIN')`（按 docs/05 § 各 REQ 的 Permission 字段精细化）
2. `ModuleServiceImpl#create` 的 `stub.getStubUserNo()` 回退路径需移除（`sCreatedBy` 必须来自 JWT principal）
3. 6 处 IT stub 用例（`postWithoutJwt_*` / `putWithoutJwt_*` / `deleteWithoutJwt_*` / `getWithoutJwt_*`）缺 `// REQ-MOD-001 stub: see USR-004 follow-up` 锚点；USR-004 上线时 grep 替换会漏

**架构遗留**
4. `backend/src/main/java/com/xly/erp/module/mod/entity/Module.java` 类名 `Module` 与 `java.lang.Module`（JPMS）冲突；建议未来重命名为 `ModuleEntity` 或 `TModule`
5. `JwtAuthenticationFilter` 加 `@Component` 同时被 Spring Security `addFilterBefore` 与原生 servlet 注册重复登记（`OncePerRequestFilter` 防住执行，但作用域不清）；建议 USR-004 时改为 `@Bean` + `FilterRegistrationBean(setEnabled(false))`
6. `JwtUtil` 暴露两个 public 构造器，`JwtUtil(String secret)` 实质 test-only；可改 package-private + 注释

**配置 / 校验**
7. `application.yml` 中 `${JWT_SECRET}` 缺 fail-fast 默认值；建议改为 `${JWT_SECRET:?JWT_SECRET 必须从 .env.local 注入}`
8. `application-dev.yml` 缺失（docs/09 § 二 列出但未生成）；下一个模块本机 dev 启动时需要
9. `GlobalExceptionHandler#handleAny` 把 `HttpMessageNotReadableException` / `HttpRequestMethodNotSupportedException` 转 `code=50000`（应当是 40001）；建议增加专用 4xx handler
10. `DTO` `iParentId` / `iSortOrder` 缺 `@PositiveOrZero` / `@Min` 约束
11. `ModuleServiceImpl#updateById` / `delete` 的返回值（受影响行数）丢弃；并发场景下 `affected=0` 仍返回成功——可断言 `affected==1` 否则抛兜底异常

**测试覆盖缺口（非阻塞）**
12. `@Size` 长度溢出（如 `sProcedureName > 100` 字符）路径仅在"缺必填"用例顺带覆盖，未独立断言
13. `iParentId` 指向 `bDeleted=1` 旧记录是否回 `40021` 未单独验证
14. 环检测 `MAX_PARENT_DEPTH=50` 超深路径仅有间接覆盖（未构造 50 层脏数据链）
15. `tCreateDate` / `sBrandsId` / `sSubsidiaryId` 在 update / delete IT 端未端到端断言保留（service 单测已 captureArgument 验 entity 字段为 null + 依赖 NOT_NULL 策略）
16. 业务方法行内 `// REQ-MOD-XXX:` 锚点缺失（4 处：create/update/delete/listTree）；建议下个模块开始前一次性补齐
17. `ModuleMapperIT#selectActiveByKeyword_filtersAndOrders` 第 108–110 行 `namesByEmpty` 局部变量构造后未被任何 assert 使用（dead code），建议清理

## ⑪ 下一模块预览

**module_usr 用户管理**（docs/02 § 二 顺序的 REQ 5–8）
- REQ-USR-001 用户新增（依赖 `tStaff` / `tPermissionCategory` 作下拉数据源）
- REQ-USR-002 用户修改
- REQ-USR-003 用户查询（多条件 + 分页）
- REQ-USR-004 用户登录（**关键**——完成后才能把 `/api/mod/**` 与 `/api/usr/**` 的 stub permitAll 收紧为 `hasAuthority('SUPER_ADMIN')`，并移除 service 层 `stub.getStubUserNo()` 回退路径）

预期范围：`backend/src/main/java/com/xly/erp/module/usr/**` 全新模块树；`tUser` / `tStaff` / `tPermissionCategory` / `tUserPermission` schema 已在 V1 就位；JWT 签发由 USR-004 接管（覆写 MOD 期间手工签发的逻辑）。

USR 模块开工前的"扫尾建议"：
- 处理 § ⑩ #16（业务方法行内 REQ 锚点 4 处）
- 处理 § ⑩ #8（生成 application-dev.yml 占位）
- 处理 § ⑩ #17（删除 dead code）

这三项是低成本一次性收口，建议作为 module_usr 的 REQ-USR-001 开工前第一个 chore commit 完成；其余 § ⑩ 项均围绕 USR-004 / 后续模块自然解决。

## ⑫ MR 链接

- !1 — http://git.xlyprint.cn/zhuzc/test/merge_requests/1
