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.
 
 
 

7.6 KiB

W2-T4 独立评审报告

状态

完成。本轮只评审提交 0c77824、修订后的契约与规划产物、V35/V36,并独立执行指定测试;未修改实现、测试、迁移或配置文件,仅写入本报告。

Verdict

FAIL

未发现 Critical。发现 1 项 Important:技能投影查询没有保证 occupation_skills.category_id 对应的分类属于同一个 occupation_id,在数据库出现逻辑错配关系时会返回跨职位数据。该问题会阻断 T4 通过,修复并复审后可进入 T5。

独立测试输出

环境:

  • JAVA_HOME=D:\java\jdk
  • Java:OpenJDK 17.0.16
  • Maven:D:\maven\apache-maven-3.9.7\bin\mvn.cmd
  • 工作目录:D:\1\zhi\zhilai\mydreamjob-server

命令:

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

结果:

SkillCloudSeedDataIntegrationTest: Tests run: 8, Failures: 0, Errors: 0, Skipped: 0
SkillTaxonomyMigrationTest:       Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
SkillTaxonomyRepositoryTest:      Tests run: 5, Failures: 0, Errors: 0, Skipped: 0
Total:                            Tests run: 15, Failures: 0, Errors: 0, Skipped: 0
BUILD SUCCESS
Total time: 44.358 s

必须检查项逐项结论

1. 实体与 V35 映射:通过

7 个实体均映射到 V35 对应表,主键为 Long + GenerationType.IDENTITY。已逐表核对表名、列名、长度、可空性和类型:

  • SkillSourceVersion 与 skill_source_versions:字段、长度和可空性一致。
  • SkillIndustry 与 skill_industries:字段、长度和可空性一致。
  • SkillOccupation 与 skill_occupations:字段、长度和可空性一致。
  • SkillCategory 与 skill_categories:字段、长度和可空性一致。
  • Skill 与 skills:字段、长度和可空性一致。
  • SkillAlias 与 skill_aliases:字段、长度和可空性一致;该表无 updated_at,实体也未映射该列。
  • OccupationSkill 与 occupation_skills:DECIMAL(4,2)、来源字段长度、TINYINT(1) 代理标记及可空性匹配;importanceIsProxy 使用 @JdbcTypeCode(SqlTypes.TINYINT)。

本次测试在 ddl-auto=validate 下执行通过,证明 V35 七张表实际参与了 Hibernate 校验。

2. Schema Filter 与生产配置:通过

SkillTaxonomyRepositoryTest 的 validate filter 明确包含全部七个 V35 表:

D:\1\zhi\zhilai\mydreamjob-server\src\test\java\com\mydreamjob\skill\SkillTaxonomyRepositoryTest.java:182-231

因此该 filter 隔离的是仓库历史中与本任务无关的表,不会跳过 V35 实体的列类型或可空性校验。测试通过 @TestPropertySource 覆盖测试数据库和 ddl-auto,提交未修改生产 application.yml、SecurityConfig 或其他配置。

3. 行业、职位、分类关联校验:通过

以下 Repository 方法正确表达了单节点关系约束:

  • SkillOccupationRepository.existsByIdAndIndustryId / findByIdAndIndustryId
  • SkillCategoryRepository.existsByIdAndOccupationId / findByIdAndOccupationId

测试覆盖了正确组合、跨行业职位、跨职位分类和不存在的 ID:

D:\1\zhi\zhilai\mydreamjob-server\src\test\java\com\mydreamjob\skill\SkillTaxonomyRepositoryTest.java:78-88

4. occupation + 可选 category 查询的同职位约束:不通过

两个投影查询都只连接 os.category_id = c.id,没有断言 c.occupation_id 与 os.occupation_id 相同:

D:\1\zhi\zhilai\mydreamjob-server\src\main\java\com\mydreamjob\skill\repository\OccupationSkillRepository.java:36-40

D:\1\zhi\zhilai\mydreamjob-server\src\main\java\com\mydreamjob\skill\repository\OccupationSkillRepository.java:62-66

V35 对 occupation_skills.category_id 只有单列外键,不阻止一条关系的 occupation_id 指向职位 A、category_id 指向职位 B 的分类:

