2026-04-30-REQ-USR-002.md 6.36 KB

req_id: REQ-USR-002 date: 2026-04-30

spec_ref: docs/superpowers/specs/2026-04-30-REQ-USR-002.md

REQ-USR-002 用户修改 Implementation Plan

Execution: Parent skill feature-tdd executes this plan task-by-task.

Goal: 在 USR-001 已建工程基础上增量实现 PUT /api/usr/users/{id}:更新可编辑字段 + 重建权限组(删旧 + 插新);保留 sPasswordHash / sCreatedBy / 标准列。

Architecture: 复用 UserService / UserServiceImpl / UserController / 全部 mappers;新增 UpdateUserDTOUserService#updateUserPermissionMapper#deleteByUserId。SecurityConfig 已对 /api/usr/** permitAll,无需改。

Tech Stack: 沿用(Spring Boot 3.3.5 / MyBatis-Plus / JJWT)。


Schema 改动

无(仅 UPDATE / DELETE / INSERT)。

文件变更清单

新增

  • backend/src/main/java/com/xly/erp/module/usr/dto/UpdateUserDTO.java

修改

  • backend/src/main/java/com/xly/erp/module/usr/mapper/UserPermissionMapper.java — 追加 deleteByUserId(Integer)
  • backend/src/main/java/com/xly/erp/module/usr/service/UserService.java — 追加 Integer update(Integer id, UpdateUserDTO dto)
  • backend/src/main/java/com/xly/erp/module/usr/service/impl/UserServiceImpl.java — 实现 update(含 5 类校验 + UPDATE + DELETE + INSERT × N)
  • backend/src/main/java/com/xly/erp/module/usr/controller/UserController.java — 追加 PUT 端点
  • backend/src/test/java/com/xly/erp/module/usr/service/UserServiceImplTest.java — 追加 10 单测
  • backend/src/test/java/com/xly/erp/module/usr/mapper/UserMapperIT.java — 追加 1 用例(deleteByUserId)
  • backend/src/test/java/com/xly/erp/module/usr/controller/UserControllerIT.java — 追加 10 IT

任务步骤

Task 1: UserPermissionMapper#deleteByUserId + IT

Files:

  • Modify: backend/src/main/java/com/xly/erp/module/usr/mapper/UserPermissionMapper.java
  • Modify: backend/src/test/java/com/xly/erp/module/usr/mapper/UserMapperIT.java

API shape:

  • @Delete("DELETE FROM tUserPermission WHERE iUserId = #{userId}") int deleteByUserId(@Param("userId") Integer userId)

  • Step 1: 写失败测试 userPermissionMapper#deleteByUserId_removesAllRowsForGivenUser

    • 准备:插 user1 + user2;user1 关联 cat1+cat2,user2 关联 cat1
    • deleteByUserId(user1.id) → 返回 2,user1 行数 = 0;user2 行数 = 1(不受影响)
  • Step 2: 实现 mapper 方法

  • Step 3: 子会话验证 PASSmvn -B test -Dtest=UserMapperIT

  • Step 4: Commitfeat(usr): mapper#deleteByUserId for permission rebuild REQ-USR-002

Task 2: UpdateUserDTO + UserService.update 主流程(合法 + 目标存在性 + bShowPerm null→false)

Files:

  • Create: backend/src/main/java/com/xly/erp/module/usr/dto/UpdateUserDTO.java
  • Modify: backend/src/main/java/com/xly/erp/module/usr/service/UserService.java
  • Modify: backend/src/main/java/com/xly/erp/module/usr/service/impl/UserServiceImpl.java
  • Modify: backend/src/test/java/com/xly/erp/module/usr/service/UserServiceImplTest.java

API shape:

  • UpdateUserDTO 字段(与 CreateUserDTO 平行,去掉 permissionCategoryIds 之外字段语义不变;剔除 sPasswordHash仍包含 permissionCategoryIds 用于重建权限组)
  • UserService#update(Integer id, UpdateUserDTO dto) : Integer
  • 实现:

    1. selectById(id) → null 或 bDeleted=true → 40400
    2. 枚举校验 sUserType / sLanguage(同 create)→ 40001
    3. iStaffId 校验(同 create)→ 40022
    4. permissionCategoryIds 校验(仅当非空 list;null/空 list 跳过校验直接清空)→ 40023
    5. 构造 entity 仅 set iIncrement + 6 个可编辑字段(其余 null);bCanModifyDocs null → false
    6. userMapper.updateById(entity) try/catch DuplicateKeyException → 40020
    7. userPermissionMapper.deleteByUserId(id)
    8. 若 permissionCategoryIds 非空:for-loop 插 UserPermission
  • Step 1: 追加 4 单测

    • updateWithValidDto_invokesUpdateById_andRebuildsPermissions
    • updateWithTargetNotFound_throws40400
    • updateWithTargetAlreadyDeleted_throws40400
    • updateWithBCanModifyDocsNull_setsFalseInEntity
  • Step 2: 实现 DTO + service 主流程

  • Step 3: 子会话验证 PASSmvn -B test -Dtest=UserServiceImplTest(期望 8+4=12)

  • Step 4: Commitfeat(usr): user update dto + service happy path REQ-USR-002

Task 3: Service 异常分支补全(枚举 / staff / permission / 唯一冲突 / 清空权限)

Files:

  • Modify: backend/src/main/java/com/xly/erp/module/usr/service/impl/UserServiceImpl.java
  • Modify: backend/src/test/java/com/xly/erp/module/usr/service/UserServiceImplTest.java

API shape: 不变

  • Step 1: 追加 6 单测
    • updateWithInvalidUserType_throws40001
    • updateWithInvalidLanguage_throws40001
    • updateWithStaffNotFound_throws40022
    • updateWithSomeInvalidPermissionIds_throws40023
    • updateWithDuplicateUserNo_throws40020
    • updateWithEmptyPermissionIds_clearsExisting — permissionCategoryIds=null;deleteByUserId 调一次、insert 永不调
  • Step 2: 实现校验分支
  • Step 3: 子会话验证 PASSmvn -B test -Dtest=UserServiceImplTest(期望 12+6=18)
  • Step 4: Commitfeat(usr): user update error branches REQ-USR-002

Task 4: Controller PUT + IT(10 用例)+ 全量回归

Files:

  • Modify: backend/src/main/java/com/xly/erp/module/usr/controller/UserController.java
  • Modify: backend/src/test/java/com/xly/erp/module/usr/controller/UserControllerIT.java

API shape:

  • @PutMapping("/users/{id}") public Result<Map<String,Object>> update(@PathVariable Integer id, @Valid @RequestBody UpdateUserDTO dto)
  • 返回 Result.ok(Map.of("iIncrement", userService.update(id, dto)))

  • Step 1: 追加 10 IT(参 spec 验收清单)

  • Step 2: 实现 controller PUT

  • Step 3: 子会话跑全量回归mvn -B test(期望 89 + 1+10+10=20 = 109+ 用例全绿)

  • Step 4: Committest(usr): user update integration coverage REQ-USR-002

提交计划

commit 覆盖
feat(usr): mapper#deleteByUserId for permission rebuild REQ-USR-002 Task 1
feat(usr): user update dto + service happy path REQ-USR-002 Task 2
feat(usr): user update error branches REQ-USR-002 Task 3
test(usr): user update integration coverage REQ-USR-002 Task 4