6.9 KiB
RIASEC 职业兴趣测评 API(功能版本 V35,Flyway V34__riasec_assessment.sql)
基于 RIASEC 理论的自研题库,仅供职业兴趣探索参考,不构成心理或职业诊断。
- 题库:72 道(R/I/A/S/E/C 各 12),SELF_AUTHORED,reverseScored 恒为 false
- 每次答卷:后端从六维中各随机抽取 5 道,共 30 道并持久化,刷新页面不重新抽题
- 量表:LIKERT_5 = 1 完全不感兴趣 … 5 非常感兴趣
- 计分:rawScore = 5 道之和,maxScore = 25,percentage = rawScore/25×100(HALF_UP, scale=2)
- 排序:percentage DESC,同分按维度字母升序;前三位组成
primaryCode - 结果:仅 RIASEC 六维聚合分 + 排名 + 探索方向,不存储原始答案
- 探索方向:career-direction-v0.1,仅"可能感兴趣的职业探索方向"
- 只读/脱敏:不写 candidate_dimension_scores;结果/审计不含原始题面、答案、手机号、邮箱、JWT
权限
| 接口 | STUDENT | COMPANY | ADMIN | 未登录 |
|---|---|---|---|---|
| 测评类型 GET /assessments/types | 200 | 200 | 200 | 401 |
| 题目 GET /assessments/{type}/questions | 200 | 200 | 200 | 401 |
| 创建答卷 POST /assessments/attempts | 200 | 403 | 403 | 401 |
| 答卷详情/保存/提交/结果 | 仅本人 | 403 | 403 | 401 |
| 我的历史 GET /assessments/me | 仅本人 | 403 | 403 | 401 |
身份一律从 JWT 获取,不接受 userId 参数。
错误码(97xx)
| code | 含义 | HTTP |
|---|---|---|
| 9701 | ASSESSMENT_TYPE_NOT_FOUND | 404 |
| 9702 | ASSESSMENT_ATTEMPT_NOT_FOUND | 404 |
| 9703 | ASSESSMENT_ATTEMPT_INVALID_STATE | 409 |
| 9705 | ASSESSMENT_ANSWER_INVALID | 400 |
| 9706 | ASSESSMENT_INCOMPLETE | 400 |
| 9707 | ASSESSMENT_CONCURRENT_UPDATE | 409 |
| 9708 | ASSESSMENT_DUPLICATE_ACTIVE | 409 |
| 9709 | ASSESSMENT_IDEMPOTENCY_CONFLICT | 409 |
重复提交不抛错,直接幂等返回成功(9704 预留,未使用)。
接口
GET /api/v1/assessments/types
返回全部 ACTIVE 测评定义(typeCode、displayName、description、instructions、scaleType、questionCount=72、attemptQuestionCount=30、minPerDimension=5、questionSetVersion、rulesVersion)。
GET /api/v1/assessments/HOLLAND_RIASEC/questions
返回指定测评的 ACTIVE 题目(按 position;类型非 ACTIVE 或不存在 → 404/9701)。对外隐藏 dimension、reverseScored、source、questionVersion、questionCode,仅返回:
{ "id": 301, "position": 1, "stem": "我对使用手工工具制作物品感兴趣。", "scaleType": "LIKERT_5" }
不返回 questionCode:题目内部编码(如 R01)前缀携带维度信息,一律不外露,前端不得用于推导 R/I/A/S/E/C 维度;维度归属仅由服务端计分使用。
POST /api/v1/assessments/attempts
仅 STUDENT 且类型须为 ACTIVE(INACTIVE/不存在 → 404/9701)。请求:{ "typeCode": "HOLLAND_RIASEC", "idempotencyKey": "uuid" }
返回 attemptId、status、totalQuestions(30)、answeredCount、completionRate、questionSetVersion、version、本次 30 道题。不返回 samplingSeed。
抽题严格约束:六维各恰好 5 道、总数严格 30;任一维度 ACTIVE 题不足 5 道或六维不齐 → 拒绝创建(400),不静默减题。
单活跃答卷(并发原子):同一学生同一类型同时最多一份 DRAFT/IN_PROGRESS。由数据库唯一约束 uk_assessment_attempts_active 原子兜底:
- 竞争失败的并发创建请求返回
409/9708(不得返回 500); - 同一
idempotencyKey的并发重放返回已创建的原 attempt(幂等200,同一 attemptId); - 仅数据库靠唯一约束判定归属,服务端不再复用被污染的 Hibernate Session 查询,
写入与冲突判定分别使用独立事务/
REQUIRES_NEW,创建外层显式READ_COMMITTED以读取已提交数据。 - 答完提交(COMPLETED)后释放
active_key,允许重新测评。
GET /api/v1/assessments/attempts/{attemptId}
仅本人。返回 30 道题(固定 position)、已保存答案、answeredCount、completionRate、version、当前状态。
PATCH /api/v1/assessments/attempts/{attemptId}/answers
请求:{ "version": 2, "answers": [ { "questionId": 301, "value": 4 } ] }
支持单题/批量;同题重复保存 upsert;同一请求内同题不得重复出现(重复 → 400/9705);value 1-5;题目必须属于本答卷;版本冲突 409/9707;首次保存后状态置 IN_PROGRESS。
POST /api/v1/assessments/attempts/{attemptId}/submit
- answeredCount < 30 → 400/9706
- 30 题(每维各 5)→ COMPLETED,生成结果,写审计,返回结果
- 重复提交 → 200 返回原结果,不重复生成结果与审计
- 不写入候选人评分表
GET /api/v1/assessments/attempts/{attemptId}/result
答卷 COMPLETED 后返回:
{
"attemptId": 101,
"typeCode": "HOLLAND_RIASEC",
"status": "COMPLETED",
"primaryCode": "SIC",
"primaryType": "S",
"dimensionScores": [ { "dimension": "S", "score": 88.00, "answeredCount": 5 } ],
"typeRanking": ["S", "I", "C", "R", "A", "E"],
"answeredCount": 30,
"completionRate": 100.00,
"questionSetVersion": "riasec-self-v0.2",
"rulesVersion": "riasec-scoring-v0.1",
"generatedAt": "2026-08-28T16:00:00",
"disclaimer": "本结果仅供职业兴趣探索参考,不构成心理或职业诊断。",
"explorationDirections": ["教育辅导", "用户运营", "客户成功", "职业咨询"]
}
不返回 studentUserId、phone、email、password、原始答案、企业信息、其他用户信息。
越权:答卷题目、已保存答案与结果均仅本人(STUDENT)可读;COMPANY / ADMIN 越权访问一律 403/2002。(来源:权限矩阵)
GET /api/v1/assessments/me?page=0&size=20
分页返回当前学生的答卷与结果概要。
数据库(V34__riasec_assessment.sql,新增 6 张表)
assessment_definitions:测评定义(HOLLAND_RIASEC)assessment_questions:72 题,UNIQUE(definition_id, question_code)、UNIQUE(definition_id, position)、CHECK(dimension IN R/I/A/S/E/C)、CHECK(reverse_scored IN 0,1)assessment_attempts:答卷主记录,@Version乐观锁,UNIQUE(student_user_id, idempotency_key)、UNIQUE(active_key)(单活跃原子约束,DRAFT/IN_PROGRESS 非空,终态置 NULL)、CHECK(status IN DRAFT/IN_PROGRESS/COMPLETED/EXPIRED)assessment_attempt_questions:本次抽中的 30 题,UNIQUE(attempt_id, question_id)、UNIQUE(attempt_id, position)assessment_answers:答案,answer_value1-5(CHECK),UNIQUE(attempt_id, question_id)(API 层字段仍为value)assessment_results:结果,attempt_id唯一(幂等)。
dimension_scores_text / type_ranking_text 使用 TEXT 保存 JSON 字符串,兼容 MySQL 8 与 H2 MODE=MySQL。答案列使用 answer_value(避免 H2 保留字 value,该列名在 MySQL 与 H2 MODE=MySQL 下均兼容)。