Merged
Merge Request #2 · created by 朱子纯


feat(module_usr): 用户管理

模块完成报告

docs/superpowers/module-reports/2026-04-30-module_usr.md(本 MR 仓库内完整贴入下方)。



module_id: module_usr date: 2026-04-30

git_range: master..HEAD (31 commits)

模块完成报告 — module_usr 用户管理

① 模块信息

  • 模块 ID: module_usr
  • 模块名: 用户管理
  • 开发区间: 31 commits,~4149 行新增
  • 分支: module-module_usr
  • 里程碑:USR-004 完成 stub 闭环——整个项目 SecurityConfig 收紧到 登录接口外全部 authenticated()

② REQ 完成清单

  • REQ-USR-001 — 用户新增
    • spec: docs/superpowers/specs/2026-04-30-REQ-USR-001.md
    • plan: docs/superpowers/plans/2026-04-30-REQ-USR-001.md
    • review: docs/superpowers/reviews/2026-04-30-REQ-USR-001.md
  • REQ-USR-002 — 用户修改
    • spec/plan/review: 同名 2026-04-30-REQ-USR-002 三件套
  • REQ-USR-003 — 用户查询
    • spec/plan/review: 同名 2026-04-30-REQ-USR-003 三件套
  • REQ-USR-004 — 用户登录(含 Stub 闭环)
    • spec/plan/review: 同名 2026-04-30-REQ-USR-004 三件套

③ 文件变更表

文件 操作 说明
backend/src/main/java/com/xly/erp/module/usr/entity/User.java 新建 17 字段 1:1 映射 tUser
backend/src/main/java/com/xly/erp/module/usr/entity/UserPermission.java 新建 8 字段映射 tUserPermission
backend/src/main/java/com/xly/erp/module/usr/dto/CreateUserDTO.java 新建 USR-001 入参
backend/src/main/java/com/xly/erp/module/usr/dto/UpdateUserDTO.java 新建 USR-002 入参(无 sPasswordHash)
backend/src/main/java/com/xly/erp/module/usr/dto/LoginDTO.java 新建 USR-004 登录入参
backend/src/main/java/com/xly/erp/module/usr/vo/UserListVO.java 新建 USR-003 列表 VO(11 字段,含 LEFT JOIN tStaff)
backend/src/main/java/com/xly/erp/module/usr/vo/UserBriefVO.java 新建 USR-004 用户简表
backend/src/main/java/com/xly/erp/module/usr/vo/LoginVO.java 新建 USR-004 登录响应
backend/src/main/java/com/xly/erp/module/usr/mapper/UserMapper.java 新建 + 多次扩展 BaseMapper + 自定义查询 + 登录用方法
backend/src/main/java/com/xly/erp/module/usr/mapper/UserPermissionMapper.java 新建 + deleteByUserId USR-001 + USR-002
backend/src/main/java/com/xly/erp/module/usr/mapper/StaffMapper.java 新建 最小 existsActiveById
backend/src/main/java/com/xly/erp/module/usr/mapper/PermissionCategoryMapper.java 新建 批量校验 countActiveByIds
backend/src/main/resources/mapper/usr/UserMapper.xml 新建 USR-003 动态 SQL(pageWithFilter / countWithFilter)
backend/src/main/java/com/xly/erp/module/usr/security/LoginAttemptStore.java 新建 USR-004 内存失败计数 + 锁定
backend/src/main/java/com/xly/erp/module/usr/service/UserService.java 新建 + 4 次扩展 create/update/list/login
backend/src/main/java/com/xly/erp/module/usr/service/impl/UserServiceImpl.java 新建 + 4 次扩展 全部业务逻辑
backend/src/main/java/com/xly/erp/module/usr/controller/UserController.java 新建 POST/PUT/GET /api/usr/users
backend/src/main/java/com/xly/erp/module/usr/controller/AuthController.java 新建 POST /api/usr/auth/login
backend/src/main/java/com/xly/erp/common/config/PasswordEncoderConfig.java 新建 BCryptPasswordEncoder bean(独立于 SecurityConfig,避免 NONE 上下文丢失)
backend/src/main/java/com/xly/erp/common/security/JwtAuthenticationEntryPoint.java 新建 USR-004 未认证返回 code=20001
backend/src/main/java/com/xly/erp/common/security/SecurityConfig.java 修改 USR-001 加 /api/usr/** permitAll → USR-004 收紧到仅 /api/usr/auth/login permitAll + entryPoint
backend/src/main/java/com/xly/erp/common/security/JwtUtil.java 修改 加 signRefresh + 通用 sign(userNo, ttl)
backend/src/main/java/com/xly/erp/module/mod/service/impl/ModuleServiceImpl.java 修改 USR-004 移除 stub.getStubUserNo() fallback
backend/src/test/java/com/xly/erp/module/usr/*/ (5 文件) 新建 UserMapperIT(7) / StaffMapperIT(1) / PermissionCategoryMapperIT(1) / UserServiceImplTest(35) / UserControllerIT(27) / LoginAttemptStoreTest(5) / AuthControllerIT(5)
backend/src/test/java/com/xly/erp/module/mod/controller/ModuleControllerIT.java 修改 USR-004 闭环:4 处 stub IT 期望从 200 改 20001
backend/src/test/java/com/xly/erp/common/security/JwtUtilTest.java 修改 加 signRefresh 验证
docs/superpowers/specs plans reviews/2026-04-30-REQ-USR-{001..004}.md
docs/superpowers/module-reports/module_usr-test-gate.md 新建 本模块 test-gate 证据
docs/08-模块任务管理.md 修改 4 个 REQ checkbox 勾上