D:\1\zhi\zhilai\mydreamjob-server\src\main\resources\db\migration\V35__skill_cloud_taxonomy.sql:114-117

这违反了契约要求“分类必须属于职位”:

D:\1\zhi\zhilai\changes\n03-skill-cloud\execution-contract.md:59

也违反了设计中的“category_id 必须属于 occupation_id”:

D:\1\zhi\zhilai\changes\n03-skill-cloud\design.md:119

现有测试 findSkillProjectionsByOccupationIdAndCategoryId(2L, 101L) 只证明当前种子数据中不存在 职位 2 + 分类 101 关系,不能发现上述缺列条件;即使插入一条 occupation_id=2, category_id=101 的逻辑错配关系,该查询仍会返回它:

D:\1\zhi\zhilai\mydreamjob-server\src\test\java\com\mydreamjob\skill\SkillTaxonomyRepositoryTest.java:113-115

修复方向明确:两个查询均增加分类所属职位约束,例如 AND c.occupationId = os.occupationId;同时增加可证伪测试,用跨职位分类关系验证查询不会返回数据。

5. 投影 14 个字段:通过

SkillCloudSkillProjection 包含 14 个字段,覆盖技能身份、名称、来源、分类、importance、importanceSourceCode、importanceElementId、importanceIsProxy、技能排序值和分类排序值。构造表达式与字段顺序一致,importance 使用 BigDecimal,代理标记使用 Boolean。

D:\1\zhi\zhilai\mydreamjob-server\src\main\java\com\mydreamjob\skill\dto\SkillCloudSkillProjection.java:5-20

6. 未提前实现 T5–T8:通过

提交只新增 7 个实体、1 个投影、7 个 Repository 和专项测试;未新增排名服务、匹配服务、学习路径服务、Controller、安全规则或 OpenAPI 变更。Repository 查询明确未实现最终排序,符合 T5 边界。

7. 测试数据覆盖与可证伪性:除跨职位查询外通过

测试使用了 V36 种子数据,覆盖计数、关联校验、整职位查询、可选分类过滤、O*NET 来源、空 Importance、代理标记、别名和按 ID 查询。查找均为精确断言,不是仅计数。

但第 4 项的跨职位分类错配缺少可证伪用例;即使 Repository 查询遗漏同职位条件,当前 15 项测试仍全部通过。

8. 提交范围与历史迁移:通过

提交只包含允许的 16 个文件。独立哈希核对确认 V35/V36 在 0c77824 中未发生变化,提交未修改其他模块、已有迁移、生产配置或安全配置。

Findings

Critical

无。

Important

W2-T4-I1:分类过滤查询未强制 category 与 occupation 属于同一职位

证据:

  • OccupationSkillRepository 两个查询均只有 os.categoryId = c.id,缺少 c.occupationId = os.occupationId:
    • D:\1\zhi\zhilai\mydreamjob-server\src\main\java\com\mydreamjob\skill\repository\OccupationSkillRepository.java:36-40
    • D:\1\zhi\zhilai\mydreamjob-server\src\main\java\com\mydreamjob\skill\repository\OccupationSkillRepository.java:62-66
  • V35 外键不提供复合 (occupation_id, category_id) 完整性:
    • D:\1\zhi\zhilai\mydreamjob-server\src\main\resources\db\migration\V35__skill_cloud_taxonomy.sql:114-117
  • 契约明确要求分类必须属于职位:
    • D:\1\zhi\zhilai\changes\n03-skill-cloud\execution-contract.md:59
    • D:\1\zhi\zhilai\changes\n03-skill-cloud\design.md:119

影响:一旦数据写入出现逻辑错配,整职位查询会返回其他职位的分类技能;带分类查询也可能在对应关系存在时返回跨职位数据,破坏词云筛选边界,并影响后续匹配和学习路径。当前种子数据恰好一致,因此测试未暴露问题。

Minor

无额外阻断项。

未决问题

无。修复语义明确:查询必须同时绑定 category 所属 occupation;修复后应增加一条跨职位分类关系的失败用例并重新评审。