You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

7.6 KiB

结构化面试评分 API(V31)

本文档描述「结构化面试评分」模块。这与候选人反馈(candidate_feedback)是两套完全独立的机制,请勿混淆:

  • candidate_feedback / Candidate Scoring Engine:既有的候选人反馈与评分治理模块,语义保持不变。
  • 结构化面试评分:企业针对某一场**已完成(COMPLETED)**的面试,按固定五个维度填写并提交评分。独立落库到 interview_scores / interview_score_history 两张新表,不写 candidate_feedback。

只支持单面试官评分模型(一场面试一条评分);不新增多面试官模型。

OpenAPI 路径总数 113 → 116(3 个 path、4 个 operation)。


一、接口总览

操作 路径 说明
PUT /interviews/{interviewId}/score 企业 owner 保存评分草稿(幂等更新)
GET /interviews/{interviewId}/score 查看评分(企业见 DRAFT/SUBMITTED;学生仅 SUBMITTED)
POST /interviews/{interviewId}/score/submit 提交草稿 → SUBMITTED(不可逆)
GET /interviews/{interviewId}/score/history 评分快照历史(无记录返回 200 空数组)

身份与权限

  • 所有接口都需要登录(JWT);身份只从 JWT/SecurityContext 获取,不接受 userId/studentUserId/companyId 参数。
  • 归属校验:companyRepository.findByOwnerUserId(userId) 解析企业;interview.companyUserId == company.ownerUserId 且经由 application→job 的 job.companyId == company.id 且 application.studentUserId == interview.studentUserId,否则 403/8402。
  • 企业 owner(COMPANY):可写可读草稿与已提交。
  • 目标学生(STUDENT):只能查看**已提交(SUBMITTED)**的评分;查看 DRAFT 或他人评分返回 403/8402。
  • ADMIN:不允许访问(403/8402)。
  • 面试不存在 404/8401。

二、保存评分草稿

PUT /interviews/{interviewId}/score

请求体:

{
  "technicalScore": null,
  "communicationScore": 4,
  "problemSolvingScore": 3,
  "roleFitScore": null,
  "professionalismScore": 5,
  "recommendation": "HIRE",
  "comment": "综合素质较好"
}

字段:

  • 五个维度 technicalScore / communicationScore / problemSolvingScore / roleFitScore / professionalismScore,取值 1-5;草稿阶段可留空(null)。
  • recommendation(可选):STRONG_HIRE / HIRE / NEUTRAL / NO_HIRE,企业手动选择,不由 overallScore 推导;非法值 400/1001。
  • comment(可选):≤2000 字符。
  • overallScore:DTO 中不提供该字段,由服务端计算并忽略客户端传入值。

约束:

  • 无评分时创建 DRAFT;已有 DRAFT 时更新同一行(@Version 递增)。
  • SUBMITTED 后再次 PUT → 409/9503。
  • 保存草稿不写历史节点(历史仅写 NULL→DRAFT、DRAFT→SUBMITTED 两次),但写审计日志 INTERVIEW_SCORE_SAVED。
  • 并发冲突 → 409/9501。

成功响应(脱敏):

{
  "code": 200,
  "message": "ok",
  "data": {
    "id": 1,
    "interviewId": 10,
    "applicationId": 20,
    "status": "DRAFT",
    "technicalScore": null,
    "communicationScore": 4,
    "problemSolvingScore": 3,
    "roleFitScore": null,
    "professionalismScore": 5,
    "overallScore": null,
    "recommendation": "HIRE",
    "comment": "综合素质较好",
    "submittedAt": null,
    "version": 1,
    "createdAt": "2026-08-26T10:00:00",
    "updatedAt": "2026-08-26T10:00:00"
  }
}

脱敏:响应不返回 companyUserId / studentUserId / evaluatorUserId / 手机号 / 邮箱。


三、查看评分

GET /interviews/{interviewId}/score

  • 企业 owner:可看 DRAFT 与 SUBMITTED。
  • 目标学生:仅可看 SUBMITTED;查看 DRAFT → 403/8402。
  • 无评分 → 404/9504。
  • 面试未完成 → 409/9502。

