# N03 行业技能词云与学习路径执行契约 ## Intent Lock **问题**:学生无法从行业和职位维度查看招聘市场热门技能,无法量化自己相对 TOP10 的掌握程度,也没有针对缺失技能的优先级建议。 **意图**:基于 ESCO + O*NET 开源知识底座,通过 Flyway 建立可追溯的行业—职位—技能分类—技能数据。ESCO 作为职位、通用技能和分类主表,O*NET `software_skills` 仅补充 ESCO 缺失的具体技术;O*NET Importance 提供热度及机器可追溯来源。系统提供仅 STUDENT 可访问的词云、个人匹配度和学习路径三个确定性接口。 **完成证明**:V35/V36 迁移成功执行;三个接口按同一热度列表工作;权限、排序、别名、匹配分母、空数据和通用学习建议均通过自动化测试;OpenAPI 与文档一致。 ## Scope Fence ### 允许范围 - 新增 `mydreamjob-server/src/main/resources/db/migration/V35__skill_cloud_taxonomy.sql` 和 `V36__skill_cloud_seed.sql`。 - 新增 `com.mydreamjob.skill` 领域包、Repository、Service、DTO 和三个 Controller。 - 修改安全配置以保护 `/skills/**`,仅允许已登录 STUDENT。 - 新增 ESCO/O*NET 来源说明、接口文档、OpenAPI 契约和测试。\n- 新增从 O*NET 31.0 官方 CSV 提取的测试证据快照,仅用于全量核对种子来源和 Importance,不参与运行时。 - 首版数据只覆盖互联网、金融、教育及代表性职位。 - 具体技术无可靠 ESCO 节点时,允许使用 O*NET `software_skills` 作为补充来源。 - 词云热度只取 O*NET Importance;无映射时 `heat=null`。 - 匹配度使用当前 TOP10 和实际技能数;学习路径只返回缺失技能、优先级和通用建议。 ### 禁止范围 - 不修改 V1–V34 历史迁移,不改变已有接口语义。 - 不接 AI、课程平台、第三方运行时接口或在线词库同步。 - 不按平台岗位 `jobId`、发布量或用户行为计算热度。 - 不实现后台词库维护、学习进度、提醒、历史快照或前端页面。 - 不向 COMPANY/ADMIN 返回个人匹配或学习路径。 - 不在响应或日志中输出原始 `skills_json`、手机号、邮箱、简历内容。 ## Approved Behavior | ID | 已批准行为 | 批次 | | --- | --- | --- | | REQ-SC-1 | `/skills/word-cloud`、`/skills/match`、`/skills/learning-path` 只允许已登录 STUDENT;匿名返回 401,COMPANY/ADMIN 返回 403。 | B3 | | REQ-SC-2 | `industryId`、`occupationId` 必填且必须互为关联;`categoryId` 可选,存在时必须属于目标职位;非法组合返回 400。 | B2、B3 | | REQ-SC-3 | 词云返回筛选范围内全部技能,按 `heat DESC, nameZh ASC, skillId ASC` 排序;`heat=null` 排在有值技能之后。 | B2 | | REQ-SC-4 | TOP10 不单独存储或返回,前端取词云前十条;少于十条时全部结果构成实际 TOP10。 | B2、B3 | | REQ-SC-5 | 每个技能项包含技能 ID、名称、分类、`heat`、稳定排名和可追溯来源标识;响应使用 `ApiResponse`。 | B2、B3 | | REQ-SM-1 | 个人匹配只使用当前筛选结果的 `min(10, actualSize)` 个技能,不另建排序。 | B2 | | REQ-SM-2 | 学生技能只从 JWT 对应学生的 `student_profiles.skills_json` 读取;缺失、空数组或损坏 JSON 按无技能处理。 | B2 | | REQ-SM-3 | 匹配使用标准技能和别名,执行 NFKC、首尾空白、大小写、连字符转空格、连续空白折叠和句点移除归一化;`+`、`#` 不得删除。 | B2 | | REQ-SM-4 | 匹配度为 `round(matched * 100 / min(10, actualSize))`;`actualSize=0` 时返回 0;重复命中同一技能只计一次。 | B2 | | REQ-SM-5 | 响应分别返回命中与缺失技能,缺失列表保持 TOP10 顺序,两者并集等于实际 TOP10。 | B2、B3 | | REQ-LP-1 | 学习路径只包含当前 TOP10 的未命中技能,不包含已命中或 TOP10 之外的技能。 | B2 | | REQ-LP-2 | 学习路径沿用词云顺序,优先级从 1 连续编号;空热度技能仍在有权重技能之后。 | B2 | | REQ-LP-3 | 学习建议由编程语言、框架、工具、通用四类确定性模板生成;不调用 AI 或外部服务,不含课程链接、时长或价格。 | B2 | | REQ-LP-4 | 每项只返回技能 ID、名称、分类、优先级和简化建议;无缺口时返回空列表和总数 0。 | B2、B3 | 需求覆盖结果:三份 spec 中全部 14 条 `MUST`/`SHALL` 行为均已映射,无未映射需求。 ## Architecture and Interface Constraints - 使用 ESCO 作为职位、通用技能和分类主关系;具体技术无可靠 ESCO 节点时,允许使用 O*NET `software_skills` 作为补充技能来源。 - O*NET Importance 作为 `heat`;项目维护中文名称、别名和细粒度分类。 - 数据模型固定为 `skill_industries`、`skill_occupations`、`skill_categories`、`skills`、`skill_aliases`、`occupation_skills`、`skill_source_versions` 七张表。 - `skill_aliases.normalized_alias` 全局唯一;`occupation_skills(occupation_id, skill_id)` 唯一;分类必须属于职位,职位必须属于行业。 - `occupation_skills` 必须保存 `importance_source_code`、`importance_element_id` 和 `importance_is_proxy`;非空 `importance` 必须可追溯到 O*NET 31.0 CSV。 - 三个接口共享同一个只读排名管道和技能投影;不得复制三套排序规则。 - 接口路径固定为: - `GET /api/v1/skills/word-cloud` - `GET /api/v1/skills/match` - `GET /api/v1/skills/learning-path` - 三个接口查询参数均为 `industryId`、`occupationId` 必填,`categoryId` 可选。 - 计算按请求实时完成,不持久化匹配或学习路径。 - 学生技能解析异常记录不含原始内容的告警,接口返回 200 和空技能匹配结果。 - 首版只使用代表性数据;迁移编号以当前仓库下一个可用版本为准。若合并前 V35/V36 已被其他变更占用,必须回退规划阶段刷新编号和契约。 ## Execution Batches ### Batch B1: 数据与来源基础 - 覆盖任务:T1、T2、T3,T3 在第 1 次评审后执行来源与分类修复。 - 交付物:来源清单、`V35__skill_cloud_taxonomy.sql`、`V36__skill_cloud_seed.sql`、迁移测试。 - 完成定义:代表性数据包含三类行业和约定职位;所有入选技能有中文标准名;ESCO 职位来源、O*NET 补充技术、每个 Importance 的 SOC/Element ID 均可机器追溯;自动化测试通过版本化证据快照全量核对 O*NET 来源和数值。 - 依赖:无。 - 评审门:B1 完成并由 `code-reviewer` 记录 `pass` 后才能开始 B2。 ### Batch B2: 排名、匹配与学习路径 - 覆盖任务:T4、T5、T6、T7。 - 交付物:实体/Repository、共享词云查询排名服务、别名归一化、个人匹配服务、学习路径模板服务及单元测试。 - 完成定义:REQ-SC-2 至 REQ-SC-5、REQ-SM-1 至 REQ-SM-5、REQ-LP-1 至 REQ-LP-4 的领域行为可重复且确定。 - 依赖:B1 通过评审。 - 评审门:B2 完成并由 `code-reviewer` 记录 `pass` 后才能开始 B3。 ### Batch B3: 接口、权限、契约与回归 - 覆盖任务:T8、T9、T10。 - 交付物:三个 Controller、权限配置、MockMvc/H2 集成测试、`docs/skill-cloud-api.md`、OpenAPI 和前端联调文档更新。 - 完成定义:REQ-SC-1 至 REQ-SC-5、REQ-SM-5、REQ-LP-4 的接口行为通过;匿名 401、非 STUDENT 403、非法参数 400;文档与运行时一致。 - 依赖:B2 通过评审。 - 评审门:B3 完成并由 `code-reviewer` 记录 `pass` 后才能进入验证和收尾。 ## Test Obligations | ID | 测试义务 | 批次 | 证据 | | --- | --- | --- | --- | | TO-1 | Flyway 创建七张表、外键、唯一约束和索引,中文名与来源 ID 完整。 | B1 | `mvn -Dtest=SkillTaxonomyMigrationTest test` | | TO-2 | 行业/职位/分类关系校验,非法组合返回 400。 | B2、B3 | Repository/Service 单测与 MockMvc | | TO-3 | 热度降序、空热度末位、同热度稳定排序、空范围列表。 | B2 | `mvn -Dtest=SkillCloudQueryServiceTest test` | | TO-4 | TOP10 少于十条时使用实际数量,匹配分母为实际数量,`1/3` 四舍五入为 33。 | B2 | `mvn -Dtest=SkillMatchServiceTest test` | | TO-5 | 大小写、空格、连字符、句点、`C++`、`C#` 和重复别名匹配。 | B2 | 归一化与匹配单测 | | TO-6 | 无技能、无资料、损坏 JSON、全部命中和全部未命中场景。 | B2、B3 | Service 与 MockMvc 测试 | | TO-7 | 学习路径只含未命中技能,优先级连续,无缺口返回空列表。 | B2 | `mvn -Dtest=SkillLearningPathServiceTest test` | | TO-8 | 四类建议模板稳定、行业无关,不含链接、时长、价格或 AI 内容。 | B2 | 模板单测 | | TO-9 | 三个接口权限、响应结构和参数错误符合契约。 | B3 | `mvn -Dtest=SkillCloudControllerTest test` | | TO-10 | 三个接口复用同一 TOP10,响应不含原始 `skills_json`。 | B3 | H2 集成测试 | | TO-11 | 全量回归、打包和静态 OpenAPI 契约通过。 | B3 | `mvn test`、`mvn clean verify`、`python docs\verify_openapi.py` |\n| TO-12 | 测试读取 O*NET 31.0 证据快照,全量核对 47 条 O*NET 技术来源及全部非空 Importance;任一来源不存在或数值被篡改都必须失败。 | B1 | `mvn -Dtest=SkillCloudSeedDataIntegrationTest test` | ## Build Rules - 每个 Batch 按 TDD 先写失败测试,再实现最小行为,不得跳过测试。 - 只修改范围内文件;发现需要修改已验收接口或历史迁移时立即停止并触发 Rewind。 - 迁移和种子数据必须可在 H2(`MODE=MySQL`)与真实 MySQL 执行,不依赖外部文件路径。 - 所有排序、匹配和建议必须确定性;禁止随机、当前时间或请求次数影响结果。 - 任何新增日志不得包含个人技能 JSON、Token、手机号、邮箱或简历文本。 - OpenAPI 注解、示例和实际响应必须一致,不能只改文档不改运行时行为。 ## Review Gates - **DP-3**:本契约必须由用户明确批准并记录后,才能开始任何实现。 - **Batch Review**:B1、B2、B3 每个 Batch 完成后由 `code-reviewer` 按规范符合性和代码质量评审,记录 `pass` 或 `fail`。 - **DP-4**:批准契约后由 `build-executor` 给出执行模式与 wave 计划,用户确认后开始执行。 - **Verification Gate**:实现完成后执行全量测试、OpenAPI 校验和必要的 MySQL/Docker 黑盒验证。 - **DP-7**:由 `release-archivist` 完成最终验证与关闭,不在本契约内自动完成。 ## Rewind Conditions 出现以下任一情况必须停止执行并回到规划阶段刷新工件和本契约: - 需要新增、删除或改变用户可见接口、筛选参数、权限或响应语义。 - TOP10、匹配度分母、排序规则或空数据处理发生改变。 - 需要引入 AI、课程平台、实时岗位热度或后台维护接口。 - 需要导入全量词库或扩大首版行业/职位范围。 - 需要新增除 ESCO、O*NET 之外的其他技能来源。 - V35/V36 被其他变更占用,或历史迁移需要修改。 - ESCO/O*NET 许可、中文映射策略或数据来源发生实质变化。 - 发现某个已批准 `MUST`/`SHALL` 无法映射到测试或执行批次。 ## DP-3 Approval Gate 执行者确认:本契约完整覆盖已批准的 14 条必需行为,无未映射需求,B1→B2→B3 依赖清晰,所有测试义务均有证据命令。 **批准方式**:用户明确回复批准本执行契约后,记录 `dp_3_result`、`dp_3_timestamp`,再执行 `specifying → bridging → approved-for-build` 状态转换。DP-3 记录前禁止进入 `build-executor`。