# 岗位评价个性化权重与权重模板 API ## 概述 本模块提供岗位评价维度的个性化权重配置和基于自定义权重的岗位评分计算。学生用户可以: - 查看预定义的权重模板 - 配置个人权重偏好(模板或自定义) - 基于个人权重计算岗位的个性化评分 ## 接口列表 | 方法 | 路径 | 权限 | 说明 | |------|------|------|------| | GET | `/review-weight-templates` | 已登录 | 获取权重模板列表 | | GET | `/profile/me/review-weights` | STUDENT | 获取当前学生的权重配置 | | PUT | `/profile/me/review-weights` | STUDENT | 保存当前学生的权重配置 | | GET | `/jobs/{jobId}/personalized-review-score` | STUDENT | 基于用户权重计算岗位个性化评分 | ### 权限矩阵 | 角色 | 模板列表 | 权重配置 | 个性化评分 | |------|---------|---------|-----------| | 未登录 | 401 | 401 | 401 | | STUDENT | 200 | 200 | 200 | | COMPANY | 200 | 403 | 403 | | ADMIN | 200 | 403 | 403 | --- ## 接口详情 ### 1. GET /review-weight-templates 获取所有预定义的权重模板。 **请求示例:** ``` GET /review-weight-templates Authorization: Bearer ``` **响应示例:** ```json { "code": 200, "message": "success", "data": [ { "code": "BALANCED", "name": "均衡权重", "description": "八个维度平均分配", "weights": { "AUTHENTICITY": 12.50, "BENEFITS": 12.50, "SALARY_TRANSPARENCY": 12.50, "JD_CLARITY": 12.50, "GROWTH": 12.50, "TEAM_ATMOSPHERE": 12.50, "PROMOTION": 12.50, "WORK_LIFE_BALANCE": 12.50 } } ] } ``` --- ### 2. GET /profile/me/review-weights 获取当前学生的权重配置。未配置时返回系统默认配置(BALANCED 模板,source="SYSTEM_DEFAULT")。 **请求示例:** ``` GET /profile/me/review-weights Authorization: Bearer ``` **响应示例(未配置):** ```json { "code": 200, "message": "success", "data": { "source": "SYSTEM_DEFAULT", "templateCode": "BALANCED", "configuredWeights": { "AUTHENTICITY": 12.50, "BENEFITS": 12.50, "SALARY_TRANSPARENCY": 12.50, "JD_CLARITY": 12.50, "GROWTH": 12.50, "TEAM_ATMOSPHERE": 12.50, "PROMOTION": 12.50, "WORK_LIFE_BALANCE": 12.50 }, "updatedAt": null } } ``` **响应示例(已配置模板):** ```json { "code": 200, "message": "success", "data": { "source": "TEMPLATE", "templateCode": "SALARY_FIRST", "configuredWeights": { "AUTHENTICITY": 15.00, "BENEFITS": 20.00, "SALARY_TRANSPARENCY": 30.00, "JD_CLARITY": 10.00, "GROWTH": 10.00, "TEAM_ATMOSPHERE": 5.00, "PROMOTION": 5.00, "WORK_LIFE_BALANCE": 5.00 }, "updatedAt": "2026-08-14T10:00:00" } } ``` **响应示例(自定义配置):** ```json { "code": 200, "message": "success", "data": { "source": "CUSTOM", "templateCode": null, "configuredWeights": { "AUTHENTICITY": 15.00, "BENEFITS": 15.00, "SALARY_TRANSPARENCY": 25.00, "JD_CLARITY": 10.00, "GROWTH": 10.00, "TEAM_ATMOSPHERE": 10.00, "PROMOTION": 10.00, "WORK_LIFE_BALANCE": 5.00 }, "updatedAt": "2026-08-14T10:00:00" } } ``` --- ### 3. PUT /profile/me/review-weights 保存当前学生的权重配置。支持模板模式和自定义模式。 **请求体(模板模式):** ```json { "templateCode": "BALANCED" } ``` **请求体(自定义模式):** ```json { "weights": { "AUTHENTICITY": 15.00, "BENEFITS": 15.00, "SALARY_TRANSPARENCY": 25.00, "JD_CLARITY": 10.00, "GROWTH": 10.00, "TEAM_ATMOSPHERE": 10.00, "PROMOTION": 10.00, "WORK_LIFE_BALANCE": 5.00 } } ``` **请求校验规则:** 1. `templateCode` 和 `weights` 必须且只能提供一个 2. 模板模式:`templateCode` 必须是 `BALANCED`、`SALARY_FIRST`、`GROWTH_FIRST`、`STABILITY_FIRST` 之一 3. 自定义模式: - 必须包含全部 8 个维度 - 每个权重值必须在 `[0.00, 100.00]` 范围内 - 每个权重值最多保留两位小数(如 `12.345` 会被拒绝,返回 400) - 8 个权重值总和必须精确等于 `100.00` 4. 并发冲突:同一用户并发更新时返回 409(错误码 9106) **错误响应示例:** ```json { "code": 1001, "message": "bad request", "requestId": "xxx", "timestamp": "2026-08-14T10:00:00Z" } ``` ```json { "code": 9106, "message": "concurrent weight update conflict", "requestId": "xxx", "timestamp": "2026-08-14T10:00:00Z" } ``` --- ### 4. GET /jobs/{jobId}/personalized-review-score 基于用户权重配置计算岗位的个性化评分。仅支持 `PUBLISHED` 状态的岗位。 **请求示例:** ``` GET /jobs/1/personalized-review-score Authorization: Bearer ``` **响应示例(正常计算):** ```json { "code": 200, "message": "success", "data": { "jobId": 1, "score": 78.50, "status": "OK", "source": "CUSTOM", "templateCode": null, "configuredWeights": { "AUTHENTICITY": 15.00, "BENEFITS": 15.00, "SALARY_TRANSPARENCY": 25.00, "JD_CLARITY": 10.00, "GROWTH": 10.00, "TEAM_ATMOSPHERE": 10.00, "PROMOTION": 10.00, "WORK_LIFE_BALANCE": 5.00 }, "effectiveWeights": { "AUTHENTICITY": 25.00, "BENEFITS": 25.00, "SALARY_TRANSPARENCY": 33.33, "GROWTH": 16.67 }, "dimensionScores": { "AUTHENTICITY": 4.20, "BENEFITS": 3.80, "SALARY_TRANSPARENCY": 4.50, "JD_CLARITY": null, "GROWTH": 3.00, "TEAM_ATMOSPHERE": null, "PROMOTION": null, "WORK_LIFE_BALANCE": null }, "coveredWeight": 60.00, "missingDimensions": ["JD_CLARITY", "TEAM_ATMOSPHERE", "PROMOTION", "WORK_LIFE_BALANCE"] } } ``` **响应示例(数据不足):** ```json { "code": 200, "message": "success", "data": { "jobId": 1, "score": null, "status": "INSUFFICIENT_DATA", "source": "SYSTEM_DEFAULT", "templateCode": "BALANCED", "configuredWeights": { ... }, "effectiveWeights": {}, "dimensionScores": { "AUTHENTICITY": null, "BENEFITS": null, "SALARY_TRANSPARENCY": null, "JD_CLARITY": null, "GROWTH": null, "TEAM_ATMOSPHERE": null, "PROMOTION": null, "WORK_LIFE_BALANCE": null }, "coveredWeight": 0, "missingDimensions": ["AUTHENTICITY", "BENEFITS", "SALARY_TRANSPARENCY", "JD_CLARITY", "GROWTH", "TEAM_ATMOSPHERE", "PROMOTION", "WORK_LIFE_BALANCE"] } } ``` **错误响应:** - 岗位不存在或非 PUBLISHED 状态:404(code=7001) - 非 STUDENT 角色:403(code=2002) - 未登录:401(code=401) --- ## 权重模板 ### 四套预定义模板 #### BALANCED(均衡权重) | 维度 | 权重 | |------|------| | AUTHENTICITY | 12.50 | | BENEFITS | 12.50 | | SALARY_TRANSPARENCY | 12.50 | | JD_CLARITY | 12.50 | | GROWTH | 12.50 | | TEAM_ATMOSPHERE | 12.50 | | PROMOTION | 12.50 | | WORK_LIFE_BALANCE | 12.50 | | **合计** | **100.00** | #### SALARY_FIRST(薪资优先) | 维度 | 权重 | |------|------| | AUTHENTICITY | 15.00 | | BENEFITS | 20.00 | | SALARY_TRANSPARENCY | 30.00 | | JD_CLARITY | 10.00 | | GROWTH | 10.00 | | TEAM_ATMOSPHERE | 5.00 | | PROMOTION | 5.00 | | WORK_LIFE_BALANCE | 5.00 | | **合计** | **100.00** | #### GROWTH_FIRST(成长优先) | 维度 | 权重 | |------|------| | AUTHENTICITY | 10.00 | | BENEFITS | 5.00 | | SALARY_TRANSPARENCY | 5.00 | | JD_CLARITY | 15.00 | | GROWTH | 30.00 | | TEAM_ATMOSPHERE | 10.00 | | PROMOTION | 20.00 | | WORK_LIFE_BALANCE | 5.00 | | **合计** | **100.00** | #### STABILITY_FIRST(稳定优先) | 维度 | 权重 | |------|------| | AUTHENTICITY | 10.00 | | BENEFITS | 25.00 | | SALARY_TRANSPARENCY | 15.00 | | JD_CLARITY | 5.00 | | GROWTH | 5.00 | | TEAM_ATMOSPHERE | 5.00 | | PROMOTION | 5.00 | | WORK_LIFE_BALANCE | 30.00 | | **合计** | **100.00** | --- ## 八维评价维度 | 维度代码 | 说明 | |---------|------| | AUTHENTICITY | 信息真实性 | | BENEFITS | 福利待遇 | | SALARY_TRANSPARENCY | 薪资透明度 | | JD_CLARITY | 职位描述清晰度 | | GROWTH | 成长空间 | | TEAM_ATMOSPHERE | 团队氛围 | | PROMOTION | 晋升机制 | | WORK_LIFE_BALANCE | 工作生活平衡 | --- ## 个性化评分计算规则 ### 数据来源 使用专用聚合查询直接从 `job_review_dimension_scores` 表按 `jobId` 聚合,只统计状态为 `PUBLISHED` 且审核状态为 `AUTO_APPROVED` 或 `APPROVED` 的评价。不修改原有 `/jobs/{id}/reviews/summary` 接口的语义。 ### 维度可用性判定 仅当某个维度同时满足以下条件时,该维度才参与计算: - `sampleCount >= 3` - `averageScore` 非空 `sampleCount` 为 1 或 2 的维度视为缺失维度。 ### 计算步骤 1. **coveredWeight**:所有可用维度的 configuredWeight 之和 2. 若 `coveredWeight = 0`: - `score = null` - `status = INSUFFICIENT_DATA` - `effectiveWeights = {}` - `missingDimensions` = 完整八维 3. **effectiveWeight**(归一化): ``` effectiveWeight = configuredWeight / coveredWeight × 100 ``` 最后一项 effectiveWeight 用差额修正,确保总和严格等于 100.00 4. **单维百分制分数**: ``` dimensionScore100 = averageScore / 5 × 100 ``` 5. **最终分数**: ``` score = Σ(dimensionScore100 × effectiveWeight / 100) ``` ### 精度要求 - 全程使用 `BigDecimal` - 中间除法 `scale=8`、`HALF_UP` - 最终 score、coveredWeight、effectiveWeight 保留两位 `HALF_UP` --- ## 并发冲突处理 当同一用户并发更新权重配置时,系统使用乐观锁(`@Version`)进行控制。 | 异常类型 | 错误码 | HTTP 状态码 | |---------|--------|------------| | `ObjectOptimisticLockingFailureException` | 9106 | 409 Conflict | | `DataIntegrityViolationException`(UNIQUE 冲突) | 9106 | 409 Conflict | 任何异常发生时,整笔事务回滚,不会留下不完整的权重配置。 --- ## 接入方式 ### Apifox 导入 1. 打开 Apifox,选择"导入数据" 2. 选择 "OpenAPI/Swagger" 格式 3. 导入文件:`docs/openapi-rc.json` 4. 配置 Base URL:`http://localhost:8080` ### Base URL - 本地开发:`http://localhost:8080` - 测试环境:根据实际部署地址配置 --- ## 不修改的既有接口 本模块不修改以下已有接口: - `GET /jobs/{id}/reviews/summary` — 岗位评价汇总 - 所有 V1–V22 迁移和已验收接口