# 入职清单与公司知识介绍 API(N04) > 状态:后端实现完成,待真实 MySQL 8.4 / Docker 黑盒验收。 > > 数据库迁移:`V38__onboarding_checklist_and_company_knowledge.sql`、`V39__onboarding_standard_checklist_seed.sql`。 ## 1. Base URL 与认证 本地 Base URL: ```text http://127.0.0.1:8080/api/v1 ``` 所有 `/onboarding/**` 接口都要求登录,并携带: ```http Authorization: Bearer Content-Type: application/json X-Request-Id: ``` 细粒度角色和所有权在 Service 层校验。除 401 外,接口统一返回 `ApiResponse`: ```json { "code": 200, "message": "ok", "data": {}, "requestId": "req-...", "timestamp": "2026-09-17T01:30:00Z" } ``` ## 2. 接口与权限总览 | # | Method | Path | 角色 | 说明 | |---|---|---|---|---| | 1 | GET | `/onboarding/companies/{companyId}/checklist` | STUDENT | 读取有效清单、完成状态和完成率 | | 2 | PUT | `/onboarding/companies/{companyId}/checklist/items/{itemId}/completion` | STUDENT | 设置本人完成或未完成 | | 3 | GET | `/onboarding/companies/{companyId}/knowledge` | STUDENT | 读取企业资料、知识内容和公开评价摘要 | | 4 | GET | `/onboarding/company/checklist-items` | COMPANY | 查询本企业活动与软删除自定义项 | | 5 | POST | `/onboarding/company/checklist-items` | COMPANY | 创建本企业自定义项 | | 6 | PUT | `/onboarding/company/checklist-items/{itemId}` | COMPANY | 按版本更新本企业条目 | | 7 | DELETE | `/onboarding/company/checklist-items/{itemId}?version={version}` | COMPANY | 按版本软删除本企业条目 | | 8 | GET | `/onboarding/company/knowledge` | COMPANY | 查询本企业知识配置 | | 9 | PUT | `/onboarding/company/knowledge` | COMPANY | 创建或更新本企业知识配置 | 权限规则: - STUDENT 接口只允许拥有 STUDENT 角色的账号访问。 - COMPANY 接口只允许拥有 COMPANY 角色的账号访问。 - COMPANY 接口不接受 `companyId`;服务端从 JWT 用户解析其所属企业,避免跨企业写入。 - ADMIN 没有 onboarding 专属管理接口;调用 STUDENT/COMPANY 接口时按非目标角色返回 `403/2002`。 - 学生访问企业清单或知识不要求先有 Offer 或投递记录,但目标企业必须存在。 - 企业条目不存在或属于其他企业时统一返回 `404/9701`,不泄露其他企业数据。 ## 3. 入职清单语义 ### 3.1 通用项与自定义项合并 有效清单由以下两部分组成: 1. 所有活动通用项 `source=STANDARD`;V39 固定写入 12 条。 2. 当前企业所有活动自定义项 `source=COMPANY`。 合并结果按 `sortOrder ASC, id ASC` 稳定排序。企业没有活动自定义项时,`customized=false`,仍返回全部通用项。企业存在活动自定义项时,`customized=true`,返回通用项与企业项。 `category` 固定为: ```text DOCUMENT | ADMIN | EQUIPMENT | TRAINING | LOGISTICS | OTHER 证件材料 | 行政手续 | 办公设备 | 培训学习 | 到岗安排 | 其他 ``` ### 3.2 学生读取有效清单 ```http GET /api/v1/onboarding/companies/10/checklist Authorization: Bearer ``` 响应: ```json { "code": 200, "message": "ok", "data": { "companyId": 10, "companyName": "示例科技", "customized": true, "totalItems": 13, "completedItems": 1, "completionRate": 7.7, "items": [ { "itemId": 101, "source": "STANDARD", "itemKey": "standard-id-card", "title": "准备身份证件", "description": "准备身份证原件或有效身份证明,用于入职登记。", "category": "DOCUMENT", "required": true, "sortOrder": 100, "active": true, "completed": true, "completedAt": "2026-09-17T09:00:00", "progressVersion": 1 }, { "itemId": 501, "source": "COMPANY", "itemKey": "company-10-8f1c", "title": "领取门禁卡", "description": "到行政前台领取门禁卡并完成权限激活。", "category": "ADMIN", "required": false, "sortOrder": 1000, "active": true, "completed": false, "completedAt": null, "progressVersion": 0 } ] }, "requestId": "req-onboarding-checklist", "timestamp": "2026-09-17T01:30:00Z" } ``` `completionRate = round(completedItems * 100 / totalItems, 1)`;总数为 0 时返回 `0.0`。完成状态只属于当前 STUDENT。 ### 3.3 更新完成状态 请求使用绝对状态,而不是切换: ```http PUT /api/v1/onboarding/companies/10/checklist/items/501/completion Authorization: Bearer {"completed": true, "version": 0} ``` 响应: ```json { "code": 200, "message": "ok", "data": { "itemId": 501, "completed": true, "completedAt": "2026-09-17T01:31:00Z", "progressVersion": 1, "totalItems": 13, "completedItems": 2, "completionRate": 15.4 }, "requestId": "req-onboarding-progress", "timestamp": "2026-09-17T01:31:00Z" } ``` 版本规则: - 首次保存某条目时可省略 `version` 或传 `0`。 - 已完成状态变化时必须传当前 `progressVersion`;过期返回 `409/9705`。 - 重复提交相同 `completed` 值保持幂等,不新增记录、不递增版本。 - 首次提交 `completed=false` 不创建记录。 - `completed=false` 会清除 `completedAt`。 - 一名学生与一个清单项最多一条进度记录。 ### 3.4 企业创建自定义项 ```http POST /api/v1/onboarding/company/checklist-items Authorization: Bearer { "title": "领取门禁卡", "description": "到行政前台领取门禁卡并完成权限激活。", "category": "ADMIN", "sortOrder": 1000, "required": false, "idempotencyKey": "company-10-access-card-20260917" } ``` 响应 `data`: ```json { "itemId": 501, "source": "COMPANY", "itemKey": "company-10-8f1c", "title": "领取门禁卡", "description": "到行政前台领取门禁卡并完成权限激活。", "category": "ADMIN", "required": false, "sortOrder": 1000, "active": true, "version": 0 } ``` 约束: - `title`:必填,最多 120 字符。 - `description`:可选,最多 1000 字符。 - `category`:必填,仅允许固定枚举。 - `sortOrder`:可选,默认 `1000`,范围 `0..9999`。 - `required`:可选,默认 `false`。 - `idempotencyKey`:可选,最多 64 字符;服务端会 trim,空串按未提供处理。 - 活动自定义项上限为 50 条;达到上限返回 `409/9703`。 幂等重试: - 同一企业、同一 `idempotencyKey`、同标题/说明/分类/排序/必填值:返回首次创建的条目。 - 同一企业、同一 key、请求语义不同:返回 `409/9704`。 - 不同企业可使用相同 key,互不冲突。 - 未传 key 时,每次请求创建新条目。 ### 3.5 企业查询、更新与软删除 查询本企业全部条目: ```http GET /api/v1/onboarding/company/checklist-items Authorization: Bearer ``` 响应包含活动与软删除条目,不返回通用项和其他企业项。更新请求: ```http PUT /api/v1/onboarding/company/checklist-items/501 Authorization: Bearer { "title": "领取门禁卡并激活", "description": "到行政前台领取门禁卡并完成权限激活。", "category": "ADMIN", "sortOrder": 900, "required": true, "active": true, "version": 0 } ``` 更新使用 `version` 乐观锁,成功递增版本;过期返回 `409/9702`。软删除: ```http DELETE /api/v1/onboarding/company/checklist-items/501?version=1 Authorization: Bearer ``` 首次删除必须使用当前版本,系统只将 `active=false`,不物理删除。重复删除已非活动条目保持幂等成功;接口仍要求携带 `version` 查询参数。 ## 4. 公司知识介绍语义 ### 4.1 学生读取与回退 ```http GET /api/v1/onboarding/companies/10/knowledge Authorization: Bearer ``` 响应: ```json { "code": 200, "message": "ok", "data": { "company": { "id": 10, "name": "示例科技", "logoUrl": "https://cdn.example.com/company-10.png", "description": "面向企业提供数据基础设施产品。", "industry": "互联网", "website": "https://example.com", "address": "杭州市余杭区", "verified": true }, "knowledgeState": "CONFIGURED", "knowledge": { "culture": "以用户价值为中心,保持透明和长期主义。", "workPolicies": "弹性上班;每周一、三线下办公。", "onboardingGuide": "报到当天携带身份证件,联系行政同事完成入职登记。", "updatedAt": "2026-09-17T01:33:00Z" }, "reviewSummary": { "state": "SUFFICIENT", "totalReviews": 4, "averageRating": 4.5, "ratingDistribution": { "rating1": 0, "rating2": 0, "rating3": 1, "rating4": 1, "rating5": 2 }, "topTags": ["发展空间", "同事友好"], "dimensionSummary": { "AUTHENTICITY": {"averageScore": 4.5, "sampleCount": 4, "dataState": "SUFFICIENT"}, "BENEFITS": {"averageScore": 4.3, "sampleCount": 4, "dataState": "SUFFICIENT"}, "SALARY_TRANSPARENCY": {"averageScore": 4.0, "sampleCount": 4, "dataState": "SUFFICIENT"}, "JD_CLARITY": {"averageScore": 4.6, "sampleCount": 4, "dataState": "SUFFICIENT"}, "GROWTH": {"averageScore": 4.4, "sampleCount": 4, "dataState": "SUFFICIENT"}, "TEAM_ATMOSPHERE": {"averageScore": 4.7, "sampleCount": 4, "dataState": "SUFFICIENT"}, "PROMOTION": {"averageScore": 4.1, "sampleCount": 4, "dataState": "SUFFICIENT"}, "WORK_LIFE_BALANCE": {"averageScore": 4.0, "sampleCount": 4, "dataState": "SUFFICIENT"} } } }, "requestId": "req-company-overview", "timestamp": "2026-09-17T01:34:00Z" } ``` 回退与状态: - 企业无知识记录:仍返回企业基础资料,`knowledgeState=BASIC_ONLY`,`knowledge` 省略。 - 企业有知识记录:`knowledgeState=CONFIGURED`,返回非空知识字段和 `updatedAt`;学生公开读取响应不返回知识配置版本。 - 企业不存在:`404/5001`。 - 响应白名单不包含 `ownerUserId`、用户 ID、手机号、邮箱、评价哈希或审核原因。 评价摘要只聚合企业全部岗位中 `PUBLISHED` 且审核状态为 `AUTO_APPROVED/APPROVED` 的评价: | `reviewSummary.state` | 条件 | 返回内容 | |---|---|---| | `EMPTY` | 0 条 | `state`、`totalReviews=0` | | `INSUFFICIENT` | 1–2 条 | `state`、真实 `totalReviews`;不返回均值、分布或维度汇总 | | `SUFFICIENT` | 至少 3 条 | 平均评分、评分分布、热门标签、八维汇总 | 热门标签最多 10 个;八个维度分别返回 `sampleCount` 和 `dataState`。维度的 `dataState` 使用同一 3 条阈值。 ### 4.2 企业查询知识配置 ```http GET /api/v1/onboarding/company/knowledge Authorization: Bearer ``` 未配置时: ```json { "code": 200, "message": "ok", "data": { "configured": false, "version": 0 }, "requestId": "req-company-knowledge", "timestamp": "2026-09-17T01:33:00Z" } ``` 已配置时返回 `configured=true`、非空内容、`updatedAt` 和当前 `version`。空字段按 `@JsonInclude(NON_NULL)` 省略。 ### 4.3 企业创建或更新知识配置 ```http PUT /api/v1/onboarding/company/knowledge Authorization: Bearer { "culture": "以用户价值为中心,保持透明和长期主义。", "workPolicies": "弹性上班;每周一、三线下办公。", "onboardingGuide": "报到当天携带身份证件,联系行政同事完成入职登记。", "version": 1 } ``` 规则: - 至少一个内容字段必须非空。 - `culture` 最多 2000 字符,`workPolicies` 和 `onboardingGuide` 各最多 4000 字符。 - 字段按纯文本处理,会 trim;包含 HTML 标签时返回 `400/1001`。 - 首次创建可以省略 `version`,创建后版本为 `0`。 - 更新已有记录必须提交当前版本;版本过期返回 `409/9706`。 - 已有内容与提交内容相同且未传版本时按幂等读取处理;内容不同但未传版本返回 `400/1001`。 ## 5. 错误码 | HTTP | code | 含义 | 前端处理 | |---|---:|---|---| | 400 | 1001 | 参数、枚举、长度、HTML、缺少必填字段 | 展示字段错误,不自动重试 | | 401 | 401 | 未登录或 Token 无效 | 清理登录态并重新登录 | | 403 | 2002 | 角色不匹配、无所属企业或无权限 | 不重试,切换到正确角色 | | 404 | 5001 | 企业不存在或 COMPANY 无绑定企业 | 检查 `companyId` 或企业绑定状态 | | 404 | 9701 | 清单项不存在、跨企业或不属于有效清单 | 刷新清单后重新选择条目 | | 409 | 9702 | 企业清单项版本过期 | 重新 `GET /company/checklist-items`,使用新版本重试 | | 409 | 9703 | 活动自定义项达到 50 条上限 | 禁用新增,提示先软删除或复用现有项 | | 409 | 9704 | 幂等键已用于不同请求体 | 更换幂等键,或确认原请求后读取原条目 | | 409 | 9705 | 学生完成状态版本过期 | 重新读取有效清单并使用新 `progressVersion` | | 409 | 9706 | 公司知识版本过期 | 重新读取企业知识配置并使用新 `version` | 401 由安全过滤器返回裸 JSON,没有统一 `ApiResponse.requestId`: ```json {"code": 401, "message": "not authenticated", "timestamp": ""} ``` 其他错误示例: ```json { "code": 9704, "message": "idempotency key conflict: request body differs", "data": null, "requestId": "req-9704", "timestamp": "2026-09-17T01:30:00Z" } ``` ## 6. 前端推荐流程 学生端: 1. 登录后调用 `GET /onboarding/companies/{companyId}/checklist`。 2. 使用响应的 `itemId` 和 `progressVersion` 调用完成状态接口。 3. 收到 `9705` 时重新读取清单,避免覆盖其他设备的新状态。 4. 调用 `GET /onboarding/companies/{companyId}/knowledge` 展示企业介绍与评价摘要。 5. 当 `reviewSummary.state=EMPTY/INSUFFICIENT` 时,不展示平均值、分布和维度均值。 企业端: 1. 登录后调用 `GET /onboarding/company/checklist-items` 获取当前版本。 2. 新增时生成稳定 `idempotencyKey` 并保存到重试请求中。 3. 更新或删除前使用最新 `version`,冲突后重新拉取列表。 4. 调用 `GET /onboarding/company/knowledge` 获取配置版本,再使用 `PUT` 保存。 5. 收到 `9703` 时停止自动新增;收到 `9704` 时不要复用旧请求体。