Commit 2db7a55a34b1bd53da1997f4c3a29bc7e77135e7

Authored by zichun
1 parent 6f372852

docs(review:REQ-USR-003:r1): approve

docs/superpowers/reviews/2026-06-01-REQ-USR-003.md 0 → 100644
  1 +# REQ-USR-003 查询用户 — AI 自审报告(review, round 1)
  2 +
  3 +> 阶段:后端(backend)。spec:`docs/superpowers/specs/2026-06-01-REQ-USR-003.md`。
  4 +> 分支:`module-usr`;审阅范围 = 本 REQ 引入的 controller / service / mapper / dto / vo / 公共分页响应体 + 配套测试。
  5 +> 通用代码审查维度:plan-alignment / 正确性 / 边界 / 错误处理 / 一致性 / 架构 / 安全。
  6 +
  7 +---
  8 +
  9 +## 1. 裁决
  10 +
  11 +**verdict = approve(round 1)**。未发现客观、可定位的 must-fix 缺陷;`issues = []`。
  12 +
  13 +下方记录亮点、观察与(非阻塞)建议,供后续治理参考。
  14 +
  15 +---
  16 +
  17 +## 2. plan-alignment(实现 ↔ 规格 / 契约)
  18 +
  19 +| spec 条款 | 实现位置 | 结论 |
  20 +|---|---|---|
  21 +| 单端点 `GET /api/usr/users`,`Result<PageResult<UserVO>>` | `UsrUserController.queryUsers`(仅 `@Valid` + 委派) | 一致 |
  22 +| 任意已认证用户可调用(D5,无管理员前置) | Controller 查询方法**未**加 `ADMIN_USER_TYPE` 前置;`SecurityConfig` 其余请求 `authenticated()` | 一致 |
  23 +| 单条件(queryField + matchType + queryValue,D2) | `parseCondition` 单分支解析 | 一致 |
  24 +| 字段→列白名单映射(§ 3.4) | `FIELD_COLUMN` `Map.of(...)` 8 项,带表别名 | 一致 |
  25 +| 文本 包含/不包含/等于;枚举/布尔/日期精确 | `UserQueryCondition` 四类 + XML `<choose>` 分支 | 一致 |
  26 +| 空 queryValue(trim 后空)= 不过滤全量分页(§ 3.2) | `parseCondition` 首段 `none()` | 一致 |
  27 +| LEFT JOIN 取员工名/部门,未关联职员两列 null(§ 3.6) | XML `LEFT JOIN usr_employee` + 显式 resultMap | 一致 |
  28 +| 分页:参数非法 → 42201;数据越界 → 钳最后一页 code=0(D1/D8) | Service 先判定范围抛 42201,再按 `pages` 钳位回查 | 一致 |
  29 +| total=0 返回空 records、pageNum=1 | Service 显式分支 | 一致 |
  30 +| 布尔/日期解析口径(D6),非法 → 40001 | `normalizeBool` / `parseDayStart` | 一致 |
  31 +| 密码/租户列绝不返回(§ 3.9 / AC13) | 显式列清单查询、VO 无密码列、无 `SELECT *` | 一致 |
  32 +| API 契约(docs/05)端点/参数/响应体形状 | 完全吻合(含 `pageSize` 上限 100) | 一致 |
  33 +| 错误码(ResultCode 0/40001/42201/401) | 复用既有枚举(42201 此前已预留) | 一致 |
  34 +
  35 +## 3. 正确性 / 边界 / 错误处理
  36 +
  37 +- 分页钳位:`pages = (total + pageSize - 1) / pageSize`,`pageNum > pages` 时用钳后页号**回查一次**保证 records 为真实末页数据,`PageResult.pageNum` 回传钳后页号、`total` 回传真实总数 —— 符合 AC10,单测 `dataPageOutOfRangeClampsToLastPage` + IT `ac10` 双重覆盖。
  38 +- 42201 与 40001 边界明确:`UserQueryDTO` 故意**不**对 `pageNum/pageSize` 加 `@Min/@Max`(否则被全局处理器统一转 40001,与要求的 42201 冲突),范围在 Service 入口显式判定 —— 设计与 D8 一致,IT `ac11` 三种非法值均回 42201。
  39 +- 布尔归一化兼容 `0/1`、`是/否`、`true/false`(大小写无关),不可解析显式 40001;日期支持 `yyyy-MM-dd` 与 `yyyy-MM-dd HH:mm:ss`,按「当日 [00:00, 次日 00:00)」半开区间匹配,非法 40001。
  40 +- 「不包含」对 NULL(未关联职员)行:依赖 SQL 三值逻辑 `NOT LIKE` 对 NULL 返回 unknown 自然不命中(D7),未引入易错的隐式 `OR col IS NULL`。
  41 +
  42 +## 4. 安全
  43 +
  44 +- **SQL 注入面已封闭**:XML 中唯一的 `${}` 是 `cond.column`,其取值**只**来自 `FIELD_COLUMN` 固定白名单 `Map.of(...)`,用户输入(queryField 中文)仅作 key 查表、查不到即抛 40001,绝不拼接到列 token;所有值经 `#{}` 预编译占位。
  45 +- LIKE 通配符 `% _ \` 经 `escapeLike` 转义并配合 XML `ESCAPE '\\'`,防止用户输入被当通配符(D3)。
  46 +- 密码零泄露:查询 SQL 显式列清单不含 `sPassword`/租户列,VO 无对应属性;IT `ac13` 断言响应体不含 `sPassword`/`password`/`$2a$`。
  47 +
  48 +## 5. 架构 / 一致性
  49 +
  50 +- 分层正确(Controller 仅校验+委派 → Service 业务+分页装配 → Mapper),Controller 不直接碰 Mapper。
  51 +- 复用 REQ-USR-001/002 既有 controller/service/mapper/entity,仅新增 `UserQueryDTO` / `UserVO` / `UserQueryCondition` / `PageResult`,未跨模块。
  52 +- `UserQueryCondition` 中间载体把「中文枚举判断 + 类型解析」收敛在 Service,XML 只做结构化 `<if>/<choose>` 分支,关注点分离清晰。
  53 +- `PageResult<T>` 作为通用分页契约(docs/04 § 1.4/§ 3.2)落地,后续分页 REQ 可复用。
  54 +- VO 对匈牙利前缀字段用 `@JsonProperty` 锁小驼峰键名,与 docs/05 契约键一致,做法与 DTO 统一。
  55 +
  56 +## 6. 观察与建议(非阻塞,不进 issues)
  57 +
  58 +- **[建议 / 测试治理] `UsrUserQueryIT` 未进标准 unit 管线**:docs/04 § 零 后端 `unit = mvn -q -B test` 且 `e2e = 无`,Surefire 默认排除 `**/*IT.java`,且 pom 无 maven-failsafe-plugin,故本 REQ 的端到端验收回归(AC1-AC13)不在锁定闸门内执行(verify 报告 § 4 已如实记录)。
  59 + - 此为**项目既有测试装配现状**(与 REQ-USR-001/002 一致),非本 REQ 代码引入的缺陷,故不构成 request-changes。
  60 + - 审阅期间我**手动执行** `mvn -Dtest=UsrUserQueryIT test`(连真实测试库,Flyway V1 已 apply),结果 **Tests run: 13, Failures: 0, Errors: 0, Skipped: 0**,10.19s 全绿;其中 `ac8` 按部门跨表过滤实跑了真实 LEFT JOIN + MyBatis-Plus 自动 COUNT 路径、`ac7` 实跑日期半开区间、`ac6` 实跑布尔归一化、`ac4/ac8` 实跑 `ESCAPE` LIKE,均通过——本 REQ SQL 路径经验性无误。
  61 + - 后续治理建议(超出本 REQ 作用域):引入 maven-failsafe-plugin 并把 `mvn verify` 锁进 docs/04 § 零,使真实安全链 IT 进入 CI 标准管线。
  62 +- **[微 nit,不影响正确性] `MybatisPlusConfig` 未显式设置 `optimizeJoin`**:3.5.7 默认 `optimizeJoin=true`,对「无 join 列过滤」的 COUNT 会去掉 LEFT JOIN(性能更优且语义正确,因为 `iEmployeeId` N:1 不放大行数),对「按 e.* 过滤」的 COUNT 会保留 JOIN——经 `ac8` 实跑验证 COUNT 行为正确。无需改动,仅记录。
  63 +
  64 +---
  65 +
  66 +## 7. 结论
  67 +
  68 +代码实现与 spec / docs/05 契约 / docs/04 技术规范一致;正确性、边界、错误处理、安全(注入面、密码泄露)、架构分层、命名/响应/异常约定均达标;单测(Service/DTO/VO/Mapper)+ 手动实跑的 IT(AC1-AC13)双重佐证行为正确。
  69 +
  70 +**approve。issues = []。**