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