# W2-T4 修复复审报告 ## 状态 完成。本轮仅复审修复提交 `52751e5`,对照第一轮报告 `W2-T4.md`,未修改任何实现、测试、迁移或配置文件;仅新增本报告。 ## Verdict **PASS** 第一轮阻断项 `W2-T4-I1` 已关闭。两个投影查询均已保证技能分类的 `occupation_id` 与职位技能关系的 `occupation_id` 一致,新增测试真实构造了跨职位分类错配数据并验证查询排除该数据。规定回归集全部通过,未发现新的 Critical、Important 或阻断项。 ## Finding 关闭状态 ### W2-T4-I1:分类过滤查询未强制 category 与 occupation 属于同一职位 状态:**已关闭**。 关闭证据: - 整职位查询在分类关联后增加了 `c.occupationId = os.occupationId`: `D:\1\zhi\zhilai\mydreamjob-server\src\main\java\com\mydreamjob\skill\repository\OccupationSkillRepository.java:36-40` - 职位加分类查询同样增加了该约束,并保留 `os.occupationId = :occupationId` 与 `os.categoryId = :categoryId`: `D:\1\zhi\zhilai\mydreamjob-server\src\main\java\com\mydreamjob\skill\repository\OccupationSkillRepository.java:63-69` - 两个查询均继续通过 `os.skillId = s.id` 和 `os.categoryId = c.id` 连接技能及分类,未改变投影字段或原过滤条件。 ## 独立测试结果 环境: - `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` 命令: ```powershell $env:JAVA_HOME='D:\java\jdk' & 'D:\maven\apache-maven-3.9.7\bin\mvn.cmd' '-Dtest=SkillTaxonomyMigrationTest,SkillCloudSeedDataIntegrationTest,SkillTaxonomyRepositoryTest' test ``` 结果: ```text SkillCloudSeedDataIntegrationTest: Tests run: 8, Failures: 0, Errors: 0, Skipped: 0 SkillTaxonomyMigrationTest: Tests run: 2, Failures: 0, Errors: 0, Skipped: 0 SkillTaxonomyRepositoryTest: Tests run: 6, Failures: 0, Errors: 0, Skipped: 0 Total: Tests run: 16, Failures: 0, Errors: 0, Skipped: 0 BUILD SUCCESS Total time: 40.769 s ``` ## 必须检查项逐项结论 ### 1. 两个投影查询均约束 category 所属 occupation,且保留过滤:通过 两个查询都在连接 `os.categoryId = c.id` 后增加: ```sql AND c.occupationId = os.occupationId ``` 整职位查询继续只按目标 `occupationId` 过滤;职位加分类查询继续同时按目标 `occupationId` 和 `categoryId` 过滤。因此: - 不会再通过职位 A 的关系读取仅属于职位 B 的分类; - 分类参数仍必须与关系中的分类 ID 完全相等; - 职位参数语义未改变。 位置: - `OccupationSkillRepository.java:36-43` - `OccupationSkillRepository.java:63-72` ### 2. 新增测试真实插入跨职位错配关系,且可证伪修复:通过 新增测试位于 `D:\1\zhi\zhilai\mydreamjob-server\src\test\java\com\mydreamjob\skill\SkillTaxonomyRepositoryTest.java:123-152`。 测试通过 `JdbcTemplate` 向真实 `occupation_skills` 表插入: - `occupation_id = 1` - `category_id = 201` - `skill_id = 1` - `source_version_id = 2` 其中分类 `201` 在 V36 中明确属于职位 `2`,见 `V36__skill_cloud_seed.sql:67`;技能 `1` 存在,见 `V36__skill_cloud_seed.sql:129`。插入使用合法外键值和完整来源字段,因此构造的是数据库允许保存、但职位与分类不属于同一节点的逻辑错配关系。 该测试没有断言 SQL 文本,而是调用真实 Repository 方法并检查返回结果。修复前的查询缺少同职位约束,插入关系会满足 `os.skillId = s.id`、`os.categoryId = c.id` 和 `os.occupationId = 1`,因此: - 整职位查询会包含技能 `1`,在第 `142` 行 `doesNotContain(1L)` 失败; - `occupation=1, category=201` 查询会包含该关系,在第 `144-146` 行失败。 修复后该关系被 `c.occupationId = os.occupationId` 排除,测试通过,可证伪性成立。 测试方法带 `@Transactional`,见 `SkillTaxonomyRepositoryTest.java:123-124`,插入数据在测试结束后回滚,不污染其他测试或 V36 计数。 ### 3. 三项断言均正确覆盖:通过 `SkillTaxonomyRepositoryTest.java:136-151` 分别验证: - 整职位查询仍包含正常技能 `3`,且不包含错配技能 `1`;当前断言在 `139-142` 行。 - 错误分类组合 `occupation=1, category=201` 返回空列表;当前断言在 `144-146` 行。 - 正确组合 `occupation=2, category=201` 仍返回技能 `1`,证明修复没有误伤合法数据;当前断言在 `148-151` 行。 三项断言覆盖了第一轮要求的安全边界和正常路径。 ### 4. 正常查询、空 Importance、来源字段和计数:通过 - 原整职位和分类过滤测试继续验证职位 `1` 返回 15 项、分类 `101` 返回精确技能集合,以及错误职位分类返回空:`SkillTaxonomyRepositoryTest.java:95-120`。 - O*NET 来源字段、`importance`、`importanceSourceCode`、`importanceElementId`、代理标记和排序字段的原断言仍通过:`SkillTaxonomyRepositoryTest.java:154-188`。 - Figma 的空 Importance 场景仍通过:`SkillTaxonomyRepositoryTest.java:176-188`。 - V36 计数仍为来源版本 2、行业 3、职位 10、分类 68、技能 72、别名 72、职位技能关系 139:`SkillTaxonomyRepositoryTest.java:72-81`。 - 指定回归集 16 项全部通过,未观察到由新增约束造成的正常查询、投影字段或种子数据回归。 ### 5. 提交范围与 T5 边界:通过 `git diff --name-only 0c77824..52751e5` 仅列出: - `mydreamjob-server/src/main/java/com/mydreamjob/skill/repository/OccupationSkillRepository.java` - `mydreamjob-server/src/test/java/com/mydreamjob/skill/SkillTaxonomyRepositoryTest.java` 未修改 V35/V36、生产配置、安全配置或其他模块,也未实现任何 T5 排名、匹配或学习路径代码。`git diff --check 0c77824..52751e5` 通过。 ## 剩余阻断项 无。 ## 非阻断备注 V35 仍允许数据库层面写入 `occupation_id` 与 `category_id` 逻辑不一致的关系,因为当前外键是单列外键;本次修复保证 Repository 查询不会返回此类错配数据。是否需要增加数据库复合完整性约束属于独立的数据模型变更,不影响 T4 通过。