# 四维能力测评 API 设计文档(V36 设计锁定版) > 状态:**设计已锁定,待编码**。功能版本 V36。题库为自研工作情境题;本版不接入候选评分引擎、不向企业开放结果。题目统计质量门槛保留为后续试测工作,不阻塞本版功能实现。 ## 0. 产品来源摘要(func.xlsx,已确认) - 功能名:**能力测评(4维度) = 逻辑 / 沟通 / 执行 / 学习 能力四维测评**(`功能详细清单 D24/F24`、`求职端功能详情 B24/C24`、`企业端功能详情`)。 - 目标:**了解自己目前的能力短板在哪里**(G24)。 - 结果展示:**四维能力测试 → 能力雷达图 + 短板提示**(F24)。 - 限时:**测评限时 30 分钟,超时强制提交**(G24)。 - 完成率:**测评完成率 ≥ 70%**(H24,中途退出保存进度的判定)。 - 质量:**测评区分度 ≥ 0.4**(H24/H52,为非功能质量门槛)。 - 能力测评评分(F1.2):基于四维得分 → **能力综合分**,「让企业看到我的能力测评结果」;未测评显示「待测评」(G52)。 - 成长信号:学习能力/成长速度潜力分(F55/G55)。 ## 1. 功能范围与非目标 **范围内(第一版 V36)**: - 四维能力测评的题型目录、创建答卷、答题保存、进度恢复、提交、结果查询。 - 结果含四维分、综合分、最低维度短板提示、雷达图数据、题目解析与有效期状态。 **非目标(V36 默认不做,除非负责人确认)**: - 不接入 Candidate Scoring / 不写 `candidate_dimension_scores`(ABILITY 保持 PENDING)。 - 不提供企业查看学生能力测评结果的接口。 - 不做岗位推荐、AI 路径建议、开放题或 AI 自动评分。 - 不做定时器批量改写结果有效期;结果是否过期在读取或新建答卷时由后端按 `valid_until` 动态判断。 ## 2. 四个能力维度的正式名称与编码 | 编码(Code) | 名称 | 说明 | |---|---|---| | LOGIC | 逻辑能力 | 逻辑/推理/分析 | | COMMUNICATION | 沟通能力 | 表达/协作/共情 | | EXECUTION | 执行能力 | 行动/落地/结果导向 | | LEARNING | 学习能力 | 学习/成长/适应 | 编码和结果字段按上表锁定。 ## 3. 题库来源、题目数量、题目版本 - 题目来源:自研(`SELF_AUTHORED`),不复制商用量表。 - 锁定题库文件:`docs/ability-question-bank-v0.1.json`。迁移种子、题库测试和题目版本必须以该文件为唯一权威来源,禁止在 SQL 或 Java 中自行改写题干、选项、答案键、分值或解析。 - 题库总量:每维 12 题,共 **48 题**;每次答卷从每个维度分层随机抽取 6 题,共 **24 题**。 - 题型:单选题、情境判断题、排序题;允许选项部分得分,不采用开放题。 - 题目版本:`ability-v0.1`。每题必须保留 `difficulty`、`discrimination`、`version` 字段;前两项允许在首版种子数据中为空,试测后回填。 ## 4. 是否固定题目或分层随机抽题 - 采用分层随机抽题:从每维 ACTIVE 题库随机抽取 6 题,并把题目集合、顺序、题目版本和题目快照持久化;刷新、恢复和重复读取均不得重新抽题。 - 新建或重测答卷均重新抽取本次 24 题;不得向前端返回内部维度、答案键、评分规则或题目版本。 ## 5. 单次答题数量与完成条件 - 单次题量固定为 4 维 × 6 = **24 题**。 - 必须回答 24/24 才能提交;任何未完成答卷均返回 `ABILITY_INCOMPLETE`,不得依据 70% 生成正式结果。 - 产品“完成率 ≥ 70%”仅用于过程进度展示,不构成提交门槛。 ## 6. 题型与计分规则 - 每题均为后端判分的工作情境题。每个选项对应 0、1、2 或 4 分;选项及其得分、标准答案键、评分规则和解析均只保存在后端。 - 每维原始分为该维 6 题得分之和,维度百分制分数为 `rawScore / 24 × 100`;保留两位小数,使用 `HALF_UP`。 - 综合分为四维百分制得分的等权平均;只有四维均完成 6 题时才生成综合分。 - 短板取最低维度;若最低分并列,返回全部并列维度。结果页同时返回每道题解析,但仅限答卷提交后由本人读取。 ## 7. 中途保存和恢复规则 - 保存答案对 (attempt, question) upsert;同请求内同题重复 → 校验拒绝。 - 获取答卷恢复:固定题目顺序 + 已答值;刷新不重抽题。 - 未答完可退出,下次 GET 恢复;与 RIASEC 一致(active 答卷阻塞重测)。 ## 8. 答卷状态机 锁定状态机: ``` DRAFT → IN_PROGRESS → COMPLETED IN_PROGRESS --(超过 expires_at 且未提交)--> EXPIRED ``` - 创建答卷仅进入 `DRAFT`,不开始计时。 - 第一次成功保存答案时,后端一次性设置 `started_at=now`、`expires_at=started_at+30分钟`,并转为 `IN_PROGRESS`。 - 后续保存、读取、提交均由后端校验 `expires_at`;超时未答完 24 题时,原子标记为 `EXPIRED`,不生成结果、不可继续答题或提交。 - `DRAFT` 没有倒计时;同一学生同一类型始终至多存在一份 `DRAFT/IN_PROGRESS` 活跃答卷。 ## 9. 重测和历史保留规则 - 历史答卷和结果永久保留,不删除。 - 完成时写入 `valid_until = completed_at + 7天`。在 `valid_until` 前为当前有效结果;到期后结果仍可查询,但 `validityStatus=EXPIRED` 且仅作为历史结果。 - 不设置“历史最高分”。当前结果只取最近一次已完成且仍有效的结果,避免用户通过反复答题刷取最高分。 - 有效结果存在期间禁止重测;结果到期后方可创建下一份答卷。 - 每名学生滚动 90 天内最多完成 3 次测评;未完成而超时的 `EXPIRED` 答卷不计入该完成次数。 ## 10. 提交幂等与并发策略 - 唯一键:`(student_user_id, idempotency_key)` 幂等;`active_key`(学生+类型+ACTIVE)保证同类型最多 1 份活跃答卷(复用 V35 已验证方案)。 - 提交原子化:`DRAFT/IN_PROGRESS → COMPLETED` 的批量更新返回行数;仅行数=1 者生成结果+写审计;并发只产生 1 条 result + 1 条提交审计;重复提交幂等返回原结果。 - 提交并发无 500(沿用 V35 REQUIRES_NEW + READ_COMMITTED 修复)。 - 创建时先校验当前有效结果是否存在,再校验 90 天完成次数;两类限制须有对应错误码,不能误报为活跃答卷冲突。 ## 11. 权限矩阵 | 接口 | 未登录 | STUDENT | COMPANY | ADMIN | |---|---|---|---|---| | 类型列表 | 401 | 200 | 200 | 200 | | 创建 / 答卷 / 保存 / 提交 / 结果 / 历史 | 401 | 仅本人 | 403/2002 | 403/2002 | - 身份一律 JWT/SecurityContext,不接受 userId 参数。 - 第一版不提供企业查看能力结果接口;能力综合分写入评分引擎与企业侧结果展示均为后续独立评审事项。 ## 12. 错误码 - V35 RIASEC 已占用 `9701–9709`(97xx)。V36 使用独立区间 **`9801–9811`**(能力测评),避免复用冲突: - `9801 ABILITY_TYPE_NOT_FOUND`、`9802 ABILITY_ATTEMPT_NOT_FOUND`、`9803 ABILITY_ATTEMPT_INVALID_STATE`、`9805 ABILITY_ANSWER_INVALID`、`9806 ABILITY_INCOMPLETE`、`9807 ABILITY_CONCURRENT_UPDATE`、`9808 ABILITY_DUPLICATE_ACTIVE`、`9809 ABILITY_IDEMPOTENCY_CONFLICT`。 - `9810 ABILITY_RETAKE_COOLDOWN`(409):当前仍存在有效完成结果,尚不可重测。 - `9811 ABILITY_RETAKE_LIMIT`(429):滚动 90 天内已完成 3 次。 - 复用 `1001/2001/2002`;超时答卷读取、保存或提交统一按 `9803 ABILITY_ATTEMPT_INVALID_STATE` 处理。 ## 13. DTO 与接口字段 接口采用独立 `/abilities/**` 命名空间(最终 7 个接口): ``` GET /abilities/types POST /abilities/attempts GET /abilities/attempts/{attemptId} PATCH /abilities/attempts/{attemptId}/answers POST /abilities/attempts/{attemptId}/submit GET /abilities/attempts/{attemptId}/result GET /abilities/me?page&size ``` V36 不提供 `GET /abilities/{type}/questions`;题目只在创建答卷后通过 `GET /abilities/attempts/{attemptId}` 返回。 - 题目响应隐藏 `dimension/questionCode/competencyTag/source/reverseScored/questionVersion/answerKey/scoringRule`(不泄露能力维度语义与计分内部字段)。 - 结果响应(白名单):`attemptId/typeCode/status/validityStatus/validUntil/dimensionScores[{code,score,answeredCount}]/overallScore/weaknesses/radar/answeredCount/completionRate/questionFeedback[{questionId,selectedOption,explanation}]/disclaimer`。不含答案键、选项得分、studentUserId、phone、email、password。 ## 14. 数据库表、索引、唯一约束、外键 采用独立 `ability_*` 六表,避免与 V35 RIASEC 的题型、判分方式和生命周期耦合: ``` ability_definitions type_code UNIQUE; status; scale_type; question_count; attempt_question_count; min_per_dimension; question_set_version; rules_version ability_questions UNIQUE(definition_id, question_code), UNIQUE(definition_id, position), CHECK(dimension IN LOGIC/COMMUNICATION/EXECUTION/LEARNING), item_type/options_json/answer_key_json/scoring_rule_json/explanation/difficulty/discrimination/version ability_attempts UNIQUE(student_user_id, idempotency_key), UNIQUE(active_key), @Version, CHECK(status IN DRAFT/IN_PROGRESS/COMPLETED/EXPIRED) ability_attempt_questions UNIQUE(attempt_id, question_id), UNIQUE(attempt_id, position), question_version/question_snapshot_json ability_answers UNIQUE(attempt_id, question_id), selected_option/awarded_score/answered_at ability_results UNIQUE(attempt_id)(幂等) ``` - 外键:attempt→definitions/users(CASCADE)、attempt_question/answer→attempt(CASCADE)+question、result→attempt(CASCADE)。题目快照用于保证题库后续修订或停用后,历史答卷和提交后的解析仍按当时版本展示与计分。 - 索引:`(student_user_id, created_at)`、`active_key` 唯一、`status` 等。 - 时间相关字段:`started_at/expires_at/submitted_at/completed_at`;结果含 `valid_until`。`active_key` 仅在 `DRAFT/IN_PROGRESS` 非空,进入 `COMPLETED/EXPIRED` 后置空。 ## 15. 脱敏要求 - 题目响应不泄露维度与内部编码;结果不返回原始答案、手机号、邮箱、password、JWT、他人信息。 - 不保存不必要的题面;审计不含题面/答案/PII。 ## 16. 审计要求 - 写入 `ABILITY_ATTEMPT_CREATED` / `ABILITY_ATTEMPT_SUBMITTED`。 - details 仅允许 `attemptId/typeCode/status/answeredCount/questionSetVersion/rulesVersion/requestId/timestamp`;禁止题面、答案、手机号、邮箱、JWT。 ## 17. 是否写入 candidate_dimension_scores - V36 第一版不写 `candidate_dimension_scores`,ABILITY 维度维持 `PENDING`;不提供企业端能力测评结果接口。 - 将能力综合分写入评分引擎、企业端查看结果均为后续独立评审事项。 ## 18. OpenAPI 增量 - 基线 134 paths / 168 operations。 - 固定 +7 paths / +7 operations(`/abilities/**` 7 端点:类型/创建/详情/保存/提交/结果/我的历史)→ **141 paths / 175 operations**;V36 不提供 `GET /abilities/{type}/questions`。运行时 OpenAPI 必须与静态一致。 ## 19. MockMvc、集成测试和 MySQL 黑盒验收方案 - 测试类(镜像 RIASEC): - `AbilityAssessmentCatalogTest`、`AbilityAssessmentResultComputerTest`、`AbilityAssessmentAttemptServiceTest`、`AbilityAssessmentControllerTest`、`AbilityAssessmentPersistenceIntegrationTest`(含并发创建/并发提交)、种子/迁移测试。 - 覆盖:创建、幂等重放、同 key 同/异请求、活跃重复、有效结果冷却、90 天完成上限、中途保存、首次保存起算、恢复同一题、30 分钟超时、非法/跨答卷题 ID、未答完提交、重复提交、并发创建/提交、STUDENT/COMPANY/ADMIN/匿名权限、结果和题目解析脱敏、业务表无异常写入、Flyway、外键/唯一约束。 - MySQL 黑盒:健康 200、Flyway 最新成功、运行时/静态 OpenAPI 一致、表/约束/数据核验、并发无 500、审计脱敏、队列归零、`candidate_dimension_scores` 行数不变、V31–V35 回归。 ## 20. 回滚与兼容方案 - 不修改 V1–V35 表与语义;V36 迁移为附加新表 `V35__ability_assessment.sql`(当前最新 Flyway 为 V34,下一编号为 V35;功能版本仍标 V36)。 - 回滚:删除 V36 迁移即可,不影响 V35 及既有数据。 - 使用独立 `/abilities/**` 命名空间与独立 `ability_*` 表,回滚不会影响 V35 RIASEC。 --- ## 21. 试测与上线声明 - 首版 48 题必须在后续试测中统计难度、选项分布与区分度;区分度低于 0.4 或存在明显歧义的题目应修改、停用或替换。 - 当前未定义试测样本量和正式上线统计门槛,因此本版不得宣称题库已完成心理测量学验证。 - 结果页必须声明:本测评反映当前工作情境答题表现,用于自我了解和能力提升参考;不构成职业资格、正式招聘结论或永久能力标签。 > 本设计已由负责人确认,可进入 V36 编码;试测统计口径为后续运营与题库质量治理工作,不阻塞本版实现。