57 KiB
职来后端前端联调说明(RC)
1. API Base URL
| 环境 | Base URL |
|---|---|
| 本地开发 | http://127.0.0.1:8080/api/v1 |
| 生产(示例) | https://api.mydreamjob.example.com/api/v1 |
所有接口统一前缀 /api/v1,OpenAPI 文档:GET /api/v1/v3/api-docs(Swagger UI:/api/v1/swagger-ui/index.html)。
2. 后端启动(Windows)
先启动基础设施容器:
cd backend-infra
docker compose up -d
然后设置环境变量并启动:
cd mydreamjob-server
$env:DB_URL = "jdbc:mysql://127.0.0.1:3307/mydreamjob?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai"
$env:DB_USERNAME = "mydreamjob"
$env:DB_PASSWORD = "<你的数据库密码>"
$env:RABBITMQ_USERNAME = "mydreamjob_dev"
$env:RABBITMQ_PASSWORD = "<你的 RabbitMQ 密码>"
# 注意:本机 Windows 环境变量 RABBITMQ_VHOST 可能被设置为无关路径,必须显式覆盖为默认 vhost
$env:RABBITMQ_VHOST = "/"
$env:JWT_SECRET = "<至少256位的随机密钥>"
$env:SMS_MOCK_ENABLED = "true"
mvn spring-boot:run -Dspring-boot.run.profiles=dev
健康检查:http://127.0.0.1:8080/api/v1/health 返回 status: UP。
3. Mock 验证码日志位置
开发环境短信为 Mock,验证码打印在服务控制台/stdout 日志中:
MOCK SMS CODE: phone=13800000002, purpose=login, code=483920
- 运行
mvn spring-boot:run→ 看终端输出 - 后台运行 → 看重定向的日志文件(如
logs/mydreamjob.log) - 每次发码后取最新一条
code=<6位数字>
4. Apifox 导入 OpenAPI
- 打开 Apifox → 新建项目
- 左侧「导入数据」→ 选择「OpenAPI/Swagger」
- 上传 docs/openapi-rc.json(或填 URL
http://127.0.0.1:8080/api/v1/v3/api-docs) - 点击「确认导入」,Apifox 自动生成全部接口、参数、响应模型
- 在 Apifox 中可直接调试:登录接口获取 token 后,设置「环境变量
token」,在接口「Authorization」填Bearer {{token}}
5. Token 使用方式
登录成功返回 accessToken(有效期 24h)与 refreshToken。
{
"code": 200,
"data": {
"userId": 2,
"accessToken": "eyJhbGciOi...",
"refreshToken": "eyJhbGciOi...",
"expiresIn": 86400,
"role": "STUDENT"
}
}
后续请求:所有需要认证的接口在请求头携带
Authorization: Bearer <accessToken>
3. 请求头
| Header | 必需 | 说明 |
|---|---|---|
Authorization |
认证接口 | Bearer <token> |
Content-Type |
POST/PUT/PATCH | application/json |
X-Request-Id |
可选 | 全链路追踪 ID,审计日志会记录 |
User-Agent |
可选 | 客户端标识,审计日志会记录 |
注意:X-Request-Id 和 User-Agent 会被写入 audit_logs,联调时建议带自定义值便于排查。
4. 分页格式
列表接口统一使用 page(从 0 起)和 size 参数,返回 PageResponse 结构:
{
"code": 200,
"data": {
"items": [ ... ],
"total": 42,
"page": 0,
"size": 20,
"totalPages": 3
}
}
| 接口 | 默认 size | 最大 size |
|---|---|---|
| GET /jobs | 20 | 50 |
| GET /applications/me | 20 | 100 |
| GET /company/applications | 20 | 100 |
| GET /jobs/{id}/reviews | 20 | 50 |
| GET /conversations/{id}/messages | 20 | 50 |
| GET /notifications | 20 | 50 |
5. 统一响应与错误码
所有接口返回统一 ApiResponse:
{ "code": 200, "message": "ok", "data": {...}, "requestId": "...", "timestamp": "..." }
| HTTP | 业务 code | 含义 |
|---|---|---|
| 401 | 401(安全过滤器) | 未登录 / 无 token |
| 403 | 2002, 8004, 8303, 8402 | 无角色权限 / 越权(含企业归属不匹配) |
| 400 | 1001, 3002-3006, 8404-8408, 8505, 9105 | 参数错误 / 非法请求体 / 资源未命中默认映射 |
| 404 | 4001, 5001, 6001, 7001, 8002, 9102, 9104, 8305, 8501 | 资源不存在 |
| 409 | 5003, 7003, 8001, 8003, 8301-8302, 8304, 8502-8504, 9101, 9103 | 重复 / 非法状态流转 |
| 500 | 1002, 9001 | 服务器内部错误 |
前端策略:优先判断 HTTP 状态码,再读取 body.code 显示业务信息。
6. 测试账号
| 账号 | 角色 | 说明 |
|---|---|---|
13800000002 |
STUDENT | 学生,可投递/匹配/查看通知 |
13800000003 |
COMPANY | 企业,可创建岗位/查看投递/更新状态 |
13800000001 |
ADMIN | 管理员(预留) |
登录方式(开发环境 Mock):
POST /auth/sms/send?phone=<手机号>发送验证码- 验证码打印在服务日志:
MOCK SMS CODE: ... code=xxxxxx POST /auth/login携带{phone, code, purpose:"login"}获取 token
7. 典型联调流程
学生端:
发码 → 登录 → GET /jobs → GET /jobs/{id}/match → POST /applications
→ GET /notifications → POST /notifications/{id}/read
企业端:
发码 → 登录 → POST /companies/me → POST /jobs → PATCH /jobs/{id}/status
→ GET /company/applications?jobId= → PATCH /applications/{id}/status
8. 投递接口与评价/对比补充
8.1 投递接口(正确写法)
创建投递为 POST /applications,请求体 {"jobId": 123}(不要写成 POST /jobs/{jobId}/applications):
{ "jobId": 123 }
8.2 岗位八维评价
POST /jobs/{jobId}/reviews 可附加可选 dimensionScores(八个固定维度,值 1-5,可稀疏提交):
{ "rating": 4, "content": "岗位信息真实", "tags": ["团队氛围"],
"dimensionScores": { "AUTHENTICITY": 5, "TEAM_ATMOSPHERE": 4 } }
- 不传
dimensionScores时旧接口完全兼容; - 评价列表/详情返回
dimensionScores; - 汇总
GET /jobs/{jobId}/reviews/summary返回dimensionSummary(INSUFFICIENTsampleCount<3 /SUFFICIENT>=3); - 详情见
docs/job-review-api.md。
8.3 岗位对比(公开)
GET /jobs/compare?ids=1,2,3(无需登录,2-3 个公开岗位),返回公开字段与公开评分。
9. Candidate Scoring(求职者评分体系)联调
默认关闭时规则版本固定 rulesVersion = "candidate-scoring-v0.1";八维 EDUCATION / INTERNSHIP / ABILITY / COMPLIANCE / STABILITY / GROWTH / INTEGRITY / ACTIVITY,默认权重各 12.50,参与计算维度重新归一化。
V38 联调(功能开关
scoring.ability-assessment.enabled,默认false):开关开启后规则版本为"candidate-scoring-v0.2",ABILITY可由最新有效四维能力测评(WORKPLACE_ABILITY、COMPLETED、答题 24/24、valid_until > capturedNow)映射为OK并参与综合分。学生ABILITY单维明细的subScores[]为四条白名单聚合分(逻辑能力/35、执行能力/30、学习能力/20、沟通能力/15),不含任何答案/答卷/来源结果 ID;企业评分卡对ABILITY仍仅暴露聚合score/status。后端本地验证已通过,真实 MySQL/Docker 黑盒待负责人验收。
9.1 接口
| 接口 | 方法 | 角色 | 说明 |
|---|---|---|---|
/profile/me/candidate-scoring |
GET | STUDENT 本人 | 综合评分卡(8 维 + overallScore + weights + status) |
/profile/me/candidate-scoring/dimensions/{dimension} |
GET | STUDENT 本人 | 单维度明细(subScores/reasons/dataSources);非法 dimension → 400 |
/profile/me/candidate-scoring/trend?months=6 |
GET | STUDENT 本人 | 月度趋势(months 1–24,越界 400) |
/company/candidates/{applicationId}/scoring |
GET | COMPANY(岗位归属) | 企业只读评分卡 |
9.2 权限矩阵
| 接口 | 未登录 | STUDENT | COMPANY | ADMIN |
|---|---|---|---|---|
| 学生 3 接口 | 401 | ✅ 本人 | 403 | 403 |
| 企业评分 | 401 | 403 | ✅(岗位归属) | 403 |
- 企业归属:
companyRepository.findByOwnerUserId(jwtUserId)无企业→403/2002;company.id != job.companyId→403/8004;application 不存在→404/8002。
9.3 企业响应脱敏字段(禁止展示)
企业评分响应仅含 applicationId / overallScore / status / dimensions[]{dimension,score,status} / generatedAt / rulesVersion;不得出现 studentUserId / candidateId / phone / email / resumeUrl / subScores / 内部日志。
9.4 状态与错误码
- 维度状态:
OK / ESTIMATED / INSUFFICIENT_DATA / PENDING;PENDING/INSUFFICIENT_DATA 的score为null。 - 综合卡状态:
OK / PARTIAL / INSUFFICIENT_DATA / PENDING;无可计算维度时overallScore=null。 - 错误码复用:
BAD_REQUEST(1001)、UNAUTHORIZED(2001)(未登录实际 401)、FORBIDDEN(2002)、APPLICATION_NOT_FOUND(8002)、APPLICATION_OWNER_MISMATCH(8004)。
9.5 前端注意
- 学生端先调综合卡,按
status决定是否渲染overallScore;PENDING/INSUFFICIENT 维度显示"数据不足/待补充"。 - 企业端评分卡仅用于候选人详情展示,勿透传任何候选人联系方式或简历地址。
10. 企业评分运营看板联调
全部只读聚合;候选人按学生去重,综合分复用评分引擎公式;岗位评价口径为「审核通过 + PUBLISHED」。详见 docs/company-scoring-dashboard-api.md。
10.1 接口
| 接口 | 方法 | 角色 | 说明 |
|---|---|---|---|
/company/scoring/dashboard/overview |
GET | COMPANY | 企业评分运营概览(岗位/评价/候选人统计) |
/company/scoring/dashboard/job-rankings?page&size |
GET | COMPANY | 岗位评分排名(avgRating DESC, jobId DESC) |
/company/scoring/dashboard/job-rankings/{jobId} |
GET | COMPANY | 单岗位评分排名明细(跨企业 400/7002) |
/company/scoring/dashboard/candidate-rankings?page&size |
GET | COMPANY | 候选人评分排名(按学生去重,overallScore DESC) |
/company/scoring/dashboard/candidate-rankings/{applicationId} |
GET | COMPANY | 单候选人评分排名明细(跨企业 403/8004) |
/company/scoring/dashboard/trend?months=6 |
GET | COMPANY | 企业评分趋势(岗位评价 + 候选人综合分,按月) |
/admin/scoring/dashboard/overview |
GET | ADMIN | 全局运营概览(全平台统计 + 企业 Top10) |
10.2 权限矩阵
| 接口 | 未登录 | STUDENT | COMPANY | ADMIN |
|---|---|---|---|---|
| 企业看板 6 接口 | 401 | 403 | ✅(本企业) | 403 |
| 全局概览 | 401 | 403 | 403 | ✅ |
10.3 关键口径
- 候选人去重:同一学生多次投递视为 1 名候选人,代表投递取企业下
applicationId最大一条;WITHDRAWN/REJECTED排除。 - 综合分:复用
CandidateScoringCalculator,读取候选人<= 当前月最新期快照计算;无可计算维度 →overallScore=null。 - 岗位评价:
status=PUBLISHED且moderation_status ∈ {AUTO_APPROVED, APPROVED}(与评价汇总口径一致)。 percentile:在有分候选人中计算(升序),无分 →null;sampleCount为有分候选人数。- 分页参数:
page(0 起)、size(1–100),months(1–24,默认 6)。
10.4 前端注意
- 看板为只读统计,勿依赖实时刷新频次;趋势冷启动期通常只有当前月数据,随评分调用/评价累积逐渐补齐。
- 候选人排名项不返回联系方式与简历地址,仅返回姓名/学校/综合分/百分位/维度分(维度分仅在详情接口)。
avgJobScore/avgCandidateScore/overallScore无数据时返回null,前端按「暂无数据」处理。
11. Offer 进阶(还价 / 对比 / 统计)联调
在基础 Offer 之上新增薪资洽谈、Offer 对比、企业统计,并扩展 WITHDRAWN 撤销状态。
完整字段/错误码见 docs/offer-advance-api.md。OpenAPI 总路径数 102 → 107。
11.1 接口
| 接口 | 方法 | 角色 | 说明 |
|---|---|---|---|
/offers/{id}/counter-offers |
POST | STUDENT 本人 | 发起还价(SENT 时自动进入 NEGOTIATING) |
/offers/{id}/counter-offers |
GET | STUDENT 本人 / COMPANY(本企业) | 还价列表 |
/offers/{id}/counter-offers/{coId}/accept |
PATCH | COMPANY(本企业) | 接受还价 → Offer 成交(ACCEPTED) |
/offers/{id}/counter-offers/{coId}/reject |
PATCH | COMPANY(本企业) | 拒绝还价 → Offer 保持 NEGOTIATING |
/offers/compare?ids=1,2,3 |
GET | STUDENT 本人 | 对比本人 2-5 个非 DRAFT Offer |
/offers/statistics?months=6 |
GET | COMPANY | 企业 Offer 统计(状态分布/接受率/响应时长) |
11.2 还价要点
- 仅
SENT/NEGOTIATING可发起;候选人发起后 Offer 自动进入NEGOTIATING,前端应刷新 Offer 状态。 - 同一 Offer 同时最多一个 PENDING 还价;二次发起返回 409
OFFER_COUNTER_OFFER_EXISTS。 - 企业
accept后会更新 Offer 薪资/入职日期并置为ACCEPTED;reject后 Offer 仍为NEGOTIATING,候选人可再次还价。
11.3 对比要点
ids为逗号分隔且 2-5 个、全为正数;非法参数一律 400,前端需先本地校验避免请求失败。- 返回项附带
companyName;被比较 Offer 含DRAFT视为 403OFFER_COMPARE_NOT_OWNER,故只选已发送/洽谈中 Offer。
11.4 统计要点
- 仅企业可访问;
months1-24,越界按默认 6 处理。 acceptanceRate/avgResponseHours无数据时为null,前端按「暂无数据」处理;byStatus恒含全部 7 个状态键。
11.5 撤销(WITHDRAWN)
- 企业
PATCH /offers/{id}/status{"status":"WITHDRAWN"},仅SENT/NEGOTIATING有效;其他状态 409。 - 撤销/过期会使该 Offer 的 PENDING 还价变为
EXPIRED,Offer 自身释放"唯一有效 Offer"槽位;企业端应刷新 Offer + 还价列表。
11.6 新增错误码(已并入统一映射)
| 错误码 | HTTP | 含义 |
|---|---|---|
| 8109 | 409 | 已存在 PENDING 还价 |
| 8110 | 404 | 还价不存在/不属于该 Offer |
| 8111 | 409 | 还价非 PENDING |
| 8113 | 409 | Offer 已撤销/过期,还价失效 |
| 8114 | 400 | Offer 状态不允许还价 |
| 8115 | 400 | 对比数量需 2-5 个 |
| 8116 | 403 | 对比含非本人/非有效 Offer |
12. 学生个人资料 GET /profile/me 联调
12.1 基础信息
| 项 | 值 |
|---|---|
| Base URL | http://127.0.0.1:8080/api/v1 |
| 状态 | 已实现、已验收(运行时黑盒 18/18 PASS) |
| 接口 | GET /api/v1/profile/me |
| 认证 | 必须携带 Authorization: Bearer <accessToken> |
| Token 获取 | 通过登录接口(POST /auth/login,开发环境为 Mock 验证码)获取 accessToken;文档不提供任何真实凭据 |
| 限流 | 当前未设置接口级限流 |
accessToken有效期 24h。Token 只通过登录流程获得,切勿在文档、代码或日志中硬编码或打印真实 Token。
12.2 请求参数
- 无 Path 参数:路径不含任何
{id}等占位符。 - 无 Query 参数:不读取任何查询串参数。
- 无 Body:不解析请求体。
- 不接受身份参数:身份仅从 JWT / SecurityContext 获取,
userId、studentUserId等入参一律忽略。- 例如请求
GET /api/v1/profile/me?userId=999,后端仍返回 当前 JWT 身份 对应的资料,userId=999不会改变 JWT 身份。
- 例如请求
12.3 角色权限说明
| 调用方 | HTTP | body.code | body.message |
|---|---|---|---|
| STUDENT | 200 | 200 |
ok |
| COMPANY | 403 | 2002 |
forbidden |
| ADMIN | 403 | 2002 |
forbidden |
| 未登录 / Token 缺失 / 非法 / 过期 | 401 | 401 |
not authenticated |
12.4 成功响应示例(有资料)
{
"code": 200,
"message": "ok",
"data": {
"id": 1,
"userId": 2,
"realName": "张三",
"education": "本科",
"school": "某某大学",
"graduationYear": 2026,
"resumeUrl": "/uploads/resumes/2/resume.pdf",
"skills": "[Java, Spring Boot]",
"createdAt": "2026-08-24T10:00:00"
},
"timestamp": "2026-08-24T10:00:00Z"
}
成功
code=200(不是 0)。null字段不序列化。
12.5 空资料响应示例
{
"code": 200,
"message": "ok",
"data": {},
"timestamp": "2026-08-24T10:00:00Z"
}
data={}是空对象,不是null。- 有资料查询也不会返回 HTTP 404。
- 不会自动创建
student_profiles记录,不修改数据库。 - 前端必须对
data进行空对象判定(Object.keys(data).length === 0),而非仅判非 null。
12.6 401 响应示例
{
"code": 401,
"message": "not authenticated",
"timestamp": "2026-08-24T10:00:00Z"
}
12.7 403 响应示例
{
"code": 2002,
"message": "forbidden",
"data": null,
"timestamp": "2026-08-24T10:00:00Z"
}
12.8 业务码说明表
| 场景 | HTTP | code | message |
|---|---|---|---|
| 成功 | 200 | 200 |
ok |
| 未登录 / Token 无效 | 401 | 401 |
not authenticated |
| 非 STUDENT 角色 | 403 | 2002 |
forbidden |
- 成功码是
200,不要写成0。- 本接口未登录错误为
401,不要写成2001。运行时认证入口实际返回code=401,以运行时契约为准。
12.9 响应字段表(StudentProfileResponse,null 不输出)
| 字段 | 类型 | 是否返回 | 说明 | 示例 |
|---|---|---|---|---|
| id | Long | 有资料时 | 学生资料 ID | 1 |
| userId | Long | 有资料时 | 当前登录用户 ID | 2 |
| realName | String | 可空 | 真实姓名 | 张三 |
| bio | String | 可空 | 个人简介 | — |
| location | String | 可空 | 所在地 | — |
| education | String | 可空 | 学历 | 本科 |
| school | String | 可空 | 学校 | 某某大学 |
| graduationYear | Short/Integer | 可空 | 毕业年份(四位) | 2026 |
| resumeUrl | String | 可空 | 简历地址 | /uploads/resumes/2/resume.pdf |
| skills | String | 可空 | 技能(字符串) | [Java, Spring Boot] |
| createdAt | date-time | 有资料时 | 创建时间 | 2026-08-24T10:00:00 |
不返回
passwordHash、手机号、验证码、JWT、内部认证字段。
12.10 skills 特别说明
- 当前类型为
String,不保证是合法的 JSON。 - 前端不要无条件
JSON.parse(skills),解析失败会抛异常。 - 示例
"[Java, Spring Boot]"只是普通字符串内容,不代表数组表示法。 - 后续若改为数组,必须作为独立版本或兼容字段变更处理,不能静默破坏现有消费方。
- 本轮不修改
skills类型,也不修改 PUT 语义。
12.11 resumeUrl 特别说明
- 值可能是相对存储路径或文件存储地址,不保证是完整的
http/httpsURL。 - 前端下载简历应调用
GET /api/v1/profile/me/resume,不要直接拼接resumeUrl做请求地址。
12.12 graduationYear 特别说明
- 当前后端没有实现年份范围校验。
- 文档不得虚构未实现的范围限制(如 1900–2100 之类)。
- 前端若展示年份,可自行决定是否校验,但接口本身不做约束。
13. 候选人反馈响应脱敏
候选人反馈接口保持原有路径、请求、权限和错误码不变:
POST /applications/{applicationId}/feedback
GET /applications/{applicationId}/feedback
PUT /applications/{applicationId}/feedback
DELETE /applications/{applicationId}/feedback
成功响应中的 data 仅包含以下业务字段:id、applicationId、rating、content、tags、createdAt、updatedAt。
学生只能查看自己的投递反馈;岗位所属企业可以创建、查看、修改和删除反馈;其他学生、其他企业和管理员不因用户 ID 巧合而获得访问权限。
响应示例:
{
"code": 200,
"message": "ok",
"data": {
"id": 1,
"applicationId": 31,
"rating": 5,
"content": "沟通良好",
"tags": ["沟通良好"],
"createdAt": "2026-08-25T10:00:00",
"updatedAt": "2026-08-25T10:00:00"
}
}
前端不得依赖或透传以下字段;这些字段不会出现在反馈响应中:
companyUserId、studentUserId、phone、email、passwordHash、resumeUrl
candidate_feedback 数据表仍保留企业用户和学生用户关联字段供权限校验和评分聚合使用,但这些内部字段不属于 API 响应。CandidateFeedback 也不等同于结构化面试评分;面试评分将在独立模块中提供。
14. 隐私设置(Privacy Settings)联调
学生隐私设置用于控制简历可见性与联系方式可见性,仅 STUDENT 角色可用:
GET /profile/me/privacy
PUT /profile/me/privacy
- 身份仅从 JWT 获取,不接受
userId/studentUserId/candidateId等参数。 - GET 无记录时返回默认对象(
resumeVisibility=PRIVATE、contactVisible=false、version=0),不写库。 - PUT 执行 upsert,同一学生始终一行;
version冲突返回409 / 9201。 - COMPANY / ADMIN →
403 / 2002;未登录 →401。 - 企业候选人详情/列表接口保持原脱敏白名单,不返回
resumeUrl与联系方式,本轮不新增联系方式字段。
详见 docs/privacy-settings-api.md。
15. 账号注销(Account Deletion)联调
当前登录用户可对自己的账号发起注销申请、查询状态、在冷静期内取消,适用于 STUDENT / COMPANY / ADMIN:
POST /profile/me/deletion # 申请注销(NONE → PENDING;PENDING 幂等 200;COMPLETED → 409/9301)
GET /profile/me/deletion # 查询状态(无记录返回默认 NONE,不写库)
POST /profile/me/deletion/cancel # 取消注销(仅 PENDING 且未过期;PENDING → NONE)
- 身份仅从 JWT 获取,不接受
userId/candidateId等参数;未登录 →401。 - 状态机:NONE → PENDING → COMPLETED(终态),可在冷静期内取消回 NONE。
- 冷静期:默认 30 天(
app.deletion.cooling-days/DELETION_COOLING_DAYS可配置)。 - PENDING 写限制:冷静期内 POST/PUT/PATCH/DELETE 业务写操作返回
409 / 9302;GET、OPTIONS 及三个注销接口放行。 - COMPLETED 旧 JWT:即使 JWT 未过期也拒绝普通业务请求(
409 / 9303);登录时亦被阻止。 - 脱敏:仅脱敏
users表 PII(phone→deleted_{id}、email/passwordHash/avatarUrl 置空、nickname→deleted、is_active=false),不删除任何业务数据。 - 原手机号禁止重新注册:注销完成后仅保存原手机号的 HMAC-SHA256 摘要(
deleted_phone_hash,密钥app.deletion.phone-hash-secret),不存明文;原手机号再次登录/注册返回409 / 9303,且不会自动创建新账号。 - 懒转换:到期 PENDING 首次
GET /profile/me/deletion即返回COMPLETED(独立事务重读最新记录),不会出现先 PENDING 后 COMPLETED 的不一致。 - ADMIN:最后一个有效 ADMIN 不允许注销 →
409 / 9306。 - 错误码
9301-9306(详见 docs/account-deletion-api.md)。
前端建议:发起注销与取消前先 GET /profile/me/deletion 确认当前状态;处于 PENDING 时应禁用业务提交类按钮并提示冷静期到期时间。
16. 个人数据导出(Personal Data Export)联调
这不是简历导出(简历导出走 /resume-export-tasks,两者独立)。个人数据导出把当前登录用户的全量业务数据异步打包成 ZIP 供本人下载,适用于 STUDENT / COMPANY / ADMIN:
POST /profile/me/data-export-tasks # 创建导出任务(异步,返回 PENDING)
GET /profile/me/data-export-tasks?page=0&size=20 # 本人任务列表(倒序分页)
GET /profile/me/data-export-tasks/{taskId} # 本人任务详情(状态/大小等)
GET /profile/me/data-export-tasks/{taskId}/download # 下载已完成 ZIP(需 JWT)
- 异步:创建后立即返回
PENDING;后台 Worker 打包完成后变为COMPLETED才可下载。 - 下载需 JWT:下载地址不能脱离 JWT 单独访问;不提供公开 URL。
- 文件有效期:默认 24 小时(
app.data-export.file-ttl-hours),过期状态为EXPIRED且不可再下载。 - 导出范围/脱敏:仅包含本人数据;不含他人手机号/邮箱/简历地址、不含
passwordHash/JWT/短信验证码/deletedPhoneHash、管理员内部审核字段、系统异常堆栈、服务器绝对路径与连接串。会话对方仅保留participantId。详见 docs/personal-data-export-api.md。 - 错误:未登录
401;访问他人任务403/2002;任务不存在404/9401;存在未完成任务409/9402;文件未就绪409/9403;已过期409/9404;失败409/9405;文件缺失409/9406。 - 注销限制:注销 PENDING 禁止创建(
409/9302);注销 COMPLETED 所有导出接口返回409/9303。
前端建议:创建后轮询任务详情直到 status=COMPLETED 且 downloadAvailable=true 再展示下载按钮;处于注销 PENDING/COMPLETED 时应隐藏「导出我的数据」入口。
17. 结构化面试评分(Structured Interview Score)联调
这不是 candidate_feedback(候选人反馈是另一模块,两者独立;本模块是结构化维度评分)。企业为已完成(COMPLETED)的面试填写并提交评分,学生仅能在提交后查看自己的评分与历史:
PUT /interviews/{interviewId}/score # 企业 owner 保存草稿(幂等更新,DRAFT)
GET /interviews/{interviewId}/score # 查看评分(企业 see DRAFT/SUBMITTED;学生仅 SUBMITTED)
POST /interviews/{interviewId}/score/submit # 提交草稿 → SUBMITTED(不可逆)
GET /interviews/{interviewId}/score/history # 评分快照历史(无记录返回 200 空数组)
- 五个维度:
technicalScore、communicationScore、problemSolvingScore、roleFitScore、professionalismScore,每项 1-5。 - 草稿:PUT 时五维可留空;重复 PUT 更新同一行(
version递增)。 - 提交:五维全部非空,否则
400/1001;overallScore由服务端按(t+c+ps+rf+p)/5计算(两位小数 HALF_UP),客户端不要传,传了也会被忽略。 - recommendation(可选):
STRONG_HIRE/HIRE/NEUTRAL/NO_HIRE,企业手动选择,不由 overallScore 推导。 - 权限:写入仅岗位所属企业 owner(COMPANY);ADMIN 不可访问;STUDENT 不可写。学生只能看自己的
SUBMITTED,查看他人或 DRAFT 返回403/8402。 - 状态机:无评分 →
DRAFT→SUBMITTED。SUBMITTED后再次 PUT409/9503;重复 submit 幂等返回原评分(不新增历史、不重复通知)。 - 通知:提交成功仅通知目标学生,
businessType=INTERVIEW,businessId=interviewId;草稿与重复提交不发通知;企业自身不通知;无 Push。 - 错误码:
9501并发冲突(409)、9502面试未完成(409)、9503已提交(409)、9504无评分(404);归属非法8402、未被授权2002、面试不存在8401。 - 脱敏:响应不返回
companyUserId/studentUserId/evaluatorUserId/手机号/邮箱。 - 历史脱敏:
GET /score/history不返回actorUserId,仅actorRole。 - 面试本身必须为
COMPLETED才能评分(否则409/9502)。
前端建议:企业端保存草稿用 PUT;「提交」用 POST submit 并在按钮上二次确认(不可逆);学生端仅在评分状态为 SUBMITTED 时展示。API 详见 docs/interview-score-api.md。
18. 同岗位竞争力排名(Competition Ranking)联调
学生查看自己在某个 已投递且为 PUBLISHED 岗位候选人池中的竞争力排名(匿名):
GET /jobs/{jobId}/competition-ranking # 仅 STUDENT 且已投递该岗位
- 权限矩阵:
- 未登录 →
401(安全过滤器) COMPANY/ADMIN→403/2002- STUDENT 但未投递该岗位 →
403/2002 - STUDENT 唯一投递为
WITHDRAWN或REJECTED→403/2002:投递已退出候选人池,与 cohort 排除口径一致,不能再查看排名。 jobId <= 0→400/1001- 岗位不存在或状态非
PUBLISHED(如DRAFT/CLOSED)→404/7001
- 未登录 →
- cohort:该岗位非
WITHDRAWN、非REJECTED的所有投递;SUBMITTED/VIEWED/INTERVIEW/HIRED计入;同一学生同一岗位由数据库唯一约束保证仅一条。无有效评分的候选人仍计入cohortSize,但不参与百分位,排在末尾。 - 综合分:复用
CandidateScoringCalculator真实公式(仅统计OK/ESTIMATED维度,PENDING/INSUFFICIENT_DATA不折算为 0),取每人period <= 当前月的最新快照。 - 百分位:严格复用企业评分看板公式
lower / (scored - 1) * 100;有分候选人数< 2时percentile = null;未取整为 2 位小数 HALF_UP。 - 本人无有效评分:
rank、overallScore、percentile均为null(仍返回cohortSize)。 - 响应结构(示例):
{
"jobId": 9, "rank": 2, "cohortSize": 8, "overallScore": 77.50,
"percentile": 66.67, "status": "PARTIAL",
"asOfPeriod": "2026-08", "mySnapshotPeriod": "2026-08",
"dimensions": [ { "dimension": "EDUCATION", "score": 80, "status": "OK", "weight": 12.50 } ],
"gaps": [],
"topCandidates": [ { "rank": 1, "overallScore": 88.50 } ],
"rulesVersion": "candidate-scoring-v0.1", "generatedAt": "2026-08-26T10:00:00Z"
}
- 脱敏:
topCandidates固定返回 Top 5、每项仅含rank与overallScore,不含studentUserId/candidateId/name/school/phone/email/resumeUrl/applicationId等任何身份字段;响应整体也不含candidateId。不提供top查询参数。 - 只读:该接口为
GET,不写任何业务表;身份仅取自 JWT,不接收userId/studentUserId/candidateId参数。 - 规则版本固定
rulesVersion = "candidate-scoring-v0.1";asOfPeriod为计算截止月(当前月),mySnapshotPeriod为当前学生实际使用的快照月(若与他人周期不同,二者可不同)。 - API 详见 docs/competition-ranking-api.md。
19. 举报与屏蔽(Report & Block)联调
提供举报、屏蔽与 ADMIN 举报处理能力。身份一律取 JWT,不接受 userId/reporterId/reportedUserId 参数。
举报(用户端)
POST /reports # 提交举报
GET /reports/me # 我的举报列表(status/page/size)
GET /reports/{id} # 举报详情(本人或 ADMIN)
targetType=USER必填targetUserId;targetType=MESSAGE必填messageId。reportedUserId由服务端推导,不传。category≤40 字符;reason1–1000 字符;evidenceUrls可选(≤5 个,仅 http/https,每个 ≤500 字符);idempotencyKey可选(≤64 字符)。- 幂等:同
idempotencyKey+ 相同请求体重放返回原举报200;同 key 不同请求体 →409/9606;同目标同分类已有 PENDING →409/9603。 - 限流:每用户每小时最多 10 次
POST /reports,超限 →429/9609。 - MESSAGE 举报:无权访问该消息 →
403/2002(不泄露消息是否存在);发送者由服务端推导,禁止客户端注入。 - 举报自己 →
400/9602;不存在目标 → 复用用户不存在错误码。
屏蔽(用户端)
PUT /blocks/{blockedUserId} # 屏蔽
DELETE /blocks/{blockedUserId} # 解除(幂等)
GET /blocks/me # 我的屏蔽列表(page/size)
- 屏蔽自己 →
400/9608;PUT 已存在返回 200 不新增;DELETE 不存在返回 200 不报错。 - 屏蔽仅影响站内信:禁止与对方新建 DIRECT 会话、禁止向对方发送新消息(
403/2002,模糊提示“message cannot be sent”);历史消息仍可读,且不影响系统通知、投递、Offer、面试、评分、评价。
ADMIN 举报管理
GET /admin/reports # 举报列表(status/targetType/category/page/size)
GET /admin/reports/{id} # 举报详情
POST /admin/reports/{id}/decision # 处理决定
- 仅 ADMIN;COMPANY/STUDENT/未登录分别
403/403/401。 decision仅RESOLVED/REJECTED;REJECTED 必填note;重复决定 →409/9604;非法流转 →409/9605。- 处理不自动封禁、不删除消息、不修改投递/面试/评分状态;处理后向举报人发送一条
REPORT_DECISION通知。
脱敏:所有举报/屏蔽响应均不返回 phone、email、passwordHash、resumeUrl、JWT、消息全文;MESSAGE 举报仅返回脱敏 messagePreview;举报列表不返回举报人内部 ID。
- API 详见 docs/report-block-api.md。
20. 统一求职进度聚合(Career Progress)联调
学生端「求职进度」页的只读聚合接口,一次返回投递分页、岗位摘要、下一场面试、最近已提交结构化面试评分、可展示 Offer、同岗位竞争力摘要,以及待办/通知计数:
GET /profile/me/career-progress?page=0&size=20&status=INTERVIEW # 仅 STUDENT;page/size/status 均可选
- 权限矩阵:
- 未登录 →
401(安全过滤器) COMPANY/ADMIN→403/2002page<0、size<1、size>50、非法status→400/1001- 身份只取 JWT,不接受
userId/studentUserId/candidateId参数
- 未登录 →
- 查询参数:
page(默认 0,≥0)、size(默认 20,1–50)、status(可选,仅SUBMITTED/VIEWED/INTERVIEW/HIRED/REJECTED/WITHDRAWN)。 - 响应结构(示例,字段为 camelCase):
{
"todo": {
"pendingInterviewResponseCount": 1,
"upcomingConfirmedInterviewCount": 2,
"pendingOfferResponseCount": 1,
"unreadNotificationCount": 3,
"unreadReportDecisionCount": 1
},
"applications": {
"items": [
{
"applicationId": 31, "applicationStatus": "INTERVIEW",
"appliedAt": "2026-08-20T10:00:00", "updatedAt": "2026-08-25T12:00:00",
"job": { "jobId": 9, "title": "Java 后端工程师", "companyName": "示例科技", "city": "杭州", "employmentType": "FULL_TIME", "jobStatus": "PUBLISHED" },
"nextInterview": { "interviewId": 23, "roundNo": 2, "title": "技术二面", "interviewType": "ONLINE", "startAt": "2026-08-30T14:00:00", "endAt": "2026-08-30T15:00:00", "status": "CONFIRMED" },
"latestSubmittedInterviewScore": { "interviewId": 22, "overallScore": 3.60, "recommendation": "HIRE", "submittedAt": "2026-08-25T16:00:00" },
"offer": { "offerId": 14, "status": "SENT", "title": "Java 后端工程师", "expireAt": "2026-09-05T23:59:59", "onboardingDate": "2026-09-15" },
"competition": { "eligible": true, "rank": 3, "cohortSize": 18, "overallScore": 77.50, "percentile": 82.35, "status": "PARTIAL", "mySnapshotPeriod": "2026-08", "rulesVersion": "candidate-scoring-v0.1" }
}
],
"total": 8, "page": 0, "size": 20, "totalPages": 1
},
"generatedAt": "2026-08-27T10:00:00Z"
}
- 空数据:无投递 →
items=[]、total=0、todo全 0;无下一场面试/无已提交评分/无可展示 Offer 时,相应对象为null(JSON 省略)。 - competition:
- 有效投递(
SUBMITTED/VIEWED/INTERVIEW/HIRED)eligible=true;有评分快照才返回rank/overallScore/percentile/mySnapshotPeriod,无快照则这些为null但仍返回cohortSize。 WITHDRAWN/REJECTED投递保留在历史,但eligible=false且不返回rank/cohortSize/overallScore/percentile/status/mySnapshotPeriod/rulesVersion。- 复用 V32 同岗位排名的同一套 cohort/排序/百分位口径,但不返回
dimensions/gaps/topCandidates。
- 有效投递(
- Todo 口径:
pendingInterviewResponseCount=状态SCHEDULED面试数;upcomingConfirmedInterviewCount=状态CONFIRMED且startAt>now;pendingOfferResponseCount=状态SENT/NEGOTIATING且expireAt>now;unreadNotificationCount=未读通知总数;unreadReportDecisionCount=未读REPORT_DECISION通知数。 - 脱敏:不返回
studentUserId/candidateId/companyUserId/evaluatorUserId/phone/email/resumeUrl/passwordHash/JWT、通知标题/正文、评分comment、Offer 薪资/条款、他人任何身份字段。本接口只返回通知计数,通知详情仍用/notifications。 - 只读:
GET,不写任何业务表;size上限 50,聚合查询次数与单页条数无关(无 N+1)。 - API 详见 docs/career-progress-api.md。
21. RIASEC 职业兴趣测评(Assessment)联调
基于 RIASEC 理论的自研题库(72 道),仅供职业兴趣探索参考,不构成心理或职业诊断。结果不写入求职者评分引擎。
接口(身份一律取 JWT,不接受 userId 参数)
GET /assessments/types # 测评类型列表(任意登录用户)
GET /assessments/HOLLAND_RIASEC/questions # 题目列表,仅 ACTIVE(隐藏 dimension/reverseScored/source/版本/questionCode)
POST /assessments/attempts # 创建答卷(六维各抽 5 题共 30 道)【仅 STUDENT,须 ACTIVE】
GET /assessments/attempts/{attemptId} # 答卷详情(固定题目顺序+已答)
PATCH /assessments/attempts/{attemptId}/answers # 保存答案(value 1-5,同请求内同题不可重复,upsert)
POST /assessments/attempts/{attemptId}/submit # 提交(30 题全答)返回结果
GET /assessments/attempts/{attemptId}/result # 结果(六维聚合分+排名+探索方向)
GET /assessments/me?page=0&size=20 # 我的测评历史
要点
- 类型 ACTIVE:题目列表与创建答卷均要求类型为 ACTIVE;INACTIVE/不存在统一
404/9701。 - 题目响应:不返回任何可推导维度或内部编码的字段,
dimension/reverseScored/source/questionVersion/questionCode一律不下发;前端只应使用id/stem/scaleType。 - 创建答卷:返回 30 道题及
version;每次创建随机抽题并持久化,刷新页面 / 再次 GET 不重新抽题。六维各恰好 5 题、总数严格 30;题库任一维度不足 5 道时拒绝创建。 - 保存答案:请求带
version(乐观锁),并发/版本过期 →409/9707;题目不属于本答卷 →400/9705;同一请求内同题重复出现 →400/9705;同一题跨请求重复保存为 upsert。首次保存后状态变为IN_PROGRESS。 - 提交:answeredCount<30 →
400/9706;30 题提交生成结果并写ASSESSMENT_ATTEMPT_SUBMITTED审计;重复提交幂等返回原结果、不重复生成/审计(原子流转 +attempt_id唯一约束,并发提交只生成一条结果/一条审计);不写入candidate_dimension_scores。 - 越权:答卷题目、已保存答案与结果均仅本人 STUDENT 可读;COMPANY/ADMIN 越权读取一律
403/2002。 - 状态机:DRAFT → IN_PROGRESS → COMPLETED;COMPLETED 不可改;同一学生同一类型最多一份活跃答卷(
409/9708,由active_key唯一约束并发原子保证);并发创建竞争失败返回409/9708(不返回 500);同一idempotencyKey并发重放返回原 attempt(幂等 200);完成后允许重新测评。 - 计分:
overallScore六维按percentage = raw/25×100(HALF_UP scale=2)降序,同分按维度字母升序;前三组成primaryCode。 - 结果脱敏:不返回
studentUserId/phone/email/password/原始答案/企业信息/他人信息;探索方向使用"可能感兴趣的职业探索方向"文案,不使用"适合/精准/最适合/诊断"表述。 - 探索方向:
career-direction-v0.1,精确三字母优先,未配置按前三个维度的兜底方向。 - 错误码:9701(404)/9702(404)/9703(409)/9705(400)/9706(400)/9707(409)/9708(409)/9709(409)。
- 数据表:新增
assessment_definitions / assessment_questions / assessment_attempts / assessment_attempt_questions / assessment_answers / assessment_results6 张表(V34 迁移)。 - API 详见 docs/riasec-assessment-api.md。
22. 四维能力测评(Ability Assessment)联调
功能版本 V36。学生四维能力自评(逻辑 / 沟通 / 执行 / 学习),每次按每维抽 6 题共 24 题,限时 30 分钟。企业端不可查看学生测评结果;本版不写入候选评分引擎。API 详见 docs/ability-assessment-api.md。
22.1 接口(7 个)
GET /abilities/types 类型列表(任意登录用户,仅 ACTIVE)
POST /abilities/attempts 创建答卷(仅 STUDENT)
GET /abilities/attempts/{attemptId} 答卷详情/恢复(仅本人)
PATCH /abilities/attempts/{attemptId}/answers 保存/修改答案 upsert(仅本人)
POST /abilities/attempts/{attemptId}/submit 提交(24/24 且四维各 6,仅本人)
GET /abilities/attempts/{attemptId}/result 结果与每题解析(仅本人)
GET /abilities/me?page=0&size=20 我的历史分页
- 不提供
GET /abilities/{type}/questions;题目只在人类创建答卷后随GET /abilities/attempts/{attemptId}返回。
22.2 创建与恢复
POST /abilities/attempts传{typeCode:"WORKPLACE_ABILITY", idempotencyKey?};同幂等键重放返回原答卷,不重抽题。- 创建仅进入
DRAFT,startedAt/expiresAt为空、remainingSeconds=null,不开始计时。 - 同学生同类型同时至多存在一份
DRAFT/IN_PROGRESS活跃答卷;再创建返回9808。 - 刷新或重进页面,用
GET /abilities/attempts/{attemptId}恢复:题目顺序固定、返回已选selectedOptionId,不重新抽题。 - 返回字段:
attemptId/status/answeredCount/totalQuestions(24)/completionRate/startedAt/expiresAt/remainingSeconds/questions[{attemptQuestionId,position,itemType,stem,options[{optionId,text}],selectedOptionId}]。
22.3 首次保存启动 30 分钟
- 第 1 个有效答案保存时一次性设置
startedAt与expiresAt=startedAt+30分钟,status转IN_PROGRESS并返回remainingSeconds。 - 后续保存不延长倒计时。同一 PATCH 内重复
attemptQuestionId或非法optionId→ 整体拒绝9805,不启动计时。 - 超时后保存/提交一律按
9803拒绝;读取时自动把超时答卷标为EXPIRED并释放活跃占位(active_key置空)。
22.4 提交条件
- 必须 24/24 且四维每维各 6 答满才能提交,否则
9806(产品「完成率 ≥70%」仅示意,不做提交门槛)。 - 提交原子推进
DRAFT/IN_PROGRESS→COMPLETED;重复/并发提交幂等返回原结果,只生成 1 条结果与 1 条提交审计。
22.5 7 天有效期 / 9810 冷却 / 9811 90 天限制
- 提交生成结果:
validUntil=completedAt+7天;validityStatus取VALID/EXPIRED(动态判断)。 - 有效结果存续期内禁止重测 →
POST /abilities/attempts返回9810(409)。 - 滚动 90 天内最多完成 3 次;达上限 →
9811(429)。EXPIRED超时答卷不计入完成次数。 - 历史答卷与结果永久保留,可随时分页查询
GET /abilities/me。
22.6 结果字段(白名单,前端可直接展示)
dimensionScores[{code,score,answeredCount}](LOGIC/COMMUNICATION/EXECUTION/LEARNING 百分制)、overallScore、weaknesses[{code,score}](并列最低维度全返回)、radar[{code,value}]、improvementAreas[{dimension,currentScore,summary,actions}]、questionFeedback[{attemptQuestionId,position,stem,selectedOptionId,awardedScore,maxScore,explanation}]、validityStatus/validUntil/answeredCount/completionRate/questionSetVersion/rulesVersion/generatedAt/disclaimer。improvementAreas只含dimension/currentScore/summary/actions,不返回competencyTag。
22.7 脱敏字段(禁止向学生/前端返回)
- 题目与结果均不得返回:
questionCode、competencyTag、answerKey、scoringRule、source、difficulty、discrimination,以及studentUserId/phone/email/password等身份字段。 questionFeedback允许题干、用户所选项、得分与解析文本,但绝不返回正确选项字母或完整评分规则。
22.8 错误码(V36,98xx)
| 码 | 含义 | HTTP |
|---|---|---|
| 9801 | 类型不存在/未启用 | 404 |
| 9802 | 答卷不存在 | 404 |
| 9803 | 答卷状态非法(含超时/已提交) | 409 |
| 9805 | 答案非法(跨答卷/同题重复/非法选项) | 400 |
| 9806 | 未答满 24/24 或某维不足 | 400 |
| 9807 | 并发保存冲突 | 409 |
| 9808 | 已有活跃答卷 | 409 |
| 9809 | 幂等冲突 | 409 |
| 9810 | 有效结果冷却期内不可重测 | 409 |
| 9811 | 90 天完成次数达上限 | 429 |
| 复用 | 1001 参数/业务非法、2001 未登录、2002 无权限 | 400/401/403 |
22.9 前端注意
- 学生每次答卷题目固定顺序且数量恒为 24;恢复页只渲染
questions即可,勿依赖外部题库接口。 - 计时用后端
remainingSeconds展示(创建阶段为 null);到期后结果不生成,提示重测需先创建新答卷。 - 结果页必须展示
disclaimer文案(自我了解与提升参考,不构成职业资格/招聘结论/永久标签)。
23. 防刷风险概览与运营规则配置(Risk Governance)联调
功能版本 V37。仅 ADMIN 使用,把既有拒绝型安全审计(
REPORT_RATE_LIMITED/BLOCKED_SEND_PROHIBITED)转为可聚合的风险事件并支持规则配置与人工标记。不自动封禁/限流,不改动用户、投递、消息、评分状态。API 详见 docs/risk-governance-api.md。
23.1 接口(7 个,仅 ADMIN)
GET /admin/scoring/risk/overview 风险概览(OPEN 总数/等级分布/规则分布/最近检测)
GET /admin/scoring/risk/trend?bucket=HOUR|DAY 风险趋势(SQL 聚合)
GET /admin/scoring/risk/distribution 按规则×等级分布
GET /admin/scoring/risk/events 事件分页(status/severity/ruleCode/from/to)
GET /admin/scoring/risk/rules 规则列表
PUT /admin/scoring/risk/rules/{ruleCode} 更新规则(expectedVersion 乐观锁)
POST /admin/scoring/risk/events/{eventId}/actions 人工标记(ACKNOWLEDGE/DISMISS)
- 身份一律 JWT/SecurityContext;不接受
adminUserId/operatorUserId/userId参数。 - 未登录
401;STUDENT/COMPANY403/2002;仅 ADMIN 可访问。
23.2 时间与分页
- 默认时间范围近 7 天,最大 90 天;
from>to或超范围 →9906。 page从 0 起、size1–50;trend.bucket仅HOUR/DAY。
23.3 规则配置
- 更新请求:
{"enabled":true,"thresholdCount":3,"windowMinutes":15,"severity":"MEDIUM","expectedVersion":2}。 - 不允许修改
ruleCode/sourceAction;expectedVersion不匹配 →9903(409)。 - 规则只影响后续扫描;禁用规则对审计照常越过 checkpoint,重新启用不回放历史。
23.4 人工标记与状态机
- 事件状态机:
OPEN→ACKNOWLEDGED、OPEN→DISMISSED、ACKNOWLEDGED→DISMISSED;DISMISSED终态。 - 操作请求:
{"action":"ACKNOWLEDGE","reasonCode":"MONITORING","expectedVersion":0}。 reasonCode仅MONITORING/CONFIRMED_FALSE_POSITIVE/OUT_OF_BAND_ESCALATED,无自由文本备注。- 冲突/非法流转 →
9904/9905(409)。
23.5 响应脱敏(ADMIN 可见字段白名单)
- 事件响应仅:
eventId/ruleCode/sourceAction/severity/status/occurrenceCount/windowStartedAt/windowEndedAt/detectedAt/version。 - 严禁返回:
subjectUserId、手机号、邮箱、ipAddress、userAgent、details、requestId、token/password/secret 等。运营仅用eventId定位,不做自动处置。
23.6 错误码(V37,99xx)
| 码 | 含义 | HTTP |
|---|---|---|
| 9901 | 规则不存在 | 404 |
| 9902 | 事件不存在 | 404 |
| 9903 | 规则并发更新 | 409 |
| 9904 | 事件并发操作 | 409 |
| 9905 | 非法 action/reasonCode/流转 | 409 |
| 9906 | 非法筛选(时间/bucket/severity/status) | 400 |
| 复用 | 2001 未登录、2002 无权限、1001 分页/参数非法 | 401/403/400 |
23.7 前端注意
- 风险入口仅在管理后台;学生/企业端无任何风险接口。
- 规则/阈值的命中可能不会立刻出现:扫描默认每 60 秒跑一次(
app.risk.scan.fixed-delay-ms)。 - 概览/趋势/分布由后端 SQL 聚合返回,前端无需额外拉取明细。
24. V40 企业评价管理与官方回复
验收状态:真实 MySQL/Docker 黑盒已通过(
PASS=62 FAIL=0 SKIP=0);OpenAPI 159/196,待 Git 检查点。
V41 企业人才发现
新增接口详见 enterprise-talent-discovery-api.md。学生先通过 /profile/me/talent-discovery 显式开启授权;企业仅能查询已认证企业自己的已发布岗位,岗位需具备 V39 审核通过且启用的画像。候选卡只返回不透明 candidateKey、匿名标签、城市、学历、毕业年份、白名单技能、匹配档位、固定概述和可用信号类别,不返回学生身份、联系方式、简历或任何测评分数。
V41 服务端先对全部合格候选计算并全局排序,再切分页;fitScore 不下发。无可用岗位画像返回 UNAVAILABLE,无候选返回 NO_ELIGIBLE_CANDIDATE。OpenAPI 实际为 161 paths / 199 operations(学生 GET/PUT 共用一个 path)。
V42 企业人才邀约与授权沟通
V42 新增 6 个 path / 6 个 operation,静态 OpenAPI 计数为 167 paths / 205 operations。已认证企业只能对本公司 PUBLISHED 且画像 enabled=true && APPROVED 的岗位使用 V41 candidateKey 创建邀约;请求只提交 candidateKey、站内 message(1–300 字)和 UUID idempotencyKey,不得提交学生 ID。服务端会重新校验授权、账号状态、当前企业全部历史投递、有效推荐信号和每日限额。
验收状态:自动化测试 1475/1475 通过;真实 MySQL/Docker 黑盒通过(
PASS=76 / FAIL=0),运行时与静态 OpenAPI 均为 167 paths / 205 operations。
学生通过 /profile/me/talent-invitations 查看邀约,详情中的 version 必须原样带入决策请求的 expectedVersion。只有 PENDING 可 ACCEPT 或 REJECT;接受成功才返回 conversationId 并建立受限 TALENT_INVITATION 会话,拒绝或过期不建会话。邀约有效期 7 天,终态后同企业同学生冷却 30 天;重复幂等请求返回原邀约,不重复占用额度。
企业响应不包含 candidateKey、studentUserId、测评/评分详情、联系方式或内部原因;邀约会话的会话、消息列表和发送响应只返回 CURRENT_USER/OTHER_PARTICIPANT 相对身份,不暴露 createdBy/senderUserId。V42 不写 candidate_dimension_scores,不发送通知。
验收状态:自动化测试 1461/1461 通过;真实 MySQL/Docker 黑盒已通过(
PASS=37 / FAIL=0)。黑盒确认最新低分测评优先于历史高分、同企业任一历史投递均排除、其他企业投递不误排,并验证全局排序在分页之前完成。
24.1 接口
GET /companies/me/job-reviews
GET /companies/me/job-reviews/{reviewId}
POST /companies/me/job-reviews/{reviewId}/reply
PUT /companies/me/job-reviews/{reviewId}/reply
GET /admin/review-replies/pending
POST /admin/review-replies/{replyId}/decision
企业接口只返回当前登录企业自有岗位评价;身份从 JWT 获取,不传 companyId、studentUserId。STUDENT/ADMIN 为 403/2002,未登录为 401,跨企业访问不泄露评价是否存在。
24.2 回复状态与操作
- 创建回复正文 1–500 字符,
idempotencyKey必须为 UUID(最长 64);安全文本为AUTO_APPROVED,风险文本为PENDING。 - 同评价最多一个当前回复;同幂等键同评价同正文重放返回原回复,内容不一致为
409/10106,已有回复为409/10102。 - 只有
REJECTED回复可以 PUT 修订,必须携带expectedVersion;AUTO_APPROVED、APPROVED、PENDING不可覆盖或删除。版本冲突为409/10104。 - ADMIN 仅可对
PENDING执行APPROVE或REJECT;驳回时reason必填(1–500 字符)。重复决策为409/10103,非法决策为400/10107。
企业列表/详情的 reply 只包含 id/content/status/version/updatedAt/rejectionReason。禁止依赖 studentUserId、学生姓名、联系方式、IP/UA、moderationScore、内部 reasonCodes 或管理员 ID。
24.3 公开评价与官方回复
既有 /jobs/{jobId}/reviews 与 /jobs/{jobId}/reviews/{reviewId} 的评价载荷不再包含 studentUserId,可显示固定 reviewerLabel="匿名求职者"。仅公开评价且回复状态为 AUTO_APPROVED/APPROVED 时显示:
{"officialReply":{"content":"感谢反馈。","repliedAt":"2026-09-04T10:05:00"}}
officialReply 不含回复 ID、公司 ID、审核状态、版本、原因码或审核人信息;PENDING/REJECTED 不渲染该区域。前端应兼容字段缺省或 null。
24.4 管理员页面
待审列表可按公司、岗位、风险等级和时间筛选;管理员响应中的风险等级/原因码只用于后台审核,不得透传到公开评价。V40 不提供回复通知、统计、申诉或公开历史接口。
24.5 错误码
10101 回复不存在(404)、10102 已有回复(409)、10103 状态非法(409)、10104 并发冲突(409)、10105 正文/幂等键非法(400)、10106 幂等冲突(409)、10107 决策非法(400)。
24. V39 测评驱动岗位推荐(编码中)
- 开关
RECOMMENDATION_ASSESSMENT_ENABLED默认false:关闭时GET /profile/me/job-recommendations仅返回mode=FALLBACK的最新公开岗位,不展示伪造的测评匹配分。 - 开启后按学生偏好 40%、RIASEC 聚合分 35%、四维能力聚合分 25% 做实时规则计算;缺少任一数据源时对可用项重归一化。推荐不读取题目、答案或候选评分快照。
- 新增公开
GET /job-recommendation-taxonomies;企业通过GET/PUT /companies/me/jobs/{jobId}/recommendation-profile选择平台受控行业/岗位族并填写画像。更新启用画像后状态为PENDING_REVIEW,仅 ADMIN 审核通过的启用画像参与个性化推荐。 - 学生接口:
GET /profile/me/job-recommendations?page&size;POST /profile/me/job-recommendations/{jobId}/feedback;DELETE .../feedback?expectedVersion=。NOT_INTERESTED会持久化排除该岗位;解释反馈不影响首版分数。 - ADMIN:
GET /admin/job-recommendation-profiles、POST /admin/job-recommendation-profiles/{jobId}/decision。企业端不得取得任何学生分数或推荐理由;学生端不得取得画像权重、测评结果 ID 或题库字段。
25. V43 候选评分公平性与稳定性监测
V43 仅供管理员后台使用,前端不得向学生或企业展示。当前状态为版本内补集定向修复完成、待独立复验;总览和申诉使用最终 SQL 聚合,并统一执行独立候选人数与补集抑制。drift 和 ranking-stability 在当前没有不可变评分历史与排名快照时返回 availability=UNAVAILABLE,页面应显示“暂无可比历史数据”。
26. N02 候选评分规则解释与合理性
学生评分卡和单维详情在保持原有路径不变的前提下增加固定解释字段。综合卡新增 explanationVersion=candidate-scoring-explanation-v1、coverage(实际参与 overallScore 的维度及排除原因)和 comparabilityNotice;单维详情新增中性 displayName、whatItMeasures、interpretation、limitations、evidenceLevel、ruleContributions、improvementActions、explanationVersion、validity(仅 ABILITY)和 calculatedAt。
ruleContributions 与真实评分分支同源,贡献之和可对账最终维度分数;固定模板不使用 AI/LLM/SHAP。dataSources 只显示“个人教育资料”“求职偏好”“平台求职活动”“简历与技能资料”“Offer 履约与候选反馈”“四维能力测评”等产品语义名称,不展示表名、字段路径、题目、答案、答案键或评分规则。INTEGRITY、GROWTH、STABILITY、COMPLIANCE、ACTIVITY 等信号均明确为平台资料/行为代理,不代表人格、信用、成长潜力、服从性、工作积极性或录用结论。
企业评分接口 /company/candidates/{applicationId}/scoring 的字段和 JSON 结构保持不变,企业不返回任何解释字段、行为细分、规则贡献、改进建议或 ABILITY 四维详情。N02 不新增 Flyway、表、ErrorCode、公网路径、消息或审计动作,也不改变分数、权重、排名、推荐、申诉和快照写入语义。
可用接口、参数和小样本规则见 candidate-scoring-fairness-api.md。前端不得根据城市、学校、姓名等字段创建敏感人群筛选,也不得尝试通过多次小样本查询反推出候选人。
总览、维度和申诉响应都显式返回 availability/reason。状态/版本/回滚原因及分布桶均按独立候选人数计算;appealCount 仅表示事件数。SUPPRESSED 时不得用总体计数、补集或其它接口字段反推出被隐藏比例,NO_DATA、INSUFFICIENT_SAMPLE、SUPPRESSED 均不得渲染为 NORMAL。