diff --git a/README.md b/README.md index 0707cd5..470d381 100644 --- a/README.md +++ b/README.md @@ -6,15 +6,16 @@ | 项目 | 当前状态 | | --- | --- | -| Git 检查点 | V34 已冻结,最新检查点为 `bf75fc` | -| 数据库迁移 | Flyway V1–V33;V34 为纯接口聚合模块,无新增迁移 | -| API 契约 | 126 paths / 160 operations | -| 自动化测试 | 1275 个通过,0 失败,0 错误 | +| Git 检查点 | N04 已建立 T7 暂停检查点 `73afd87`;T8 完成后形成最终检查点 | +| 数据库迁移 | Flyway V1–V39;N04 新增 V38–V39 | +| API 契约 | 135 paths / 172 operations | +| 自动化测试 | 1492 个执行:1481 通过、2 失败、9 错误;失败均在既有 CareerProgress/JobReviewDimension/LocalResumeStorage 测试,与 N04 文档变更无关 | | 已验收范围 | V27–V34;V33 已完成真实 MySQL/Docker 黑盒验收 | +| N04 入职清单与公司知识介绍 | 实现完成,待真实 MySQL/Docker 黑盒验收 | | 账号注销冷静期 | 30 天 | | 本地服务端口 | `8080` | -V35 霍兰德职业兴趣测评目前仅完成设计,尚未进入编码和数据库迁移阶段。 +N04 入职清单与公司知识介绍已完成代码、N04 专项测试、OpenAPI 和联调文档;全量 `mvn clean verify` 仍受既有非 N04 测试失败阻塞,待真实 MySQL/Docker 黑盒验收。V35 霍兰德职业兴趣测评目前仅完成设计,尚未进入编码和数据库迁移阶段。 ## 仓库结构 @@ -123,6 +124,7 @@ python .\docs\verify_openapi.py - [OpenAPI 契约](docs/openapi-rc.json) - [前端联调说明](docs/frontend-integration.md) - [后端开发路线图](docs/backend-development-roadmap.md) +- [入职清单与公司知识介绍](docs/onboarding-api.md) - [统一求职进度接口](docs/career-progress-api.md) - [同岗位竞争力排名](docs/competition-ranking-api.md) - [举报与屏蔽](docs/report-block-api.md) diff --git a/changes/n04-onboard/.spec-superflow.yaml b/changes/n04-onboard/.spec-superflow.yaml index 2f922c7..7c72627 100644 --- a/changes/n04-onboard/.spec-superflow.yaml +++ b/changes/n04-onboard/.spec-superflow.yaml @@ -31,8 +31,10 @@ dp_4_result: approved: SDD selected; W1 serial T1-T2, W2 serial T3-T5 depends W1 dp_4_confirmed: true dp_4_timestamp: 2026-09-16T07:58:31Z batches_completed: 2 -execution_paused: true +execution_paused: false paused_at: 2026-09-16T09:51:43Z paused_reason: user requested pause for the day; T7 tests are complete but independent T7 review and T8 remain pending resume_from: T7-review resume_mode: manual +resumed_at: 2026-09-17T01:17:47Z +resume_count: 1 diff --git a/changes/n04-onboard/.superpowers/sdd/execution-plan.yaml b/changes/n04-onboard/.superpowers/sdd/execution-plan.yaml index 0cdba13..07fb15d 100644 --- a/changes/n04-onboard/.superpowers/sdd/execution-plan.yaml +++ b/changes/n04-onboard/.superpowers/sdd/execution-plan.yaml @@ -20,5 +20,5 @@ waves: strategy: serial tasks: [T6, T7, T8] depends_on: [W2] - state: paused - receipt: T6 pass; T7 tests complete, independent review pending; T8 pending; manual resume only \ No newline at end of file + state: eligible + receipt: T6 pass; T7 pass; T8 pending \ No newline at end of file diff --git a/changes/n04-onboard/.superpowers/sdd/progress.md b/changes/n04-onboard/.superpowers/sdd/progress.md index c6ae9cc..c9232d2 100644 --- a/changes/n04-onboard/.superpowers/sdd/progress.md +++ b/changes/n04-onboard/.superpowers/sdd/progress.md @@ -15,3 +15,6 @@ - 2026-09-16T09:40:55Z Task T6: complete after focused repair (commit 97b3b16; W3-T6-rereview PASS; 22 focused tests, 85 onboarding tests). - 2026-09-16T09:51:11Z Task T7: complete (no commit per instruction; RED->GREEN coverage added; OnboardingEndToEndIntegrationTest 7 tests; 2-review/tag/HIDDEN focused gaps; 96 onboarding tests pass; randomized E2E order pass). - 2026-09-16T09:51:43Z Execution paused by user for the day. T7 tests complete (96 onboarding tests green); independent T7 review and T8 pending. Manual resume from T7-review only. +- 2026-09-17T01:17:47Z Manual resume accepted. W3 resumes at T7 independent review; T6 remains passed and T8 remains pending. +- 2026-09-17T01:21:54Z Task T7: review W3-T7 PASS; 30 focused tests, 96 onboarding regressions, randomized E2E pass. +- 2026-09-17T09:33:54Z Task T8: documentation implementation complete; after adding missing onboarding assertions, verifier RED was `Missing onboarding path: /onboarding/companies/{companyId}/checklist`; static OpenAPI now 135 paths / 172 operations and contains 9 onboarding operations / 6 paths / 20 schemas; `python docs/verify_openapi.py` PASS; N04 focused regression PASS (96 tests); `mvn clean verify` FAIL (1492 tests, 2 failures, 9 errors) in existing CareerProgress/JobReviewDimension/LocalResumeStorage tests unrelated to T8; `mvn -DskipTests package` PASS; no commit; independent T8/W3 review pending. diff --git a/changes/n04-onboard/.superpowers/sdd/reviews/W3-T7.md b/changes/n04-onboard/.superpowers/sdd/reviews/W3-T7.md new file mode 100644 index 0000000..f69f871 --- /dev/null +++ b/changes/n04-onboard/.superpowers/sdd/reviews/W3-T7.md @@ -0,0 +1,81 @@ +# W3 T7 Review: Onboarding End-to-End and Gap Tests + +## Verdict + +- **Spec compliance:** PASS +- **Task quality:** Approved +- **Overall:** PASS +- **Base:** `97b3b16` +- **Head:** `73afd87` +- **Blocking findings:** none +- **Cannot verify from this task alone:** real MySQL 8.4 execution remains a later verification-gate obligation. + +## Verification + +- Read `task-reviewer-prompt.md`, `writing-good-tests.md`, the T7 brief, execution contract, both onboarding specs, the full `97b3b16..73afd87` diff, and the complete changed test files where the diff output was truncated. +- Scope check: `git diff --name-only 97b3b16..73afd87` contains no `mydreamjob-server/src/main/**` files. The T7 implementation scope is limited to three test files plus workflow/review artifacts. +- Ran `OnboardingEndToEndIntegrationTest,CompanyKnowledgeServiceTest,OnboardingRepositoryIntegrationTest`: `Tests run: 30, Failures: 0, Errors: 0, Skipped: 0`, `BUILD SUCCESS`. +- Ran `Onboarding*Test,CompanyKnowledge*Test`: `Tests run: 96, Failures: 0, Errors: 0, Skipped: 0`, `BUILD SUCCESS`. +- Ran `OnboardingEndToEndIntegrationTest` with JUnit random method ordering: `Tests run: 7, Failures: 0, Errors: 0, Skipped: 0`, `BUILD SUCCESS`. +- Scoped `git diff --check 97b3b16..73afd87 -- ` passed. Range-wide `git diff --check` reports two pre-existing blank lines at EOF in `W2-T4.md` and `W2-T5.md`, outside T7. + +## Spec Compliance + +- All nine distinct onboarding paths are exercised through the full Spring context, MockMvc, JWT filter, H2 `MODE=MySQL`, repositories, and DTO serialization: `OnboardingEndToEndIntegrationTest.java:82-212`. +- Student fallback and merged-company behavior are covered: standard-only after soft deletion, `customized=false`, merged standard/company items, source values, completion count, and rate (`:135-167`, `:202-208`). +- Student absolute-state completion behavior is covered end to end: first completion, idempotent replay with a stale version, cancellation clearing `completedAt`, stale-version conflict, persisted row count, and unchanged state after conflict (`:214-264`). +- Company checklist behavior is covered end to end: list, create, idempotent replay, request-hash conflict, 50-active-item boundary, update, soft delete, repeated delete, cross-company update/delete isolation, and state preservation (`:266-364`). +- Company knowledge behavior is covered across `BASIC_ONLY`, `CONFIGURED`, `EMPTY`, exact two-review `INSUFFICIENT`, and `SUFFICIENT`, including absence of aggregates below three reviews (`:366-441`). +- Public-review filtering excludes `HIDDEN` and non-public moderation states from counts, tags, ratings, and dimension aggregates. Repository coverage is explicit at `OnboardingRepositoryIntegrationTest.java:267-301`; the E2E flow also adds hidden/pending records before asserting the visible totals and aggregates (`OnboardingEndToEndIntegrationTest.java:410-440`). +- Anonymous 401, wrong-role 403, and missing-company/item 404 paths are covered across the endpoint families, with database-state assertions after failures (`OnboardingEndToEndIntegrationTest.java:443-543`). +- Response privacy checks run for every success and error response and cover owner/student IDs, moderation reason, request/content/identity hashes, IP/User-Agent, password/token fields, phone, email, and idempotency keys (`OnboardingEndToEndIntegrationTest.java:545-577`). +- The focused service tests cover the exact two-review threshold, all eight dimension summaries, deterministic tag top-ten limiting, and recursively quoted JSON tags (`CompanyKnowledgeServiceTest.java:292-358`). + +## Test Falsifiability + +- The E2E tests assert externally observable HTTP status, response JSON, persisted row state, and cross-company ownership effects. They would fail for incorrect routing, authorization, fallback, merging, version handling, soft-delete behavior, or aggregate filtering. +- The threshold tests independently assert the absence of aggregates at zero, one, and two public reviews, and their presence at three. A change to the threshold or query filtering would fail them. +- The repository test independently verifies `PUBLISHED` plus approved moderation filtering and excludes `HIDDEN` records from all aggregate queries. A filter regression would fail the counts, tags, and dimension sample assertions. +- The privacy helper checks serialized response bodies rather than trusting service DTO construction alone. +- The randomized-order E2E run passed without reliance on wall-clock precision, external services, or test-order state. + +## Strengths + +- The new E2E test uses the real application context and persistence layer, not mocked controllers or repositories. +- Error-path state preservation is asserted at the database level, not only through HTTP status codes. +- The coverage combines a broad endpoint contract flow with focused service/repository tests for threshold, tag, and moderation-filter edge cases. +- No production files were changed; the task stayed within the intended test-hardening scope. + +## Issues + +### Critical (Must Fix) + +None. + +### Important (Should Fix) + +None. + +### Minor (Nice to Have) + +- `OnboardingEndToEndIntegrationTest.java:82-212` combines success checks for all nine endpoints into one long flow. It is correct, but a failure gives a broad diagnosis surface. Splitting the endpoint families into smaller E2E tests would improve failure isolation without changing behavior. +- `OnboardingEndToEndIntegrationTest.java:559-576` checks sensitive fields with raw serialized-string substrings. It covers the named fields in the current contract, but a recursive JSON field-name check would be more robust if DTO serialization changes or sensitive data is nested under a renamed field. +- Tag top-ten limiting and recursively quoted JSON parsing are covered at the service layer (`CompanyKnowledgeServiceTest.java:314-358`), while the E2E flow only exercises the three-tag sufficient case (`OnboardingEndToEndIntegrationTest.java:425-434`). This is acceptable for T7 because the focused behavior is covered, but retaining an HTTP-level edge case would provide broader integration evidence. +- Non-T7 range artifact: `git diff --check 97b3b16..73afd87` reports blank lines at EOF in `W2-T4.md:94` and `W2-T5.md:75`. The three T7 test files themselves pass `git diff --check`. + +## Residual Verification Warning + +- Real MySQL 8.4 behavior has not been run in this environment. H2 `MODE=MySQL` passed; MySQL/Docker black-box verification remains owned by the later verification gate. +- The prior T6 observation about knowledge-service lock-timeout/deadlock exception mapping remains outside T7 and was not modified here. + +## Assessment + +**Task quality:** Approved + +**Reasoning:** The T7 additions provide real full-context coverage for all nine endpoints, the required fallback, idempotency, concurrency-conflict, threshold, filtering, authorization, privacy, and state-preservation behaviors. The focused and full required suites, plus randomized ordering, all pass, and no production files were changed. + +Controller receipt command when `ssf` is available: + +```bash +ssf execution review changes/n04-onboard --wave W3 --base 97b3b16 --head 73afd87 --report changes/n04-onboard/.superpowers/sdd/reviews/W3-T7.md --verdict pass +``` \ No newline at end of file diff --git a/docs/backend-development-roadmap.md b/docs/backend-development-roadmap.md index 815194d..3543b39 100644 --- a/docs/backend-development-roadmap.md +++ b/docs/backend-development-roadmap.md @@ -26,11 +26,11 @@ | 项目 | 当前状态 | |---|---| -| OpenAPI | **126 paths、160 operations** | -| Flyway | **V1–V33** 已存在(V34 为纯接口扩展,不含 Flyway 迁移) | -| 全量测试 | **1275/1275 通过**(本次 `mvn clean verify` 实际数量) | -| 已完成范围 | V24 Candidate Scoring、V25 Governance、V26 Offer Advance、V27 Privacy、V28/V29 Account Deletion、V30 Data Export、V31 Interview Score、V32 Competition Ranking、V33 Report/Block、V34 统一求职进度聚合,及认证、资料、岗位、投递、面试、Offer、看板等核心闭环 | -| 当前结论 | 已达到已实现范围的 Release Candidate;V33 Report/Block 已完成真实 MySQL/Docker 黑盒验收并正式冻结;V34 统一求职进度聚合已完成实现并通过真实 MySQL/Docker 黑盒验收(PASS=99 / FAIL=0 / SKIP=0),待建立正式 Git 检查点 | +| OpenAPI | **135 paths、172 operations** | +| Flyway | **V1–V39** 已存在(V34 为纯接口扩展;N04 新增 V38–V39) | +| 全量测试 | **1492 个执行:1481 通过、2 失败、9 错误**;失败集中在既有 CareerProgress、JobReviewDimension、LocalResumeStorage 测试,与 N04 文档变更无关 | +| 已完成范围 | V24 Candidate Scoring、V25 Governance、V26 Offer Advance、V27 Privacy、V28/V29 Account Deletion、V30 Data Export、V31 Interview Score、V32 Competition Ranking、V33 Report/Block、V34 统一求职进度聚合,以及 N04 入职清单与公司知识介绍;另含认证、资料、岗位、投递、面试、Offer、看板等核心闭环 | +| 当前结论 | N04 入职清单与公司知识介绍已完成代码、N04 专项 96/96 测试和 OpenAPI/文档更新;全量 `mvn clean verify` 仍受既有非 N04 测试失败阻塞,待真实 MySQL/Docker 黑盒验收 | | 本规划范围 | 代码修复、新功能开发、接口契约、数据权限和治理能力 | ### 2.0 模块状态分类 @@ -38,9 +38,9 @@ | 类别 | 模块 | |---|---| | 已完成并已验收 | V24 Candidate Scoring、V25 Governance、V26 Offer Advance、V27 Privacy、V28/V29 Account Deletion、V30 Data Export、V31 Interview Score、V32 Competition Ranking、V33 Report/Block、V34 统一求职进度聚合 | -| 已完成、待真实 MySQL/Docker 验收 | — | +| 已完成、待真实 MySQL/Docker 验收 | N04 入职清单与公司知识介绍(V38/V39,9 个接口;专项测试 96/96,全量回归待清除既有失败) | | 已设计未编码 | 测评系统(Holland/能力测评)、公平性检测、SHAP/规则解释、防刷审计、企业评价管理、企业人才推荐 | -| 未开始 | AI 简历诊断/优化、AI 模拟面试、行业技能词云、入职准备清单、薪资计算器、诚信积分、消息撤回 | +| 未开始 | AI 简历诊断/优化、AI 模拟面试、行业技能词云、薪资计算器、诚信积分、消息撤回 | ### 2.1 已确认的产品决策 @@ -76,7 +76,9 @@ - 简历版本管理和简历导出; - 求职者评分卡; - Candidate Scoring Engine; -- 评分申诉、审核和回滚。 +- 评分申诉、审核和回滚; +- 入职清单:通用项与企业自定义项合并、个人完成状态和完成率; +- 公司知识介绍:企业资料、企业知识内容和公开评价摘要。 ### 3.2 企业端 @@ -89,7 +91,9 @@ - Offer 管理; - 面试管理; - 评价审核基础能力; -- 双向匹配、通知和直聊。 +- 双向匹配、通知和直聊; +- 本企业入职清单自定义项管理; +- 本企业公司知识内容维护。 ## 4. Figma 与后端覆盖结论 @@ -114,6 +118,7 @@ | 企业候选人看板 | `/company/candidates`、`/company/candidates/{id}` | 已完成 | | 企业评分看板 | `/company/scoring/dashboard/*` | 已完成 | | 企业认证 | `/companies/me/verification`、`/admin/companies/verifications*` | 已完成 | +| 入职清单与公司知识介绍 | `/onboarding/**`(9 个接口) | **实现完成,待真实 MySQL/Docker 验收** | ### 4.2 部分完成或口径不一致 @@ -136,13 +141,12 @@ - AI 简历诊断和优化; - AI 模拟面试和面试题预测; - 行业技能词云; -- 入职准备清单和公司知识介绍; - 薪资计算器和薪资参考; - 公平性检测; - SHAP/规则解释; - ADMIN 防刷审计和规则配置; - 诚信积分和激励。 -- 已非缺失(已实现):个人数据导出(V30)、隐私设置(V27)、账号注销(V28/V29)、举报与屏蔽(V33)、结构化面试评分(V31)、同岗位竞争力排名(V32)、统一求职进度聚合(V34)。 +- 已非缺失(已实现):个人数据导出(V30)、隐私设置(V27)、账号注销(V28/V29)、举报与屏蔽(V33)、结构化面试评分(V31)、同岗位竞争力排名(V32)、统一求职进度聚合(V34)、入职清单和公司知识介绍(N04)。 ## 5. 已有代码的优先修复项 @@ -440,8 +444,6 @@ scoring_risk_actions - AI 模拟面试; - 面试题预测; - 行业技能词云; -- 入职准备清单; -- 公司知识介绍; - 简历查重; - 诚信积分; - 消息撤回。 @@ -464,7 +466,7 @@ scoring_risk_actions | P1 | ADMIN 防刷审计 | | P2 | SHAP 高级解释 | | P2 | AI 简历、AI 模拟面试 | -| P2 | 技能词云、入职清单、诚信积分 | +| P2 | 技能词云、入职清单(N04 实现完成,待验收)、诚信积分 | ## 8. 每个模块的统一交付要求 diff --git a/docs/frontend-integration.md b/docs/frontend-integration.md index d4b397e..7379f67 100644 --- a/docs/frontend-integration.md +++ b/docs/frontend-integration.md @@ -125,8 +125,8 @@ Authorization: Bearer | 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 | 重复 / 非法状态流转 | +| 404 | 4001, 5001, 6001, 7001, 8002, 9102, 9104, 8305, 8501, 9701 | 资源不存在 | +| 409 | 5003, 7003, 8001, 8003, 8301-8302, 8304, 8502-8504, 9101, 9103, 9702-9706 | 重复 / 非法状态流转 / 版本或幂等冲突 | | 500 | 1002, 9001 | 服务器内部错误 | **前端策略**:优先判断 HTTP 状态码,再读取 `body.code` 显示业务信息。 @@ -734,3 +734,126 @@ GET /profile/me/career-progress?page=0&size=20&status=INTERVIEW # 仅 STUDENT - TOP10 不是独立接口字段;统一使用 `data.items.slice(0, min(10, data.total))`。 - 匹配度公式为 `round(matchedCount * 100 / min(10, total))`;范围 15 条、命中 5 条时为 `33`。 - 学习路径不返回课程链接、AI 文本、学习时长或进度记录。 + + +## 22. 入职清单与公司知识介绍联调 + +完整接口、字段和错误示例见 [onboarding-api.md](onboarding-api.md)。Base URL 与认证方式沿用本文第 1、5 节: + +```text +http://127.0.0.1:8080/api/v1 +Authorization: Bearer +``` + +### 22.1 接口与角色 + +| Method | Path | 角色 | 关键交互 | +|---|---|---|---| +| `GET` | `/onboarding/companies/{companyId}/checklist` | STUDENT | 返回通用项与企业活动自定义项;无自定义项时回退通用清单。 | +| `PUT` | `/onboarding/companies/{companyId}/checklist/items/{itemId}/completion` | STUDENT | 绝对状态设置完成/未完成;状态变化时使用 `progressVersion`。 | +| `GET` | `/onboarding/companies/{companyId}/knowledge` | STUDENT | 企业基础资料 + 企业知识 + 公开评价摘要。 | +| `GET` | `/onboarding/company/checklist-items` | COMPANY | 查询本企业活动与软删除自定义项。 | +| `POST` | `/onboarding/company/checklist-items` | COMPANY | 创建本企业自定义项,支持 `idempotencyKey`。 | +| `PUT` | `/onboarding/company/checklist-items/{itemId}` | COMPANY | 使用 `version` 乐观锁更新。 | +| `DELETE` | `/onboarding/company/checklist-items/{itemId}?version={version}` | COMPANY | 软删除;重复删除幂等。 | +| `GET` | `/onboarding/company/knowledge` | COMPANY | 查询本企业知识配置;未配置返回 `configured=false`。 | +| `PUT` | `/onboarding/company/knowledge` | COMPANY | 创建或更新本企业知识配置。 | + +### 22.2 请求示例 + +学生更新完成状态: + +```http +PUT /api/v1/onboarding/companies/10/checklist/items/501/completion +Authorization: Bearer +Content-Type: application/json + +{"completed": true, "version": 0} +``` + +企业创建自定义项: + +```http +POST /api/v1/onboarding/company/checklist-items +Authorization: Bearer +Content-Type: application/json + +{ + "title": "领取门禁卡", + "description": "到行政前台领取门禁卡并完成权限激活。", + "category": "ADMIN", + "sortOrder": 1000, + "required": false, + "idempotencyKey": "company-10-access-card-20260917" +} +``` + +企业更新知识: + +```http +PUT /api/v1/onboarding/company/knowledge +Authorization: Bearer +Content-Type: application/json + +{ + "culture": "以用户价值为中心,保持透明和长期主义。", + "workPolicies": "弹性上班;每周一、三线下办公。", + "onboardingGuide": "报到当天携带身份证件,联系行政同事完成入职登记。", + "version": 1 +} +``` + +### 22.3 响应与前端状态处理 + +成功响应统一读取 `data`。学生有效清单关键字段: + +```json +{ + "companyId": 10, + "companyName": "示例科技", + "customized": true, + "totalItems": 13, + "completedItems": 1, + "completionRate": 7.7, + "items": [ + { + "itemId": 101, + "source": "STANDARD", + "title": "准备身份证件", + "category": "DOCUMENT", + "required": true, + "sortOrder": 100, + "completed": true, + "completedAt": "2026-09-17T09:00:00", + "progressVersion": 1 + } + ] +} +``` + +公司知识回退: + +- `knowledgeState=CONFIGURED`:展示 `knowledge`。 +- `knowledgeState=BASIC_ONLY`:只展示 `company` 基础资料,不展示知识字段。 +- `reviewSummary.state=EMPTY/INSUFFICIENT`:只展示评价数量;不要展示均值、分布和八维汇总。 +- `reviewSummary.state=SUFFICIENT`:可以展示平均分、评分分布、热门标签和八维汇总。 + +错误处理: + +| HTTP/code | 前端动作 | +|---|---| +| `401/401` | 清理 Token,跳转登录;不要自动重试。 | +| `403/2002` | 当前角色或企业不匹配,不重试并提示切换正确账号。 | +| `404/5001` | 企业不存在或企业账号未绑定企业,停止当前页面数据加载。 | +| `404/9701` | 条目已删除、跨企业或已不在有效清单;重新加载清单。 | +| `409/9702` | 企业条目版本过期;重新查询 `/company/checklist-items` 后让用户确认再提交。 | +| `409/9703` | 活动自定义项达到 50 条;禁用新增,提示先软删除旧项。 | +| `409/9704` | 幂等键已用于不同请求体;更换 key 或展示原条目,不可盲目重试旧请求。 | +| `409/9705` | 学生进度版本过期;重新查询有效清单,使用最新 `progressVersion`。 | +| `409/9706` | 企业知识版本过期;重新查询配置,使用最新 `version`。 | + +幂等与软删除注意: + +- 创建重试必须复用原 `idempotencyKey` 和完全相同的标题、说明、分类、排序、必填标记。 +- DELETE 只在首次删除时要求当前版本;重复删除虽幂等,但客户端仍应携带 `version` 查询参数。 +- 更新/删除成功后必须用响应中的新 `version` 覆盖本地状态,不能继续使用旧值。 diff --git a/docs/onboarding-api.md b/docs/onboarding-api.md new file mode 100644 index 0000000..683c436 --- /dev/null +++ b/docs/onboarding-api.md @@ -0,0 +1,433 @@ +# 入职清单与公司知识介绍 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` 时不要复用旧请求体。 diff --git a/docs/openapi-rc.json b/docs/openapi-rc.json index 2c8a229..ee39142 100644 --- a/docs/openapi-rc.json +++ b/docs/openapi-rc.json @@ -118,6 +118,14 @@ { "name": "企业", "description": "企业资料创建、查询与更新" + }, + { + "name": "入职清单", + "description": "学生读取有效入职清单并更新本人完成状态;企业维护本企业自定义项" + }, + { + "name": "公司知识介绍", + "description": "企业维护知识内容,学生读取公司基础资料、知识与公开评价摘要" } ], "paths": { @@ -6739,118 +6747,1401 @@ } } } - } - }, - "components": { - "schemas": { - "AccountDeletionResponse": { - "type": "object", - "properties": { - "userId": { - "type": "integer", - "format": "int64", - "example": 2 - }, - "status": { - "type": "string", - "enum": [ - "NONE", - "PENDING", - "COMPLETED" - ], - "example": "PENDING", - "description": "注销状态:NONE/PENDING/COMPLETED" - }, - "requestedAt": { - "type": "string", - "format": "date-time", - "nullable": true - }, - "effectiveAt": { - "type": "string", - "format": "date-time", - "nullable": true - }, - "cancelledAt": { - "type": "string", - "format": "date-time", - "nullable": true - }, - "completedAt": { - "type": "string", - "format": "date-time", - "nullable": true - }, - "version": { - "type": "integer", - "format": "int64", - "example": 0 + }, + "/onboarding/company/checklist-items": { + "get": { + "tags": [ + "入职清单" + ], + "summary": "查询本企业自定义清单项", + "description": "返回当前 COMPANY 所属企业的活动与软删除自定义项,不包含通用项和其他企业项。", + "operationId": "listCompanyChecklistItems", + "security": [ + { + "bearerAuth": [] } - } - }, - "AdminDashboardOverviewResponse": { - "type": "object", - "properties": { - "totalCompanies": { - "type": "integer", - "format": "int32" - }, - "totalJobs": { - "type": "integer", - "format": "int32" + ], + "parameters": [], + "responses": { + "200": { + "description": "查询成功;无自定义项时返回空数组", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/ApiResponseListCompanyChecklistItemView" + }, + "example": { + "code": 200, + "message": "ok", + "data": [ + { + "itemId": 501, + "source": "COMPANY", + "itemKey": "company-10-8f1c", + "title": "领取门禁卡", + "description": "到行政前台领取门禁卡并完成权限激活。", + "category": "ADMIN", + "required": false, + "sortOrder": 1000, + "active": true, + "version": 0 + } + ], + "requestId": "req-onboarding-company-items", + "timestamp": "2026-09-17T01:32:00Z" + } + } + } }, - "totalCandidates": { - "type": "integer", - "format": "int32" + "401": { + "description": "未登录或令牌无效,由全局 Security 入口返回裸 JSON", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedResponse" + }, + "example": { + "code": 401, + "message": "not authenticated", + "timestamp": "" + } + } + } }, - "avgCandidateScore": { - "type": "number" + "403": { + "description": "仅 COMPANY 角色可访问", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + }, + "example": { + "code": 2002, + "message": "forbidden", + "data": null, + "requestId": "req-2002", + "timestamp": "2026-09-17T01:30:00Z" + } + } + } }, - "topCompanies": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CompanyScoreItem" + "404": { + "description": "当前账号没有所属企业", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + }, + "example": { + "code": 5001, + "message": "company not found", + "data": null, + "requestId": "req-5001", + "timestamp": "2026-09-17T01:30:00Z" + } + } } } } }, - "ApiResponse": { - "type": "object", - "properties": { - "code": { - "type": "integer", - "format": "int32" + "post": { + "tags": [ + "入职清单" + ], + "summary": "创建本企业自定义清单项", + "description": "为当前 COMPANY 所属企业创建自定义项;idempotencyKey 用于网络重试,活动项累计不超过 50 条。", + "operationId": "createCompanyChecklistItem", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CompanyChecklistItemCreateRequest" + }, + "example": { + "title": "领取门禁卡", + "description": "到行政前台领取门禁卡并完成权限激活。", + "category": "ADMIN", + "sortOrder": 1000, + "required": false, + "idempotencyKey": "company-10-access-card-20260917" + } + } + } + }, + "responses": { + "200": { + "description": "创建成功;相同幂等请求返回首次创建的条目", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/ApiResponseCompanyChecklistItemView" + }, + "example": { + "code": 200, + "message": "ok", + "data": { + "itemId": 501, + "source": "COMPANY", + "itemKey": "company-10-8f1c", + "title": "领取门禁卡", + "description": "到行政前台领取门禁卡并完成权限激活。", + "category": "ADMIN", + "required": false, + "sortOrder": 1000, + "active": true, + "version": 0 + }, + "requestId": "req-onboarding-company-item", + "timestamp": "2026-09-17T01:32:00Z" + } + } + } }, - "message": { - "type": "string" + "400": { + "description": "标题、分类、排序、必填标记或幂等键不符合约束", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + }, + "example": { + "code": 1001, + "message": "bad request", + "data": null, + "requestId": "req-1001", + "timestamp": "2026-09-17T01:30:00Z" + } + } + } }, - "data": {}, - "requestId": { - "type": "string" + "401": { + "description": "未登录或令牌无效,由全局 Security 入口返回裸 JSON", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedResponse" + }, + "example": { + "code": 401, + "message": "not authenticated", + "timestamp": "" + } + } + } }, - "timestamp": { - "type": "string", - "format": "date-time" - } - } - }, - "CareerProgressResponse": { - "type": "object", - "properties": { - "todo": { - "$ref": "#/components/schemas/ProgressTodoSummary" + "403": { + "description": "仅 COMPANY 角色可访问", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + }, + "example": { + "code": 2002, + "message": "forbidden", + "data": null, + "requestId": "req-2002", + "timestamp": "2026-09-17T01:30:00Z" + } + } + } }, - "applications": { - "$ref": "#/components/schemas/PaginatedApplications" + "404": { + "description": "当前账号没有所属企业", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + }, + "example": { + "code": 5001, + "message": "company not found", + "data": null, + "requestId": "req-5001", + "timestamp": "2026-09-17T01:30:00Z" + } + } + } }, - "generatedAt": { - "type": "string", - "format": "date-time" + "409": { + "description": "活动自定义项已达到 50 条;或 9704 幂等键已用于不同请求体", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + }, + "example": { + "code": 9703, + "message": "onboarding checklist item limit exceeded", + "data": null, + "requestId": "req-9703", + "timestamp": "2026-09-17T01:30:00Z" + } + } + } } } - }, - "ProgressTodoSummary": { - "type": "object", + } + }, + "/onboarding/company/checklist-items/{itemId}": { + "put": { + "tags": [ + "入职清单" + ], + "summary": "更新本企业自定义清单项", + "description": "使用当前 version 更新本企业条目;版本过期返回 9702,不静默覆盖。", + "operationId": "updateCompanyChecklistItem", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "name": "itemId", + "in": "path", + "required": true, + "description": "企业自定义清单项 ID", + "schema": { + "type": "integer", + "format": "int64", + "minimum": 1 + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CompanyChecklistItemUpdateRequest" + }, + "example": { + "title": "领取门禁卡并激活", + "description": "到行政前台领取门禁卡并完成权限激活。", + "category": "ADMIN", + "sortOrder": 900, + "required": true, + "active": true, + "version": 0 + } + } + } + }, + "responses": { + "200": { + "description": "更新成功并递增 version", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/ApiResponseCompanyChecklistItemView" + }, + "example": { + "code": 200, + "message": "ok", + "data": { + "itemId": 501, + "source": "COMPANY", + "itemKey": "company-10-8f1c", + "title": "领取门禁卡", + "description": "到行政前台领取门禁卡并完成权限激活。", + "category": "ADMIN", + "required": false, + "sortOrder": 1000, + "active": true, + "version": 0 + }, + "requestId": "req-onboarding-company-item", + "timestamp": "2026-09-17T01:32:00Z" + } + } + } + }, + "400": { + "description": "参数或请求体验证失败", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + }, + "example": { + "code": 1001, + "message": "bad request", + "data": null, + "requestId": "req-1001", + "timestamp": "2026-09-17T01:30:00Z" + } + } + } + }, + "401": { + "description": "未登录或令牌无效,由全局 Security 入口返回裸 JSON", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedResponse" + }, + "example": { + "code": 401, + "message": "not authenticated", + "timestamp": "" + } + } + } + }, + "403": { + "description": "仅 COMPANY 角色可访问", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + }, + "example": { + "code": 2002, + "message": "forbidden", + "data": null, + "requestId": "req-2002", + "timestamp": "2026-09-17T01:30:00Z" + } + } + } + }, + "404": { + "description": "条目不存在、已不属于该企业,或不属于该企业的有效清单", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + }, + "example": { + "code": 9701, + "message": "onboarding checklist item not found", + "data": null, + "requestId": "req-9701", + "timestamp": "2026-09-17T01:30:00Z" + } + } + } + }, + "409": { + "description": "请求 version 已过期", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + }, + "example": { + "code": 9702, + "message": "onboarding checklist item was modified concurrently", + "data": null, + "requestId": "req-9702", + "timestamp": "2026-09-17T01:30:00Z" + } + } + } + } + } + }, + "delete": { + "tags": [ + "入职清单" + ], + "summary": "软删除本企业自定义清单项", + "description": "使用当前 version 将 active 置为 false;重复删除保持幂等成功。", + "operationId": "deleteCompanyChecklistItem", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "name": "itemId", + "in": "path", + "required": true, + "description": "企业自定义清单项 ID", + "schema": { + "type": "integer", + "format": "int64", + "minimum": 1 + } + }, + { + "name": "version", + "in": "query", + "required": true, + "description": "客户端最后读取到的条目版本", + "schema": { + "type": "integer", + "format": "int64", + "minimum": 0 + } + } + ], + "responses": { + "200": { + "description": "软删除成功;重复删除返回当前非活动条目", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/ApiResponseCompanyChecklistItemView" + }, + "example": { + "code": 200, + "message": "ok", + "data": { + "itemId": 501, + "source": "COMPANY", + "itemKey": "company-10-8f1c", + "title": "领取门禁卡", + "description": "到行政前台领取门禁卡并完成权限激活。", + "category": "ADMIN", + "required": false, + "sortOrder": 1000, + "active": false, + "version": 1 + }, + "requestId": "req-onboarding-company-item", + "timestamp": "2026-09-17T01:32:00Z" + } + } + } + }, + "400": { + "description": "缺少 version 或参数类型不正确", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + }, + "example": { + "code": 1001, + "message": "bad request", + "data": null, + "requestId": "req-1001", + "timestamp": "2026-09-17T01:30:00Z" + } + } + } + }, + "401": { + "description": "未登录或令牌无效,由全局 Security 入口返回裸 JSON", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedResponse" + }, + "example": { + "code": 401, + "message": "not authenticated", + "timestamp": "" + } + } + } + }, + "403": { + "description": "仅 COMPANY 角色可访问", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + }, + "example": { + "code": 2002, + "message": "forbidden", + "data": null, + "requestId": "req-2002", + "timestamp": "2026-09-17T01:30:00Z" + } + } + } + }, + "404": { + "description": "条目不存在、已不属于该企业,或不属于该企业的有效清单", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + }, + "example": { + "code": 9701, + "message": "onboarding checklist item not found", + "data": null, + "requestId": "req-9701", + "timestamp": "2026-09-17T01:30:00Z" + } + } + } + }, + "409": { + "description": "请求 version 已过期", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + }, + "example": { + "code": 9702, + "message": "onboarding checklist item was modified concurrently", + "data": null, + "requestId": "req-9702", + "timestamp": "2026-09-17T01:30:00Z" + } + } + } + } + } + } + }, + "/onboarding/company/knowledge": { + "get": { + "tags": [ + "公司知识介绍" + ], + "summary": "读取本企业知识配置", + "description": "返回当前 COMPANY 所属企业的知识配置;尚未配置时 configured=false、version=0。", + "operationId": "getCompanyKnowledge", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [], + "responses": { + "200": { + "description": "查询成功", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/ApiResponseCompanyKnowledgeView" + }, + "example": { + "code": 200, + "message": "ok", + "data": { + "configured": true, + "culture": "以用户价值为中心,保持透明和长期主义。", + "workPolicies": "弹性上班;每周一、三线下办公。", + "onboardingGuide": "报到当天携带身份证件,联系行政同事完成入职登记。", + "updatedAt": "2026-09-17T01:33:00Z", + "version": 2 + }, + "requestId": "req-company-knowledge", + "timestamp": "2026-09-17T01:33:00Z" + } + } + } + }, + "401": { + "description": "未登录或令牌无效,由全局 Security 入口返回裸 JSON", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedResponse" + }, + "example": { + "code": 401, + "message": "not authenticated", + "timestamp": "" + } + } + } + }, + "403": { + "description": "仅 COMPANY 角色可访问", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + }, + "example": { + "code": 2002, + "message": "forbidden", + "data": null, + "requestId": "req-2002", + "timestamp": "2026-09-17T01:30:00Z" + } + } + } + }, + "404": { + "description": "当前账号没有所属企业", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + }, + "example": { + "code": 5001, + "message": "company not found", + "data": null, + "requestId": "req-5001", + "timestamp": "2026-09-17T01:30:00Z" + } + } + } + } + } + }, + "put": { + "tags": [ + "公司知识介绍" + ], + "summary": "创建或更新本企业知识配置", + "description": "首次创建可省略 version;已有配置必须提交当前 version。字段按纯文本保存且禁止 HTML 标签。", + "operationId": "upsertCompanyKnowledge", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CompanyKnowledgeUpdateRequest" + }, + "example": { + "culture": "以用户价值为中心,保持透明和长期主义。", + "workPolicies": "弹性上班;每周一、三线下办公。", + "onboardingGuide": "报到当天携带身份证件,联系行政同事完成入职登记。", + "version": 1 + } + } + } + }, + "responses": { + "200": { + "description": "创建或更新成功", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/ApiResponseCompanyKnowledgeView" + }, + "example": { + "code": 200, + "message": "ok", + "data": { + "configured": true, + "culture": "以用户价值为中心,保持透明和长期主义。", + "workPolicies": "弹性上班;每周一、三线下办公。", + "onboardingGuide": "报到当天携带身份证件,联系行政同事完成入职登记。", + "updatedAt": "2026-09-17T01:33:00Z" + }, + "requestId": "req-company-knowledge", + "timestamp": "2026-09-17T01:33:00Z" + } + } + } + }, + "400": { + "description": "内容为空、超长、包含 HTML 标签,或已有配置缺少 version", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + }, + "example": { + "code": 1001, + "message": "bad request", + "data": null, + "requestId": "req-1001", + "timestamp": "2026-09-17T01:30:00Z" + } + } + } + }, + "401": { + "description": "未登录或令牌无效,由全局 Security 入口返回裸 JSON", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedResponse" + }, + "example": { + "code": 401, + "message": "not authenticated", + "timestamp": "" + } + } + } + }, + "403": { + "description": "仅 COMPANY 角色可访问", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + }, + "example": { + "code": 2002, + "message": "forbidden", + "data": null, + "requestId": "req-2002", + "timestamp": "2026-09-17T01:30:00Z" + } + } + } + }, + "404": { + "description": "当前账号没有所属企业", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + }, + "example": { + "code": 5001, + "message": "company not found", + "data": null, + "requestId": "req-5001", + "timestamp": "2026-09-17T01:30:00Z" + } + } + } + }, + "409": { + "description": "知识配置版本已过期", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + }, + "example": { + "code": 9706, + "message": "company knowledge was modified concurrently", + "data": null, + "requestId": "req-9706", + "timestamp": "2026-09-17T01:30:00Z" + } + } + } + } + } + } + }, + "/onboarding/companies/{companyId}/checklist": { + "get": { + "tags": [ + "入职清单" + ], + "summary": "读取有效入职清单", + "description": "STUDENT 读取通用活动项与当前企业活动自定义项的合并清单;无企业自定义项时回退通用清单。", + "operationId": "getEffectiveChecklist", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "name": "companyId", + "in": "path", + "required": true, + "description": "企业 ID", + "schema": { + "type": "integer", + "format": "int64", + "minimum": 1 + } + } + ], + "responses": { + "200": { + "description": "查询成功", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/ApiResponseEffectiveChecklistResponse" + }, + "example": { + "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" + } + } + } + }, + "400": { + "description": "companyId 不是正整数", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + }, + "example": { + "code": 1001, + "message": "bad request", + "data": null, + "requestId": "req-1001", + "timestamp": "2026-09-17T01:30:00Z" + } + } + } + }, + "401": { + "description": "未登录或令牌无效,由全局 Security 入口返回裸 JSON", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedResponse" + }, + "example": { + "code": 401, + "message": "not authenticated", + "timestamp": "" + } + } + } + }, + "403": { + "description": "仅 STUDENT 角色可访问", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + }, + "example": { + "code": 2002, + "message": "forbidden", + "data": null, + "requestId": "req-2002", + "timestamp": "2026-09-17T01:30:00Z" + } + } + } + }, + "404": { + "description": "企业不存在", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + }, + "example": { + "code": 5001, + "message": "company not found", + "data": null, + "requestId": "req-5001", + "timestamp": "2026-09-17T01:30:00Z" + } + } + } + } + } + } + }, + "/onboarding/companies/{companyId}/checklist/items/{itemId}/completion": { + "put": { + "tags": [ + "入职清单" + ], + "summary": "更新清单条目完成状态", + "description": "STUDENT 使用绝对 completed 状态更新本人进度;已有进度变化时必须提交当前 version,首次创建只接受 version=0 或空。", + "operationId": "updateCompletion", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "name": "companyId", + "in": "path", + "required": true, + "description": "企业 ID", + "schema": { + "type": "integer", + "format": "int64", + "minimum": 1 + } + }, + { + "name": "itemId", + "in": "path", + "required": true, + "description": "有效清单条目 ID", + "schema": { + "type": "integer", + "format": "int64", + "minimum": 1 + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProgressUpdateRequest" + }, + "example": { + "completed": true, + "version": 0 + } + } + } + }, + "responses": { + "200": { + "description": "更新成功;重复提交相同状态返回幂等结果", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/ApiResponseProgressUpdateResponse" + }, + "example": { + "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" + } + } + } + }, + "400": { + "description": "completed 缺失或 version 为负数", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + }, + "example": { + "code": 1001, + "message": "bad request", + "data": null, + "requestId": "req-1001", + "timestamp": "2026-09-17T01:30:00Z" + } + } + } + }, + "401": { + "description": "未登录或令牌无效,由全局 Security 入口返回裸 JSON", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedResponse" + }, + "example": { + "code": 401, + "message": "not authenticated", + "timestamp": "" + } + } + } + }, + "403": { + "description": "仅 STUDENT 角色可访问", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + }, + "example": { + "code": 2002, + "message": "forbidden", + "data": null, + "requestId": "req-2002", + "timestamp": "2026-09-17T01:30:00Z" + } + } + } + }, + "404": { + "description": "条目不存在、已不属于该企业,或不属于该企业的有效清单", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + }, + "example": { + "code": 9701, + "message": "onboarding checklist item not found", + "data": null, + "requestId": "req-9701", + "timestamp": "2026-09-17T01:30:00Z" + } + } + } + }, + "409": { + "description": "进度版本已过期", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + }, + "example": { + "code": 9705, + "message": "onboarding progress was modified concurrently", + "data": null, + "requestId": "req-9705", + "timestamp": "2026-09-17T01:30:00Z" + } + } + } + } + } + } + }, + "/onboarding/companies/{companyId}/knowledge": { + "get": { + "tags": [ + "公司知识介绍" + ], + "summary": "读取公司知识介绍", + "description": "STUDENT 读取企业基础资料、企业维护内容和公开评价摘要;企业未配置知识时回退基础资料,公开评价少于 3 条时不返回均值。", + "operationId": "getCompanyKnowledgeOverview", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "name": "companyId", + "in": "path", + "required": true, + "description": "企业 ID", + "schema": { + "type": "integer", + "format": "int64", + "minimum": 1 + } + } + ], + "responses": { + "200": { + "description": "查询成功", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/ApiResponseCompanyKnowledgeOverviewResponse" + }, + "example": { + "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" + } + } + } + }, + "400": { + "description": "companyId 不是正整数", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + }, + "example": { + "code": 1001, + "message": "bad request", + "data": null, + "requestId": "req-1001", + "timestamp": "2026-09-17T01:30:00Z" + } + } + } + }, + "401": { + "description": "未登录或令牌无效,由全局 Security 入口返回裸 JSON", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedResponse" + }, + "example": { + "code": 401, + "message": "not authenticated", + "timestamp": "" + } + } + } + }, + "403": { + "description": "仅 STUDENT 角色可访问", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + }, + "example": { + "code": 2002, + "message": "forbidden", + "data": null, + "requestId": "req-2002", + "timestamp": "2026-09-17T01:30:00Z" + } + } + } + }, + "404": { + "description": "企业不存在", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + }, + "example": { + "code": 5001, + "message": "company not found", + "data": null, + "requestId": "req-5001", + "timestamp": "2026-09-17T01:30:00Z" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "AccountDeletionResponse": { + "type": "object", + "properties": { + "userId": { + "type": "integer", + "format": "int64", + "example": 2 + }, + "status": { + "type": "string", + "enum": [ + "NONE", + "PENDING", + "COMPLETED" + ], + "example": "PENDING", + "description": "注销状态:NONE/PENDING/COMPLETED" + }, + "requestedAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "effectiveAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "cancelledAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "completedAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "version": { + "type": "integer", + "format": "int64", + "example": 0 + } + } + }, + "AdminDashboardOverviewResponse": { + "type": "object", + "properties": { + "totalCompanies": { + "type": "integer", + "format": "int32" + }, + "totalJobs": { + "type": "integer", + "format": "int32" + }, + "totalCandidates": { + "type": "integer", + "format": "int32" + }, + "avgCandidateScore": { + "type": "number" + }, + "topCompanies": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CompanyScoreItem" + } + } + } + }, + "ApiResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + }, + "data": {}, + "requestId": { + "type": "string" + }, + "timestamp": { + "type": "string", + "format": "date-time" + } + } + }, + "CareerProgressResponse": { + "type": "object", + "properties": { + "todo": { + "$ref": "#/components/schemas/ProgressTodoSummary" + }, + "applications": { + "$ref": "#/components/schemas/PaginatedApplications" + }, + "generatedAt": { + "type": "string", + "format": "date-time" + } + } + }, + "ProgressTodoSummary": { + "type": "object", "properties": { "pendingInterviewResponseCount": { "type": "integer", @@ -7366,7 +8657,164 @@ "type": "string" }, "data": { - "$ref": "#/components/schemas/CandidateScoringTrendResponse" + "$ref": "#/components/schemas/CandidateScoringTrendResponse" + }, + "requestId": { + "type": "string" + }, + "timestamp": { + "type": "string", + "format": "date-time" + } + } + }, + "ApiResponseCompanyCandidateScoringResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/CompanyCandidateScoringResponse" + }, + "requestId": { + "type": "string" + }, + "timestamp": { + "type": "string", + "format": "date-time" + } + } + }, + "ApiResponseCompanyPublicView": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/CompanyPublicView" + }, + "requestId": { + "type": "string" + }, + "timestamp": { + "type": "string", + "format": "date-time" + } + } + }, + "ApiResponseDashboardOverviewResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/DashboardOverviewResponse" + }, + "requestId": { + "type": "string" + }, + "timestamp": { + "type": "string", + "format": "date-time" + } + } + }, + "ApiResponseDimensionScoreDetailResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/DimensionScoreDetailResponse" + }, + "requestId": { + "type": "string" + }, + "timestamp": { + "type": "string", + "format": "date-time" + } + } + }, + "ApiResponseJobRankingItem": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/JobRankingItem" + }, + "requestId": { + "type": "string" + }, + "timestamp": { + "type": "string", + "format": "date-time" + } + } + }, + "ApiResponseJobRankingListResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/JobRankingListResponse" + }, + "requestId": { + "type": "string" + }, + "timestamp": { + "type": "string", + "format": "date-time" + } + } + }, + "ApiResponseListAppealHistoryItem": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AppealHistoryItem" + } }, "requestId": { "type": "string" @@ -7377,7 +8825,7 @@ } } }, - "ApiResponseCompanyCandidateScoringResponse": { + "ApiResponseListMapStringObject": { "type": "object", "properties": { "code": { @@ -7388,7 +8836,11 @@ "type": "string" }, "data": { - "$ref": "#/components/schemas/CompanyCandidateScoringResponse" + "type": "array", + "items": { + "type": "object", + "additionalProperties": {} + } }, "requestId": { "type": "string" @@ -7399,7 +8851,7 @@ } } }, - "ApiResponseCompanyPublicView": { + "ApiResponseListSnapshotHistoryItem": { "type": "object", "properties": { "code": { @@ -7410,7 +8862,10 @@ "type": "string" }, "data": { - "$ref": "#/components/schemas/CompanyPublicView" + "type": "array", + "items": { + "$ref": "#/components/schemas/SnapshotHistoryItem" + } }, "requestId": { "type": "string" @@ -7421,7 +8876,7 @@ } } }, - "ApiResponseDashboardOverviewResponse": { + "ApiResponseListWeightTemplateResponse": { "type": "object", "properties": { "code": { @@ -7432,7 +8887,10 @@ "type": "string" }, "data": { - "$ref": "#/components/schemas/DashboardOverviewResponse" + "type": "array", + "items": { + "$ref": "#/components/schemas/WeightTemplateResponse" + } }, "requestId": { "type": "string" @@ -7443,7 +8901,7 @@ } } }, - "ApiResponseDimensionScoreDetailResponse": { + "ApiResponseMapStringObject": { "type": "object", "properties": { "code": { @@ -7454,7 +8912,8 @@ "type": "string" }, "data": { - "$ref": "#/components/schemas/DimensionScoreDetailResponse" + "type": "object", + "additionalProperties": {} }, "requestId": { "type": "string" @@ -7465,7 +8924,7 @@ } } }, - "ApiResponseJobRankingItem": { + "ApiResponseMapStringString": { "type": "object", "properties": { "code": { @@ -7476,7 +8935,10 @@ "type": "string" }, "data": { - "$ref": "#/components/schemas/JobRankingItem" + "type": "object", + "additionalProperties": { + "type": "string" + } }, "requestId": { "type": "string" @@ -7487,7 +8949,7 @@ } } }, - "ApiResponseJobRankingListResponse": { + "ApiResponsePersonalizedScoreResponse": { "type": "object", "properties": { "code": { @@ -7498,7 +8960,7 @@ "type": "string" }, "data": { - "$ref": "#/components/schemas/JobRankingListResponse" + "$ref": "#/components/schemas/PersonalizedScoreResponse" }, "requestId": { "type": "string" @@ -7509,24 +8971,24 @@ } } }, - "ApiResponseListAppealHistoryItem": { + "ApiResponsePrivacySettingsResponse": { "type": "object", "properties": { "code": { "type": "integer", - "format": "int32" + "format": "int32", + "example": 200 }, "message": { - "type": "string" + "type": "string", + "example": "ok" }, "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/AppealHistoryItem" - } + "$ref": "#/components/schemas/PrivacySettingsResponse" }, "requestId": { - "type": "string" + "type": "string", + "example": "req-1" }, "timestamp": { "type": "string", @@ -7534,7 +8996,7 @@ } } }, - "ApiResponseListMapStringObject": { + "ApiResponseReviewWeightResponse": { "type": "object", "properties": { "code": { @@ -7545,11 +9007,7 @@ "type": "string" }, "data": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": {} - } + "$ref": "#/components/schemas/ReviewWeightResponse" }, "requestId": { "type": "string" @@ -7560,7 +9018,7 @@ } } }, - "ApiResponseListSnapshotHistoryItem": { + "ApiResponseScoreTrendResponse": { "type": "object", "properties": { "code": { @@ -7571,10 +9029,7 @@ "type": "string" }, "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/SnapshotHistoryItem" - } + "$ref": "#/components/schemas/ScoreTrendResponse" }, "requestId": { "type": "string" @@ -7585,301 +9040,453 @@ } } }, - "ApiResponseListWeightTemplateResponse": { + "AppealDecisionRequest": { "type": "object", "properties": { - "code": { + "decision": { + "type": "string", + "minLength": 1 + }, + "reason": { + "type": "string" + } + }, + "required": [ + "decision" + ] + }, + "AppealHistoryItem": { + "type": "object", + "properties": { + "id": { "type": "integer", - "format": "int32" + "format": "int64" }, - "message": { + "fromStatus": { "type": "string" }, - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/WeightTemplateResponse" - } + "toStatus": { + "type": "string" }, - "requestId": { + "actorUserId": { + "type": "integer", + "format": "int64" + }, + "actorRole": { "type": "string" }, - "timestamp": { + "reason": { + "type": "string" + }, + "createdAt": { "type": "string", "format": "date-time" } } }, - "ApiResponseMapStringObject": { + "AppealResponse": { "type": "object", "properties": { - "code": { + "appealId": { "type": "integer", - "format": "int32" + "format": "int64" }, - "message": { + "candidateId": { + "type": "integer", + "format": "int64" + }, + "dimension": { "type": "string" }, - "data": { - "type": "object", - "additionalProperties": {} + "period": { + "type": "string" }, - "requestId": { + "status": { "type": "string" }, - "timestamp": { + "reason": { + "type": "string" + }, + "decision": { + "type": "string" + }, + "decisionReason": { + "type": "string" + }, + "reviewerId": { + "type": "integer", + "format": "int64" + }, + "reviewedAt": { + "type": "string", + "format": "date-time" + }, + "rollbackAt": { + "type": "string", + "format": "date-time" + }, + "scoreAtAppeal": { + "type": "number" + }, + "snapshotVersionAtAppeal": { + "type": "integer", + "format": "int64" + }, + "rulesVersion": { + "type": "string" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "updatedAt": { "type": "string", "format": "date-time" } } }, - "ApiResponseMapStringString": { + "ApplicationCreateRequest": { "type": "object", "properties": { - "code": { + "jobId": { "type": "integer", - "format": "int32" + "format": "int64" + } + }, + "required": [ + "jobId" + ] + }, + "ApplicationStatusRequest": { + "type": "object", + "properties": { + "status": { + "type": "string", + "minLength": 1, + "pattern": "^(VIEWED|INTERVIEW|REJECTED|HIRED)$" + } + }, + "required": [ + "status" + ] + }, + "CandidateDetailView": { + "type": "object", + "properties": { + "applicationId": { + "type": "integer", + "format": "int64" }, - "message": { + "jobId": { + "type": "integer", + "format": "int64" + }, + "jobTitle": { "type": "string" }, - "data": { - "type": "object", - "additionalProperties": { - "type": "string" - } + "jobCity": { + "type": "string" }, - "requestId": { + "jobEmploymentType": { "type": "string" }, - "timestamp": { + "status": { + "type": "string" + }, + "coverLetter": { + "type": "string" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "updatedAt": { "type": "string", "format": "date-time" + }, + "candidateName": { + "type": "string" + }, + "candidateAvatarUrl": { + "type": "string" + }, + "candidateBio": { + "type": "string" + }, + "candidateLocation": { + "type": "string" + }, + "candidateSchool": { + "type": "string" + }, + "candidateEducation": { + "type": "string" + }, + "candidateGraduationYear": { + "type": "integer", + "format": "int32" + }, + "candidateSkills": { + "type": "array", + "items": { + "type": "string" + } } } }, - "ApiResponsePersonalizedScoreResponse": { + "CandidateFeedbackRequest": { "type": "object", "properties": { - "code": { + "rating": { "type": "integer", - "format": "int32" - }, - "message": { - "type": "string" - }, - "data": { - "$ref": "#/components/schemas/PersonalizedScoreResponse" - }, - "requestId": { - "type": "string" + "format": "int32", + "maximum": 5, + "minimum": 1 }, - "timestamp": { + "content": { "type": "string", - "format": "date-time" + "maxLength": 1000, + "minLength": 0 + }, + "tags": { + "type": "array", + "items": { + "type": "string", + "maxLength": 50, + "minLength": 1 + }, + "maxItems": 10, + "minItems": 0 } - } + }, + "required": [ + "content", + "rating" + ] }, - "ApiResponsePrivacySettingsResponse": { + "CandidateFeedbackResponse": { "type": "object", + "description": "脱敏后的候选人反馈", "properties": { - "code": { + "id": { + "type": "integer", + "format": "int64", + "description": "反馈 ID", + "example": 1 + }, + "applicationId": { + "type": "integer", + "format": "int64", + "description": "投递 ID", + "example": 31 + }, + "rating": { "type": "integer", "format": "int32", - "example": 200 + "description": "评分,范围 1-5", + "example": 5 }, - "message": { + "content": { "type": "string", - "example": "ok" + "description": "反馈内容", + "example": "沟通良好" }, - "data": { - "$ref": "#/components/schemas/PrivacySettingsResponse" + "tags": { + "type": "array", + "description": "反馈标签", + "items": { + "type": "string" + } }, - "requestId": { + "createdAt": { "type": "string", - "example": "req-1" + "format": "date-time", + "description": "创建时间", + "example": "2026-08-25T10:00:00" }, - "timestamp": { + "updatedAt": { "type": "string", - "format": "date-time" + "format": "date-time", + "description": "更新时间", + "example": "2026-08-25T10:00:00" } } }, - "ApiResponseReviewWeightResponse": { + "CandidateFeedbackSummary": { "type": "object", "properties": { - "code": { + "totalCount": { "type": "integer", "format": "int32" }, - "message": { - "type": "string" - }, - "data": { - "$ref": "#/components/schemas/ReviewWeightResponse" + "averageRating": { + "type": "number" }, - "requestId": { - "type": "string" + "ratingDistribution": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int32" + } }, - "timestamp": { - "type": "string", - "format": "date-time" + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TagCount" + } } } }, - "ApiResponseScoreTrendResponse": { + "CandidateItemView": { "type": "object", "properties": { - "code": { + "applicationId": { "type": "integer", - "format": "int32" - }, - "message": { - "type": "string" + "format": "int64" }, - "data": { - "$ref": "#/components/schemas/ScoreTrendResponse" + "jobId": { + "type": "integer", + "format": "int64" }, - "requestId": { + "jobTitle": { "type": "string" }, - "timestamp": { - "type": "string", - "format": "date-time" - } - } - }, - "AppealDecisionRequest": { - "type": "object", - "properties": { - "decision": { - "type": "string", - "minLength": 1 + "status": { + "type": "string" }, - "reason": { + "candidateName": { "type": "string" - } - }, - "required": [ - "decision" - ] - }, - "AppealHistoryItem": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64" }, - "fromStatus": { + "candidateSchool": { "type": "string" }, - "toStatus": { + "candidateEducation": { "type": "string" }, - "actorUserId": { + "candidateGraduationYear": { "type": "integer", - "format": "int64" - }, - "actorRole": { - "type": "string" + "format": "int32" }, - "reason": { + "candidateAvatarUrl": { "type": "string" }, "createdAt": { "type": "string", "format": "date-time" + }, + "updatedAt": { + "type": "string", + "format": "date-time" } } }, - "AppealResponse": { + "CandidateListResponse": { "type": "object", "properties": { - "appealId": { - "type": "integer", - "format": "int64" + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CandidateItemView" + } }, - "candidateId": { + "total": { "type": "integer", "format": "int64" }, - "dimension": { - "type": "string" + "page": { + "type": "integer", + "format": "int32" }, - "period": { - "type": "string" + "size": { + "type": "integer", + "format": "int32" }, - "status": { - "type": "string" + "totalPages": { + "type": "integer", + "format": "int32" + } + } + }, + "CandidateMatchSignals": { + "type": "object", + "properties": { + "candidateInterestScore": { + "$ref": "#/components/schemas/MatchSignal" }, - "reason": { - "type": "string" + "hrSuitabilityScore": { + "$ref": "#/components/schemas/MatchSignal" + } + } + }, + "CandidatePercentileResponse": { + "type": "object", + "properties": { + "candidateId": { + "type": "integer", + "format": "int64" }, - "decision": { + "status": { "type": "string" }, - "decisionReason": { + "scoreType": { "type": "string" }, - "reviewerId": { - "type": "integer", - "format": "int64" - }, - "reviewedAt": { - "type": "string", - "format": "date-time" - }, - "rollbackAt": { - "type": "string", - "format": "date-time" + "score": { + "type": "number" }, - "scoreAtAppeal": { + "percentile": { "type": "number" }, - "snapshotVersionAtAppeal": { + "cohortSize": { "type": "integer", - "format": "int64" - }, - "rulesVersion": { - "type": "string" + "format": "int32" }, - "createdAt": { - "type": "string", - "format": "date-time" + "sampleCount": { + "type": "integer", + "format": "int32" }, - "updatedAt": { - "type": "string", - "format": "date-time" + "minimumSampleCount": { + "type": "integer", + "format": "int32" } } }, - "ApplicationCreateRequest": { + "CandidateRankingDetailResponse": { "type": "object", "properties": { + "applicationId": { + "type": "integer", + "format": "int64" + }, "jobId": { "type": "integer", "format": "int64" + }, + "candidateName": { + "type": "string" + }, + "candidateSchool": { + "type": "string" + }, + "overallScore": { + "type": "number" + }, + "percentile": { + "type": "number" + }, + "cohortSize": { + "type": "integer", + "format": "int32" + }, + "dimensionScores": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DimensionScoreItem" + } } - }, - "required": [ - "jobId" - ] - }, - "ApplicationStatusRequest": { - "type": "object", - "properties": { - "status": { - "type": "string", - "minLength": 1, - "pattern": "^(VIEWED|INTERVIEW|REJECTED|HIRED)$" - } - }, - "required": [ - "status" - ] + } }, - "CandidateDetailView": { + "CandidateRankingItem": { "type": "object", "properties": { "applicationId": { @@ -7890,2022 +9497,2324 @@ "type": "integer", "format": "int64" }, - "jobTitle": { - "type": "string" - }, - "jobCity": { - "type": "string" - }, - "jobEmploymentType": { + "candidateName": { "type": "string" }, - "status": { + "candidateSchool": { "type": "string" }, - "coverLetter": { - "type": "string" + "overallScore": { + "type": "number" }, - "createdAt": { - "type": "string", - "format": "date-time" + "percentile": { + "type": "number" }, - "updatedAt": { - "type": "string", - "format": "date-time" + "sampleCount": { + "type": "integer", + "format": "int32" + } + } + }, + "CandidateRankingListResponse": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CandidateRankingItem" + } }, - "candidateName": { - "type": "string" + "total": { + "type": "integer", + "format": "int64" }, - "candidateAvatarUrl": { - "type": "string" + "page": { + "type": "integer", + "format": "int32" }, - "candidateBio": { - "type": "string" + "size": { + "type": "integer", + "format": "int32" }, - "candidateLocation": { - "type": "string" + "totalPages": { + "type": "integer", + "format": "int32" + } + } + }, + "CandidateScoreCardResponse": { + "type": "object", + "properties": { + "candidateId": { + "type": "integer", + "format": "int64" }, - "candidateSchool": { + "status": { "type": "string" }, - "candidateEducation": { - "type": "string" + "overallScore": { + "type": "number" }, - "candidateGraduationYear": { + "sampleCount": { "type": "integer", "format": "int32" }, - "candidateSkills": { + "feedbackSummary": { + "$ref": "#/components/schemas/CandidateFeedbackSummary" + }, + "matchSignals": { + "$ref": "#/components/schemas/CandidateMatchSignals" + }, + "unsupportedDimensions": { "type": "array", "items": { "type": "string" } + }, + "generatedAt": { + "type": "string", + "format": "date-time" } } }, - "CandidateFeedbackRequest": { + "CandidateScoreTrendItem": { "type": "object", "properties": { - "rating": { + "month": { + "type": "string" + }, + "feedbackCount": { "type": "integer", - "format": "int32", - "maximum": 5, - "minimum": 1 + "format": "int32" }, - "content": { - "type": "string", - "maxLength": 1000, - "minLength": 0 + "averageRating": { + "type": "number" }, - "tags": { - "type": "array", - "items": { - "type": "string", - "maxLength": 50, - "minLength": 1 - }, - "maxItems": 10, - "minItems": 0 + "ratingScore": { + "type": "number" } - }, - "required": [ - "content", - "rating" - ] + } }, - "CandidateFeedbackResponse": { + "CandidateScoreTrendResponse": { "type": "object", - "description": "脱敏后的候选人反馈", "properties": { - "id": { + "candidateId": { "type": "integer", - "format": "int64", - "description": "反馈 ID", - "example": 1 + "format": "int64" }, - "applicationId": { + "months": { "type": "integer", - "format": "int64", - "description": "投递 ID", - "example": 31 + "format": "int32" }, - "rating": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CandidateScoreTrendItem" + } + } + } + }, + "CandidateScoringOverviewResponse": { + "type": "object", + "properties": { + "candidateId": { "type": "integer", - "format": "int32", - "description": "评分,范围 1-5", - "example": 5 + "format": "int64" }, - "content": { - "type": "string", - "description": "反馈内容", - "example": "沟通良好" + "status": { + "type": "string" }, - "tags": { + "overallScore": { + "type": "number" + }, + "weights": { + "type": "object", + "additionalProperties": { + "type": "number" + } + }, + "dimensions": { "type": "array", - "description": "反馈标签", "items": { - "type": "string" + "$ref": "#/components/schemas/DimensionItem" } }, - "createdAt": { - "type": "string", - "format": "date-time", - "description": "创建时间", - "example": "2026-08-25T10:00:00" + "rulesVersion": { + "type": "string" }, - "updatedAt": { + "generatedAt": { "type": "string", - "format": "date-time", - "description": "更新时间", - "example": "2026-08-25T10:00:00" + "format": "date-time" } } }, - "CandidateFeedbackSummary": { + "CandidateScoringTrendResponse": { "type": "object", "properties": { - "totalCount": { + "candidateId": { "type": "integer", - "format": "int32" - }, - "averageRating": { - "type": "number" + "format": "int64" }, - "ratingDistribution": { - "type": "object", - "additionalProperties": { - "type": "integer", - "format": "int32" - } + "months": { + "type": "integer", + "format": "int32" }, - "tags": { + "items": { "type": "array", "items": { - "$ref": "#/components/schemas/TagCount" + "$ref": "#/components/schemas/TrendItem" } } } }, - "CandidateItemView": { + "CompanyCandidateScoringResponse": { "type": "object", "properties": { "applicationId": { "type": "integer", "format": "int64" }, - "jobId": { - "type": "integer", - "format": "int64" - }, - "jobTitle": { - "type": "string" + "overallScore": { + "type": "number" }, "status": { "type": "string" }, - "candidateName": { - "type": "string" + "dimensions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CompanyDimensionItem" + } }, - "candidateSchool": { + "generatedAt": { + "type": "string", + "format": "date-time" + }, + "rulesVersion": { "type": "string" + } + } + }, + "CompanyCreateRequest": { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 100, + "minLength": 0 }, - "candidateEducation": { + "logoUrl": { "type": "string" }, - "candidateGraduationYear": { - "type": "integer", - "format": "int32" + "description": { + "type": "string" }, - "candidateAvatarUrl": { + "industry": { "type": "string" }, - "createdAt": { - "type": "string", - "format": "date-time" + "website": { + "type": "string" }, - "updatedAt": { - "type": "string", - "format": "date-time" + "address": { + "type": "string" } - } + }, + "required": [ + "name" + ] }, - "CandidateListResponse": { + "CompanyDimensionItem": { "type": "object", "properties": { - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CandidateItemView" - } - }, - "total": { - "type": "integer", - "format": "int64" - }, - "page": { - "type": "integer", - "format": "int32" + "dimension": { + "type": "string" }, - "size": { - "type": "integer", - "format": "int32" + "score": { + "type": "number" }, - "totalPages": { - "type": "integer", - "format": "int32" + "status": { + "type": "string" } } }, - "CandidateMatchSignals": { + "CompanyMatchRuleRequest": { "type": "object", "properties": { - "candidateInterestScore": { - "$ref": "#/components/schemas/MatchSignal" - }, - "hrSuitabilityScore": { - "$ref": "#/components/schemas/MatchSignal" + "requiredCandidateScore": { + "type": "integer", + "format": "int32", + "maximum": 100, + "minimum": 0 } - } + }, + "required": [ + "requiredCandidateScore" + ] }, - "CandidatePercentileResponse": { + "CompanyPublicView": { "type": "object", "properties": { - "candidateId": { + "id": { "type": "integer", "format": "int64" }, - "status": { + "name": { "type": "string" }, - "scoreType": { + "logoUrl": { "type": "string" }, - "score": { - "type": "number" - }, - "percentile": { - "type": "number" - }, - "cohortSize": { - "type": "integer", - "format": "int32" + "description": { + "type": "string" }, - "sampleCount": { - "type": "integer", - "format": "int32" + "industry": { + "type": "string" }, - "minimumSampleCount": { - "type": "integer", - "format": "int32" + "verified": { + "type": "boolean" } } }, - "CandidateRankingDetailResponse": { + "CompanyScoreItem": { "type": "object", "properties": { - "applicationId": { - "type": "integer", - "format": "int64" - }, - "jobId": { + "companyId": { "type": "integer", "format": "int64" }, - "candidateName": { - "type": "string" - }, - "candidateSchool": { + "companyName": { "type": "string" }, - "overallScore": { - "type": "number" - }, - "percentile": { + "avgCandidateScore": { "type": "number" }, - "cohortSize": { + "jobCount": { "type": "integer", "format": "int32" - }, - "dimensionScores": { - "type": "array", - "items": { - "$ref": "#/components/schemas/DimensionScoreItem" - } } } }, - "CandidateRankingItem": { + "CompanyUpdateRequest": { "type": "object", "properties": { - "applicationId": { - "type": "integer", - "format": "int64" + "name": { + "type": "string", + "maxLength": 120, + "minLength": 0 }, - "jobId": { - "type": "integer", - "format": "int64" + "logoUrl": { + "type": "string", + "maxLength": 500, + "minLength": 0 }, - "candidateName": { + "description": { "type": "string" }, - "candidateSchool": { - "type": "string" + "industry": { + "type": "string", + "maxLength": 80, + "minLength": 0 }, - "overallScore": { - "type": "number" + "website": { + "type": "string", + "maxLength": 255, + "minLength": 0 }, - "percentile": { - "type": "number" + "address": { + "type": "string", + "maxLength": 255, + "minLength": 0 + } + }, + "required": [ + "name" + ] + }, + "CounterOfferCreateRequest": { + "type": "object", + "properties": { + "proposedSalaryMin": { + "type": "integer", + "format": "int32", + "minimum": 0 }, - "sampleCount": { + "proposedSalaryMax": { "type": "integer", - "format": "int32" + "format": "int32", + "minimum": 0 + }, + "proposedOnboardingDate": { + "type": "string", + "format": "date" + }, + "reason": { + "type": "string", + "maxLength": 2000, + "minLength": 0 } } }, - "CandidateRankingListResponse": { + "CreateAppealRequest": { "type": "object", "properties": { - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CandidateRankingItem" - } - }, - "total": { - "type": "integer", - "format": "int64" + "dimension": { + "type": "string", + "minLength": 1 }, - "page": { - "type": "integer", - "format": "int32" + "period": { + "type": "string", + "minLength": 1 }, - "size": { - "type": "integer", - "format": "int32" + "reason": { + "type": "string" }, - "totalPages": { + "idempotencyKey": { + "type": "string", + "maxLength": 64, + "minLength": 0 + } + }, + "required": [ + "dimension", + "period" + ] + }, + "CreateConversationRequest": { + "type": "object", + "properties": { + "participantUserId": { "type": "integer", - "format": "int32" + "format": "int64" } - } + }, + "required": [ + "participantUserId" + ] }, - "CandidateScoreCardResponse": { + "DashboardOverviewResponse": { "type": "object", "properties": { - "candidateId": { + "companyId": { "type": "integer", "format": "int64" }, - "status": { - "type": "string" + "totalJobs": { + "type": "integer", + "format": "int32" }, - "overallScore": { + "publishedJobs": { + "type": "integer", + "format": "int32" + }, + "avgJobScore": { "type": "number" }, - "sampleCount": { + "totalCandidates": { "type": "integer", "format": "int32" }, - "feedbackSummary": { - "$ref": "#/components/schemas/CandidateFeedbackSummary" - }, - "matchSignals": { - "$ref": "#/components/schemas/CandidateMatchSignals" + "avgCandidateScore": { + "type": "number" }, - "unsupportedDimensions": { - "type": "array", - "items": { - "type": "string" + "scoreDistribution": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int32" } }, - "generatedAt": { + "updatedAt": { "type": "string", "format": "date-time" } } }, - "CandidateScoreTrendItem": { + "DataSourceItem": { "type": "object", "properties": { - "month": { + "source": { "type": "string" }, - "feedbackCount": { - "type": "integer", - "format": "int32" - }, - "averageRating": { - "type": "number" + "time": { + "type": "string", + "format": "date-time" }, - "ratingScore": { - "type": "number" + "note": { + "type": "string" } } }, - "CandidateScoreTrendResponse": { + "DimensionItem": { "type": "object", "properties": { - "candidateId": { - "type": "integer", - "format": "int64" + "dimension": { + "type": "string" }, - "months": { - "type": "integer", - "format": "int32" + "score": { + "type": "number" }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CandidateScoreTrendItem" - } + "status": { + "type": "string" + }, + "weight": { + "type": "number" + }, + "summary": { + "type": "string" } } }, - "CandidateScoringOverviewResponse": { + "DimensionScoreDetailResponse": { "type": "object", "properties": { - "candidateId": { - "type": "integer", - "format": "int64" - }, - "status": { + "dimension": { "type": "string" }, - "overallScore": { + "score": { "type": "number" }, - "weights": { - "type": "object", - "additionalProperties": { - "type": "number" + "status": { + "type": "string" + }, + "subScores": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SubScore" } }, - "dimensions": { + "reasons": { "type": "array", "items": { - "$ref": "#/components/schemas/DimensionItem" + "type": "string" } }, - "rulesVersion": { - "type": "string" + "dataSources": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DataSourceItem" + } }, - "generatedAt": { + "updatedAt": { "type": "string", "format": "date-time" + }, + "rulesVersion": { + "type": "string" } } }, - "CandidateScoringTrendResponse": { + "DimensionScoreItem": { "type": "object", "properties": { - "candidateId": { - "type": "integer", - "format": "int64" + "dimension": { + "type": "string" }, - "months": { - "type": "integer", - "format": "int32" + "score": { + "type": "number" }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TrendItem" - } + "status": { + "type": "string" } } }, - "CompanyCandidateScoringResponse": { + "InterviewCancelRequest": { "type": "object", "properties": { - "applicationId": { - "type": "integer", - "format": "int64" - }, - "overallScore": { - "type": "number" - }, - "status": { - "type": "string" - }, - "dimensions": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CompanyDimensionItem" - } - }, - "generatedAt": { - "type": "string", - "format": "date-time" - }, - "rulesVersion": { + "reason": { "type": "string" } } }, - "CompanyCreateRequest": { + "InterviewCreateRequest": { "type": "object", "properties": { - "name": { + "roundNo": { + "type": "integer", + "format": "int32" + }, + "title": { "type": "string", - "maxLength": 100, + "maxLength": 200, "minLength": 0 }, - "logoUrl": { - "type": "string" + "interviewType": { + "type": "string", + "minLength": 1 }, - "description": { - "type": "string" + "startAt": { + "type": "string", + "format": "date-time" }, - "industry": { + "endAt": { + "type": "string", + "format": "date-time" + }, + "timezone": { "type": "string" }, - "website": { + "meetingUrl": { "type": "string" }, "address": { "type": "string" + }, + "interviewerName": { + "type": "string", + "maxLength": 100, + "minLength": 0 + }, + "notes": { + "type": "string" } }, "required": [ - "name" + "endAt", + "interviewType", + "startAt", + "title" ] }, - "CompanyDimensionItem": { + "InterviewRespondRequest": { "type": "object", "properties": { - "dimension": { - "type": "string" - }, - "score": { - "type": "number" + "action": { + "type": "string", + "minLength": 1 }, - "status": { + "reason": { "type": "string" } - } - }, - "CompanyMatchRuleRequest": { - "type": "object", - "properties": { - "requiredCandidateScore": { - "type": "integer", - "format": "int32", - "maximum": 100, - "minimum": 0 - } }, "required": [ - "requiredCandidateScore" + "action" ] }, - "CompanyPublicView": { + "InterviewUpdateRequest": { "type": "object", "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "name": { + "title": { "type": "string" }, - "logoUrl": { + "startAt": { + "type": "string", + "format": "date-time" + }, + "endAt": { + "type": "string", + "format": "date-time" + }, + "timezone": { "type": "string" }, - "description": { + "meetingUrl": { "type": "string" }, - "industry": { + "address": { "type": "string" }, - "verified": { - "type": "boolean" - } - } - }, - "CompanyScoreItem": { - "type": "object", - "properties": { - "companyId": { - "type": "integer", - "format": "int64" - }, - "companyName": { + "interviewerName": { "type": "string" }, - "avgCandidateScore": { - "type": "number" + "notes": { + "type": "string" }, - "jobCount": { - "type": "integer", - "format": "int32" + "timeRangeValid": { + "type": "boolean" } } }, - "CompanyUpdateRequest": { + "JobCreateRequest": { "type": "object", "properties": { - "name": { + "title": { "type": "string", "maxLength": 120, "minLength": 0 }, - "logoUrl": { + "description": { "type": "string", - "maxLength": 500, - "minLength": 0 + "minLength": 1 }, - "description": { + "requirements": { "type": "string" }, - "industry": { + "city": { "type": "string", "maxLength": 80, "minLength": 0 }, - "website": { + "employmentType": { "type": "string", - "maxLength": 255, + "maxLength": 30, "minLength": 0 }, - "address": { - "type": "string", - "maxLength": 255, - "minLength": 0 + "salaryMin": { + "type": "integer", + "format": "int32" + }, + "salaryMax": { + "type": "integer", + "format": "int32" } }, "required": [ - "name" + "city", + "description", + "employmentType", + "title" ] }, - "CounterOfferCreateRequest": { + "JobRankingItem": { "type": "object", "properties": { - "proposedSalaryMin": { + "jobId": { "type": "integer", - "format": "int32", - "minimum": 0 + "format": "int64" }, - "proposedSalaryMax": { - "type": "integer", - "format": "int32", - "minimum": 0 + "title": { + "type": "string" }, - "proposedOnboardingDate": { - "type": "string", - "format": "date" + "city": { + "type": "string" }, - "reason": { - "type": "string", - "maxLength": 2000, - "minLength": 0 - } - } - }, - "CreateAppealRequest": { - "type": "object", - "properties": { - "dimension": { - "type": "string", - "minLength": 1 + "employmentType": { + "type": "string" }, - "period": { - "type": "string", - "minLength": 1 + "avgRating": { + "type": "number" }, - "reason": { - "type": "string" + "reviewCount": { + "type": "integer", + "format": "int32" }, - "idempotencyKey": { - "type": "string", - "maxLength": 64, - "minLength": 0 - } - }, - "required": [ - "dimension", - "period" - ] - }, - "CreateConversationRequest": { - "type": "object", - "properties": { - "participantUserId": { + "rank": { "type": "integer", - "format": "int64" + "format": "int32" + }, + "cohortSize": { + "type": "integer", + "format": "int32" + }, + "dimensionAverages": { + "type": "object", + "additionalProperties": { + "type": "number" + } } - }, - "required": [ - "participantUserId" - ] + } }, - "DashboardOverviewResponse": { + "JobRankingListResponse": { "type": "object", "properties": { - "companyId": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/JobRankingItem" + } + }, + "total": { "type": "integer", "format": "int64" }, - "totalJobs": { + "page": { "type": "integer", "format": "int32" }, - "publishedJobs": { + "size": { "type": "integer", "format": "int32" }, - "avgJobScore": { - "type": "number" - }, - "totalCandidates": { + "totalPages": { "type": "integer", "format": "int32" + } + } + }, + "JobReviewCreateRequest": { + "type": "object", + "properties": { + "rating": { + "type": "integer", + "format": "int32", + "maximum": 5, + "minimum": 1 }, - "avgCandidateScore": { - "type": "number" + "content": { + "type": "string", + "maxLength": 1000, + "minLength": 0 }, - "scoreDistribution": { + "tags": { + "type": "array", + "items": { + "type": "string", + "maxLength": 50, + "minLength": 1 + }, + "maxItems": 10, + "minItems": 0 + }, + "dimensionScores": { "type": "object", "additionalProperties": { "type": "integer", "format": "int32" } - }, - "updatedAt": { - "type": "string", - "format": "date-time" } - } + }, + "required": [ + "content", + "rating" + ] }, - "DataSourceItem": { + "JobReviewUpdateRequest": { "type": "object", "properties": { - "source": { - "type": "string" + "rating": { + "type": "integer", + "format": "int32", + "maximum": 5, + "minimum": 1 }, - "time": { + "content": { "type": "string", - "format": "date-time" - }, - "note": { - "type": "string" - } - } - }, - "DimensionItem": { - "type": "object", - "properties": { - "dimension": { - "type": "string" + "maxLength": 1000, + "minLength": 1 }, - "score": { - "type": "number" + "tags": { + "type": "array", + "items": { + "type": "string", + "maxLength": 50, + "minLength": 1 + }, + "maxItems": 10, + "minItems": 0 }, "status": { - "type": "string" - }, - "weight": { - "type": "number" + "type": "string", + "pattern": "^(PUBLISHED|HIDDEN)$" }, - "summary": { - "type": "string" + "dimensionScores": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int32" + } } } }, - "DimensionScoreDetailResponse": { + "JobStatusRequest": { "type": "object", "properties": { - "dimension": { - "type": "string" - }, - "score": { - "type": "number" + "status": { + "type": "string", + "minLength": 1, + "pattern": "^(PUBLISHED|CLOSED)$" + } + }, + "required": [ + "status" + ] + }, + "JobUpdateRequest": { + "type": "object", + "properties": { + "title": { + "type": "string", + "maxLength": 120, + "minLength": 0 }, - "status": { + "description": { "type": "string" }, - "subScores": { - "type": "array", - "items": { - "$ref": "#/components/schemas/SubScore" - } - }, - "reasons": { - "type": "array", - "items": { - "type": "string" - } + "requirements": { + "type": "string" }, - "dataSources": { - "type": "array", - "items": { - "$ref": "#/components/schemas/DataSourceItem" - } + "city": { + "type": "string", + "maxLength": 80, + "minLength": 0 }, - "updatedAt": { + "employmentType": { "type": "string", - "format": "date-time" + "maxLength": 30, + "minLength": 0 }, - "rulesVersion": { - "type": "string" + "salaryMin": { + "type": "integer", + "format": "int32" + }, + "salaryMax": { + "type": "integer", + "format": "int32" } } }, - "DimensionScoreItem": { + "LoginRequest": { "type": "object", "properties": { - "dimension": { - "type": "string" + "phone": { + "type": "string", + "minLength": 1, + "pattern": "[0-9]{11}" }, - "score": { - "type": "number" + "code": { + "type": "string", + "minLength": 1, + "pattern": "[0-9]{6}" }, - "status": { + "purpose": { "type": "string" } - } + }, + "required": [ + "code", + "phone" + ] }, - "InterviewCancelRequest": { + "MatchSignal": { "type": "object", "properties": { - "reason": { - "type": "string" + "average": { + "type": "number" + }, + "sampleCount": { + "type": "integer", + "format": "int32" } } }, - "InterviewCreateRequest": { + "OfferCreateRequest": { "type": "object", "properties": { - "roundNo": { + "applicationId": { "type": "integer", - "format": "int32" + "format": "int64" }, "title": { "type": "string", - "maxLength": 200, + "maxLength": 120, "minLength": 0 }, - "interviewType": { + "salaryMin": { + "type": "integer", + "format": "int32", + "minimum": 0 + }, + "salaryMax": { + "type": "integer", + "format": "int32", + "minimum": 0 + }, + "salaryCurrency": { "type": "string", - "minLength": 1 + "maxLength": 10, + "minLength": 0 }, - "startAt": { + "salaryPeriod": { "type": "string", - "format": "date-time" + "pattern": "^(MONTH|YEAR|HOUR)$" }, - "endAt": { + "workCity": { "type": "string", - "format": "date-time" + "maxLength": 80, + "minLength": 0 }, - "timezone": { - "type": "string" + "employmentType": { + "type": "string", + "pattern": "^(FULL_TIME|PART_TIME|INTERN|INTERNSHIP)$" }, - "meetingUrl": { - "type": "string" + "probationMonths": { + "type": "integer", + "format": "int32", + "minimum": 0 }, - "address": { - "type": "string" + "benefits": { + "type": "string", + "maxLength": 2000, + "minLength": 0 }, - "interviewerName": { + "description": { "type": "string", - "maxLength": 100, + "maxLength": 5000, "minLength": 0 }, - "notes": { - "type": "string" + "expireAt": { + "type": "string", + "format": "date-time" + }, + "onboardingDate": { + "type": "string", + "format": "date" + }, + "baseSalary": { + "type": "integer", + "format": "int32", + "minimum": 0 + }, + "housingAllowance": { + "type": "integer", + "format": "int32", + "minimum": 0 + }, + "annualBonusMonths": { + "type": "integer", + "format": "int32", + "minimum": 0 + }, + "otherAllowances": { + "type": "string", + "maxLength": 1000, + "minLength": 0 } }, "required": [ - "endAt", - "interviewType", - "startAt", - "title" + "applicationId", + "description", + "expireAt", + "title", + "workCity" ] }, - "InterviewRespondRequest": { + "OfferStatusRequest": { "type": "object", "properties": { - "action": { + "status": { "type": "string", - "minLength": 1 + "minLength": 1, + "pattern": "^(SENT|NEGOTIATING|ACCEPTED|REJECTED|EXPIRED|WITHDRAWN)$" }, "reason": { - "type": "string" + "type": "string", + "maxLength": 255, + "minLength": 0 } }, "required": [ - "action" + "status" ] }, - "InterviewUpdateRequest": { + "OfferUpdateRequest": { "type": "object", "properties": { "title": { - "type": "string" - }, - "startAt": { - "type": "string", - "format": "date-time" - }, - "endAt": { "type": "string", - "format": "date-time" - }, - "timezone": { - "type": "string" + "maxLength": 120, + "minLength": 0 }, - "meetingUrl": { - "type": "string" + "salaryMin": { + "type": "integer", + "format": "int32", + "minimum": 0 }, - "address": { - "type": "string" + "salaryMax": { + "type": "integer", + "format": "int32", + "minimum": 0 }, - "interviewerName": { - "type": "string" + "salaryCurrency": { + "type": "string", + "maxLength": 10, + "minLength": 0 }, - "notes": { - "type": "string" + "salaryPeriod": { + "type": "string", + "pattern": "^(MONTH|YEAR|HOUR)$" }, - "timeRangeValid": { - "type": "boolean" - } - } - }, - "JobCreateRequest": { - "type": "object", - "properties": { - "title": { + "workCity": { "type": "string", - "maxLength": 120, + "maxLength": 80, "minLength": 0 }, - "description": { + "employmentType": { "type": "string", - "minLength": 1 + "pattern": "^(FULL_TIME|PART_TIME|INTERN|INTERNSHIP)$" }, - "requirements": { - "type": "string" + "probationMonths": { + "type": "integer", + "format": "int32", + "minimum": 0 }, - "city": { + "benefits": { "type": "string", - "maxLength": 80, + "maxLength": 2000, "minLength": 0 }, - "employmentType": { + "description": { "type": "string", - "maxLength": 30, + "maxLength": 5000, "minLength": 0 }, - "salaryMin": { + "expireAt": { + "type": "string", + "format": "date-time" + }, + "onboardingDate": { + "type": "string", + "format": "date" + }, + "baseSalary": { "type": "integer", - "format": "int32" + "format": "int32", + "minimum": 0 }, - "salaryMax": { + "housingAllowance": { "type": "integer", - "format": "int32" + "format": "int32", + "minimum": 0 + }, + "annualBonusMonths": { + "type": "integer", + "format": "int32", + "minimum": 0 + }, + "otherAllowances": { + "type": "string", + "maxLength": 1000, + "minLength": 0 } - }, - "required": [ - "city", - "description", - "employmentType", - "title" - ] + } }, - "JobRankingItem": { + "PersonalizedScoreResponse": { "type": "object", "properties": { "jobId": { "type": "integer", "format": "int64" }, - "title": { - "type": "string" + "score": { + "type": "number" }, - "city": { + "status": { "type": "string" }, - "employmentType": { + "source": { "type": "string" }, - "avgRating": { - "type": "number" - }, - "reviewCount": { - "type": "integer", - "format": "int32" + "templateCode": { + "type": "string" }, - "rank": { - "type": "integer", - "format": "int32" + "configuredWeights": { + "type": "object", + "additionalProperties": { + "type": "number" + } }, - "cohortSize": { - "type": "integer", - "format": "int32" + "effectiveWeights": { + "type": "object", + "additionalProperties": { + "type": "number" + } }, - "dimensionAverages": { + "dimensionScores": { "type": "object", "additionalProperties": { "type": "number" } + }, + "coveredWeight": { + "type": "number" + }, + "missingDimensions": { + "type": "array", + "items": { + "type": "string" + } } } }, - "JobRankingListResponse": { + "PrivacySettingsResponse": { "type": "object", "properties": { - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/JobRankingItem" - } - }, - "total": { + "id": { "type": "integer", - "format": "int64" + "format": "int64", + "example": 1, + "nullable": true }, - "page": { + "studentUserId": { "type": "integer", - "format": "int32" + "format": "int64", + "example": 2 }, - "size": { - "type": "integer", - "format": "int32" + "resumeVisibility": { + "$ref": "#/components/schemas/ResumeVisibility", + "description": "简历可见性(PRIVATE/APPLIED_COMPANIES/VERIFIED_COMPANIES)", + "example": "APPLIED_COMPANIES" }, - "totalPages": { - "type": "integer", - "format": "int32" - } - } - }, - "JobReviewCreateRequest": { - "type": "object", - "properties": { - "rating": { + "contactVisible": { + "type": "boolean", + "example": true + }, + "version": { "type": "integer", - "format": "int32", - "maximum": 5, - "minimum": 1 + "format": "int64", + "example": 0 }, - "content": { + "createdAt": { "type": "string", - "maxLength": 1000, - "minLength": 0 - }, - "tags": { - "type": "array", - "items": { - "type": "string", - "maxLength": 50, - "minLength": 1 - }, - "maxItems": 10, - "minItems": 0 + "format": "date-time", + "nullable": true }, - "dimensionScores": { - "type": "object", - "additionalProperties": { - "type": "integer", - "format": "int32" - } + "updatedAt": { + "type": "string", + "format": "date-time", + "nullable": true } - }, - "required": [ - "content", - "rating" - ] + } }, - "JobReviewUpdateRequest": { + "PrivacySettingsUpdateRequest": { "type": "object", + "required": [ + "resumeVisibility", + "contactVisible" + ], "properties": { - "rating": { - "type": "integer", - "format": "int32", - "maximum": 5, - "minimum": 1 - }, - "content": { - "type": "string", - "maxLength": 1000, - "minLength": 1 - }, - "tags": { - "type": "array", - "items": { - "type": "string", - "maxLength": 50, - "minLength": 1 - }, - "maxItems": 10, - "minItems": 0 + "resumeVisibility": { + "$ref": "#/components/schemas/ResumeVisibility", + "description": "简历可见性(必填)", + "example": "APPLIED_COMPANIES" }, - "status": { - "type": "string", - "pattern": "^(PUBLISHED|HIDDEN)$" + "contactVisible": { + "type": "boolean", + "description": "联系方式是否对企业可见(必填)", + "example": true }, - "dimensionScores": { - "type": "object", - "additionalProperties": { - "type": "integer", - "format": "int32" - } + "version": { + "type": "integer", + "format": "int64", + "description": "乐观锁版本号(可选)", + "example": 0 } } }, - "JobStatusRequest": { + "ResumeVisibility": { + "type": "string", + "enum": [ + "PRIVATE", + "APPLIED_COMPANIES", + "VERIFIED_COMPANIES" + ], + "description": "简历可见性:PRIVATE 私有;APPLIED_COMPANIES 仅对企业有效投递可见;VERIFIED_COMPANIES 仅对认证企业可见", + "example": "PRIVATE" + }, + "ReviewDecisionRequest": { "type": "object", "properties": { - "status": { + "decision": { "type": "string", "minLength": 1, - "pattern": "^(PUBLISHED|CLOSED)$" + "pattern": "^(APPROVED|REJECTED)$" + }, + "reason": { + "type": "string", + "maxLength": 500, + "minLength": 0 } }, "required": [ - "status" + "decision" ] }, - "JobUpdateRequest": { + "ReviewWeightResponse": { "type": "object", "properties": { - "title": { - "type": "string", - "maxLength": 120, - "minLength": 0 - }, - "description": { + "source": { "type": "string" }, - "requirements": { + "templateCode": { "type": "string" }, - "city": { - "type": "string", - "maxLength": 80, - "minLength": 0 - }, - "employmentType": { - "type": "string", - "maxLength": 30, - "minLength": 0 - }, - "salaryMin": { - "type": "integer", - "format": "int32" + "configuredWeights": { + "type": "object", + "additionalProperties": { + "type": "number" + } }, - "salaryMax": { - "type": "integer", - "format": "int32" + "updatedAt": { + "type": "string", + "format": "date-time" } } }, - "LoginRequest": { + "SaveWeightRequest": { "type": "object", "properties": { - "phone": { - "type": "string", - "minLength": 1, - "pattern": "[0-9]{11}" - }, - "code": { - "type": "string", - "minLength": 1, - "pattern": "[0-9]{6}" - }, - "purpose": { + "templateCode": { "type": "string" + }, + "weights": { + "type": "object", + "additionalProperties": { + "type": "number" + } + } + } + }, + "ScoreRequest": { + "type": "object", + "properties": { + "score": { + "type": "integer", + "format": "int32", + "maximum": 100, + "minimum": 0 } }, "required": [ - "code", - "phone" + "score" ] }, - "MatchSignal": { + "ScoreTrendResponse": { "type": "object", "properties": { - "average": { - "type": "number" + "companyId": { + "type": "integer", + "format": "int64" }, - "sampleCount": { + "months": { "type": "integer", "format": "int32" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TrendItem" + } } } }, - "OfferCreateRequest": { + "SendMessageRequest": { "type": "object", "properties": { - "applicationId": { + "conversationId": { "type": "integer", "format": "int64" }, - "title": { + "content": { "type": "string", - "maxLength": 120, + "maxLength": 2000, "minLength": 0 - }, - "salaryMin": { + } + }, + "required": [ + "content", + "conversationId" + ] + }, + "SnapshotHistoryItem": { + "type": "object", + "properties": { + "id": { "type": "integer", - "format": "int32", - "minimum": 0 + "format": "int64" }, - "salaryMax": { + "appealId": { "type": "integer", - "format": "int32", - "minimum": 0 - }, - "salaryCurrency": { - "type": "string", - "maxLength": 10, - "minLength": 0 - }, - "salaryPeriod": { - "type": "string", - "pattern": "^(MONTH|YEAR|HOUR)$" - }, - "workCity": { - "type": "string", - "maxLength": 80, - "minLength": 0 - }, - "employmentType": { - "type": "string", - "pattern": "^(FULL_TIME|PART_TIME|INTERN|INTERNSHIP)$" + "format": "int64" }, - "probationMonths": { - "type": "integer", - "format": "int32", - "minimum": 0 + "action": { + "type": "string" }, - "benefits": { - "type": "string", - "maxLength": 2000, - "minLength": 0 + "dimension": { + "type": "string" }, - "description": { - "type": "string", - "maxLength": 5000, - "minLength": 0 + "period": { + "type": "string" }, - "expireAt": { - "type": "string", - "format": "date-time" + "beforeScore": { + "type": "number" }, - "onboardingDate": { - "type": "string", - "format": "date" + "afterScore": { + "type": "number" }, - "baseSalary": { + "beforeVersion": { "type": "integer", - "format": "int32", - "minimum": 0 + "format": "int64" }, - "housingAllowance": { + "afterVersion": { "type": "integer", - "format": "int32", - "minimum": 0 + "format": "int64" }, - "annualBonusMonths": { - "type": "integer", - "format": "int32", - "minimum": 0 + "beforeRulesVersion": { + "type": "string" }, - "otherAllowances": { + "afterRulesVersion": { + "type": "string" + }, + "createdAt": { "type": "string", - "maxLength": 1000, - "minLength": 0 + "format": "date-time" } - }, - "required": [ - "applicationId", - "description", - "expireAt", - "title", - "workCity" - ] + } }, - "OfferStatusRequest": { + "StudentMatchRuleRequest": { "type": "object", "properties": { - "status": { - "type": "string", - "minLength": 1, - "pattern": "^(SENT|NEGOTIATING|ACCEPTED|REJECTED|EXPIRED|WITHDRAWN)$" - }, - "reason": { - "type": "string", - "maxLength": 255, - "minLength": 0 + "requiredHrScore": { + "type": "integer", + "format": "int32", + "maximum": 100, + "minimum": 0 } }, "required": [ - "status" + "requiredHrScore" ] }, - "OfferUpdateRequest": { + "StudentPreferenceRequest": { "type": "object", "properties": { - "title": { - "type": "string", - "maxLength": 120, - "minLength": 0 - }, - "salaryMin": { + "expectedSalaryMin": { "type": "integer", "format": "int32", "minimum": 0 }, - "salaryMax": { + "expectedSalaryMax": { "type": "integer", "format": "int32", "minimum": 0 }, - "salaryCurrency": { - "type": "string", - "maxLength": 10, - "minLength": 0 + "preferredCities": { + "type": "array", + "items": { + "type": "string", + "maxLength": 100, + "minLength": 1 + }, + "maxItems": 20, + "minItems": 0 }, - "salaryPeriod": { - "type": "string", - "pattern": "^(MONTH|YEAR|HOUR)$" + "employmentTypes": { + "type": "array", + "items": { + "type": "string", + "maxLength": 100, + "minLength": 1 + }, + "maxItems": 20, + "minItems": 0 }, - "workCity": { - "type": "string", - "maxLength": 80, - "minLength": 0 + "preferredIndustries": { + "type": "array", + "items": { + "type": "string", + "maxLength": 100, + "minLength": 1 + }, + "maxItems": 20, + "minItems": 0 }, - "employmentType": { - "type": "string", - "pattern": "^(FULL_TIME|PART_TIME|INTERN|INTERNSHIP)$" + "preferredJobTypes": { + "type": "array", + "items": { + "type": "string", + "maxLength": 100, + "minLength": 1 + }, + "maxItems": 20, + "minItems": 0 }, - "probationMonths": { - "type": "integer", - "format": "int32", - "minimum": 0 + "remoteAllowed": { + "type": "boolean" }, - "benefits": { + "salaryRangeValid": { + "type": "boolean" + } + } + }, + "StudentProfileUpdateRequest": { + "type": "object", + "properties": { + "realName": { "type": "string", - "maxLength": 2000, + "maxLength": 50, "minLength": 0 }, - "description": { + "bio": { + "type": "string" + }, + "location": { "type": "string", - "maxLength": 5000, + "maxLength": 100, "minLength": 0 }, - "expireAt": { + "education": { "type": "string", - "format": "date-time" + "maxLength": 100, + "minLength": 0 }, - "onboardingDate": { + "school": { "type": "string", - "format": "date" - }, - "baseSalary": { - "type": "integer", - "format": "int32", - "minimum": 0 - }, - "housingAllowance": { - "type": "integer", - "format": "int32", - "minimum": 0 + "maxLength": 150, + "minLength": 0 }, - "annualBonusMonths": { + "graduationYear": { "type": "integer", - "format": "int32", - "minimum": 0 + "format": "int32" }, - "otherAllowances": { + "resumeUrl": { "type": "string", - "maxLength": 1000, + "maxLength": 500, "minLength": 0 + }, + "skills": { + "type": "array", + "items": { + "type": "string" + } } } }, - "PersonalizedScoreResponse": { + "SubScore": { "type": "object", "properties": { - "jobId": { - "type": "integer", - "format": "int64" + "name": { + "type": "string" }, "score": { "type": "number" }, - "status": { + "weight": { + "type": "number" + }, + "description": { + "type": "string" + } + } + }, + "TagCount": { + "type": "object", + "properties": { + "name": { "type": "string" }, - "source": { + "count": { + "type": "integer", + "format": "int32" + } + } + }, + "TrendItem": { + "type": "object", + "properties": { + "period": { "type": "string" }, - "templateCode": { + "overallScore": { + "type": "number" + }, + "rulesVersion": { "type": "string" }, - "configuredWeights": { + "dimensions": { "type": "object", "additionalProperties": { "type": "number" } + } + } + }, + "VerificationDecisionRequest": { + "type": "object", + "properties": { + "decision": { + "type": "string", + "minLength": 1 }, - "effectiveWeights": { - "type": "object", - "additionalProperties": { - "type": "number" - } + "reason": { + "type": "string" + } + }, + "required": [ + "decision" + ] + }, + "VerificationSubmitRequest": { + "type": "object", + "properties": { + "materialsJson": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "materialsJson" + ] + }, + "WeightTemplateResponse": { + "type": "object", + "properties": { + "code": { + "type": "string" }, - "dimensionScores": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "weights": { "type": "object", "additionalProperties": { "type": "number" } + } + } + }, + "InterviewScoreSaveRequest": { + "type": "object", + "properties": { + "technicalScore": { + "type": "integer", + "minimum": 1, + "maximum": 5, + "nullable": true }, - "coveredWeight": { - "type": "number" + "communicationScore": { + "type": "integer", + "minimum": 1, + "maximum": 5, + "nullable": true }, - "missingDimensions": { - "type": "array", - "items": { - "type": "string" - } + "problemSolvingScore": { + "type": "integer", + "minimum": 1, + "maximum": 5, + "nullable": true + }, + "roleFitScore": { + "type": "integer", + "minimum": 1, + "maximum": 5, + "nullable": true + }, + "professionalismScore": { + "type": "integer", + "minimum": 1, + "maximum": 5, + "nullable": true + }, + "recommendation": { + "type": "string", + "enum": [ + "STRONG_HIRE", + "HIRE", + "NEUTRAL", + "NO_HIRE" + ], + "nullable": true, + "description": "企业手动选择,不根据 overallScore 自动推导" + }, + "comment": { + "type": "string", + "nullable": true } - } + }, + "description": "评分草稿保存请求;overallScore 由服务端计算,客户端不得传入" }, - "PrivacySettingsResponse": { + "InterviewScoreResponse": { "type": "object", "properties": { "id": { "type": "integer", - "format": "int64", - "example": 1, + "format": "int64" + }, + "interviewId": { + "type": "integer", + "format": "int64" + }, + "applicationId": { + "type": "integer", + "format": "int64" + }, + "status": { + "type": "string", + "enum": [ + "DRAFT", + "SUBMITTED" + ] + }, + "technicalScore": { + "type": "integer", "nullable": true }, - "studentUserId": { + "communicationScore": { "type": "integer", - "format": "int64", - "example": 2 + "nullable": true }, - "resumeVisibility": { - "$ref": "#/components/schemas/ResumeVisibility", - "description": "简历可见性(PRIVATE/APPLIED_COMPANIES/VERIFIED_COMPANIES)", - "example": "APPLIED_COMPANIES" + "problemSolvingScore": { + "type": "integer", + "nullable": true }, - "contactVisible": { - "type": "boolean", - "example": true + "roleFitScore": { + "type": "integer", + "nullable": true + }, + "professionalismScore": { + "type": "integer", + "nullable": true + }, + "overallScore": { + "type": "number", + "format": "decimal", + "nullable": true, + "description": "由服务端 BigDecimal 计算:(t+c+ps+rf+p)/5,保留两位小数 HALF_UP" + }, + "recommendation": { + "type": "string", + "enum": [ + "STRONG_HIRE", + "HIRE", + "NEUTRAL", + "NO_HIRE" + ], + "nullable": true + }, + "comment": { + "type": "string", + "nullable": true + }, + "submittedAt": { + "type": "string", + "format": "date-time", + "nullable": true }, "version": { "type": "integer", - "format": "int64", - "example": 0 + "format": "int64" }, "createdAt": { "type": "string", - "format": "date-time", - "nullable": true + "format": "date-time" }, "updatedAt": { "type": "string", - "format": "date-time", - "nullable": true + "format": "date-time" } - } + }, + "description": "面试评分响应(脱敏:不返回 companyUserId/studentUserId/evaluatorUserId/手机号/邮箱)" }, - "PrivacySettingsUpdateRequest": { + "ApiResponseInterviewScoreResponse": { "type": "object", - "required": [ - "resumeVisibility", - "contactVisible" - ], "properties": { - "resumeVisibility": { - "$ref": "#/components/schemas/ResumeVisibility", - "description": "简历可见性(必填)", - "example": "APPLIED_COMPANIES" + "code": { + "type": "integer", + "format": "int32" }, - "contactVisible": { - "type": "boolean", - "description": "联系方式是否对企业可见(必填)", - "example": true + "message": { + "type": "string" }, - "version": { - "type": "integer", - "format": "int64", - "description": "乐观锁版本号(可选)", - "example": 0 + "data": { + "$ref": "#/components/schemas/InterviewScoreResponse" + }, + "requestId": { + "type": "string" + }, + "timestamp": { + "type": "string", + "format": "date-time" } } }, - "ResumeVisibility": { - "type": "string", - "enum": [ - "PRIVATE", - "APPLIED_COMPANIES", - "VERIFIED_COMPANIES" - ], - "description": "简历可见性:PRIVATE 私有;APPLIED_COMPANIES 仅对企业有效投递可见;VERIFIED_COMPANIES 仅对认证企业可见", - "example": "PRIVATE" - }, - "ReviewDecisionRequest": { + "InterviewScoreHistoryItem": { "type": "object", "properties": { - "decision": { + "id": { + "type": "integer", + "format": "int64" + }, + "scoreId": { + "type": "integer", + "format": "int64" + }, + "interviewId": { + "type": "integer", + "format": "int64" + }, + "fromStatus": { "type": "string", - "minLength": 1, - "pattern": "^(APPROVED|REJECTED)$" + "enum": [ + "DRAFT", + "SUBMITTED" + ], + "nullable": true }, - "reason": { + "toStatus": { "type": "string", - "maxLength": 500, - "minLength": 0 + "enum": [ + "DRAFT", + "SUBMITTED" + ] + }, + "actorRole": { + "type": "string", + "example": "COMPANY" + }, + "snapshot": { + "type": "object", + "additionalProperties": {} + }, + "createdAt": { + "type": "string", + "format": "date-time" } }, - "required": [ - "decision" - ] + "description": "评分历史条目(脱敏:不返回 actorUserId/手机号/邮箱)" }, - "ReviewWeightResponse": { + "ApiResponseListInterviewScoreHistoryItem": { "type": "object", "properties": { - "source": { - "type": "string" + "code": { + "type": "integer", + "format": "int32" }, - "templateCode": { + "message": { "type": "string" }, - "configuredWeights": { - "type": "object", - "additionalProperties": { - "type": "number" + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/InterviewScoreHistoryItem" } }, - "updatedAt": { + "requestId": { + "type": "string" + }, + "timestamp": { "type": "string", "format": "date-time" } } }, - "SaveWeightRequest": { + "CreateReportRequest": { "type": "object", "properties": { - "templateCode": { - "type": "string" - }, - "weights": { - "type": "object", - "additionalProperties": { - "type": "number" - } - } + "targetType": { "type": "string", "enum": ["USER", "MESSAGE"], "description": "举报目标类型;USER 必填 targetUserId,MESSAGE 必填 messageId" }, + "targetUserId": { "type": "integer", "format": "int64", "description": "targetType=USER 时必填" }, + "messageId": { "type": "integer", "format": "int64", "description": "targetType=MESSAGE 时必填" }, + "category": { "type": "string", "maxLength": 40, "description": "举报分类,如 HARASSMENT" }, + "reason": { "type": "string", "minLength": 1, "maxLength": 1000 }, + "evidenceUrls": { "type": "array", "items": { "type": "string", "maxLength": 500 }, "description": "最多 5 个,仅 http/https,最多 500 字符" }, + "idempotencyKey": { "type": "string", "maxLength": 64, "description": "幂等键(可选)" } } }, - "ScoreRequest": { + "ReportDecisionRequest": { "type": "object", "properties": { - "score": { - "type": "integer", - "format": "int32", - "maximum": 100, - "minimum": 0 - } - }, - "required": [ - "score" - ] + "decision": { "type": "string", "enum": ["RESOLVED", "REJECTED"] }, + "note": { "type": "string", "maxLength": 1000, "description": "REJECTED 必填" } + } }, - "ScoreTrendResponse": { + "ReportResponse": { "type": "object", "properties": { - "companyId": { - "type": "integer", - "format": "int64" - }, - "months": { - "type": "integer", - "format": "int32" + "id": { "type": "integer", "format": "int64" }, + "targetType": { "type": "string", "enum": ["USER", "MESSAGE"] }, + "targetUserId": { "type": "integer", "format": "int64" }, + "messageId": { "type": "integer", "format": "int64" }, + "reportedUserId": { "type": "integer", "format": "int64" }, + "category": { "type": "string" }, + "reason": { "type": "string" }, + "evidenceUrls": { "type": "array", "items": { "type": "string" } }, + "status": { "type": "string", "enum": ["PENDING", "RESOLVED", "REJECTED"] }, + "decision": { "type": "string", "enum": ["RESOLVED", "REJECTED"] }, + "decisionNote": { "type": "string" }, + "decidedAt": { "type": "string", "format": "date-time" }, + "createdAt": { "type": "string", "format": "date-time" }, + "updatedAt": { "type": "string", "format": "date-time" }, + "messagePreview": { "type": "string", "description": "MESSAGE 举报的脱敏消息预览,不返回会话其他敏感内容" }, + "reportedUser": { "$ref": "#/components/schemas/ReportResponseReportedUser" }, + "history": { "type": "array", "items": { "$ref": "#/components/schemas/ReportResponseDecisionHistoryItem" } } + } + }, + "ReportResponseReportedUser": { + "type": "object", + "properties": { + "userId": { "type": "integer", "format": "int64" }, + "nickname": { "type": "string" }, + "role": { "type": "string" } + } + }, + "ReportResponseDecisionHistoryItem": { + "type": "object", + "properties": { + "id": { "type": "integer", "format": "int64" }, + "fromStatus": { "type": "string" }, + "toStatus": { "type": "string" }, + "actorUserId": { "type": "integer", "format": "int64" }, + "actorRole": { "type": "string" }, + "note": { "type": "string" }, + "createdAt": { "type": "string", "format": "date-time" } + } + }, + "BlockRelationResponse": { + "type": "object", + "properties": { + "id": { "type": "integer", "format": "int64" }, + "blockedUserId": { "type": "integer", "format": "int64" }, + "nickname": { "type": "string" }, + "createdAt": { "type": "string", "format": "date-time" } + } + }, + "ApiResponseReportResponse": { + "type": "object", + "properties": { + "code": { "type": "integer", "format": "int32" }, + "message": { "type": "string" }, + "data": { "$ref": "#/components/schemas/ReportResponse" }, + "requestId": { "type": "string" }, + "timestamp": { "type": "string", "format": "date-time" } + } + }, + "ApiResponsePageResponseReportResponse": { + "type": "object", + "properties": { + "code": { "type": "integer", "format": "int32" }, + "message": { "type": "string" }, + "data": { + "type": "object", + "properties": { + "items": { "type": "array", "items": { "$ref": "#/components/schemas/ReportResponse" } }, + "totalElements": { "type": "integer", "format": "int64" }, + "page": { "type": "integer", "format": "int32" }, + "size": { "type": "integer", "format": "int32" } + } }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TrendItem" + "requestId": { "type": "string" }, + "timestamp": { "type": "string", "format": "date-time" } + } + }, + "ApiResponseBlockRelationResponse": { + "type": "object", + "properties": { + "code": { "type": "integer", "format": "int32" }, + "message": { "type": "string" }, + "data": { "$ref": "#/components/schemas/BlockRelationResponse" }, + "requestId": { "type": "string" }, + "timestamp": { "type": "string", "format": "date-time" } + } + }, + "ApiResponsePageResponseBlockRelationResponse": { + "type": "object", + "properties": { + "code": { "type": "integer", "format": "int32" }, + "message": { "type": "string" }, + "data": { + "type": "object", + "properties": { + "items": { "type": "array", "items": { "$ref": "#/components/schemas/BlockRelationResponse" } }, + "totalElements": { "type": "integer", "format": "int64" }, + "page": { "type": "integer", "format": "int32" }, + "size": { "type": "integer", "format": "int32" } } - } + }, + "requestId": { "type": "string" }, + "timestamp": { "type": "string", "format": "date-time" } } }, - "SendMessageRequest": { + "SkillCloudNode": { "type": "object", "properties": { - "conversationId": { + "id": { "type": "integer", "format": "int64" }, - "content": { - "type": "string", - "maxLength": 2000, - "minLength": 0 + "code": { + "type": "string" + }, + "name": { + "type": "string" } - }, - "required": [ - "content", - "conversationId" - ] + } }, - "SnapshotHistoryItem": { + "SkillCloudItem": { "type": "object", "properties": { - "id": { + "rank": { "type": "integer", - "format": "int64" + "format": "int32", + "minimum": 1, + "description": "当前筛选范围内的稳定排名,从 1 开始。" }, - "appealId": { + "skillId": { "type": "integer", "format": "int64" }, - "action": { - "type": "string" - }, - "dimension": { + "skillCode": { "type": "string" }, - "period": { + "nameZh": { "type": "string" }, - "beforeScore": { - "type": "number" + "nameEn": { + "type": "string", + "nullable": true }, - "afterScore": { - "type": "number" + "sourceType": { + "type": "string", + "enum": [ + "ESCO", + "O*NET" + ], + "description": "技能身份主来源。" }, - "beforeVersion": { - "type": "integer", - "format": "int64" + "sourceCode": { + "type": "string", + "description": "ESCO URI 或 O*NET 三元组标识。" }, - "afterVersion": { + "categoryId": { "type": "integer", "format": "int64" }, - "beforeRulesVersion": { + "categoryName": { "type": "string" }, - "afterRulesVersion": { - "type": "string" + "heat": { + "type": "number", + "nullable": true, + "description": "O*NET Importance;无映射为 null,且排在非空热度之后。" }, - "createdAt": { + "heatSourceCode": { "type": "string", - "format": "date-time" + "nullable": true, + "description": "热度来源 SOC。" + }, + "heatElementId": { + "type": "string", + "nullable": true, + "description": "热度来源 O*NET Element ID。" + }, + "heatProxy": { + "type": "boolean", + "description": "热度是否为代理映射。" } } }, - "StudentMatchRuleRequest": { + "SkillCloudResponse": { "type": "object", "properties": { - "requiredHrScore": { + "industry": { + "$ref": "#/components/schemas/SkillCloudNode" + }, + "occupation": { + "$ref": "#/components/schemas/SkillCloudNode" + }, + "category": { + "$ref": "#/components/schemas/SkillCloudNode", + "nullable": true + }, + "total": { "type": "integer", "format": "int32", - "maximum": 100, "minimum": 0 + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SkillCloudItem" + } } - }, - "required": [ - "requiredHrScore" - ] + } }, - "StudentPreferenceRequest": { + "SkillMatchResponse": { "type": "object", "properties": { - "expectedSalaryMin": { + "matchScore": { "type": "integer", "format": "int32", - "minimum": 0 + "minimum": 0, + "maximum": 100, + "description": "round(matchedCount*100/min(10,total));total=0 时为 0。" }, - "expectedSalaryMax": { + "matchedCount": { "type": "integer", "format": "int32", "minimum": 0 }, - "preferredCities": { - "type": "array", - "items": { - "type": "string", - "maxLength": 100, - "minLength": 1 - }, - "maxItems": 20, - "minItems": 0 - }, - "employmentTypes": { - "type": "array", - "items": { - "type": "string", - "maxLength": 100, - "minLength": 1 - }, - "maxItems": 20, - "minItems": 0 + "totalCount": { + "type": "integer", + "format": "int32", + "minimum": 0, + "description": "实际匹配集合大小 min(10,total)。" }, - "preferredIndustries": { + "matchedSkills": { "type": "array", "items": { - "type": "string", - "maxLength": 100, - "minLength": 1 - }, - "maxItems": 20, - "minItems": 0 + "$ref": "#/components/schemas/SkillCloudItem" + } }, - "preferredJobTypes": { + "missingSkills": { "type": "array", "items": { - "type": "string", - "maxLength": 100, - "minLength": 1 - }, - "maxItems": 20, - "minItems": 0 - }, - "remoteAllowed": { - "type": "boolean" - }, - "salaryRangeValid": { - "type": "boolean" + "$ref": "#/components/schemas/SkillCloudItem" + } } } }, - "StudentProfileUpdateRequest": { + "SkillLearningPathItem": { "type": "object", "properties": { - "realName": { - "type": "string", - "maxLength": 50, - "minLength": 0 + "priority": { + "type": "integer", + "format": "int32", + "minimum": 1 }, - "bio": { + "skillId": { + "type": "integer", + "format": "int64" + }, + "skillName": { "type": "string" }, - "location": { - "type": "string", - "maxLength": 100, - "minLength": 0 + "categoryId": { + "type": "integer", + "format": "int64" }, - "education": { - "type": "string", - "maxLength": 100, - "minLength": 0 + "categoryName": { + "type": "string" }, - "school": { + "suggestion": { "type": "string", - "maxLength": 150, - "minLength": 0 - }, - "graduationYear": { + "description": "按技能分类生成的通用建议,不含课程、AI、链接、时长或进度。" + } + } + }, + "SkillLearningPathResponse": { + "type": "object", + "properties": { + "total": { "type": "integer", - "format": "int32" - }, - "resumeUrl": { - "type": "string", - "maxLength": 500, - "minLength": 0 + "format": "int32", + "minimum": 0 }, - "skills": { + "items": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/SkillLearningPathItem" } } } }, - "SubScore": { + "ApiResponseSkillCloudResponse": { "type": "object", "properties": { - "name": { - "type": "string" + "code": { + "type": "integer", + "format": "int32" }, - "score": { - "type": "number" + "message": { + "type": "string" }, - "weight": { - "type": "number" + "data": { + "$ref": "#/components/schemas/SkillCloudResponse" }, - "description": { + "requestId": { "type": "string" + }, + "timestamp": { + "type": "string", + "format": "date-time" } } }, - "TagCount": { + "ApiResponseSkillMatchResponse": { "type": "object", "properties": { - "name": { - "type": "string" - }, - "count": { + "code": { "type": "integer", "format": "int32" + }, + "message": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/SkillMatchResponse" + }, + "requestId": { + "type": "string" + }, + "timestamp": { + "type": "string", + "format": "date-time" } } }, - "TrendItem": { + "ApiResponseSkillLearningPathResponse": { "type": "object", "properties": { - "period": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { "type": "string" }, - "overallScore": { - "type": "number" + "data": { + "$ref": "#/components/schemas/SkillLearningPathResponse" }, - "rulesVersion": { + "requestId": { "type": "string" }, - "dimensions": { - "type": "object", - "additionalProperties": { - "type": "number" - } + "timestamp": { + "type": "string", + "format": "date-time" } } }, - "VerificationDecisionRequest": { + "UnauthorizedResponse": { "type": "object", + "additionalProperties": false, + "required": [ + "code", + "message", + "timestamp" + ], "properties": { - "decision": { + "code": { + "type": "integer", + "format": "int32", + "enum": [ + 401 + ] + }, + "message": { "type": "string", - "minLength": 1 + "enum": [ + "not authenticated" + ] }, - "reason": { - "type": "string" + "timestamp": { + "type": "string", + "enum": [ + "" + ] } - }, - "required": [ - "decision" - ] + } }, - "VerificationSubmitRequest": { + "CompanyChecklistItemCreateRequest": { "type": "object", "properties": { - "materialsJson": { + "title": { "type": "string", - "minLength": 1 + "maxLength": 120, + "minLength": 0 + }, + "description": { + "type": "string", + "maxLength": 1000, + "minLength": 0 + }, + "category": { + "type": "string", + "minLength": 1, + "pattern": "DOCUMENT|ADMIN|EQUIPMENT|TRAINING|LOGISTICS|OTHER" + }, + "sortOrder": { + "type": "integer", + "format": "int32", + "maximum": 9999, + "minimum": 0 + }, + "required": { + "type": "boolean" + }, + "idempotencyKey": { + "type": "string", + "maxLength": 64, + "minLength": 0 } }, "required": [ - "materialsJson" + "category", + "title" ] }, - "WeightTemplateResponse": { + "CompanyChecklistItemUpdateRequest": { "type": "object", "properties": { - "code": { - "type": "string" - }, - "name": { - "type": "string" + "title": { + "type": "string", + "maxLength": 120, + "minLength": 0 }, "description": { - "type": "string" + "type": "string", + "maxLength": 1000, + "minLength": 0 }, - "weights": { - "type": "object", - "additionalProperties": { - "type": "number" - } - } - } - }, - "InterviewScoreSaveRequest": { - "type": "object", - "properties": { - "technicalScore": { - "type": "integer", - "minimum": 1, - "maximum": 5, - "nullable": true + "category": { + "type": "string", + "minLength": 1, + "pattern": "DOCUMENT|ADMIN|EQUIPMENT|TRAINING|LOGISTICS|OTHER" }, - "communicationScore": { + "sortOrder": { "type": "integer", - "minimum": 1, - "maximum": 5, - "nullable": true + "format": "int32", + "maximum": 9999, + "minimum": 0 }, - "problemSolvingScore": { - "type": "integer", - "minimum": 1, - "maximum": 5, - "nullable": true + "required": { + "type": "boolean" }, - "roleFitScore": { - "type": "integer", - "minimum": 1, - "maximum": 5, - "nullable": true + "active": { + "type": "boolean" }, - "professionalismScore": { + "version": { "type": "integer", - "minimum": 1, - "maximum": 5, - "nullable": true - }, - "recommendation": { - "type": "string", - "enum": [ - "STRONG_HIRE", - "HIRE", - "NEUTRAL", - "NO_HIRE" - ], - "nullable": true, - "description": "企业手动选择,不根据 overallScore 自动推导" - }, - "comment": { - "type": "string", - "nullable": true + "format": "int64", + "minimum": 0 } }, - "description": "评分草稿保存请求;overallScore 由服务端计算,客户端不得传入" + "required": [ + "active", + "category", + "required", + "sortOrder", + "title", + "version" + ] }, - "InterviewScoreResponse": { + "CompanyChecklistItemView": { "type": "object", "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "interviewId": { + "itemId": { "type": "integer", "format": "int64" }, - "applicationId": { - "type": "integer", - "format": "int64" + "source": { + "type": "string" }, - "status": { - "type": "string", - "enum": [ - "DRAFT", - "SUBMITTED" - ] + "itemKey": { + "type": "string" }, - "technicalScore": { - "type": "integer", - "nullable": true + "title": { + "type": "string" }, - "communicationScore": { - "type": "integer", - "nullable": true + "description": { + "type": "string" }, - "problemSolvingScore": { - "type": "integer", - "nullable": true + "category": { + "type": "string" }, - "roleFitScore": { - "type": "integer", - "nullable": true + "required": { + "type": "boolean" }, - "professionalismScore": { + "sortOrder": { "type": "integer", - "nullable": true - }, - "overallScore": { - "type": "number", - "format": "decimal", - "nullable": true, - "description": "由服务端 BigDecimal 计算:(t+c+ps+rf+p)/5,保留两位小数 HALF_UP" - }, - "recommendation": { - "type": "string", - "enum": [ - "STRONG_HIRE", - "HIRE", - "NEUTRAL", - "NO_HIRE" - ], - "nullable": true - }, - "comment": { - "type": "string", - "nullable": true + "format": "int32" }, - "submittedAt": { - "type": "string", - "format": "date-time", - "nullable": true + "active": { + "type": "boolean" }, "version": { - "type": "integer", - "format": "int64" - }, - "createdAt": { - "type": "string", - "format": "date-time" - }, - "updatedAt": { - "type": "string", - "format": "date-time" + "type": "integer", + "format": "int64" } - }, - "description": "面试评分响应(脱敏:不返回 companyUserId/studentUserId/evaluatorUserId/手机号/邮箱)" + } }, - "ApiResponseInterviewScoreResponse": { + "ApiResponseCompanyChecklistItemView": { "type": "object", "properties": { "code": { @@ -9916,7 +11825,7 @@ "type": "string" }, "data": { - "$ref": "#/components/schemas/InterviewScoreResponse" + "$ref": "#/components/schemas/CompanyChecklistItemView" }, "requestId": { "type": "string" @@ -9927,52 +11836,7 @@ } } }, - "InterviewScoreHistoryItem": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "scoreId": { - "type": "integer", - "format": "int64" - }, - "interviewId": { - "type": "integer", - "format": "int64" - }, - "fromStatus": { - "type": "string", - "enum": [ - "DRAFT", - "SUBMITTED" - ], - "nullable": true - }, - "toStatus": { - "type": "string", - "enum": [ - "DRAFT", - "SUBMITTED" - ] - }, - "actorRole": { - "type": "string", - "example": "COMPANY" - }, - "snapshot": { - "type": "object", - "additionalProperties": {} - }, - "createdAt": { - "type": "string", - "format": "date-time" - } - }, - "description": "评分历史条目(脱敏:不返回 actorUserId/手机号/邮箱)" - }, - "ApiResponseListInterviewScoreHistoryItem": { + "ApiResponseListCompanyChecklistItemView": { "type": "object", "properties": { "code": { @@ -9985,7 +11849,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/InterviewScoreHistoryItem" + "$ref": "#/components/schemas/CompanyChecklistItemView" } }, "requestId": { @@ -9997,338 +11861,227 @@ } } }, - "CreateReportRequest": { - "type": "object", - "properties": { - "targetType": { "type": "string", "enum": ["USER", "MESSAGE"], "description": "举报目标类型;USER 必填 targetUserId,MESSAGE 必填 messageId" }, - "targetUserId": { "type": "integer", "format": "int64", "description": "targetType=USER 时必填" }, - "messageId": { "type": "integer", "format": "int64", "description": "targetType=MESSAGE 时必填" }, - "category": { "type": "string", "maxLength": 40, "description": "举报分类,如 HARASSMENT" }, - "reason": { "type": "string", "minLength": 1, "maxLength": 1000 }, - "evidenceUrls": { "type": "array", "items": { "type": "string", "maxLength": 500 }, "description": "最多 5 个,仅 http/https,最多 500 字符" }, - "idempotencyKey": { "type": "string", "maxLength": 64, "description": "幂等键(可选)" } - } - }, - "ReportDecisionRequest": { - "type": "object", - "properties": { - "decision": { "type": "string", "enum": ["RESOLVED", "REJECTED"] }, - "note": { "type": "string", "maxLength": 1000, "description": "REJECTED 必填" } - } - }, - "ReportResponse": { - "type": "object", - "properties": { - "id": { "type": "integer", "format": "int64" }, - "targetType": { "type": "string", "enum": ["USER", "MESSAGE"] }, - "targetUserId": { "type": "integer", "format": "int64" }, - "messageId": { "type": "integer", "format": "int64" }, - "reportedUserId": { "type": "integer", "format": "int64" }, - "category": { "type": "string" }, - "reason": { "type": "string" }, - "evidenceUrls": { "type": "array", "items": { "type": "string" } }, - "status": { "type": "string", "enum": ["PENDING", "RESOLVED", "REJECTED"] }, - "decision": { "type": "string", "enum": ["RESOLVED", "REJECTED"] }, - "decisionNote": { "type": "string" }, - "decidedAt": { "type": "string", "format": "date-time" }, - "createdAt": { "type": "string", "format": "date-time" }, - "updatedAt": { "type": "string", "format": "date-time" }, - "messagePreview": { "type": "string", "description": "MESSAGE 举报的脱敏消息预览,不返回会话其他敏感内容" }, - "reportedUser": { "$ref": "#/components/schemas/ReportResponseReportedUser" }, - "history": { "type": "array", "items": { "$ref": "#/components/schemas/ReportResponseDecisionHistoryItem" } } - } - }, - "ReportResponseReportedUser": { - "type": "object", - "properties": { - "userId": { "type": "integer", "format": "int64" }, - "nickname": { "type": "string" }, - "role": { "type": "string" } - } - }, - "ReportResponseDecisionHistoryItem": { - "type": "object", - "properties": { - "id": { "type": "integer", "format": "int64" }, - "fromStatus": { "type": "string" }, - "toStatus": { "type": "string" }, - "actorUserId": { "type": "integer", "format": "int64" }, - "actorRole": { "type": "string" }, - "note": { "type": "string" }, - "createdAt": { "type": "string", "format": "date-time" } - } - }, - "BlockRelationResponse": { - "type": "object", - "properties": { - "id": { "type": "integer", "format": "int64" }, - "blockedUserId": { "type": "integer", "format": "int64" }, - "nickname": { "type": "string" }, - "createdAt": { "type": "string", "format": "date-time" } - } - }, - "ApiResponseReportResponse": { - "type": "object", - "properties": { - "code": { "type": "integer", "format": "int32" }, - "message": { "type": "string" }, - "data": { "$ref": "#/components/schemas/ReportResponse" }, - "requestId": { "type": "string" }, - "timestamp": { "type": "string", "format": "date-time" } - } - }, - "ApiResponsePageResponseReportResponse": { + "EffectiveChecklistResponse": { "type": "object", "properties": { - "code": { "type": "integer", "format": "int32" }, - "message": { "type": "string" }, - "data": { - "type": "object", - "properties": { - "items": { "type": "array", "items": { "$ref": "#/components/schemas/ReportResponse" } }, - "totalElements": { "type": "integer", "format": "int64" }, - "page": { "type": "integer", "format": "int32" }, - "size": { "type": "integer", "format": "int32" } - } + "companyId": { + "type": "integer", + "format": "int64" }, - "requestId": { "type": "string" }, - "timestamp": { "type": "string", "format": "date-time" } - } - }, - "ApiResponseBlockRelationResponse": { - "type": "object", - "properties": { - "code": { "type": "integer", "format": "int32" }, - "message": { "type": "string" }, - "data": { "$ref": "#/components/schemas/BlockRelationResponse" }, - "requestId": { "type": "string" }, - "timestamp": { "type": "string", "format": "date-time" } - } - }, - "ApiResponsePageResponseBlockRelationResponse": { - "type": "object", - "properties": { - "code": { "type": "integer", "format": "int32" }, - "message": { "type": "string" }, - "data": { - "type": "object", - "properties": { - "items": { "type": "array", "items": { "$ref": "#/components/schemas/BlockRelationResponse" } }, - "totalElements": { "type": "integer", "format": "int64" }, - "page": { "type": "integer", "format": "int32" }, - "size": { "type": "integer", "format": "int32" } - } + "companyName": { + "type": "string" }, - "requestId": { "type": "string" }, - "timestamp": { "type": "string", "format": "date-time" } + "customized": { + "type": "boolean" + }, + "totalItems": { + "type": "integer", + "format": "int32" + }, + "completedItems": { + "type": "integer", + "format": "int32" + }, + "completionRate": { + "type": "number", + "format": "double" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ChecklistItemView" + } + } } }, - "SkillCloudNode": { + "ChecklistItemView": { "type": "object", "properties": { - "id": { + "itemId": { "type": "integer", "format": "int64" }, - "code": { + "source": { "type": "string" }, - "name": { + "itemKey": { "type": "string" - } - } - }, - "SkillCloudItem": { - "type": "object", - "properties": { - "rank": { - "type": "integer", - "format": "int32", - "minimum": 1, - "description": "当前筛选范围内的稳定排名,从 1 开始。" }, - "skillId": { - "type": "integer", - "format": "int64" + "title": { + "type": "string" }, - "skillCode": { + "description": { "type": "string" }, - "nameZh": { + "category": { "type": "string" }, - "nameEn": { - "type": "string", - "nullable": true + "required": { + "type": "boolean" }, - "sourceType": { - "type": "string", - "enum": [ - "ESCO", - "O*NET" - ], - "description": "技能身份主来源。" + "sortOrder": { + "type": "integer", + "format": "int32" }, - "sourceCode": { + "active": { + "type": "boolean" + }, + "completed": { + "type": "boolean" + }, + "completedAt": { "type": "string", - "description": "ESCO URI 或 O*NET 三元组标识。" + "format": "date-time" }, - "categoryId": { + "progressVersion": { "type": "integer", "format": "int64" + } + } + }, + "ApiResponseEffectiveChecklistResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" }, - "categoryName": { - "type": "string" - }, - "heat": { - "type": "number", - "nullable": true, - "description": "O*NET Importance;无映射为 null,且排在非空热度之后。" + "message": { + "type": "string" }, - "heatSourceCode": { - "type": "string", - "nullable": true, - "description": "热度来源 SOC。" + "data": { + "$ref": "#/components/schemas/EffectiveChecklistResponse" }, - "heatElementId": { - "type": "string", - "nullable": true, - "description": "热度来源 O*NET Element ID。" + "requestId": { + "type": "string" }, - "heatProxy": { - "type": "boolean", - "description": "热度是否为代理映射。" + "timestamp": { + "type": "string", + "format": "date-time" } } }, - "SkillCloudResponse": { + "ProgressUpdateRequest": { "type": "object", "properties": { - "industry": { - "$ref": "#/components/schemas/SkillCloudNode" - }, - "occupation": { - "$ref": "#/components/schemas/SkillCloudNode" - }, - "category": { - "$ref": "#/components/schemas/SkillCloudNode", - "nullable": true + "completed": { + "type": "boolean" }, - "total": { + "version": { "type": "integer", - "format": "int32", + "format": "int64", "minimum": 0 - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/SkillCloudItem" - } } - } + }, + "required": [ + "completed" + ] }, - "SkillMatchResponse": { + "ProgressUpdateResponse": { "type": "object", "properties": { - "matchScore": { + "itemId": { "type": "integer", - "format": "int32", - "minimum": 0, - "maximum": 100, - "description": "round(matchedCount*100/min(10,total));total=0 时为 0。" + "format": "int64" }, - "matchedCount": { + "completed": { + "type": "boolean" + }, + "completedAt": { + "type": "string", + "format": "date-time" + }, + "progressVersion": { "type": "integer", - "format": "int32", - "minimum": 0 + "format": "int64" }, - "totalCount": { + "totalItems": { "type": "integer", - "format": "int32", - "minimum": 0, - "description": "实际匹配集合大小 min(10,total)。" + "format": "int32" }, - "matchedSkills": { - "type": "array", - "items": { - "$ref": "#/components/schemas/SkillCloudItem" - } + "completedItems": { + "type": "integer", + "format": "int32" }, - "missingSkills": { - "type": "array", - "items": { - "$ref": "#/components/schemas/SkillCloudItem" - } + "completionRate": { + "type": "number", + "format": "double" } } }, - "SkillLearningPathItem": { + "ApiResponseProgressUpdateResponse": { "type": "object", "properties": { - "priority": { - "type": "integer", - "format": "int32", - "minimum": 1 - }, - "skillId": { + "code": { "type": "integer", - "format": "int64" + "format": "int32" }, - "skillName": { + "message": { "type": "string" }, - "categoryId": { - "type": "integer", - "format": "int64" + "data": { + "$ref": "#/components/schemas/ProgressUpdateResponse" }, - "categoryName": { + "requestId": { "type": "string" }, - "suggestion": { + "timestamp": { "type": "string", - "description": "按技能分类生成的通用建议,不含课程、AI、链接、时长或进度。" + "format": "date-time" } } }, - "SkillLearningPathResponse": { + "CompanyKnowledgeUpdateRequest": { "type": "object", "properties": { - "total": { + "culture": { + "type": "string", + "maxLength": 2000, + "minLength": 0 + }, + "workPolicies": { + "type": "string", + "maxLength": 4000, + "minLength": 0 + }, + "onboardingGuide": { + "type": "string", + "maxLength": 4000, + "minLength": 0 + }, + "version": { "type": "integer", - "format": "int32", + "format": "int64", "minimum": 0 - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/SkillLearningPathItem" - } } } }, - "ApiResponseSkillCloudResponse": { + "CompanyKnowledgeView": { "type": "object", "properties": { - "code": { - "type": "integer", - "format": "int32" + "configured": { + "type": "boolean" }, - "message": { + "culture": { "type": "string" }, - "data": { - "$ref": "#/components/schemas/SkillCloudResponse" + "workPolicies": { + "type": "string" }, - "requestId": { + "onboardingGuide": { "type": "string" }, - "timestamp": { + "updatedAt": { "type": "string", "format": "date-time" + }, + "version": { + "type": "integer", + "format": "int64" } } }, - "ApiResponseSkillMatchResponse": { + "ApiResponseCompanyKnowledgeView": { "type": "object", "properties": { "code": { @@ -10339,7 +12092,7 @@ "type": "string" }, "data": { - "$ref": "#/components/schemas/SkillMatchResponse" + "$ref": "#/components/schemas/CompanyKnowledgeView" }, "requestId": { "type": "string" @@ -10350,55 +12103,145 @@ } } }, - "ApiResponseSkillLearningPathResponse": { + "CompanyKnowledgeOverviewResponse": { "type": "object", "properties": { - "code": { + "company": { + "$ref": "#/components/schemas/CompanyView" + }, + "knowledgeState": { + "type": "string" + }, + "knowledge": { + "$ref": "#/components/schemas/KnowledgeView" + }, + "reviewSummary": { + "$ref": "#/components/schemas/ReviewSummaryView" + } + } + }, + "CompanyView": { + "type": "object", + "properties": { + "id": { "type": "integer", - "format": "int32" + "format": "int64" }, - "message": { + "name": { "type": "string" }, - "data": { - "$ref": "#/components/schemas/SkillLearningPathResponse" + "logoUrl": { + "type": "string" }, - "requestId": { + "description": { "type": "string" }, - "timestamp": { + "industry": { + "type": "string" + }, + "website": { + "type": "string" + }, + "address": { + "type": "string" + }, + "verified": { + "type": "boolean" + } + } + }, + "KnowledgeView": { + "type": "object", + "properties": { + "culture": { + "type": "string" + }, + "workPolicies": { + "type": "string" + }, + "onboardingGuide": { + "type": "string" + }, + "updatedAt": { "type": "string", "format": "date-time" + }, + "version": { + "type": "integer", + "format": "int64" } } }, - "UnauthorizedResponse": { + "ReviewSummaryView": { + "type": "object", + "properties": { + "state": { + "type": "string" + }, + "totalReviews": { + "type": "integer", + "format": "int32" + }, + "averageRating": { + "type": "number", + "format": "double" + }, + "ratingDistribution": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int32" + } + }, + "topTags": { + "type": "array", + "items": { + "type": "string" + } + }, + "dimensionSummary": { + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/DimensionSummaryView" + } + } + } + }, + "DimensionSummaryView": { + "type": "object", + "properties": { + "averageScore": { + "type": "number", + "format": "double" + }, + "sampleCount": { + "type": "integer", + "format": "int64" + }, + "dataState": { + "type": "string" + } + } + }, + "ApiResponseCompanyKnowledgeOverviewResponse": { "type": "object", - "additionalProperties": false, - "required": [ - "code", - "message", - "timestamp" - ], "properties": { "code": { "type": "integer", - "format": "int32", - "enum": [ - 401 - ] + "format": "int32" }, "message": { - "type": "string", - "enum": [ - "not authenticated" - ] + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/CompanyKnowledgeOverviewResponse" + }, + "requestId": { + "type": "string" }, "timestamp": { "type": "string", - "enum": [ - "" - ] + "format": "date-time" } } } diff --git a/docs/verify_openapi.py b/docs/verify_openapi.py index 6f3b5ca..05b8472 100644 --- a/docs/verify_openapi.py +++ b/docs/verify_openapi.py @@ -6,9 +6,55 @@ p = PROJECT_ROOT / 'docs' / 'openapi-rc.json' d = json.load(open(p, encoding='utf-8')) print('paths:', len(d['paths'])) -assert len(d['paths']) == 129, f"Expected 129 paths, got {len(d['paths'])}" paths = d['paths'] +onboarding_paths = [ + '/onboarding/companies/{companyId}/checklist', + '/onboarding/companies/{companyId}/checklist/items/{itemId}/completion', + '/onboarding/companies/{companyId}/knowledge', + '/onboarding/company/checklist-items', + '/onboarding/company/checklist-items/{itemId}', + '/onboarding/company/knowledge', +] +for path in onboarding_paths: + assert path in paths, f'Missing onboarding path: {path}' + +onboarding_operations = { + ('/onboarding/companies/{companyId}/checklist', 'get'): 'getEffectiveChecklist', + ('/onboarding/companies/{companyId}/checklist/items/{itemId}/completion', 'put'): 'updateCompletion', + ('/onboarding/companies/{companyId}/knowledge', 'get'): 'getCompanyKnowledgeOverview', + ('/onboarding/company/checklist-items', 'get'): 'listCompanyChecklistItems', + ('/onboarding/company/checklist-items', 'post'): 'createCompanyChecklistItem', + ('/onboarding/company/checklist-items/{itemId}', 'put'): 'updateCompanyChecklistItem', + ('/onboarding/company/checklist-items/{itemId}', 'delete'): 'deleteCompanyChecklistItem', + ('/onboarding/company/knowledge', 'get'): 'getCompanyKnowledge', + ('/onboarding/company/knowledge', 'put'): 'upsertCompanyKnowledge', +} +http_methods = {'get', 'post', 'put', 'patch', 'delete', 'options', 'head', 'trace'} +for (path, method), operation_id in onboarding_operations.items(): + assert method in paths[path], f'Missing {method.upper()} {path}' + assert paths[path][method].get('operationId') == operation_id, f'Wrong operationId for {method.upper()} {path}' + matching_operations = [ + (candidate_method, candidate_path) + for candidate_path, path_item in paths.items() + for candidate_method, operation in path_item.items() + if candidate_method in http_methods + and operation.get('operationId') == operation_id + ] + assert matching_operations == [(method, path)], ( + f'{operation_id} must be globally unique: expected {method.upper()} {path}, got {matching_operations}' + ) + assert paths[path][method].get('security') == [{'bearerAuth': []}], ( + f'{method.upper()} {path} must require bearerAuth' + ) +assert sum( + 1 + for path_item in paths.values() + for method in path_item + if method in http_methods +) == 172, 'Expected 172 operations after N04 onboarding' + +assert len(d['paths']) == 135, f"Expected 135 paths, got {len(d['paths'])}" required = [ '/review-weight-templates', @@ -205,6 +251,65 @@ print(" Offer advance endpoints all present") print(" 26 operations all present") print(" Account deletion endpoints present (3 operations)") +onboarding_success_refs = { + ('/onboarding/companies/{companyId}/checklist', 'get'): 'ApiResponseEffectiveChecklistResponse', + ('/onboarding/companies/{companyId}/checklist/items/{itemId}/completion', 'put'): 'ApiResponseProgressUpdateResponse', + ('/onboarding/companies/{companyId}/knowledge', 'get'): 'ApiResponseCompanyKnowledgeOverviewResponse', + ('/onboarding/company/checklist-items', 'get'): 'ApiResponseListCompanyChecklistItemView', + ('/onboarding/company/checklist-items', 'post'): 'ApiResponseCompanyChecklistItemView', + ('/onboarding/company/checklist-items/{itemId}', 'put'): 'ApiResponseCompanyChecklistItemView', + ('/onboarding/company/checklist-items/{itemId}', 'delete'): 'ApiResponseCompanyChecklistItemView', + ('/onboarding/company/knowledge', 'get'): 'ApiResponseCompanyKnowledgeView', + ('/onboarding/company/knowledge', 'put'): 'ApiResponseCompanyKnowledgeView', +} +for (path, method), schema_name in onboarding_success_refs.items(): + response = paths[path][method]['responses']['200'] + ref = response['content']['*/*']['schema']['$ref'] + assert ref == f'#/components/schemas/{schema_name}', f'Wrong success schema for {method.upper()} {path}: {ref}' + for status_code in ('401', '403'): + assert status_code in paths[path][method]['responses'], f'Missing {status_code} for {method.upper()} {path}' + unauthorized = paths[path][method]['responses']['401'] + assert unauthorized['content']['application/json']['schema']['$ref'] == '#/components/schemas/UnauthorizedResponse' + forbidden = paths[path][method]['responses']['403'] + assert forbidden['content']['application/json']['schema']['$ref'] == '#/components/schemas/ApiResponse' + assert forbidden['content']['application/json']['example']['code'] == 2002 + +onboarding_schema_fields = { + 'CompanyChecklistItemCreateRequest': {'title', 'description', 'category', 'sortOrder', 'required', 'idempotencyKey'}, + 'CompanyChecklistItemUpdateRequest': {'title', 'description', 'category', 'sortOrder', 'required', 'active', 'version'}, + 'CompanyChecklistItemView': {'itemId', 'source', 'itemKey', 'title', 'description', 'category', 'required', 'sortOrder', 'active', 'version'}, + 'EffectiveChecklistResponse': {'companyId', 'companyName', 'customized', 'totalItems', 'completedItems', 'completionRate', 'items'}, + 'ChecklistItemView': {'itemId', 'source', 'itemKey', 'title', 'description', 'category', 'required', 'sortOrder', 'active', 'completed', 'completedAt', 'progressVersion'}, + 'ProgressUpdateRequest': {'completed', 'version'}, + 'ProgressUpdateResponse': {'itemId', 'completed', 'completedAt', 'progressVersion', 'totalItems', 'completedItems', 'completionRate'}, + 'CompanyKnowledgeUpdateRequest': {'culture', 'workPolicies', 'onboardingGuide', 'version'}, + 'CompanyKnowledgeView': {'configured', 'culture', 'workPolicies', 'onboardingGuide', 'updatedAt', 'version'}, + 'CompanyKnowledgeOverviewResponse': {'company', 'knowledgeState', 'knowledge', 'reviewSummary'}, + 'CompanyView': {'id', 'name', 'logoUrl', 'description', 'industry', 'website', 'address', 'verified'}, + 'KnowledgeView': {'culture', 'workPolicies', 'onboardingGuide', 'updatedAt', 'version'}, + 'ReviewSummaryView': {'state', 'totalReviews', 'averageRating', 'ratingDistribution', 'topTags', 'dimensionSummary'}, + 'DimensionSummaryView': {'averageScore', 'sampleCount', 'dataState'}, +} +for schema_name, expected_fields in onboarding_schema_fields.items(): + schema = schemas.get(schema_name) + assert schema is not None, f'Missing onboarding schema: {schema_name}' + assert expected_fields <= set(schema.get('properties', {})), ( + f'Onboarding schema fields missing: {schema_name}' + ) + +onboarding_error_paths = { + 9701: ('/onboarding/company/checklist-items/{itemId}', 'put', '404'), + 9702: ('/onboarding/company/checklist-items/{itemId}', 'put', '409'), + 9703: ('/onboarding/company/checklist-items', 'post', '409'), + 9704: ('/onboarding/company/checklist-items', 'post', '409'), + 9705: ('/onboarding/companies/{companyId}/checklist/items/{itemId}/completion', 'put', '409'), + 9706: ('/onboarding/company/knowledge', 'put', '409'), +} +for error_code, (path, method, status_code) in onboarding_error_paths.items(): + response_text = json.dumps(paths[path][method]['responses'][status_code], ensure_ascii=False) + assert str(error_code) in response_text, f'Missing onboarding error code {error_code} in {method.upper()} {path}' +print(' N04 onboarding endpoints and schemas present (9 operations / 6 paths)') + data = json.dumps(d) assert '"" : "#/components/schemas/' not in data, "Found empty ref key" print("No empty ref keys found")