2026-05-15-REQ-USR-002.md 3.98 KB

req_id: REQ-USR-002 date: 2026-05-15 round: 1

reviewer: superpower-code-reviewer

Review: REQ-USR-002 — round 1

结论

approve

Must-fix

(无)

Nice-to-have

  • backend/src/main/java/com/xly/erp/module/usr/service/impl/UserCreateServiceImpl.java:82 — DataIntegrityViolationException 转 40901/40902 用 message 文本匹配,与驱动版本/locale 强耦合;建议改判 SQLState='23000' + 错误号 1062,并补 @SpyBean Mockito 回归测试(spec § 15 唯一索引并发兜底测试缺口)
  • backend/src/main/java/com/xly/erp/module/usr/service/impl/UserCreateServiceImpl.java:93 — 权限分类批量插入用 for + 单条 insert(N 次 IO);建议改 saveBatch 或在注释里写明 'N < 20 不批量'
  • backend/src/main/java/com/xly/erp/module/usr/service/impl/UserCreateServiceImpl.java:57 — countActiveByIds 用 COUNT(*) + size 比较,重复 ID(如 [1,1,2])会误判 40004;建议先 dedup 或改用 COUNT(DISTINCT iIncrement)
  • backend/src/main/java/com/xly/erp/module/usr/service/impl/UserCreateServiceImpl.java:52 — employee/permissionCategory 校验失败抛 COMPANY_NOT_FOUND 但 message 含义错位;后续可拆 RESOURCE_NOT_FOUND 段位或重命名 COMPANY_NOT_FOUND → REFERENCE_NOT_FOUND
  • backend/src/main/java/com/xly/erp/common/security/JwtHandlerInterceptor.java:48 — 每请求 1 次 selectByUsername DB 查询;后续可加 Caffeine 短期缓存(容量评估时再做)
  • backend/src/main/java/com/xly/erp/common/security/JwtHandlerInterceptor.java:60 — claims.get('companyCode') 未做 null 校验;建议在 LoginUser 处文档化或加 log.warn
  • backend/src/main/java/com/xly/erp/common/security/JwtHandlerInterceptor.java:66 — set LoginContext 早于角色守卫;建议加注释说明该顺序是有意的,afterCompletion 保证清理
  • backend/src/main/java/com/xly/erp/common/security/JwtHandlerInterceptor.java:35 — plan 声明 'handler 非 HandlerMethod → return true',实际未实现该顶部短路;建议补齐对齐 plan
  • backend/src/test/java/com/xly/erp/common/security/JwtHandlerInterceptorTest.java:124 — plan 列出 loginContext_clearedAfterRequest 测试用例但未实现;建议补显式清理回归
  • backend/src/main/java/com/xly/erp/module/usr/dto/CreateUserReq.java:24 — userType/language 用 @Pattern 枚举校验失类型安全;可改为 service 层 toEnum 模式
  • backend/src/main/resources/application.yml:8 — fail-on-unknown-properties=true 是全局开关;建议在 docs/04 § 1.3 留痕说明所有 DTO 必须完整定义
  • backend/src/main/java/com/xly/erp/module/usr/controller/UserController.java:28 — LoginContext.current() 未 null 校验;建议加防御性 Optional.ofNullable
  • backend/src/main/java/com/xly/erp/module/usr/entity/SysPermissionCategory.java:17 — entity 含未写入的多租户字段(sId/sBrandsId 等),与 SysUser 风格一致,可接受

反例 / 测试覆盖缺口

  1. spec § 15「唯一索引兜底(DataIntegrityViolationException 路径)」未实测——UserCreateServiceImplTest 缺乏对 catch 分支的回归
  2. plan § Step 1 列出 loginContext_clearedAfterRequest 但实际未实现,ThreadLocal 清理路径无显式回归
  3. docs/04 § 1.3 段位(10xxx/20xxx/...)与代码 / docs/05 实际使用的 HTTP-aligned 段位(40001/40101/40301/...)冲突——round 1 REQ-USR-001 已记录,本 REQ 沿用未拉齐;建议单独 PR 修订 docs/04 § 1.3
  4. permissionCategoryIds 含重复 ID 行为未定义(countActiveByIds 用 COUNT(*) + size 会 false-negative),无对应测试

总结

REQ-USR-002 整体实现质量较高:鉴权 / 角色守卫拦截器流程清晰、事务边界正确、唯一性预检 + DB 兜底双层防御、初始密码 BCrypt 哈希、Jackson 严格反序列化拒绝 password 字段、85 测试覆盖 spec 验收 1-14。文档同步(docs/05 删除 password+40002)已完成,错误码新增 40301/40901/40902 + HTTP 映射准确。功能、安全、文档与 plan/spec 三方一致,无高/中级阻塞问题。Approve。