④ 数据库使用表

  • 读:tUser / tStaff / tPermissionCategory(各 REQ 的存在性校验 / JOIN 显示)
  • 写:
    • tUser:INSERT (USR-001) / UPDATE 可编辑列 (USR-002) / UPDATE tLastLoginDate (USR-004)
    • tUserPermission:INSERT (USR-001) / DELETE+INSERT 重建 (USR-002)

⑤ 测试结果

  • scripts/test.sh 最终:GREEN
  • 通过: 149 / 失败: 0 / 跳过: 0(前端段因 frontend/ 未初始化而 skip)
  • 详见: docs/superpowers/module-reports/module_usr-test-gate.md
  • 测试分布:
    • SmokeTest: 1
    • 全局基础设施: GlobalExceptionHandlerTest 4 / JwtUtilTest 4 / JwtAuthenticationFilterTest 3
    • MOD 模块: ModuleMapperIT 5 / ModuleServiceImplTest 25 / ModuleControllerIT 26
    • USR 模块: UserMapperIT 7 / StaffMapperIT 1 / PermissionCategoryMapperIT 1 / LoginAttemptStoreTest 5 / UserServiceImplTest 35 / UserControllerIT 27 / AuthControllerIT 5

⑥ 本模块新增 Migration

—(无 schema 改动;本模块 4 张表均在 V1 就位)

⑦ 跨模块改动清单(软规则 S2)

1 处跨模块改动(USR-004 stub 闭环必需,spec 已声明):

  • backend/src/main/java/com/xly/erp/module/mod/service/impl/ModuleServiceImpl.java:create()/delete() — 移除 stub.getStubUserNo() fallback
  • backend/src/test/java/com/xly/erp/module/mod/controller/ModuleControllerIT.java — 4 处 *WithoutJwt_permitAllStub_* 用例改名为 *WithoutJwt_returns20001,期望 200 → 20001
  • backend/src/test/java/com/xly/erp/module/mod/service/ModuleServiceImplTest.java — 2 处 sCreatedBy 期望从 "STUB_ADMIN" 改为 null

原因 / 影响评估

  • 原因:USR-004 是模块循环最后一个 REQ,按 docs/02 § 二 顺序正好在收尾。spec 把 stub 闭环作为 Phase B 纳入本 REQ;项目首次具备真正的 JWT 鉴权能力,必须把 MOD 模块 stub-friendly 的测试期望同步收紧
  • 影响评估:MOD 模块业务逻辑未变(仅 fallback 一行被删,行为等价于"sCreatedBy 必须由 SecurityContextHelper 提供"——authenticated 守卫保证非 null);MOD 模块的 IT 测试在改写后行为更接近生产实际(无 token 必拒),增强了回归质量

⑧ 偏离 spec 清单

  • REQ-USR-001 & spec § 实现范围 #3:spec 写 BCryptPasswordEncoder bean 在 SecurityConfig 注册,实际移到独立 PasswordEncoderConfig.java。原因:SecurityConfig @ConditionalOnWebApplication(SERVLET) 在 webEnvironment=NONE 的 mapperIT 上下文不加载。良性设计修正——密码编码器应是领域基础设施而非 Web 安全基础设施。
  • REQ-USR-002 & spec/plan § "controller 先 trim":实际实现 trim 在 service。功能等价;spec 文字与实现微差。
  • REQ-USR-003 & plan § 文件变更清单:未创建 UserListQuery DTO,5 参直接传 service。简化合理。
  • REQ-USR-002 错误码 40022:docs/05 § USR-002 错误码列表未列 40022;本实现复用 USR-001 已建立的语义(同字段两个接口错误码一致更优)。
  • REQ-USR-004 LoginDTO.version @NotBlank:spec § 输入说"仅记录, 不参与校验",实际加了 @NotBlank。微差。

