# V39:测评结果驱动岗位推荐设计 > 状态:**编码完成,真实 MySQL/Docker 关键黑盒已通过,待 Git 检查点。** > 功能版本:V39。 > 当前基线:Flyway 迁移新增 `V37__job_recommendation_profiles.sql`;静态 OpenAPI 为 `154 paths / 190 operations`。 > 文档用途:V39 第一版规则、接口、安全边界与验收口径的权威设计说明。 ## 1. 目标与边界 ### 1.1 目标 为学生提供“可解释、可复现、可关闭”的岗位推荐列表。推荐只使用学生已授权产生的偏好、V35 RIASEC 职业兴趣结果和 V36 四维能力结果,并结合企业为岗位维护的结构化推荐画像。 推荐的含义是“当前岗位与学生已知偏好的匹配程度”,不保证录用概率,不构成职业、心理或就业诊断。 ### 1.2 第一版范围 - 仅 STUDENT 查看自己的推荐岗位。 - 仅推荐状态为 `PUBLISHED` 且学生尚未投递的岗位。 - 使用学生偏好、最新完成的 RIASEC 结果、最新有效的四维能力结果和岗位推荐画像。 - 企业只能维护自己岗位的推荐画像;不能查询任何学生的测评明细、推荐分或推荐理由。 - 返回有限、白名单化的推荐理由和缺口提示。 - 规则实时计算;不保存学生级推荐历史、点击画像或行为画像。 ### 1.3 明确不在本版范围 - 不使用 AI、大模型、简历文本向量或隐式行为画像。 - 不自动投递、不替学生收藏、不发送推荐通知。 - 不增加企业人才推荐或企业查看学生测评详情。 - 不接入 V38 之外的 `GROWTH` 评分维度,不写 `candidate_dimension_scores`。 - 不把 RIASEC 结果当作职业诊断,不根据测评结果拒绝学生投递。 - 实现“我不感兴趣”持久化,以及推荐解释的有帮助/无帮助反馈;首版不将反馈用于模型训练。 ## 2. 现状核查结论 | 已有能力 | 当前状态 | 对 V39 的影响 | |---|---|---| | 岗位搜索 | `/jobs` 已支持城市、薪资、用工类型、关键词和分页 | 可复用公开岗位视图和发布状态口径 | | 单岗位匹配诊断 | `GET /jobs/{jobId}/match`,按学生偏好计算 | 只适合单岗位说明;内部会写审计,不应在推荐列表中逐岗位循环调用 | | 学生偏好 | 城市、薪资、用工类型、行业、岗位类型、远程意愿 | 可作为硬过滤和软匹配数据 | | V35 RIASEC | 最新完成结果可提供六维兴趣聚合分 | 可作为兴趣匹配输入;不读取答案和题目 | | V36 四维能力 | 最新有效完成结果可提供四维聚合分和有效期 | 可作为能力适配输入;不依赖 V38 开关 | | V38 评分接入 | ABILITY 可有限进入候选评分 | V39 直接读 V36 聚合结果,不读取候选评分快照,避免推荐随评分开关改变 | | `jobs` 表 | 只有标题、描述、要求、城市、薪资、用工类型 | 缺少可靠的 RIASEC/能力要求字段,必须新增结构化岗位推荐画像 | ## 3. 总体方案 ### 3.1 方案选择 采用“**规则推荐 + 岗位结构化画像 + 实时只读计算**”方案。 ```text 学生偏好 ─────────────┐ V35 RIASEC 聚合结果 ──┼─> RecommendationService ─> 推荐岗位列表 + 理由/缺口 V36 能力聚合结果 ─────┤ ↑ 岗位推荐画像 ─────────┘ └─ 仅查询 PUBLISHED、未投递岗位 ``` 不能使用岗位标题、岗位描述或 `requirements` 进行关键词猜测来代替岗位画像:该做法不可解释、难以测试,也会把同义词、否定语句和描述质量差异误当成职业要求。 ### 3.2 推荐画像的维护主体 企业在创建或维护岗位后,为本人岗位维护一份结构化推荐画像。画像只描述岗位,不包含学生数据;它包含: - 行业代码与岗位族代码,用于与学生偏好精确比较; - RIASEC 六维权重,用于表达该岗位偏好的兴趣侧重点; - 四维能力期望值,用于表达完成该岗位所需的基础能力水平; - 是否启用测评推荐。 企业画像是“岗位推荐参考”,不是招聘门槛。企业仍需通过原有岗位说明、投递、面试与 Offer 流程进行评估。 ### 3.3 平台受控分类与审核流 行业代码和岗位族代码采用“**平台维护、企业选择、不得自由输入**”的两级受控字典: ```text 行业大类(industry) └─ 岗位族(job family) └─ 企业保留自由填写的岗位标题(jobs.title) ``` 该选择参考了主流招聘产品按大类、细分职类组织职位浏览和筛选的做法:分类用于匹配与检索,岗位标题保留企业对具体职位的表达。第一版字典由产品负责人确定编码、后端随 Flyway 种子发布;ADMIN 只审核企业的岗位画像,不可在线任意新增分类。分类变更必须通过代码评审、版本化种子和兼容迁移完成。 建议首批受控岗位族覆盖当前产品最常见的求职方向:互联网技术、产品、设计、运营/市场、销售/客户成功、人力/行政/财务/法务、生产制造/工程、教育/医疗、供应链/物流、零售/生活服务和综合管理。每个岗位族必须隶属唯一行业大类,编码一经发布不得重用。 企业首次保存或修改已审核画像后,画像进入 `PENDING_REVIEW`;只有 `APPROVED` 且 `enabled=true` 的画像可用于个性化推荐。ADMIN 使用固定理由码审批或驳回。企业主动停用画像可立即生效,不需要等待审核。 ### 3.4 时间一致性 一次推荐请求在服务入口生成唯一的 `capturedNow`,V36 结果有效性、规则版本和返回时间均使用该时间点。不得在同一请求内多次调用 `now()` 导致结果边界不一致。 ## 4. 数据来源与有效性 | 来源 | 选择规则 | 无数据/异常处理 | 禁止读取 | |---|---|---|---| | 学生偏好 | 本人 `student_preferences`,至多一条 | 缺字段时该偏好项不计入分母 | 其他学生偏好 | | RIASEC | `HOLLAND_RIASEC` 类型、`COMPLETED`、按完成时间倒序和答卷 ID 倒序取一条 | JSON 损坏、缺维、分数越界时兴趣信号不可用 | 原始答案、题目、questionCode | | 四维能力 | `WORKPLACE_ABILITY`、`COMPLETED`、`valid_until > capturedNow`、按完成时间倒序和答卷 ID 倒序取一条 | 过期、缺维、不是 24/24 完成时能力信号不可用 | 原始答案、题目反馈、答案键、评分规则 | | 岗位画像 | `job_recommendation_profiles.enabled=true`、`review_status=APPROVED` 且关联 `PUBLISHED` 岗位 | 无画像或待审核画像不宣称“测评匹配”;仅进入兜底列表 | 企业内部非公开数据 | | 投递记录 | 当前学生与岗位的任何已有投递记录 | 已投递岗位从推荐候选中排除 | 其他学生投递数据 | | 推荐反馈 | 本人对岗位的有效 `NOT_INTERESTED` 反馈 | 仅排除本人明确不感兴趣的岗位;理由反馈不改变排序 | 其他学生反馈 | RIASEC 只使用六维聚合百分比;四维能力只使用 LOGIC、COMMUNICATION、EXECUTION、LEARNING 四个聚合分。两类结果均须通过服务端结构校验后才可参与排序。 ## 5. 推荐候选、过滤与排序 ### 5.1 硬过滤 推荐候选必须同时满足: 1. 岗位状态为 `PUBLISHED`; 2. 当前学生不存在该岗位的任何投递记录,避免推荐一个无法再次投递的岗位; 3. 若学生已设置期望城市,岗位城市必须命中; 4. 若学生已设置期望薪资下限且岗位有薪资上限,学生下限不得高于岗位上限; 5. 当前学生没有对该岗位提交有效的 `NOT_INTERESTED` 反馈; 6. 岗位推荐画像存在、已启用且审核状态为 `APPROVED` 时,才进入“个性化推荐”排序。 城市和薪资口径与现有单岗位匹配诊断保持一致。用工类型、行业和岗位族是软信号,不作为拒绝或隐藏条件。 ### 5.2 三类软信号 | 信号 | 默认权重 | 计算方式 | |---|---:|---| | 偏好适配 `P` | 40% | 城市 30、用工类型 20、薪资 20、行业 15、岗位族 15;只对学生已设置且岗位有对应结构化值的字段纳入分母 | | 兴趣适配 `I` | 35% | `Σ(RIASEC学生维度分 × 岗位该维权重) / Σ岗位兴趣权重` | | 能力适配 `A` | 25% | 对每个已配置能力期望值:学生分数大于等于期望值记 100,否则 `学生分 / 期望分 × 100`;再取已配置维度平均值 | 总分公式: ```text recommendationScore = (P × 40 + I × 35 + A × 25) / 可用信号权重之和 ``` 结果保留两位小数,使用 `HALF_UP`。缺失或无效信号不记为 0,而是从分母中移除;这样不会因为学生尚未完成某项测评而被无理由降分。 如果偏好、兴趣、能力三者都不可用,则该岗位不能标为个性化推荐。 ### 5.3 排序与兜底 个性化岗位按以下顺序排序: 1. `recommendationScore DESC`; 2. `published_at DESC`; 3. `job_id DESC`。 若没有任何可用个性化结果,接口返回 `mode=FALLBACK` 的公开岗位兜底列表,只按发布时间排序,`recommendationScore=null`,并明确说明“完善偏好或完成测评后可获得个性化推荐”。兜底列表绝不伪装为个性化结果。 ### 5.4 理由与缺口 每个个性化岗位最多返回 3 条理由和 2 条建议,均由固定白名单模板生成,例如: - `PREFERENCE_CITY_MATCH`:工作城市符合你的偏好; - `PREFERENCE_SALARY_MATCH`:薪资范围满足你的期望; - `RIASEC_INTEREST_MATCH`:职业兴趣与岗位侧重点较匹配; - `ABILITY_STRENGTH_MATCH`:你的能力表现满足该岗位的主要侧重点; - `ABILITY_GAP`:可优先提升与岗位相关的某项能力; - `PREFERENCE_GAP`:用工类型或岗位族与当前偏好存在差异。 不返回 RIASEC 原始六维向量、能力答卷 ID、结果 ID、题目、答案、答案键、评分规则或企业配置原文。 ## 6. 数据模型与 Flyway(建议,待确认) ### 6.1 迁移 当前最新迁移为 V36;建议新增: ```text V37__job_recommendation_profiles.sql ``` 功能版本仍为 V39。该单一迁移创建岗位画像、受控分类和学生反馈三张相关表,不修改 V1–V36 的语义。 ### 6.2 `job_recommendation_taxonomies` 平台受控字典表,随 `job-taxonomy-v0.1` 种子初始化。 | 字段 | 说明 | |---|---| | `code` | 不可重用的稳定编码,主键 | | `parent_code` | 岗位族指向所属行业大类;行业大类为 `NULL` | | `taxonomy_type` | `INDUSTRY` 或 `JOB_FAMILY` | | `display_name` | 前端展示名 | | `active` | 是否仍允许企业新选用 | | `sort_order`、`catalog_version` | 展示顺序与目录版本 | 服务端必须校验 `industry_code` 是有效行业大类、`job_family_code` 是该行业下有效岗位族;企业不得传任意文本或跨层级组合。 ### 6.3 `job_recommendation_profiles` | 字段 | 说明 | |---|---| | `id` | 主键 | | `job_id` | 外键至 `jobs.id`,唯一,一岗一份画像 | | `enabled` | 是否参与个性化推荐,默认 `false` | | `review_status` | `PENDING_REVIEW`、`APPROVED`、`REJECTED`、`DISABLED` | | `industry_code` | 必填、受控行业大类编码 | | `job_family_code` | 必填、受控岗位族编码 | | `riasec_r` 至 `riasec_c` | 六维兴趣权重,`0..100`,至少一个非零才可启用兴趣信号 | | `logic_requirement` 等四列 | 四维能力期望值,`0..100`;`NULL` 表示该维不参与能力适配 | | `reviewed_by_user_id`、`reviewed_at` | 最近一次审核人和审核时间,仅内部使用 | | `review_reason_code` | 审核/驳回的固定理由码,不存自由文本 | | `profile_version` | 乐观锁版本 | | `created_at`、`updated_at` | 审计时间 | 约束与索引: - `UNIQUE(job_id)`; - `FOREIGN KEY(job_id) REFERENCES jobs(id)`; - 所有权重和能力期望必须在 `0..100`; - 建立 `(enabled, review_status, job_id)` 索引,支撑可推荐画像的候选岗位查询; - 使用 JPA `@Version` 和 `expectedVersion` 防止企业并发覆盖配置。 ### 6.4 `student_job_recommendation_feedback` 仅保存学生主动提交的最小化反馈,不保存自动点击画像。 | 字段 | 说明 | |---|---| | `id` | 主键 | | `student_user_id`、`job_id` | 反馈所属学生和岗位 | | `feedback_type` | `NOT_INTERESTED`、`REASON_HELPFUL`、`REASON_NOT_HELPFUL` | | `reason_code` | 可选固定理由码;不得提交自由文本、测评内容或个人信息 | | `feedback_key` | `student + job + type + reasonCode` 组成的非空去重键 | | `status` | `ACTIVE` 或 `REVOKED`;撤销不物理删除 | | `idempotency_key` | 同一学生请求幂等键唯一;同键异体返回冲突 | | `created_at`、`updated_at`、`version` | 审计时间与乐观锁 | `NOT_INTERESTED` 有效时排除该岗位;理由是否“有帮助”只用于后续人工优化规则,不自动改变学生分、岗位分或排序公式。 ## 7. API 设计(建议,待确认) ### 7.1 学生推荐列表 ```http GET /profile/me/job-recommendations?page=0&size=20 ``` 权限:仅 STUDENT 本人。`page` 从 0 开始,`size` 范围 1–50。 响应核心字段: ```json { "mode": "PERSONALIZED", "rulesVersion": "job-recommendation-v0.1", "items": [ { "job": { "id": 101, "title": "后端开发工程师", "city": "合肥" }, "recommendationScore": 82.50, "reasons": ["PREFERENCE_CITY_MATCH", "RIASEC_INTEREST_MATCH"], "suggestions": ["ABILITY_GAP"], "availableSignals": ["PREFERENCE", "RIASEC", "ABILITY"] } ], "page": 0, "size": 20, "total": 1, "totalPages": 1, "generatedAt": "2026-09-04T12:00:00" } ``` 岗位详情继续复用既有 `GET /jobs/{jobId}`;如需查看单个岗位的偏好诊断,继续复用既有 `GET /jobs/{jobId}/match`。 ### 7.2 企业岗位画像查询与更新 ```http GET /companies/me/jobs/{jobId}/recommendation-profile PUT /companies/me/jobs/{jobId}/recommendation-profile ``` 仅岗位所属 COMPANY 可查询和更新。更新请求需带 `expectedVersion`,并仅接受固定字段:`enabled`、`industryCode`、`jobFamilyCode`、`riasecWeights`、`abilityRequirements`。新建/更新后进入 `PENDING_REVIEW`;只有企业主动 `enabled=false` 时可转为 `DISABLED`。 不提供 DELETE;企业通过 `enabled=false` 停用画像,保留配置和审计链路。 ### 7.3 受控分类、学生反馈与 ADMIN 审核 ```http GET /job-recommendation-taxonomies POST /profile/me/job-recommendations/{jobId}/feedback DELETE /profile/me/job-recommendations/{jobId}/feedback GET /admin/job-recommendation-profiles?page=0&size=20&status=PENDING_REVIEW POST /admin/job-recommendation-profiles/{jobId}/decision ``` 分类接口只返回 `code`、`parentCode`、`type`、`displayName`、`sortOrder` 和 `catalogVersion`;无需登录即可让岗位发布和岗位搜索复用同一字典。 反馈创建必须携带 `idempotencyKey`。`NOT_INTERESTED` 的撤销只把对应记录改为 `REVOKED`;不删除审计记录。ADMIN 审核请求只接受 `APPROVE` 或 `REJECT` 与固定 `reasonCode`,不接受自由文本。 ### 7.4 OpenAPI 预估 新增 6 个路径、8 个 operation: ```text 148 paths / 182 operations → 154 paths / 190 operations ``` 编码时必须以运行时 OpenAPI 为准更新 `docs/openapi-rc.json` 与 `docs/verify_openapi.py`,不允许仅修改静态计数。 ## 8. 权限、隐私与审计 | 操作 | STUDENT | COMPANY | ADMIN | 未登录 | |---|---:|---:|---:|---:| | 查看本人推荐列表 | 200 | 403/2002 | 403/2002 | 401 | | 提交/撤销本人推荐反馈 | 200 | 403/2002 | 403/2002 | 401 | | 查看本人岗位匹配诊断 | 既有行为 | 既有行为 | 既有行为 | 既有行为 | | 查看自己岗位画像 | 403/2002 | 仅所属岗位 200 | 403/2002 | 401 | | 更新自己岗位画像 | 403/2002 | 仅所属岗位 200,进入待审核 | 403/2002 | 401 | | 查询受控分类 | 200 | 200 | 200 | 200 | | 查看待审核岗位画像 | 403/2002 | 403/2002 | 200 | 401 | | 审批岗位画像 | 403/2002 | 403/2002 | 200 | 401 | 审计动作建议: - `JOB_RECOMMENDATION_PROFILE_UPDATED`:仅记录 `jobId`、画像版本、是否启用、请求 ID 和时间; - `JOB_RECOMMENDATION_PROFILE_DECIDED`:仅记录 `jobId`、决定、固定理由码、画像版本、请求 ID 和时间; - `JOB_RECOMMENDATION_FEEDBACK_CREATED` / `JOB_RECOMMENDATION_FEEDBACK_REVOKED`:仅记录 `jobId`、反馈类型、固定理由码、请求 ID 和时间; - `JOB_RECOMMENDATIONS_VIEWED`:可选,仅记录返回数量、规则版本和请求 ID;不得记录完整排序、测评分、题目、答案、手机、邮箱、IP、User-Agent 或 token。 公司端绝不获得学生的 RIASEC、四维能力、候选评分、推荐分、推荐理由或缺口。学生端也不获得企业配置的完整权重和能力期望值。 ## 9. 模块边界、性能与并发 ### 9.1 建议模块划分 - `JobRecommendationProfile` / Repository:岗位画像配置; - `JobRecommendationTaxonomy` / Repository:平台受控行业与岗位族字典; - `StudentJobRecommendationFeedback` / Repository:学生显式反馈及撤销状态; - `JobRecommendationProfileService`:企业画像读取、所有权校验和乐观锁更新; - `JobRecommendationAdminService`:待审核画像分页和固定决定流转; - `StudentRecommendationSource`:一次读取学生偏好、最新有效 V35/V36 聚合结果; - `JobRecommendationCalculator`:纯函数,执行结构校验、分数公式和理由模板; - `JobRecommendationService`:过滤、分页、排序和响应白名单; - `JobRecommendationController`:仅从 `SecurityContext` 获取当前用户 ID。 ### 9.2 N+1 防护 推荐列表不得对每个岗位调用现有 `MatchService.diagnose()`:该方法会逐岗位读数据并写审计,既会造成 N+1,也会污染审计。 推荐请求的目标查询上限为固定次数: 1. 学生偏好一次; 2. 最新 RIASEC 结果一次; 3. 最新有效四维能力结果一次; 4. 分页候选岗位及画像一次; 5. 当前学生已投递岗位 ID 集合一次; 6. 当前学生有效 `NOT_INTERESTED` 岗位 ID 集合一次; 7. 页面企业信息批量加载一次。 岗位排序应使用单条参数化 SQL/JPA 查询按推荐表达式完成,或在有明确候选上限时使用固定批量加载;禁止“每岗位再查画像、公司、投递或测评”的循环查询。编码后必须用 Hibernate/JDBC Spy 断言查询次数不随页面 items 数线性增加。 ### 9.3 并发 - 推荐查询只读、无幂等键要求; - 企业更新画像、学生反馈撤销和 ADMIN 审核均使用 `expectedVersion` + `@Version`,冲突返回 409; - 岗位关闭、画像停用或学生完成投递后,下一次查询自然不再返回该岗位;不缓存旧结果。 ## 10. 错误码(建议) 建议使用新段 `10001–10009`: | 错误码 | HTTP | 含义 | |---|---:|---| | `10001` | 404 | `JOB_RECOMMENDATION_PROFILE_NOT_FOUND` | | `10002` | 400 | `JOB_RECOMMENDATION_PROFILE_INVALID` | | `10003` | 409 | `JOB_RECOMMENDATION_PROFILE_CONCURRENT_UPDATE` | | `10004` | 400 | `JOB_RECOMMENDATION_INVALID_QUERY` | | `10005` | 400 | `JOB_RECOMMENDATION_TAXONOMY_INVALID` | | `10006` | 409 | `JOB_RECOMMENDATION_FEEDBACK_CONCURRENT_UPDATE` | | `10007` | 409 | `JOB_RECOMMENDATION_FEEDBACK_IDEMPOTENCY_CONFLICT` | | `10008` | 404 | `JOB_RECOMMENDATION_FEEDBACK_NOT_FOUND` | | `10009` | 409 | `JOB_RECOMMENDATION_PROFILE_INVALID_REVIEW_STATE` | 现有岗位不存在、岗位归属不匹配、未登录和角色越权继续使用既有错误码,不重复造码。 ## 11. 功能开关、兼容与回滚 建议新增默认关闭的开关: ```yaml recommendation: assessment: enabled: ${RECOMMENDATION_ASSESSMENT_ENABLED:false} ``` 开关关闭时,学生推荐接口只返回 `FALLBACK` 公开岗位列表,不查询测评结果、不生成个性化分;企业仍可维护岗位画像,为灰度开启做准备。 开关开启时规则版本为 `job-recommendation-v0.1`。回滚只需关闭开关;画像数据保留,不删除岗位、测评、投递、评分或审计数据。 V39 不改变: - V35/V36 的题库、答题、重测、有效期和审计语义; - V38 的候选评分开关、ABILITY 快照和 V32 排名口径; - 既有 `/jobs` 搜索、`/jobs/{jobId}/match`、投递和收藏接口语义。 ## 12. 测试与验收计划 ### 12.1 单元测试 - RIASEC 与四维能力来源选择:最新优先、过期回退、结构异常不抛 500; - 公式:单信号、双信号、三信号、缺失重归一化、`HALF_UP`、稳定排序; - 硬过滤:未发布、已投递、城市不符、薪资不符; - 理由/建议白名单和最大数量; - 受控分类父子校验、画像字段边界、启用条件和审核状态机; - 反馈幂等、撤销、不感兴趣过滤和理由反馈不改变推荐分; - 画像、反馈和审核的乐观锁冲突。 ### 12.2 MockMvc 与持久化测试 - STUDENT/COMPANY/ADMIN/未登录权限矩阵; - 企业只能读写自己的岗位画像,更新后必须进入待审核状态; - ADMIN 只能审核、不可修改企业画像内容;非 ADMIN 审核返回 403; - 分类接口仅返回受控字典;自由行业/岗位族编码返回 400; - 推荐响应不含测评题目、答案、答案键、结果 ID、原始画像权重; - 无画像/无测评返回 `FALLBACK`,不伪造个性化分; - 分页参数、空列表、推荐总数和稳定排序; - 查询次数固定,禁止推荐列表 N+1; - 不写 `candidate_dimension_scores`、不改变 V32 排名。 ### 12.3 MySQL/Docker 黑盒 1. 执行 V37 迁移,校验三张表、唯一约束、外键、范围约束、分类种子和索引; 2. 企业创建/更新自己岗位画像,其他企业返回 403,ADMIN 审批后才可用于推荐; 3. 学生完成 RIASEC 和能力测评后,推荐分与理由符合固定 fixture; 4. 验证历史高分、最新低分和能力过期三种选择口径; 5. 开关关闭返回 `FALLBACK`,开启后才产生 `PERSONALIZED`; 6. 已投递、明确不感兴趣、关闭、城市/薪资不符岗位不会进入个性化列表; 7. 反馈重复提交、同键重放、撤销重试和审核并发均满足预期业务码; 8. 响应和审计不泄露题目、答案、评分规则、结果 ID 或凭据; 9. RabbitMQ 队列终态正常,并回归 V34–V38 关键接口。 冻结条件:全量 `mvn clean verify`、运行时/静态 OpenAPI 对账、真实 MySQL/Docker 黑盒关键项全部 PASS,且 FAIL=0。 ## 13. 已确认决策与编码前最后口径 负责人已确认: 1. 新增岗位推荐画像与 Flyway `V37__job_recommendation_profiles.sql`; 2. 接受默认排序权重:偏好 40%、RIASEC 35%、能力 25%; 3. 无结构化岗位画像时仅返回 `FALLBACK`,不伪造测评推荐; 4. 任何已有投递的岗位均不再推荐; 5. 默认关闭 `recommendation.assessment.enabled`,支持灰度; 6. 第一版加入学生“不感兴趣”持久化和推荐理由反馈; 7. 按负责人第 8 项“是”处理:第一版需要 ADMIN 审核企业岗位画像。 基于第 6、7 项,原先的 3 个 operation 扩展为 8 个 operation,且迁移包含三个相关表。这是已确认产品范围变化,不是无关扩张。 编码开始前只需最终确认以下两项具体口径: 1. 受控分类采用“平台种子维护、企业只能选择、不允许自由输入”,并按 `job-taxonomy-v0.1` 首批目录落库; 2. ADMIN 审核语义确为“企业提交/更新画像后进入 `PENDING_REVIEW`,审核通过才参与推荐”。 若以上两项确认,即可进入 V39 编码;在此之前不得创建迁移、Controller 或前端接口实现。