2026-06-01-REQ-USR-003.md
6.4 KB
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<PageResult<UserVO>>
|
UsrUserController.queryUsers(仅 @Valid + 委派) |
一致 |
| 任意已认证用户可调用(D5,无管理员前置) | Controller 查询方法未加 ADMIN_USER_TYPE 前置;SecurityConfig 其余请求 authenticated()
|
一致 |
| 单条件(queryField + matchType + queryValue,D2) |
parseCondition 单分支解析 |
一致 |
| 字段→列白名单映射(§ 3.4) |
FIELD_COLUMN Map.of(...) 8 项,带表别名 |
一致 |
| 文本 包含/不包含/等于;枚举/布尔/日期精确 |
UserQueryCondition 四类 + XML <choose> 分支 |
一致 |
| 空 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+ ITac10双重覆盖。 - 42201 与 40001 边界明确:
UserQueryDTO故意不对pageNum/pageSize加@Min/@Max(否则被全局处理器统一转 40001,与要求的 42201 冲突),范围在 Service 入口显式判定 —— 设计与 D8 一致,ITac11三种非法值均回 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转义并配合 XMLESCAPE '\\',防止用户输入被当通配符(D3)。 - 密码零泄露:查询 SQL 显式列清单不含
sPassword/租户列,VO 无对应属性;ITac13断言响应体不含sPassword/password/$2a$。
5. 架构 / 一致性
- 分层正确(Controller 仅校验+委派 → Service 业务+分页装配 → Mapper),Controller 不直接碰 Mapper。
- 复用 REQ-USR-001/002 既有 controller/service/mapper/entity,仅新增
UserQueryDTO/UserVO/UserQueryCondition/PageResult,未跨模块。 -
UserQueryCondition中间载体把「中文枚举判断 + 类型解析」收敛在 Service,XML 只做结构化<if>/<choose>分支,关注点分离清晰。 -
PageResult<T>作为通用分页契约(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实跑ESCAPELIKE,均通过——本 REQ SQL 路径经验性无误。 - 后续治理建议(超出本 REQ 作用域):引入 maven-failsafe-plugin 并把
mvn verify锁进 docs/04 § 零,使真实安全链 IT 进入 CI 标准管线。
-
[微 nit,不影响正确性]
MybatisPlusConfig未显式设置optimizeJoin:3.5.7 默认optimizeJoin=true,对「无 join 列过滤」的 COUNT 会去掉 LEFT JOIN(性能更优且语义正确,因为iEmployeeIdN:1 不放大行数),对「按 e.* 过滤」的 COUNT 会保留 JOIN——经ac8实跑验证 COUNT 行为正确。无需改动,仅记录。
7. 结论
代码实现与 spec / docs/05 契约 / docs/04 技术规范一致;正确性、边界、错误处理、安全(注入面、密码泄露)、架构分层、命名/响应/异常约定均达标;单测(Service/DTO/VO/Mapper)+ 手动实跑的 IT(AC1-AC13)双重佐证行为正确。
approve。issues = []。