四、提交评分

POST /interviews/{interviewId}/score/submit

无请求体。将 DRAFT 提交为 SUBMITTED:

  • 五个维度必须全部非空,否则 400/1001。
  • overallScore 由服务端计算:(t + c + ps + rf + p) / 5,BigDecimal,保留两位小数,RoundingMode.HALF_UP。
  • 提交成功后 submittedAt 落库;不可逆。
  • 写历史节点 DRAFT→SUBMITTED;写审计日志 INTERVIEW_SCORE_SUBMITTED。
  • 幂等:重复 submit 已 SUBMITTED → 200 返回原评分;不新增历史、不重复通知。
  • 通知:仅通知目标学生一次,businessType=INTERVIEW、businessId=interviewId、幂等键 interview-score-submitted-{interviewId};草稿不发通知;不通知企业自身;无 Push。
  • 并发冲突 → 409/9501。

五、查看评分历史

GET /interviews/{interviewId}/score/history

  • 企业 owner:可查看全部历史。
  • 目标学生:仅在评分 SUBMITTED 后可查看。
  • 无历史记录 → 200 空数组 items=[]。
  • 历史快照 snapshot 为解析后的维度分 + overallScore(JSON 字符串落库于 snapshot_text TEXT 字段,不返回内部用户标识)。

响应示例:

{
  "code": 200,
  "message": "ok",
  "data": [
    {
      "id": 1,
      "scoreId": 1,
      "interviewId": 10,
      "fromStatus": null,
      "toStatus": "DRAFT",
      "actorRole": "COMPANY",
      "snapshot": { "technicalScore": null, "overallScore": null },
      "createdAt": "2026-08-26T10:00:00"
    },
    {
      "id": 2,
      "scoreId": 1,
      "interviewId": 10,
      "fromStatus": "DRAFT",
      "toStatus": "SUBMITTED",
      "actorRole": "COMPANY",
      "snapshot": { "technicalScore": 4, "overallScore": 4.20 },
      "createdAt": "2026-08-26T10:05:00"
    }
  ]
}

脱敏:历史条目不返回 actorUserId、手机号、邮箱。


六、状态机与历史

无评分 → DRAFT → SUBMITTED
  • PUT 无评分 → 创建 DRAFT(写历史 NULL→DRAFT)。
  • PUT 已有 DRAFT → 更新同一行,不写历史,写审计 INTERVIEW_SCORE_SAVED。
  • POST submit:DRAFT → SUBMITTED(写历史 DRAFT→SUBMITTED,写审计 INTERVIEW_SCORE_SUBMITTED)。
  • SUBMITTED 不可逆;重复 submit 幂等。
  • 草稿每次 PUT version 递增;@Version 乐观锁防并发。

七、错误码

错误码 含义 HTTP
9501 并发冲突(更新或首次并发创建撞唯一约束) 409
9502 面试未完成(非 COMPLETED),不可评分 409
9503 评分已提交,不可再修改 409
9504 评分不存在 404
8401 面试不存在 404
8402 归属校验失败 / 越权访问 403
2002 非 COMPANY(对写入),未授权访问 403
1001 参数校验失败(非 1-5 维度、非法推荐值、提交缺维度) 400

八、数据库(V31)

新增两张表,均 ENGINE=InnoDB CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci:

  • interview_scores:interview_id 唯一;五个评分字段允许 NULL 且在 1-5;overall_score 1.00-5.00;status CHECK(DRAFT/SUBMITTED);recommendation CHECK;SUBMITTED 时五维、overallScore、submittedAt 均非空;@Version;外键 ON DELETE CASCADE。
  • interview_score_history:snapshot_text TEXT(不使用 JSON 类型,避免 MySQL/H2 兼容问题);外键 ON DELETE CASCADE。

不修改 V1-V30、不修改 CandidateFeedback、不修改 Interview 状态机、不修改 Candidate Scoring Engine、不修改个人数据导出/账号注销/隐私设置。


九、文档与验证

  • OpenAPI 路径数 113 → 116;docs/openapi-rc.json 已同步。
  • 验证:python docs/verify_openapi.py;项目 mvn test、mvn clean verify。