2026-06-01-REQ-USR-002.md
5.63 KB
REQ-USR-002 修改用户 — AI 自审报告(review, round 1)
阶段:后端(backend)。spec:
docs/superpowers/specs/2026-06-01-REQ-USR-002.md。 分支:module-usr;审查范围 = 本 REQ diff(09db895..8a1b01b)。 维度:通用后端代码审查(plan-alignment / 正确性 / 边界 / 错误处理 / 一致性 / 架构 / 文档)。
裁决
verdict = approve(issues = 空数组)。
实现与 spec / docs/05 契约 / docs/04 技术规范一致,8 条验收标准均有对应实现与测试覆盖,无客观可验证的 must-fix 缺陷。
1. 变更清单
| 文件 | 性质 |
|---|---|
backend/.../usr/dto/UpdateUserDTO.java |
新增入参 DTO(匈牙利前缀 + @JsonProperty 锁键名 + Bean Validation) |
backend/.../usr/service/UsrUserService.java |
接口新增 updateUser(Integer, UpdateUserDTO)
|
backend/.../usr/service/impl/UsrUserServiceImpl.java |
新增 updateUser 实现(存在性校验 / 部分更新 / 权限全量覆盖 / 单事务) |
backend/.../usr/controller/UsrUserController.java |
新增 PUT /api/usr/users/{id} + 管理员前置 |
backend/.../usr/dto/UpdateUserDTOValidationTest.java |
DTO 校验单测(5) |
backend/.../usr/service/UsrUserServiceImplTest.java |
Service 单测续写(T2 3 + T3 5) |
backend/.../usr/controller/UsrUserControllerTest.java |
Controller 单测续写(4) |
backend/.../usr/UsrUserUpdateIT.java |
端到端验收回归(6,AC1/2/3/6/7/8) |
无 SQL migration 变更(符合 D5:所需表均在 V1 建好);无跨模块改动。
2. plan-alignment(与 spec / 契约对照)
- 端点 / 方法 / 路径与
docs/05 § REQ-USR-002(PUT /api/usr/users/{id})一致;响应Result<{id}>、code=0一致。 - DTO 字段集与契约请求体逐项吻合:
sUserNo / iEmployeeId / sUserType / sLanguage / iCanModifyBill / iIsVoid / permissionIds;不接收sUserName / sPassword / 审计列 / 租户列(spec § 2.1 注)。 - 决策落实:D1(不碰密码)、D2(
iIsVoid承载禁用)、D3(可选列 null = 不更新)、D4(permissionIds全量覆盖 /[]清空 / null 不改)、D5(不新增 migration)、D6(管理员 =超级管理员)。 - 错误码:
40401(不存在)/40001(关联职员或权限元素不存在、枚举越界)/40301(非管理员)/0(成功)——与 spec § 6 一致。
3. 正确性 / 边界 / 错误处理
- 目标用户存在性校验置于所有写入之前,命中即
40401且不落库(AC2)。 - 关联职员、权限元素存在性校验置于主记录更新前,非法即抛
40001,配合@Transactional(rollbackFor=Exception.class)整体回滚、无副作用(AC3)。 - 权限全量覆盖:先删该用户全部旧授权再按去重集合逐行插入;
[]→ 只删不插(清空);null→ 不进入分支(不改);distinct()+Objects::nonNull过滤去重去空(AC6)。 - 主记录部分更新:仅 set 主键 + 必填两列 + 非 null 可选列;密码 /
sUserName/ 审计列 / 租户列全不 set,依赖 MPupdateById默认NOT_NULL字段策略(已核对MybatisPlusConfig与application.yml无策略覆盖)从 SET 子句剔除,保持原值(AC1 / AC8)。 - 响应仅
{id},sPassword实体侧@JsonIgnore,IT 断言响应体不含password子串(AC8)。
4. 架构 / 一致性
- 分层正确:Controller 仅
@Valid+ 管理员前置 + 委派,未直接调 Mapper;业务在 Service;数据访问走LambdaQueryWrapper/ MP 内置(docs/04 § 1.2 / § 3.4)。 - 复用 REQ-USR-001 已建 controller / service / mapper / entity,未另起类,未跨
modules/usr/**之外(spec § 4)。 - 统一响应 / 全局异常 / 错误码枚举沿用既有公共设施,风格一致。
5. 文档
- 类 / 方法 Javadoc 完整,含
REQ-USR-002追溯标记与决策引用,符合项目注释约定。
6. 非阻塞建议(口头,非 must-fix)
-
路径参数非正整数口径:spec § 2.1 / § 6 期望非正整数
id返回40001,当前 Controller 未加@Positive,0/ 负数id会落到selectById返回 null →40401。因0/ 负数主键实际不可能命中记录,行为安全(仍被拒绝、不产生数据污染),但与契约文字口径略有出入;后续可在@PathVariable加@Positive并开启@Validated使其归一为40001。 -
注释 tag 笔误:
UsrUserController中管理员判定注释引用spec § 8 D2,实际管理员口径决策为D6(D2 是iIsVoid状态决策);纯注释问题,不影响行为。 - 并发健壮性(理论):权限"先删后插"在高并发同一用户授权场景下存在间隙锁 / 死锁的理论可能;当前管理员低频写场景可接受,不构成缺陷。
-
非整数
iIsVoid/iCanModifyBill反序列化:传入非数字会触发 JacksonHttpMessageNotReadableException,落到兜底Exception处理器返回50000而非40001;此为既有全局行为(与 REQ-USR-001 一致),不在本 REQ 作用域单独修。
7. 测试与闸门
- verify 证据(
2026-06-01-REQ-USR-002-verify.md):mvn checkstyle:checkexit 0、mvn test36 通过 / 0 失败,BUILD SUCCESS。 - 单测覆盖:DTO 校验、Service 存在性 / 部分更新 / 关联职员 / 权限全量覆盖(含去重、清空、不改)、Controller 管理员前置 / 路由 / 404 转译。
- IT 覆盖 AC1/2/3/6/7/8;AC4(禁用实时生效)/ AC5(角色变更实时生效)依赖 REQ-USR-004 / REQ-USR-003,本 REQ 以"落库层读回等价验证"覆盖,符合 spec § 7 边界说明。