---
req_id: REQ-MOD-004
date: 2026-04-30
round: 1
reviewer: superpower-code-reviewer
---

# Review: REQ-MOD-004 — round 1

## 结论
approve

## Must-fix
（无）

## Nice-to-have

- backend/src/main/java/com/xly/erp/module/mod/service/impl/ModuleServiceImpl.java:122 — spec § 业务规则 #1 与 plan Architecture 节均写「controller 先 trim」，实现把 trim 放在 service。行为等价（controller 立刻调 service），但 spec/plan 与代码不一致。要么把 trim 移到 controller，要么把 spec 改成「service 集中归一化」。建议改 spec（service 归一化 + 7 个单测覆盖更易测）。
- backend/src/test/java/com/xly/erp/module/mod/controller/ModuleControllerIT.java:420 — `getKeywordMatch_returnsForest` 只断言「返回数组里每个节点 sModuleNameZh 含『系统』」，没断言本测试 INSERT 的 `sp_test_get_kw_a` 行确实出现。日后 V1 seed 含「系统」的行恰好命中时，断言依旧通过——但本测试自身插入的真值未被验证。建议追加按 sModuleNameZh="系统模块A" 定位节点的断言。
- backend/src/test/java/com/xly/erp/module/mod/controller/ModuleControllerIT.java:466 — `getWithoutJwt_permitAllStub_returns200` / `getTamperedJwt_returns20001` 沿袭 MOD-001/002/003 stub 路径仍缺 `// REQ-MOD-001 stub: see USR-004 follow-up` 锚点。USR-004 上线时一次性 grep 替换为 `authenticated()` + 401 会漏这两条。
- backend/src/main/java/com/xly/erp/module/mod/service/impl/ModuleServiceImpl.java:122 — `listTree` 方法体缺行内 `// REQ-MOD-004: 模块查询` 锚点；MOD-001/002/003 的 create/update/delete 同源缺失。建议在 module-report 阶段统一补齐 4 处。
- backend/src/test/java/com/xly/erp/module/mod/service/ModuleServiceImplTest.java:312 — `listTree_emptyKeyword_invokesMapperWithEmptyString_returnsAssembledTree` 中 mock 返回顺序已是 SQL 期望顺序，children 顺序断言 `containsExactly(3, 4)` 实际验的是「mapper 返回顺序被 service 保留」。语义正确（依赖 ModuleMapperIT 验 SQL ORDER BY），加一行注释指向 mapper IT 让 review 路径更清晰。
- backend/src/test/java/com/xly/erp/module/mod/mapper/ModuleMapperIT.java:108 — `selectActiveByKeyword_filtersAndOrders` 第 108–110 行 `namesByEmpty` 局部变量构造后未被任何 assert 使用（dead code）；真正的断言路径在第 111–117 行的 `insertedIds` 过滤。建议删除 108–110 这段未用 stream。

## 反例 / 测试覆盖缺口

Spec 验收清单（service 7 + mapperIT 1 + controllerIT 6 + 工程 3）100% 落地，mvn test 67/67 全绿。

拼树算法正确性核查通过：HashMap 仅用于 O(1) 查父，roots/children 均按 rows 迭代顺序填充，SQL ORDER BY 顺序穿透到 JSON；同一节点不会既出现在 roots 也挂在 parent.children（if/else 互斥）。VO 字段集严格匹配 docs/05（7 字段，敏感列 sProcedureName / sCreatedBy / sBrandsId 等不暴露）。keyword 归一化（null→''、trim、长度>100→40001）+ 拼树森林（孤儿节点→root）单测覆盖完整。LIKE `%`/`_` 注入在 spec § 边界与约束已显式说明本期不转义。Controller IT 通过 JsonNode 递归读取 children 数组层级，真正验证 JSON 端层级结构。trim 位置（spec/plan 写 controller，实现在 service）为文字不一致而非行为缺陷。

继承自 MOD-001~003 的 stub 锚点缺失 / REQ 行内锚点缺失沿袭存在但 spec 已纳入 USR-004 followup 范围，本期接受。**本 REQ 是模块最后一个 REQ，建议在 module-report 阶段一次性把 4 个 REQ 的行内锚点 + 6 处 stub 测试锚点补齐。**