⑨ AI reviewer 报告汇总

  • REQ-USR-001: round 1 — approve
  • REQ-USR-002: round 1 — approve
  • REQ-USR-003: round 1 — approve
  • REQ-USR-004: round 1 — approve(含 Phase B stub 闭环)

4/4 round 1 一次性通过;4 份 review 共约 28 条 nice-to-have,无 must-fix。

⑩ 已知问题

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

架构遗留(建议下一模块开始前 chore commit 一次性收口)

  1. ModuleServiceImpl / UserServiceImpl 移除 stub fallback 后 private final StubSecurityProperties stub 字段已无引用(但构造器仍注入);按 surgical-changes 原则建议删字段 + 构造器参数 + 测试 setUp
  2. JwtAuthenticationFilter 仍带 @Component,与 SecurityConfig addFilterBefore 重叠注册;可改 @Bean + FilterRegistrationBean disable
  3. JwtUtil 暴露两个 public 构造器;JwtUtil(String secret) 实质 test-only
  4. 业务方法 (create/update/delete/listTree/login 5 处) 缺行内 // REQ-USR-XXX 锚点
  5. entity Module 类名与 java.lang.Module 冲突
  6. application-dev.yml 缺失(docs/09 § 二 列出)

测试覆盖增强(非阻塞)

  1. LoginAttemptStoreTest.lockExpiresAfterDuration 未驱动同 userName 跨过 lockExpireAt 验证 records.remove 自动清除
  2. PUT-stub-regression IT 只断单列 unchanged,建议同时断 sCreatedBy / tCreateDate
  3. USR-001/002 permissionCategoryIds 重复 id 假阴性(IN 隐式去重,建议 service distinct
  4. USR-003 响应 records 缺直接 has("sPasswordHash") == false 断言
  5. USR-003 ORDER BY iIncrement DESC / tLastLoginDate JSON ISO 格式无显式断言
  6. tCreateDate / sBrandsId / sSubsidiaryId 在 update / delete IT 端未端到端断言保留
  7. iParentId 指向 bDeleted=1 旧记录是否回 40021 未单独验证
  8. AuthControllerIT 字段级负例(仅缺 sUserName)未单独覆盖

配置 / 校验

  1. application.yml ${JWT_SECRET} 缺 fail-fast 默认值
  2. GlobalExceptionHandler handleAny(Exception) 把 4xx 框架异常转 50000
  3. DTO iParentId / iSortOrder@PositiveOrZero / @Min 约束
  4. LoginDTO.version @NotBlank 与 spec "仅记录" 不一致

信息泄漏

  1. UserListVO 设计上不暴露敏感字段,但 IT 缺 regression-locking 断言

⑪ 下一模块预览

项目所有 REQ 已完成:docs/02 § 二 共 8 个 REQ(MOD 4 + USR 4),全部 review approve 完毕。

USR 模块合并到 master 后,整个项目处于:

  • 后端工程:149 用例全绿
  • 鉴权:仅 POST /api/usr/auth/login 公开,其余 authenticated()
  • 数据库:tUser / tStaff / tPermissionCategory / tUserPermission / tModule 全部就位

后续工作(不属于本期 REQ 范围,建议作为新 epic)

  • 引入 frontend/(React + Vite)
  • 引入 Redis 替换 LoginAttemptStore 内存实现
  • jacoco 覆盖率
  • application-dev.yml + dev profile
  • ModuleEntity 重命名(Module 与 java.lang.Module 冲突)
  • 4 处行内 // REQ-XXX 锚点补齐

⑫ MR 链接

待 mr-create 填入。


本地闸门证据

  • test.sh: green(subagent: a80ebb0dfb4c4a6f4)

审核入口

  • 本 MR = 模块 module_usr 的唯一人工介入点
  • Approve + Merge 后,下次用户运行 /erp-workflow:coding-start 时入口会自动扫描到 GitLab API state=merged,探测默认分支后 git pull --ff-only 同步并推进下一模块

From module-module_usr into master

Merged by 朱子纯

1 participants