2026-04-30-REQ-USR-001.md
4.5 KB
req_id: REQ-USR-001 date: 2026-04-30 round: 1
reviewer: superpower-code-reviewer
Review: REQ-USR-001 — round 1
结论
approve
Must-fix
(无)
Nice-to-have
- docs/superpowers/specs/2026-04-30-REQ-USR-001.md § 实现范围与边界抉择 #3 + § 验收标准 工程验收 #2 写「BCryptPasswordEncoder bean 在 SecurityConfig 注册」,但实现已移到
common/config/PasswordEncoderConfig.java(理由:SecurityConfig 上@ConditionalOnWebApplication(SERVLET)在webEnvironment=NONE的 mapperIT/serviceTest 上下文不加载会让 encoder 缺失)。正向设计修正——密码编码器是领域基础设施而非 Web 安全基础设施。建议把 spec 这两处改为「BCryptPasswordEncoder 在 common/config/PasswordEncoderConfig 注册」,并在 PasswordEncoderConfig.java 类注释里写一行可追溯说明。 - backend/src/main/java/com/xly/erp/module/usr/service/impl/UserServiceImpl.java:104 — 循环里每次
LocalDateTime.now()让 N 行 tUserPermission 的tCreateDate出现微秒级偏差。建议在 create 入口取一次LocalDateTime now,user + 全部 permission 共享,更贴合「同一事务一次创建」的语义。 - backend/src/main/java/com/xly/erp/module/usr/service/impl/UserServiceImpl.java:69-75 —
permissionCategoryIds含重复 id(如[1,1,2]全部有效)时 SQL IN 隐式去重 →countActiveByIds=2 ≠ ids.size=3→ 误抛 40023 假阴性。spec 没要求去重;建议 service 层先ids = ids.stream().distinct().toList(),4 行修复。 - backend/src/test/java/com/xly/erp/module/usr/controller/UserControllerIT.java:73-75 —
postValidBody_with_jwt_returns200_andPersists没断言响应data中不含sPasswordHash。spec § 验收 工程 #5 明言「不返回 sPasswordHash」。建议加assertThat(jb.get("data").has("sPasswordHash")).isFalse()锁住该不变量,未来若有人改 Map.of 加字段不会无声泄漏。 - backend/src/test/java/com/xly/erp/module/usr/controller/UserControllerIT.java:170-198 —
postWithoutJwt_permitAllStub_returns200_andCreatedBySTUBADMIN/postTamperedJwt_returns20001沿袭 MOD 模块 stub 路径仍缺// REQ-MOD-001 stub: see USR-004 follow-up锚点。建议在 module-report 阶段统一一次性补齐(与 MOD-004 review 同处理)。 - backend/src/main/java/com/xly/erp/common/security/SecurityConfig.java:26 — stub 锚点放在
/api/mod/**与/api/usr/**共同上方。USR-004 实际收紧时需要分两条 requestMatchers 各自指向 hasAuthority/anyRequest,建议在 USR-004 plan 阶段显式记一笔回填动作。 - backend/src/main/java/com/xly/erp/module/usr/service/impl/UserServiceImpl.java:59 —
create方法体缺// REQ-USR-001: 用户新增行内锚点(与 MOD 模块同源遗漏)。建议在 module-report 阶段一次性把 USR / MOD 两个模块的 REQ 行内锚点统一补齐。
反例 / 测试覆盖缺口
Spec 验收清单(service 8 + mapperIT 4 + controllerIT 9 + 工程 5)100% 落地,sourcing .env.local 后 mvn -B test 全量 89/89 全绿。
- BCryptPasswordEncoder 重定位是必要正向修正,被 mapperIT × 5(NONE 环境)+ serviceTest × 8 + controllerIT × 9(RANDOM_PORT 环境)双向覆盖。
- 校验顺序(type/lang 枚举 → staff 存在 → permission 存在 → INSERT user catch DuplicateKey → INSERT permission × N)严格匹配 spec § 业务规则 1–6,
@Transactional(rollbackFor=Exception.class)包整个流程。 -
permissionCategoryMapper.countActiveByIds由 service 短路空 list 保护,避免 SQLIN ()错误。 - N+1 INSERT tUserPermission 是 spec § 业务规则 #7 显式 YAGNI 取舍;docs/04 § 3.4「循环中禁止执行 DB 查询」语义针对 SELECT N+1 不是 INSERT。
-
sPasswordHash由 service 返回 Map 仅 put 两个 key 不会泄漏(构造保证),但缺直接断言锁不变量。 - BCrypt 每次 salt 不同——单测用真实
BCryptPasswordEncoder+startsWith("$2a$")断言(非 mock),正确。 - IT cleanup 顺序 tUserPermission → tUser → tStaff → tPermissionCategory 满足 FK(iUserId CASCADE / iStaffId SET NULL / iCategoryId RESTRICT),无外键孤儿。
- SecurityContextHelper 匿名处理与 MOD 同款,stub 锚点缺失沿袭 MOD-004 已点出的范畴。
非阻塞缺口:spec 文字与实现 BCryptPasswordEncoder 位置不一致(见 nice-to-have #1);sPasswordHash regression 锁断言(#4);permissionCategoryIds 重复值假阴性(#3);行内 REQ 锚点 + stub 锚点统一补齐(留 module-report)。