You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

9.6 KiB

N03 SDD W3 整批独立评审报告

评审对象

  • 范围:df7238b..cd9220b
  • Wave:W3
  • 批次:B3 / T8、T9、T10
  • 提交:
    • 3e353bd feat: expose student skill cloud endpoints
    • 7873a42 test: add skill cloud end-to-end regression
    • cd9220b docs: publish skill cloud api contract
  • 评审依据:修订后的 execution-contract.md、design.md、tasks.md、三份 spec,以及 W3-T8*、W3-T9.md。
  • 环境:JAVA_HOME=D:\java\jdk,Maven D:\maven\apache-maven-3.9.7\bin\mvn.cmd。

Verdict

FAIL

W3 的运行时接口、STUDENT 权限、参数校验、DTO 序列化、端到端一致性和打包均通过独立验证;但 T10 发布的 OpenAPI 契约存在两项 Important 问题:

  1. 新增 /skills/match 的 operationId=getMatch 与既有 /applications/{id}/match 冲突。
  2. 匿名或无效 JWT 的运行时 401 响应为 code=401 的裸 JSON,而文档和静态 OpenAPI 声明 2001 及统一 ApiResponse 语义。

因此 T10 不能判定通过,且本 wave 不允许进入收尾或 TO-11。

T8:Controller、权限、参数与 ApiResponse

