13 KiB
R01-A5 独立代码复验报告(第五批:8 个 Controller 请求身份与 requestId 收束)
日期:2026-09-17
分支:refactor/r01-structure-performance
HEAD:0226977063243fce101e3f6c8dcf66492d306ef7(A4 后最新提交,复验全程未变)
复验对象:docs/r01-a5-request-context-migration-report.md
性质:仅复验 —— 未修改生产代码、未修改既有测试断言、未暂存、未提交、未推送
0. 结论
R01-A5独立代码复验通过,可以进入精确暂存、本地提交与推送检查点。
FAIL = 0;SKIP = 0。未发现生产缺陷。
1. 开始前状态
| 检查项 | 结果 |
|---|---|
| 分支 | refactor/r01-structure-performance ✔ |
| HEAD | 0226977063243fce101e3f6c8dcf66492d306ef7(A4 后最新)✔ |
| 与上游同步 | 0 / 0 ✔ |
| 暂存区 | 空 ✔ |
git diff --check |
无空白错误 ✔ |
| 工作区改动 | 仅 8 个 A5 Controller + 路线图,无 A5 之外的生产代码改动 ✔ |
| 历史未跟踪文件 | 全部保留 ✔ |
reset/restore/checkout/clean、git add -A、Docker |
均未执行 ✔ |
GlobalExceptionHandler.java 三方 blob 比对
| 来源 | blob |
|---|---|
worktree(git hash-object) |
1fdc341ebe3653393b3ec04f0c2d0bb9dfbb6d21 |
index(git ls-files -s) |
1fdc341ebe3653393b3ec04f0c2d0bb9dfbb6d21 |
HEAD(git rev-parse HEAD:<path>) |
1fdc341ebe3653393b3ec04f0c2d0bb9dfbb6d21 |
三者完全一致 → 纯 Windows stat 缓存现象,未触碰该文件 ✔
2. 8 个 Controller 静态审查
| Controller | 代表端点 | Accessor 调用(auth / reqId) |
|---|---|---|
| CandidateScoringGovernanceController | GET /profile/me/candidate-scoring/appeals |
9 / 9 |
| InterviewScoreController | GET /interviews/{id}/score |
4 / 4 |
| FavoriteController | GET /jobs/favorites |
3 / 3 |
| AdminReviewController | GET /admin/reviews/pending |
3 / 3 |
| AdminReviewReplyController | GET /admin/review-replies/pending |
2 / 2 |
| CompanyReviewManagementController | GET /companies/me/job-reviews |
4 / 4 |
| JobReviewController | DELETE /jobs/{jobId}/reviews/{reviewId} |
3 / 7 |
| StudentReviewWeightController | GET /profile/me/review-weights |
3 / 4 |
(JobReviewController / StudentReviewWeightController 的 reqId 调用数高于 auth 属正常 —— 部分只读端点只需 requestId。)
改动性质:8 files changed, 101 insertions(+), 198 deletions(-),删除的 ErrorCode.UNAUTHORIZED 共 10 处,全部来自被移除的私有解析方法;角色检查(assertAdminRole/assertCompanyOwner/assertStudentRole)、资源归属、FORBIDDEN/NOT_FOUND 及 Service 调用顺序未删未增、未移动 ✔
JobReviewController.parseIds 完整保留 ✔ —— 它解析 ids 查询参数(业务参数,非认证主体),并以 catch (NumberFormatException) → BAD_REQUEST 处理,属正确的业务参数语义,未被误删或误判。
3. 禁止模式扫描
任务书指定模式在 8 个文件中的命中:全部为 0 ✔
覆盖:SecurityContextHolder、AnonymousAuthenticationToken、anonymousUser、auth.getName()、authentication.getName()、Long.(valueOf|parseLong)(...(auth|authentication)、private (resolveUserId|getRequestId|requestId)(、getHeader(...X-Request-Id|REQUEST_ID_HEADER)、@Autowired(required=false)、@Nullable、ObjectProvider、new CurrentUserAccessor、new RequestIdAccessor
4. 构造器注入与无回退证明
8 个 Controller 均声明:
private final CurrentUserAccessor currentUserAccessor;
private final RequestIdAccessor requestIdAccessor;
- 全部为
private final构造器注入(Lombok@RequiredArgsConstructor)✔ - 无
@Autowired(required = false)/@Nullable/ObjectProvider/new CurrentUserAccessor()/new RequestIdAccessor()回退 ✔ - 身份统一为
currentUserAccessor.requireUserId();requestId 统一为requestIdAccessor.get(request)✔
5. 独立接线变异测试(Accessor 真实生效的证明)
新增 A5AccessorWiringIndependentTest(4 例),不复用实施者断言,使用 @MockBean 替换 CurrentUserAccessor、RequestIdAccessor 与 8 个 Service:
| 用例 | 证明 |
|---|---|
allEightEndpoints_useAccessorUserId |
mock 返回 4242L → 逐一验证 8 个 Service 均收到 4242L(listMyAppeals / getScore / listFavorites / listPendingReviews / listPending / listMine / deleteReview / getMyWeights),并 verify(currentUserAccessor, times(8)).requireUserId() |
illegalIdentity_rejectedBeforeService |
mock 抛 InsufficientAuthenticationException → 8 端点全部 401、无 500,且各 Service 零调用 |
responseBodyRequestId_comesFromAccessor |
mock 返回 a5-independent-request → 8 个端点响应体 requestId 均为该值,verify(requestIdAccessor, times(8)).get(...) |
noToken_is401 |
无 Token → Security 链 401 |
变异有效性:若删除任一 Controller 中的 currentUserAccessor.requireUserId() 或 requestIdAccessor.get(request) 调用,对应 verify 与断言必然失败 —— 即删除 Accessor 调用会使本测试失败 ✔
说明:响应头
X-Request-Id由RequestIdFilter设置,不来自 Controller;本测试只断言响应体的requestId来源于 Accessor,未错误声称响应头来源。
6. 八端点非法主体结果(独立身份边界复验)
A5IndependentVerificationTest 使用真实 CurrentUserAccessor 与真实 SecurityFilterChain(不 mock Accessor):
| 主体 | 结果 |
|---|---|
anonymousUser |
401 ✔ |
not-a-number |
401 ✔ |
99999999999999999999(20 位溢出) |
401 ✔ |
0 |
401 ✔ |
-1 |
401 ✔ |
AnonymousAuthenticationToken / 无 Token |
401 ✔ |
逐端点断言(7 个 GET 端点 + DELETE /jobs/1/reviews/1):
- HTTP 401,无 500 ✔
- 业务 Service 零调用 ✔(
verify(..., never())) - 不回显非法主体 —— 仅解析 JSON 后检查
code/message稳定字段,未对整段 JSON 做数字搜索 ✔ - 响应不包含
Exception/java./principal/anonymousUser/com.mydreamjob/NumberFormat✔
未使用任何捕获 NumberFormatException 的方式"修复"测试。
7. 合法主体传入 Service 的 userId 证据
§5 的 verify 系列即为证据:mock Accessor 返回 4242L 时,8 个 Service 各自收到 4242L(逐一 eq(4242L) 断言)✔
同时 A5IndependentVerificationTest#legalPrincipal_reachesService 使用真实 Accessor(数字主体 "7")验证 8 端点不被身份层判为未登录(非 401)✔
8. requestId Accessor 接线证据
见 §5 第 3 项:mock RequestIdAccessor#get 返回固定值 → 8 个端点(7 个 GET + 1 个 DELETE,均返回 ApiResponse)响应体 requestId 均为 a5-independent-request,并 verify(requestIdAccessor, times(8)).get(any(HttpServletRequest.class)) ✔
9. 401 / 403 / 404 分层保持
| 检查项 | 结果 |
|---|---|
| 静态审查:角色/归属检查是否被删改或移动 | 否(git diff 确认未删未增)✔ |
FORBIDDEN / NOT_FOUND 错误码 |
未修改 ✔ |
ErrorCode.java |
零改动 ✔ |
| 合法主体不被判为未登录 | ✔(§7) |
401 与 403/404 的分层未被统一:身份失败为 401,角色/归属失败仍由既有 Service 逻辑产生 403/404,语义未漂移。
10. 失败请求的业务副作用
新增 A5SideEffectSnapshotTest(2 例),使用 H2 行级快照(显式列 + 稳定 ORDER BY,逐字段比较,非 COUNT/SUM):
快照覆盖 9 张核心表:JOB_FAVORITES、JOB_REVIEWS、COMPANY_REVIEW_REPLIES、INTERVIEW_SCORES、CANDIDATE_SCORING_APPEALS、STUDENT_REVIEW_WEIGHT_PREFERENCES、NOTIFICATIONS、MESSAGES、AUDIT_LOGS。
| 用例 | 结果 |
|---|---|
非法身份请求(anonymousUser + not-a-number × 7 端点 + DELETE)前后 |
9 张表逐字段零变化 ✔ |
| 无 Token 请求前后 | 9 张表逐字段零变化 ✔ |
结论:非法身份请求不写收藏、评价、回复、面试评分、治理申诉、权重;不发通知;不发 RabbitMQ 消息;不写审计成功记录(AUDIT_LOGS 零变化)✔
本轮未启动 Docker,以上为 H2 独立复验结论;不声称真实 MySQL 结论。
11. 测试数
| 命令 | 结果 |
|---|---|
mvn -Dtest="A5AccessorWiringIndependentTest,A5IndependentVerificationTest" test |
8 / 8,F=0 E=0 S=0 |
mvn -Dtest="A5SideEffectSnapshotTest" test |
2 / 2,F=0 E=0 S=0 |
A5 定向(R01A5…,A5*Test, 8 个 Controller 测试 + 3 个 Accessor/Filter 测试) |
98 / 98,F=0 E=0 S=0,BUILD SUCCESS |
mvn clean verify |
2255 / 2255,Failures=0、Errors=0、Skipped=0,BUILD SUCCESS |
- 实施基线 2245 → 复验后 2255(新增 10 个独立测试:4 + 4 + 2),不低于基线 ✔
- 未见新的基线测试污染 ✔
12. OpenAPI
python docs\verify_openapi.py → 176 paths / 214 operations,All checks passed ✔
docs/openapi-rc.json 零修改 ✔
13. Flyway / ErrorCode / DTO / 接口
| 项 | 结果 |
|---|---|
| V1–V45 迁移 | 零修改(git diff --name-only -- db/migration 为空)✔ |
ErrorCode.java |
零改动 ✔ |
| DTO | 零改动 ✔ |
| 公网接口 / OpenAPI | 零新增零修改 ✔ |
| 新增表 / 索引 | 无 ✔ |
14. 全仓剩余统计(逐文件分类)
身份解析(SecurityContextHolder.getContext().getAuthentication | Long.(valueOf|parseLong)([^)]*getName())—— 4 文件
| 分类 | 文件 |
|---|---|
| 基础设施(3) | security/CurrentUserAccessor(Accessor 自身)、auth/JwtAuthenticationFilter(认证过滤器)、deletion/DeletionGuardInterceptor(拦截器) |
| 业务 Controller(1) | scoringmonitoring/FairnessMonitoringController |
| 误报 | 0 |
requestId 解析(含 X-Request-Id 直读、RequestIdFilter.REQUEST_ID_HEADER、String (getRequestId|resolveRequestId|requestId)()—— 10 文件
| 分类 | 文件 |
|---|---|
| 基础设施 / 非业务入口(5) | audit/AuditContext、common/exception/GlobalExceptionHandler、common/logging/RequestIdAccessor、common/logging/RequestLoggingFilter、deletion/DeletionGuardInterceptor |
| 业务 Controller(5) | controller/ApiController、controller/AuthController、controller/SystemController、job/JobController、scoringmonitoring/FairnessMonitoringController |
| 误报 | 0 |
与路线图记数的一处差异(如实记录):路线图 R01 行记「全仓 requestId 11 文件 = 基础设施/非业务入口 6 + 业务 5」。本次独立复验用任务书指定模式实测为 10 文件 = 基础设施 5 + 业务 5。业务 Controller 数一致(5);差异在基础设施计数(5 vs 6),推测为路线图对"基础设施/非业务入口"的分类口径略有不同(如是否计入某调用点)。不应据此修改代码或路线图;建议后续批次统一口径。
15. 精确新增文件
新增测试(3)
mydreamjob-server/src/test/java/com/mydreamjob/common/r01a5/A5AccessorWiringIndependentTest.java
mydreamjob-server/src/test/java/com/mydreamjob/common/r01a5/A5IndependentVerificationTest.java
mydreamjob-server/src/test/java/com/mydreamjob/common/r01a5/A5SideEffectSnapshotTest.java
复验报告(1)
docs/r01-a5-independent-verification-20260917.md
未修改:8 个 A5 Controller 生产代码、既有测试断言、GlobalExceptionHandler、SecurityConfig、Filter、Accessor、ErrorCode、DTO、OpenAPI、Flyway。
16. Git 状态
分支:refactor/r01-structure-performance
HEAD:0226977063243fce101e3f6c8dcf66492d306ef7(未变)
暂存区:空
git add -A / commit / push:均未执行
reset / restore / checkout / clean:均未执行
Docker:未操作
工作区:8 个 A5 Controller + 路线图(实施者改动,仍未暂存)+ 本报告与 3 个独立测试(未跟踪)
历史未跟踪文件:全部保留
17. 是否发现生产缺陷
未发现生产缺陷。 8 个 Controller 的迁移完整、正确:
- 禁止模式 0 命中;构造器注入无回退;
parseIds业务解析正确保留 - 角色/归属/403/404 语义未漂移
- 独立接线变异测试证明 Accessor 真实生效(删除调用即失败)
- 独立身份边界测试证明非法主体在业务调用前被拒(401、无 500、零 Service 调用)且不回显主体
- 失败请求 9 张核心表逐字段零变化
18. 明确声明
- 未暂存、未提交、未推送。
- 未修改 8 个 Controller 或任何生产代码;未修改、删除或放宽既有测试断言;未修改
GlobalExceptionHandler、SecurityConfig、Filter、Accessor、ErrorCode、DTO、OpenAPI、Flyway。 - 未进入 R01-A6;未删除历史未跟踪文件。
- 本轮未启动 Docker,未伪造真实 MySQL 结论。
R01-A5独立代码复验通过,可以进入精确暂存、本地提交与推送检查点。