# N04 入职清单与公司知识介绍执行契约 ## Intent Lock **问题**:产品一期要求提供入职清单和公司知识介绍,但当前后端没有对应表、接口、权限或测试。学生无法获得“通用准备项 + 企业自定义项”的有效清单,无法保存个人完成状态,也无法集中查看企业基础资料、文化制度、入职指南和公开评价摘要;企业无法维护本公司差异内容。 **意图**:新增最小可用的 `onboarding` 后端模块。通过 V38/V39 建立三张业务表和通用清单种子;提供 9 个确定性接口;STUDENT 读取有效清单、更新本人完成状态、读取公司知识,COMPANY 仅维护本企业自定义清单与知识内容;公司知识使用企业基础资料、企业维护内容及公开评价摘要,其中公开评价少于 3 条时不返回均值。不调用 AI,不引入 ADMIN 内容治理。 **完成证明**:V38/V39 在 H2 与真实 MySQL 成功执行;9 个接口的权限、所有权、幂等、并发、回退、排序、完成率、评价摘要和小样本保护均通过自动化测试;OpenAPI、接口文档和前端联调文档与运行时一致;`mvn clean verify` 与 `python docs/verify_openapi.py` 通过。 ## Scope Fence ### 允许范围 - 新增 `mydreamjob-server/src/main/resources/db/migration/V38__onboarding_checklist_and_company_knowledge.sql` 和 `V39__onboarding_standard_checklist_seed.sql`。 - 新增 `com.mydreamjob.onboarding` 领域包,包括实体、Repository、Service、Controller、DTO 和测试。 - 修改 `SecurityConfig.java`,使 `/onboarding/**` 仅允许认证访问,并在服务层执行 STUDENT/COMPANY 角色与公司所有权校验。 - 修改 `ErrorCode.java`,新增 9701-9706。 - 更新 `docs/openapi-rc.json`、`docs/onboarding-api.md`、`docs/frontend-integration.md`、`docs/backend-development-roadmap.md` 和 `README.md`。 - 只读复用 `Company`、`CompanyRepository`、`Job`、`JobReview`、`JobReviewDimensionScore` 及其 Repository。 - 采用 9 个固定接口: - `GET /api/v1/onboarding/companies/{companyId}/checklist` - `PUT /api/v1/onboarding/companies/{companyId}/checklist/items/{itemId}/completion` - `GET /api/v1/onboarding/companies/{companyId}/knowledge` - `GET /api/v1/onboarding/company/checklist-items` - `POST /api/v1/onboarding/company/checklist-items` - `PUT /api/v1/onboarding/company/checklist-items/{itemId}` - `DELETE /api/v1/onboarding/company/checklist-items/{itemId}?version={version}` - `GET /api/v1/onboarding/company/knowledge` - `PUT /api/v1/onboarding/company/knowledge` ### 禁止范围 - 不修改 V1-V37 历史迁移或既有表结构。 - 不修改既有企业、岗位、Offer、评价和维度评分接口的路径、请求、响应或权限语义。 - 不实现 ADMIN 模板管理、审核发布、版本回滚、CMS 或内容治理后台。 - 不实现通知、内容推荐、试用期提醒、入职回访、在职反馈或前端页面。 - 不按岗位、投递或 Offer 生成个性化清单,不引入入职资格门槛。 - 不接 AI、第三方内容平台、外部搜索或实时同步。 - 不存储或渲染 HTML/富文本;知识字段按纯文本处理并限制长度。 - 不在响应或日志中输出 `ownerUserId`、员工用户 ID、手机号、邮箱、Token、IP、User-Agent、评价哈希或审核原因。 - 不启动或停止 Docker;真实 MySQL/Docker 黑盒验收需要用户单独授权。 ## Approved Behavior | ID | 已批准行为 | 批次 | | --- | --- | --- | | REQ-OC-1 | STUDENT 按存在的 `companyId` 获取有效清单;响应包含通用项与企业活动自定义项、排序、分类、必填标记、个人完成状态、完成数、总数、完成率和 `customized`;企业不存在返回 404。 | B1、B2 | | REQ-OC-2 | STUDENT 通过 PUT 将本人对有效条目的状态设置为完成或未完成;相同状态重复提交幂等;取消完成清除 `completedAt`;跨企业或非活动 itemId 返回 404;响应返回版本和最新完成率。 | B1、B2、B3 | | REQ-OC-3 | COMPANY 创建本企业自定义项;请求校验标题、说明、分类、排序和必填标记;相同 `idempotencyKey` 与相同请求体返回原条目,不同请求体返回 409 `ONBOARDING_IDEMPOTENCY_CONFLICT`;活动自定义项达到 50 条后返回 409 `ONBOARDING_ITEM_LIMIT_EXCEEDED`。 | B1、B2、B3 | | REQ-OC-4 | COMPANY 查询本企业全部自定义项;使用当前 `version` 更新标题、说明、分类、排序、必填和活动状态;过期版本返回 409 `ONBOARDING_CHECKLIST_ITEM_CONCURRENT_UPDATE`;不得读取或修改其他企业条目。 | B1、B2、B3 | | REQ-OC-5 | COMPANY 使用当前 `version` 软删除本企业条目;条目从学生有效清单消失,历史和完成记录保留;重复删除幂等成功;跨企业删除返回 404 且状态不变。 | B1、B2、B3 | | REQ-OC-6 | 学生接口仅允许 STUDENT;企业配置接口仅允许 COMPANY 且只能操作当前用户拥有的企业;未登录返回 401,角色不符返回 403,无企业资料返回 404;所有响应使用白名单 DTO。 | B2、B3 | | REQ-CK-1 | STUDENT 按存在的 `companyId` 读取公司知识;企业有知识记录返回 `CONFIGURED`,无记录返回 `BASIC_ONLY` 和企业基础资料;不得返回 `ownerUserId` 等内部字段;企业不存在返回 404。 | B1、B2 | | REQ-CK-2 | 公司评价摘要只使用该公司岗位下 `PUBLISHED` 且审核为 `AUTO_APPROVED/APPROVED` 的评价;无评价返回 `EMPTY`;少于 3 条返回 `INSUFFICIENT` 和不含均值的真实数量;至少 3 条返回平均分、评分分布、热门标签和八维摘要,维度返回样本数和 `SUFFICIENT/INSUFFICIENT`。 | B1、B2、B3 | | REQ-CK-3 | COMPANY 读取本企业知识配置;无记录返回 `configured=false`、空内容和版本 0;有记录返回内容、更新时间和当前版本。 | B1、B2 | | REQ-CK-4 | COMPANY Upsert 本企业知识内容;文化最多 2000 字符,工作制度和入职指南最多 4000 字符,至少一个字段非空,按纯文本处理;已有记录使用当前版本,冲突返回 409 `ONBOARDING_KNOWLEDGE_CONCURRENT_UPDATE`。 | B1、B2、B3 | | REQ-CK-5 | 公司知识学生读取仅允许 STUDENT;企业配置仅允许 COMPANY 且只操作本企业;非角色访问返回 401/403;不存在的公司返回 404。 | B2、B3 | | REQ-CK-6 | 本变更只读复用既有企业、岗位、评价和维度数据,不修改历史表结构和既有接口语义;既有测试与 OpenAPI 路径必须回归通过。 | B3 | 需求覆盖结果:两份 spec 中全部 12 条 Requirement 的 `MUST`/`SHALL` 行为均已映射到 Approved Behavior、至少一个测试义务和至少一个执行批次,无未映射需求。 ## Architecture and Interface Constraints - 数据模型固定为 `onboarding_checklist_items`、`student_onboarding_progress`、`company_knowledge` 三张表。 - `onboarding_checklist_items` 使用 `scope_type/scope_id/item_key` 区分并唯一化 STANDARD 与 COMPANY 条目;`company_id` 仅对企业项建立外键。 - `student_onboarding_progress(student_user_id, item_id)` 唯一;完成状态采用绝对值 Upsert,`completedAt` 仅在完成时存在。 - `company_knowledge.company_id` 唯一;知识内容按纯文本存储并使用 `@Version`。 - 通用清单由 V39 固定种子提供,企业自定义项只增量追加,不允许覆盖或裁剪通用项。 - 学生有效清单必须在一次查询中合并活动通用项和企业项,并使用 `sortOrder ASC, id ASC` 稳定排序。 - 完成率计算为 `completedItems * 100.0 / totalItems`,保留一位小数并最大为 100.0;空清单为 0.0。 - 创建清单项使用 `idempotencyKey` 和规范化请求体 SHA-256;更新、删除、完成状态和知识内容使用乐观锁语义。 - 公司评价摘要必须通过公司下所有岗位聚合,不得只使用单个岗位汇总;热门标签从公开评价 tags 中计数,按次数降序、字典序升序,最多 10 个。 - 八维摘要复用 `ReviewDimension`,八个维度均返回,缺失维度样本数为 0、`dataState=INSUFFICIENT`。 - 所有响应使用 `ApiResponse`;请求 ID 从 `X-Request-Id` 读取。 - 新增错误码固定为: - `9701 ONBOARDING_CHECKLIST_ITEM_NOT_FOUND` - `9702 ONBOARDING_CHECKLIST_ITEM_CONCURRENT_UPDATE` - `9703 ONBOARDING_ITEM_LIMIT_EXCEEDED` - `9704 ONBOARDING_IDEMPOTENCY_CONFLICT` - `9705 ONBOARDING_PROGRESS_CONCURRENT_UPDATE` - `9706 ONBOARDING_KNOWLEDGE_CONCURRENT_UPDATE` - 复用 `FORBIDDEN`、`UNAUTHORIZED`、`BAD_REQUEST` 和 `COMPANY_NOT_FOUND`。 - 迁移编号以当前仓库下一个可用版本为准;若 V38/V39 被其他变更占用,必须回退规划阶段刷新编号和契约。 ## Execution Batches ### Batch B1: 数据模型、迁移与只读聚合基础 - 覆盖任务:T1、T2。 - 交付物:V38/V39 迁移、三个实体、三个模块 Repository、现有评价跨岗位聚合查询、迁移和 Repository 集成测试。 - 完成定义:H2/MySQL 可创建三张表、约束和索引;通用种子数量和排序稳定;活动通用项与企业项可合并查询;学生完成记录可批量读取;公司公开评价可按评分、维度和 tags 聚合。 - 依赖:无。 - 评审门:B1 完成并由 `code-reviewer` 记录 `pass` 后才能开始 B2。 ### Batch B2: 学生清单、企业配置与公司知识接口 - 覆盖任务:T3、T4、T5。 - 交付物:两个 Controller、两个 Service、全部 DTO、清单合并与完成状态、自定义项 CRUD、知识 Upsert、基础信息和评价摘要组装、Service 与 MockMvc 专项测试。 - 完成定义:REQ-OC-1 至 REQ-OC-6、REQ-CK-1 至 REQ-CK-5 的接口和服务行为实现;企业无自定义项时回退通用清单;无知识时返回基础信息;公开评价少于 3 条不返回均值。 - 依赖:B1 通过评审。 - 评审门:B2 完成并由 `code-reviewer` 记录 `pass` 后才能开始 B3。 ### Batch B3: 权限、并发、契约、全量测试与文档 - 覆盖任务:T6、T7、T8。 - 交付物:`SecurityConfig` 与 `ErrorCode` 更新、角色和所有权集成测试、幂等与乐观锁冲突测试、隐私断言、端到端测试、OpenAPI、接口文档和前端联调文档。 - 完成定义:匿名 401、非角色 403、企业不存在 404、跨企业 404、版本冲突 409、幂等冲突 409、活动项上限 409 均符合契约;OpenAPI 包含 9 个路径并运行时一致;`mvn clean verify` 与 `python docs/verify_openapi.py` 通过。 - 依赖:B2 通过评审。 - 评审门:B3 完成并由 `code-reviewer` 记录 `pass` 后才能进入验证和收尾。 ## Test Obligations | ID | 测试义务 | 批次 | 证据 | | --- | --- | --- | --- | | TO-1 | V38/V39 创建三张表、主外键、唯一键和索引;通用种子内容、分类和排序稳定。 | B1 | `mvn -Dtest=OnboardingMigrationTest,OnboardingSeedDataIntegrationTest test` | | TO-2 | 企业无自定义项回退通用清单;有项时合并排序、来源和 category 正确。 | B1、B2 | Repository 集成测试、`OnboardingChecklistServiceTest` | | TO-3 | 完成状态首次设置、重复相同状态、取消完成、跨企业 itemId 和完成率。 | B2 | `mvn -Dtest=OnboardingChecklistServiceTest,OnboardingChecklistControllerTest test` | | TO-4 | 创建校验、幂等重放、请求体哈希冲突和 50 条活动项上限。 | B2、B3 | `OnboardingCompanyChecklistServiceTest`、持久化测试 | | TO-5 | 企业列表、更新、软删除、版本冲突、重复删除和跨企业隔离。 | B2、B3 | `OnboardingCompanyChecklistServiceTest`、`OnboardingConcurrencyIntegrationTest` | | TO-6 | STUDENT/COMPANY/ADMIN/匿名权限矩阵和白名单响应。 | B2、B3 | `OnboardingAuthorizationIntegrationTest`、`OnboardingPrivacyTest` | | TO-7 | 公司知识无记录回退、企业读取、首次创建、更新和长度校验。 | B2 | `mvn -Dtest=CompanyKnowledgeServiceTest,CompanyKnowledgeControllerTest test` | | TO-8 | 评价只取公开记录;0、1、2、3 条边界;评分分布、标签和八维摘要。 | B1、B2 | 聚合 Repository 测试、`CompanyKnowledgeServiceTest` | | TO-9 | 知识版本冲突返回 9706,不覆盖新内容。 | B2、B3 | `OnboardingConcurrencyIntegrationTest` | | TO-10 | 9 个接口的状态码、请求/响应 DTO、错误码和 OpenAPI 一致性。 | B3 | `OnboardingEndToEndIntegrationTest`、`python docs/verify_openapi.py` | | TO-11 | 迁移、种子、实体、Repository、Service、Controller 和权限集成场景全部可重复。 | B1、B2、B3 | `mvn -Dtest=Onboarding*Test test` | | TO-12 | 既有模块回归、完整打包、静态契约和无敏感字段泄露。 | B3 | `mvn clean verify`、`python docs/verify_openapi.py` | ## Build Rules - 每个批次按 TDD 先写失败测试,再实现最小行为,不得跳过测试。 - 只修改允许范围内文件;发现需要修改已验收接口、历史迁移或既有响应语义时立即停止并触发 Rewind。 - 迁移和种子数据必须可在 H2(`MODE=MySQL`)与真实 MySQL 执行,不依赖外部文件路径。 - 所有清单排序、完成率、评价汇总和标签排序必须确定性;禁止随机数、当前时间或请求顺序影响结果。 - 新增日志不得包含个人完成明细、Token、手机号、邮箱、IP、User-Agent、评价哈希或审核原因。 - OpenAPI 注解、示例和实际响应必须一致,不能只改文档或只改运行时。 - 企业内容按纯文本处理,不拼接 HTML;前端展示安全由 JSON 字符串和前端转义共同保证。 - 不启动或停止 Docker;若需要真实 MySQL/Docker 黑盒验证,必须先取得用户单独授权。 ## Review Gates - **DP-3**:本执行契约必须由用户明确批准并记录 `dp_3_result`、`dp_3_timestamp` 后,才能开始任何实现。 - **Batch Review**:B1、B2、B3 每批完成后由 `code-reviewer` 按规范符合性和代码质量评审,记录 `pass` 或 `fail`。 - **DP-4**:批准契约后由 `build-executor` 给出执行模式和 wave 计划,用户确认后开始执行。 - **Verification Gate**:实现完成后执行专项测试、全量测试、OpenAPI 校验和必要的 MySQL/Docker 黑盒验证。 - **DP-7**:由 `release-archivist` 完成最终验证与关闭,不在本契约内自动完成。 ## Rewind Conditions 出现以下任一情况必须停止执行并回到规划阶段刷新工件和本契约: - 需要新增、删除或改变用户可见接口、请求字段、响应字段、权限或错误语义。 - 需要改变数据表结构、唯一键、外键、通用/企业项来源模型或迁移编号。 - 需要改变完成状态幂等、乐观锁、完成率公式或软删除语义。 - 需要改变公开评价审核状态、3 条小样本阈值、八维维度或热门标签排序。 - 需要引入 ADMIN 模板治理、内容审核、版本回滚、通知、推荐、Offer 资格门槛或 AI。 - 需要修改 V1-V37、既有企业/岗位/评价接口或既有表结构。 - V38/V39 被其他变更占用。 - 发现任一已批准 `MUST`/`SHALL` 无法映射到测试义务或执行批次。 ## DP-3 Approval Gate 执行者确认:本契约完整覆盖已批准的 12 条 Requirement,无未映射需求;B1→B2→B3 依赖清晰;不同批次之间的实体、Repository、DTO 和错误码接口已在 Architecture and Interface Constraints 中固定;所有测试义务均有证据命令。 **批准方式**:用户明确回复批准本执行契约后,记录 `dp_3_result`、`dp_3_timestamp`,再执行 `specifying → bridging → approved-for-build` 状态转换。DP-3 记录前禁止进入 `build-executor`。