通过项

  • 三个路由存在且均为 GET:
    • /skills/word-cloud
    • /skills/match
    • /skills/learning-path
  • industryId、occupationId 为必填正整数;categoryId 为可选正整数。
  • 成功后统一返回 ResponseEntity<ApiResponse<T>>,并保留 X-Request-Id。
  • SecurityConfig 仅放行已认证请求到 /skills/**;Controller 再通过真实 ProfileService 按数据库角色校验 STUDENT。
  • SkillCloudAuthorizationIntegrationTest 使用真实 UserRepository、数据库角色集合、JWT 过滤器和 ProfileService,没有 mock 核心权限链路。
  • 已覆盖 STUDENT=200、COMPANY=403、ADMIN=403、STUDENT+COMPANY=200、COMPANY+ADMIN=403。
  • 额外传入 userId、studentId 不能覆盖 JWT 主体。
  • SkillCloudControllerTest 共 14 项,授权集成测试共 2 项,独立执行均通过。
  • 未发现 B3 修改已有接口语义或引入无关模块变更。提交范围仅涉及新增 Controller、SecurityConfig 一条 GET 规则、技能测试和文档/OpenAPI。

阻断项

W3-I1:operationId=getMatch 重复

  • docs/openapi-rc.json:1186:既有 /applications/{id}/match GET。
  • docs/openapi-rc.json:6460:新增 /skills/match GET。
  • 两处均使用 operationId: "getMatch"。
  • SkillCloudController.java:120-121 的方法名也是 getMatch,运行时生成的 operationId 很可能同样冲突;现有测试没有检查全局 operationId 唯一性。
  • docs/verify_openapi.py:146 反而明确断言新增接口的 operationId 必须是 getMatch,把冲突值固化为通过条件。
  • OpenAPI 规范要求 operationId 全局唯一;重复值会导致客户端/文档代码生成器出现歧义或相互覆盖。

修复方向:给 SkillCloudController.getMatch 显式设置唯一 operationId,例如 getSkillMatch,同步静态 OpenAPI 和 verify 脚本,并增加全局 operationId 唯一性断言。

W3-I2:401 运行时响应与文档/OpenAPI 不一致

  • 运行时匿名请求由 SecurityConfig.java:50-54 直接写出 {"code":401,...},且不包含 requestId。
  • SkillCloudControllerTest.java:158-164 明确断言匿名 401 的 $.code=401。
  • docs/skill-cloud-api.md:14 声明使用统一 ApiResponse。
  • docs/skill-cloud-api.md:230-240 声明 401 的典型业务码为 2001,示例也返回 code=2001 和 requestId。
  • docs/openapi-rc.json 的三个新增 401 响应示例均使用 code=2001。
  • 因此静态契约与真实运行时对相同的“未登录/无效 JWT”场景给出了不同的业务码和响应语义。

修复方向:二选一并做可证伪测试:

  • 保持现有全局安全行为,更新接口文档和静态 OpenAPI 为真实 code=401 响应;或
  • 在授权范围内调整安全入口,使 401 使用统一的 ApiResponse 和 2001,同时评估对所有既有接口的兼容性。

T9:端到端一致性

通过项

SkillCloudEndToEndIntegrationTest 共 5 项,使用真实 H2/Flyway、Security、Controller、Service、Repository 和 V36 数据,仅 Mock RedisTemplate 以隔离基础设施依赖,独立执行通过。

已验证:

  • 词云返回完整排序列表,rank 连续,heat DESC,空热度末位。
  • 匹配集合严格等于 min(10,total),matchedSkills + missingSkills 等于同一 TOP10,且各自保持顺序。
  • 学习路径严格复用 missingSkills 顺序,priority 从 1 连续递增。
  • 少于 10 项时按实际大小计算,1/3 返回 33;空范围返回 0 和空列表。
  • 重复调用三个接口返回完全相同的数据。
  • 合法空分类覆盖 total=0、matchScore=0、learningPath.total=0。
  • 成功响应未发现手机号、邮箱、简历文件名/URL 或原始技能 JSON。
  • 额外身份查询参数不能覆盖学生 JWT 主体。

非阻断测试缺口

  • “无写入”目前只比较 8 张表的行数,只能发现 INSERT/DELETE,不能发现仅 UPDATE 的写操作。
  • 隐私断言未显式覆盖 realName、bio、skillsJson 等驼峰或实际值。
  • 端到端测试主要证明三个接口复用同一 TOP10,具体名称/别名匹配分支由 T6 测试覆盖,这一点可以接受。

以上均为可加强项,不单独阻断 T9 运行时实现。

T10:文档与 OpenAPI

通过项

  • 静态 OpenAPI path 数从 126 增至 129,三个新路径均存在。
  • 三个新路径均声明 bearerAuth、200/400/401/403,以及必填/可选参数。
  • 新增 Schema 字段与 Java DTO 字段在静态检查中一致。
  • docs/skill-cloud-api.md 和 docs/frontend-integration.md 覆盖 TOP10 截取、匹配公式、排序、空数据、权限、无 AI 和无持久化语义。
  • python docs\verify_openapi.py 独立执行成功,输出 paths: 129 和 All checks passed。
  • 未发现三个新接口之外的接口语义被改变。

阻断项

见上方 W3-I1、W3-I2。T10 当前不能判定通过。

verify 脚本的不足

  • docs/verify_openapi.py:164 使用 expected_fields <= properties,只验证字段至少存在,不验证字段集合完全相等、类型、nullable、required 或静态契约与运行时的对应关系;新增多余字段仍可通过。
  • 脚本没有全局 operationId 唯一性断言,因此 W3-I1 未被发现。
  • 脚本检查静态 OpenAPI,而 SkillCloudControllerTest 检查运行时 /v3/api-docs,两者没有做等价性比较;当前静态文件使用 example,运行时注解生成使用 examples,进一步说明两者可能各自漂移。
  • 精确 path 数断言可以用于回归基线,但不能替代 operationId 唯一性、Schema 完整性和错误响应一致性验证。

独立执行结果

T2–T9 指定测试组

命令:

$env:JAVA_HOME='D:\java\jdk'
& 'D:\maven\apache-maven-3.9.7\bin\mvn.cmd' '-Dtest=SkillTaxonomyMigrationTest,SkillCloudSeedDataIntegrationTest,SkillTaxonomyRepositoryTest,SkillCloudQueryServiceTest,SkillMatchServiceTest,SkillLearningPathServiceTest,SkillCloudControllerTest,SkillCloudAuthorizationIntegrationTest,SkillCloudEndToEndIntegrationTest' test

结果:

Tests run: 71, Failures: 0, Errors: 0, Skipped: 0
BUILD SUCCESS
Total time: 59.133 s

分类:

  • SkillTaxonomyMigrationTest:2
  • SkillCloudSeedDataIntegrationTest:8
  • SkillTaxonomyRepositoryTest:6
  • SkillCloudQueryServiceTest:8
  • SkillMatchServiceTest:16
  • SkillLearningPathServiceTest:10
  • SkillCloudControllerTest:14
  • SkillCloudAuthorizationIntegrationTest:2
  • SkillCloudEndToEndIntegrationTest:5

OpenAPI 校验

python docs\verify_openapi.py
paths: 129
All checks passed!

该命令通过,但不覆盖上述 operationId 冲突、401 运行时差异和静态/运行时 Schema 等价性,因此不能抵消 W3-I1、W3-I2。

打包

mvn -DskipTests package
BUILD SUCCESS
Total time: 2.401 s

完整 mvn test

独立运行结果:

Tests run: 1393, Failures: 2, Errors: 9, Skipped: 0
BUILD FAILURE
Total time: 01:58 min

Skill Cloud 全部测试类在该套件中均通过。

全量 blocker 归类

以下失败均不在 df7238b..cd9220b 修改范围内,不能归因于 W3 引入的行为回归:

  • CareerProgressServiceTest.fullAggregation_returnsCorrectSummaries:固定 Offer 过期时间 2026-09-05 已早于当前日期,属于时间依赖测试问题。
  • JobReviewDimensionControllerTest.listReviews_shouldReturnDimensionScores:仍 stub 旧服务方法签名。
  • JobReviewDimensionServiceTest.listReviews_shouldReturnDimensionScoresWithoutN1:仍 stub 旧 Repository 方法,导致 NPE。
  • LocalResumeStorageServiceTest:8 项均在 Windows JUnit 临时目录清理阶段因 AccessDeniedException 报错,属于环境/临时目录问题。

归类结论:

  • 不是 W3 代码阻断。
  • 仍然是项目级验证阻断,应阻止 closing/TO-11,除非先修复,或由项目层面正式记录并批准豁免;合同退出条件不能被视为满足。
  • 本次独立验证未运行真实 MySQL 黑盒迁移,仍应保留为发布级验证风险。

最终放行结论

  • 是否允许 T8 运行时实现进入后续:允许。 权限、参数和运行时响应链路通过。
  • 是否允许 T9 端到端实现进入后续:允许。 端到端行为通过,仅有非阻断覆盖缺口。
  • 是否允许 W3 整批通过:不允许。 T10 的 OpenAPI/文档契约存在 Important 阻断。
  • 是否允许进入收尾/TO-11:不允许。 需要先修复 W3-I1、W3-I2 并进行聚焦复审;全量 mvn test 还需全绿或形成正式豁免记录。