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.
 
 
 

5.9 KiB

W3-T8 独立评审报告

评审结论

  • Verdict:FAIL
  • 评审对象:
    • mydreamjob-server/src/main/java/com/mydreamjob/skill/SkillCloudController.java
    • mydreamjob-server/src/main/java/com/mydreamjob/config/SecurityConfig.java
    • mydreamjob-server/src/test/java/com/mydreamjob/skill/SkillCloudControllerTest.java
  • 依据:修订后的 execution-contract.md、design.md、tasks.md,三份 spec,以及现有 JWT/Security/Controller 约定。
  • 评审方式:静态核对实现与契约,独立运行指定回归,检查 Git 工作区范围;未修改实现文件。

独立测试结果

执行命令:

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

结果:

Tests run: 69, Failures: 0, Errors: 0, Skipped: 0
BUILD SUCCESS

其中 SkillCloudControllerTest 为 19 项通过。git diff --check HEAD 对 T8 三个文件无空白错误。docs/openapi-rc.json、docs/frontend-integration.md 无改动,docs/skill-cloud-api.md 不存在;未发现 T9/T10 实现。

阻断项

T8-I1(Important):OpenAPI 成功示例与 DTO/运行时契约矛盾

文件与行号:

  • SkillCloudController.java:56:词云示例中 data.total=15,但 items 只有 1 项。
  • SkillCloudController.java:103:匹配示例中 matchedCount=8、totalCount=10,但同时返回 matchedSkills=[]、missingSkills=[]。
  • SkillCloudController.java:150:学习路径示例中 data.total=2,但 items 只有 1 项。
  • SkillCloudControllerTest.java:247-269:OpenAPI 测试只检查路径存在、bearerAuth、状态码和参数必填性,没有校验示例内容或 200 响应 schema,因此上述矛盾不会使测试失败。
  • changes/n03-skill-cloud/execution-contract.md:120:明确要求“OpenAPI 注解、示例和实际响应必须一致”。

影响:客户端联调、接口文档和运行时契约可能按错误示例理解 TOP10、命中/缺失列表与总数关系。应修正三个示例,使计数、数组长度和 DTO 字段一致,并在 T8 的 OpenAPI 测试中锁定关键示例字段;同时建议为 200 响应声明对应 SkillCloudResponse、SkillMatchResponse、SkillLearningPathResponse schema,而非只有示例。

T8-I2(Important):角色测试通过 Mock 按 userId 放行,未证明真实 STUDENT 校验和多角色集合

文件与行号:

  • SkillCloudControllerTest.java:93-97:ProfileService 被 @MockBean 替换,并按 100L/200L/300L 直接设置成功或抛 FORBIDDEN。
  • SkillCloudControllerTest.java:161-171:测试传入 COMPANY/ADMIN 角色声明,但实际 403 取决于 mock 对 200L 的硬编码行为,而非数据库角色集合。
  • SkillCloudControllerTest.java:296-301:JWT 的 roles claim 只作为测试辅助值传入。
  • SkillCloudController.java:185-198:Controller 确实从 SecurityContext 取身份并调用 profileService.assertStudentRole(userId)。
  • ProfileService.java:50-57:生产实现从数据库读取用户,并使用 anyMatch(STUDENT),因此单角色和包含 STUDENT 的多角色在代码层面是正确的。
  • 现有 ProfileControllerTest.java:167-185 覆盖了数据库单角色 COMPANY/ADMIN 的 403,但没有覆盖 STUDENT+COMPANY 或 STUDENT+ADMIN 多角色集合,也没有覆盖本组 /skills/** 端点的真实角色链路。

影响:T8 的 Controller 测试无法证明 COMPANY/ADMIN 是被真实服务端角色检查拒绝,也无法证明多角色用户中只要包含 STUDENT 就能通过;它可能在 ProfileService.assertStudentRole 被替换或角色逻辑回归时仍通过。应增加使用真实 ProfileService、真实数据库用户的集成场景,至少覆盖:

  • COMPANY、ADMIN:预期 403。
  • STUDENT+COMPANY:预期允许。
  • 无 STUDENT 的多角色集合:预期 403。

非阻断项

T8-M1:身份注入测试只覆盖 /skills/match

文件与行号:SkillCloudControllerTest.java:214-225。

当前实现三个端点都调用同一个 resolveAuthenticatedStudent(),代码层面正确;但测试只验证 match 端点会忽略 userId/studentId,没有对词云和学习路径端点做同样断言。建议参数化覆盖三个路径,并 verify 服务收到的是 JWT 中的 100L。

T8-M2:隐私断言只匹配 skills_json

文件与行号:SkillCloudControllerTest.java:227-244。

测试没有覆盖驼峰形式 skillsJson 或其他可能的新增个人技能字段。当前返回 DTO 为受控记录类型,因此非阻断;建议后续端到端测试使用真实服务响应,并对 DTO schema/序列化字段做更严格断言。

检查结论

  • 三个路径和参数语义:通过。industryId、occupationId 必填,categoryId 可选;实测缺参/0/负数返回 400。
  • 身份来源:通过。Controller 只从 SecurityContext 取主体,不绑定 userId/studentId。
  • 权限行为:实现通过,测试证明不完整。匿名 401、COMPANY/ADMIN 403 的 MockMvc 场景通过;但角色拒绝依赖 mock,多角色未覆盖。
  • ApiResponse 与成功 DTO:通过。成功响应统一包装,字段与 Service DTO 一致。
  • OpenAPI:不通过。注解具备 Tag、Operation、SecurityRequirement、参数说明和 200/400/401/403,但成功示例与 DTO/运行时数据矛盾,且 200 响应缺少明确 schema。
  • 改动范围:通过。未修改 docs/openapi-rc.json,未实现 T9/T10。

未决问题

  • 当前安全层的 401/403 JSON 由既有 SecurityConfig 直接写出,不是 ApiResponse 实体序列化结果;是否在本变更中统一该历史约定,需由项目维护者决定。本次未将其列为 T8 阻断项,因为它与仓库既有安全配置约定一致。