9 changed files with 945 additions and 0 deletions
@ -0,0 +1,33 @@ |
|||||
|
version: 1 |
||||
|
state: executing |
||||
|
workflow: full |
||||
|
change_name: n04-onboard |
||||
|
artifact_language: zh-CN |
||||
|
dp_0_confirmed: true |
||||
|
dp_0_result: confirmed |
||||
|
dp_0_timestamp: 2026-09-16T07:10:12Z |
||||
|
dp_0_decisions: | |
||||
|
变更名称:n04-onboard;一句话意图:为学生提供标准项与企业自定义项组合的入职清单,并基于企业资料、员工评价和公司维护内容提供公司知识介绍。 |
||||
|
工作流:full;选择原因:新增业务模块、Flyway 表结构、九个 API、角色权限、幂等与并发控制、跨模块评价聚合。 |
||||
|
约束:仅登录用户可访问;学生端读取/更新本人完成状态,企业端仅维护本企业自定义清单与知识内容;通用清单通过 Flyway 种子数据提供;企业未配置自定义项时回退通用清单;公司知识信息不足时返回企业基础信息;不调用 AI。 |
||||
|
相关优化:保持聚焦,不纳入 ADMIN 模板治理、内容审核发布、版本回滚、通知提醒、职场内容推荐、试用期提醒、岗位或 Offer 个性化清单、前端开发。 |
||||
|
沟通方式:四类规划工件一次生成,完成验证后统一进入 DP-2 审查。 |
||||
|
artifact_language=zh-CN |
||||
|
dp_1_result: confirmed |
||||
|
dp_1_timestamp: 2026-09-16T07:10:12Z |
||||
|
artifacts: |
||||
|
order: [proposal, specs, design, tasks] |
||||
|
skip: [] |
||||
|
dp_2_result: approved: proposal, two delta specs, design, and tasks validated |
||||
|
dp_2_confirmed: true |
||||
|
dp_2_timestamp: 2026-09-16T07:45:22Z |
||||
|
contract_hash: 79e718e36a2fd349a8c26dfd080e74a1844c01a6e52363613414694bc75a74d1 |
||||
|
dp_3_result: approved: execution contract B1-B3; all 12 approved requirements mapped |
||||
|
dp_3_confirmed: true |
||||
|
dp_3_timestamp: 2026-09-16T07:55:53Z |
||||
|
execution_mode: sdd |
||||
|
plan_revision: 1 |
||||
|
dp_4_result: approved: SDD selected; W1 serial T1-T2, W2 serial T3-T5 depends W1, W3 serial T6-T8 depends W2 |
||||
|
dp_4_confirmed: true |
||||
|
dp_4_timestamp: 2026-09-16T07:58:31Z |
||||
|
batches_completed: 0 |
||||
@ -0,0 +1,24 @@ |
|||||
|
plan_revision: 1 |
||||
|
selected_mode: sdd |
||||
|
contract_hash: 79e718e36a2fd349a8c26dfd080e74a1844c01a6e52363613414694bc75a74d1 |
||||
|
created_at: 2026-09-16T07:58:31Z |
||||
|
current: W1 |
||||
|
waves: |
||||
|
- id: W1 |
||||
|
strategy: serial |
||||
|
tasks: [T1, T2] |
||||
|
depends_on: [] |
||||
|
state: eligible |
||||
|
receipt: pending |
||||
|
- id: W2 |
||||
|
strategy: serial |
||||
|
tasks: [T3, T4, T5] |
||||
|
depends_on: [W1] |
||||
|
state: blocked |
||||
|
receipt: pending |
||||
|
- id: W3 |
||||
|
strategy: serial |
||||
|
tasks: [T6, T7, T8] |
||||
|
depends_on: [W2] |
||||
|
state: blocked |
||||
|
receipt: pending |
||||
@ -0,0 +1,6 @@ |
|||||
|
# N04 SDD Progress Ledger |
||||
|
|
||||
|
- 2026-09-16T07:58:31Z Plan revision 1 created for mode SDD. |
||||
|
- W1 eligible: T1 -> T2 (serial, no prerequisite). |
||||
|
- W2 blocked on W1: T3 -> T4 -> T5. |
||||
|
- W3 blocked on W2: T6 -> T7 -> T8. |
||||
@ -0,0 +1,329 @@ |
|||||
|
# n04-onboard 设计 |
||||
|
|
||||
|
## Facts and Constraints |
||||
|
|
||||
|
- 当前后端为 Java 17、Spring Boot 3.5.16、Spring MVC、Spring Security、Spring Data JPA、Flyway、MySQL 8.4,测试使用 JUnit、Mockito、MockMvc 和 H2 `MODE=MySQL`。 |
||||
|
- 现有迁移已到 V37。本模块使用 V38 建表、V39 写入通用清单种子,不修改既有迁移或既有表结构。 |
||||
|
- `companies.owner_user_id` 唯一,现有 `CompanyService` 已提供 COMPANY 角色与所有权校验模式。 |
||||
|
- `job_reviews` 关联 `job_id`,企业评价必须通过 `jobs.company_id` 关联;公开评价限定为 `status=PUBLISHED` 且 `moderation_status IN (AUTO_APPROVED, APPROVED)`。 |
||||
|
- 现有八维评价维度由 `ReviewDimension` 枚举统一维护;公司知识摘要复用该枚举,不新增维度定义。 |
||||
|
- 统一响应使用 `ApiResponse`,错误使用 `BusinessException` 与 `ErrorCode`,请求 ID 从 `X-Request-Id` 读取。 |
||||
|
- 产品要求企业未配置时回退通用清单,公司信息不足时回退企业基础信息,且不调用 AI。 |
||||
|
|
||||
|
## Goals |
||||
|
|
||||
|
- 用最少的数据表和接口完成学生清单、个人完成状态、企业清单配置、公司知识配置与公开评价摘要。 |
||||
|
- 保持角色、企业所有权、响应字段和错误码可测试。 |
||||
|
- 保持创建幂等、更新并发可控、软删除可重复执行。 |
||||
|
- 不改变企业、岗位、Offer、评价等既有接口语义。 |
||||
|
|
||||
|
## Non-Goals |
||||
|
|
||||
|
- 不实现 ADMIN 模板治理、审核发布、版本回滚和 CMS。 |
||||
|
- 不实现通知、内容推荐、试用期提醒、入职回访、在职反馈。 |
||||
|
- 不按岗位、投递或 Offer 生成个性化内容。 |
||||
|
- 不存储或渲染 HTML/富文本,不接入 AI 生成或 AI 审核。 |
||||
|
- 不实现前端页面和前端缓存策略。 |
||||
|
|
||||
|
## Architecture |
||||
|
|
||||
|
新增包 `com.mydreamjob.onboarding`: |
||||
|
|
||||
|
```text |
||||
|
onboarding/ |
||||
|
├─ OnboardingChecklistController.java |
||||
|
├─ CompanyKnowledgeController.java |
||||
|
├─ OnboardingChecklistService.java |
||||
|
├─ CompanyKnowledgeService.java |
||||
|
├─ OnboardingChecklistItem.java |
||||
|
├─ StudentOnboardingProgress.java |
||||
|
├─ CompanyKnowledge.java |
||||
|
├─ OnboardingChecklistItemRepository.java |
||||
|
├─ StudentOnboardingProgressRepository.java |
||||
|
├─ CompanyKnowledgeRepository.java |
||||
|
└─ dto/ |
||||
|
├─ ChecklistItemView.java |
||||
|
├─ EffectiveChecklistResponse.java |
||||
|
├─ ProgressUpdateRequest.java |
||||
|
├─ ProgressUpdateResponse.java |
||||
|
├─ CompanyChecklistItemCreateRequest.java |
||||
|
├─ CompanyChecklistItemUpdateRequest.java |
||||
|
├─ CompanyChecklistItemView.java |
||||
|
├─ CompanyKnowledgeUpdateRequest.java |
||||
|
├─ CompanyKnowledgeView.java |
||||
|
├─ CompanyKnowledgeOverviewResponse.java |
||||
|
└─ ReviewSummaryView.java |
||||
|
``` |
||||
|
|
||||
|
`SecurityConfig` 将 `/onboarding/**` 设为认证访问;Controller/Service 再按角色和所有权执行细粒度校验。所有接口返回 `ApiResponse<T>`。 |
||||
|
|
||||
|
## API Contract |
||||
|
|
||||
|
所有路径均位于 `/api/v1` 之下。 |
||||
|
|
||||
|
| Method | Path | Role | Request | Success Response | |
||||
|
|---|---|---|---|---| |
||||
|
| GET | `/onboarding/companies/{companyId}/checklist` | STUDENT | path `companyId` | `EffectiveChecklistResponse` | |
||||
|
| PUT | `/onboarding/companies/{companyId}/checklist/items/{itemId}/completion` | STUDENT | `ProgressUpdateRequest` | `ProgressUpdateResponse` | |
||||
|
| GET | `/onboarding/companies/{companyId}/knowledge` | STUDENT | path `companyId` | `CompanyKnowledgeOverviewResponse` | |
||||
|
| GET | `/onboarding/company/checklist-items` | COMPANY | 无 | `List<CompanyChecklistItemView>` | |
||||
|
| POST | `/onboarding/company/checklist-items` | COMPANY | `CompanyChecklistItemCreateRequest` | `CompanyChecklistItemView` | |
||||
|
| PUT | `/onboarding/company/checklist-items/{itemId}` | COMPANY | `CompanyChecklistItemUpdateRequest` | `CompanyChecklistItemView` | |
||||
|
| DELETE | `/onboarding/company/checklist-items/{itemId}?version={version}` | COMPANY | query `version` | `CompanyChecklistItemView`,`active=false` | |
||||
|
| GET | `/onboarding/company/knowledge` | COMPANY | 无 | `CompanyKnowledgeView` | |
||||
|
| PUT | `/onboarding/company/knowledge` | COMPANY | `CompanyKnowledgeUpdateRequest` | `CompanyKnowledgeView` | |
||||
|
|
||||
|
### Effective Checklist Response |
||||
|
|
||||
|
```json |
||||
|
{ |
||||
|
"companyId": 2, |
||||
|
"companyName": "本源科技", |
||||
|
"customized": true, |
||||
|
"totalItems": 13, |
||||
|
"completedItems": 4, |
||||
|
"completionRate": 30.8, |
||||
|
"items": [ |
||||
|
{ |
||||
|
"itemId": 11, |
||||
|
"source": "COMPANY", |
||||
|
"itemKey": "company-2-uuid", |
||||
|
"title": "提交导师联系方式", |
||||
|
"description": "入职前由 HR 补充", |
||||
|
"category": "ADMIN", |
||||
|
"required": false, |
||||
|
"sortOrder": 200, |
||||
|
"active": true, |
||||
|
"completed": false, |
||||
|
"completedAt": null, |
||||
|
"version": 0 |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
通用项使用 `source=STANDARD`;企业项使用 `source=COMPANY`。完成率保留一位小数,最大为 100.0;清单为空时完成率为 0.0。 |
||||
|
|
||||
|
### Completion Request |
||||
|
|
||||
|
```json |
||||
|
{ |
||||
|
"completed": true, |
||||
|
"version": 0 |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
- 无完成记录时 `version` 可省略或为 0。 |
||||
|
- 已有记录且请求值不同,必须提供当前 `version`;版本不一致返回 409 `ONBOARDING_PROGRESS_CONCURRENT_UPDATE`。 |
||||
|
- 已有记录且请求值与当前值相同,按幂等成功返回,不因旧版本报错。 |
||||
|
|
||||
|
### Company Item Create Request |
||||
|
|
||||
|
```json |
||||
|
{ |
||||
|
"title": "提交导师联系方式", |
||||
|
"description": "入职前由 HR 补充", |
||||
|
"category": "ADMIN", |
||||
|
"sortOrder": 200, |
||||
|
"required": false, |
||||
|
"idempotencyKey": "client-generated-key" |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
- `title` 必填,长度 1-120。 |
||||
|
- `description` 可空,最大 1000。 |
||||
|
- `category` 仅允许 `DOCUMENT`、`ADMIN`、`EQUIPMENT`、`TRAINING`、`LOGISTICS`、`OTHER`。 |
||||
|
- `sortOrder` 范围 0-9999,默认 1000。 |
||||
|
- `required` 默认 `false`。 |
||||
|
- `idempotencyKey` 可空,最大 64;服务端保存规范化请求体 SHA-256,用于同 key 不同内容冲突检测。 |
||||
|
|
||||
|
### Company Item Update/Delete |
||||
|
|
||||
|
更新请求包含 `version`;删除请求通过 `version` 查询参数传入。两者版本不匹配返回 409 `ONBOARDING_CHECKLIST_ITEM_CONCURRENT_UPDATE`。删除采用软删除,重复删除按成功返回。 |
||||
|
|
||||
|
### Company Knowledge Request |
||||
|
|
||||
|
```json |
||||
|
{ |
||||
|
"culture": "重视透明沟通和学习", |
||||
|
"workPolicies": "弹性工作制;每周三远程", |
||||
|
"onboardingGuide": "9:30 到 A 座 8 层前台报到", |
||||
|
"version": 0 |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
- 三个文本字段可空,但至少一个非空。 |
||||
|
- `culture` 最大 2000,`workPolicies` 最大 4000,`onboardingGuide` 最大 4000。 |
||||
|
- 首次创建不要求版本;已有记录必须提供当前版本,冲突返回 409 `ONBOARDING_KNOWLEDGE_CONCURRENT_UPDATE`。 |
||||
|
|
||||
|
### Student Company Knowledge Response |
||||
|
|
||||
|
```json |
||||
|
{ |
||||
|
"company": { |
||||
|
"id": 2, |
||||
|
"name": "本源科技", |
||||
|
"logoUrl": null, |
||||
|
"description": "企业简介", |
||||
|
"industry": "互联网", |
||||
|
"website": "https://example.com", |
||||
|
"address": "上海市", |
||||
|
"verified": true |
||||
|
}, |
||||
|
"knowledgeState": "CONFIGURED", |
||||
|
"knowledge": { |
||||
|
"culture": "重视透明沟通和学习", |
||||
|
"workPolicies": "弹性工作制;每周三远程", |
||||
|
"onboardingGuide": "9:30 到 A 座 8 层前台报到", |
||||
|
"updatedAt": "2026-09-16T07:00:00" |
||||
|
}, |
||||
|
"reviewSummary": { |
||||
|
"state": "SUFFICIENT", |
||||
|
"totalReviews": 12, |
||||
|
"averageRating": 4.4, |
||||
|
"ratingDistribution": { |
||||
|
"rating1": 0, |
||||
|
"rating2": 1, |
||||
|
"rating3": 2, |
||||
|
"rating4": 4, |
||||
|
"rating5": 5 |
||||
|
}, |
||||
|
"topTags": ["成长空间", "团队氛围"], |
||||
|
"dimensionSummary": { |
||||
|
"TEAM_ATMOSPHERE": { |
||||
|
"averageScore": 4.5, |
||||
|
"sampleCount": 8, |
||||
|
"dataState": "SUFFICIENT" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
无知识记录时 `knowledgeState=BASIC_ONLY`、`knowledge=null`,企业基础信息仍返回。评价少于 3 条时 `reviewSummary.state=INSUFFICIENT` 或 `EMPTY`,且 `averageRating`、`ratingDistribution`、`topTags`、`dimensionSummary` 为空。 |
||||
|
|
||||
|
## Data Model |
||||
|
|
||||
|
### `onboarding_checklist_items` |
||||
|
|
||||
|
```text |
||||
|
id BIGINT PK |
||||
|
scope_type VARCHAR(20) NOT NULL -- STANDARD / COMPANY |
||||
|
scope_id BIGINT NOT NULL -- 0 或 company_id |
||||
|
company_id BIGINT NULL -- 企业项外键;通用项为 NULL |
||||
|
item_key VARCHAR(80) NOT NULL |
||||
|
title VARCHAR(120) NOT NULL |
||||
|
description VARCHAR(1000) NULL |
||||
|
category VARCHAR(30) NOT NULL |
||||
|
sort_order INT NOT NULL DEFAULT 1000 |
||||
|
required BOOLEAN NOT NULL DEFAULT FALSE |
||||
|
active BOOLEAN NOT NULL DEFAULT TRUE |
||||
|
idempotency_key VARCHAR(64) NULL |
||||
|
request_hash CHAR(64) NULL |
||||
|
version BIGINT NOT NULL DEFAULT 0 |
||||
|
created_at DATETIME NOT NULL |
||||
|
updated_at DATETIME NOT NULL |
||||
|
UNIQUE (scope_type, scope_id, item_key) |
||||
|
UNIQUE (company_id, idempotency_key) |
||||
|
INDEX (scope_type, scope_id, active, sort_order, id) |
||||
|
``` |
||||
|
|
||||
|
`scope_type/scope_id` 为公司项唯一性提供稳定语义,同时允许通用项使用 `scope_id=0`;`company_id` 仅对企业项建立外键,标准项为 NULL。应用层检查 `scope_type=COMPANY` 时 `scope_id=company_id` 且 `company_id` 非空。 |
||||
|
|
||||
|
### `student_onboarding_progress` |
||||
|
|
||||
|
```text |
||||
|
id BIGINT PK |
||||
|
student_user_id BIGINT NOT NULL |
||||
|
item_id BIGINT NOT NULL |
||||
|
completed BOOLEAN NOT NULL |
||||
|
completed_at DATETIME NULL |
||||
|
version BIGINT NOT NULL DEFAULT 0 |
||||
|
created_at DATETIME NOT NULL |
||||
|
updated_at DATETIME NOT NULL |
||||
|
UNIQUE (student_user_id, item_id) |
||||
|
FOREIGN KEY student_user_id -> users(id) |
||||
|
FOREIGN KEY item_id -> onboarding_checklist_items(id) |
||||
|
``` |
||||
|
|
||||
|
完成状态采用绝对值 Upsert;同一学生与条目唯一。`completedAt` 在首次置为完成时写入,取消完成时清空。 |
||||
|
|
||||
|
### `company_knowledge` |
||||
|
|
||||
|
```text |
||||
|
id BIGINT PK |
||||
|
company_id BIGINT NOT NULL |
||||
|
culture VARCHAR(2000) NULL |
||||
|
work_policies VARCHAR(4000) NULL |
||||
|
onboarding_guide VARCHAR(4000) NULL |
||||
|
version BIGINT NOT NULL DEFAULT 0 |
||||
|
created_at DATETIME NOT NULL |
||||
|
updated_at DATETIME NOT NULL |
||||
|
UNIQUE (company_id) |
||||
|
FOREIGN KEY company_id -> companies(id) |
||||
|
``` |
||||
|
|
||||
|
## Decisions |
||||
|
|
||||
|
### Decision 1: 使用统一清单表并保留标准/企业来源 |
||||
|
|
||||
|
- **Choice**:`onboarding_checklist_items` 使用 `scope_type` 区分 `STANDARD` 与 `COMPANY`;有效清单查询返回两类记录。 |
||||
|
- **Rationale**:学生只需要一个稳定 itemId 更新完成状态,统一表避免两套进度表、两套排序和重复响应逻辑。 |
||||
|
- **Alternatives**:标准表与企业表分离;企业配置 JSON 存储在一个字段中。 |
||||
|
- **Consequences**:需要额外维护 scope 一致性检查;换来了统一的完成状态和软删除语义。 |
||||
|
|
||||
|
### Decision 2: 通用清单固定种子,企业项只做增量 |
||||
|
|
||||
|
- **Choice**:V39 写入通用清单;企业自定义项只追加,不允许覆盖或替换通用项。 |
||||
|
- **Rationale**:产品明确要求企业未配置时显示通用清单,固定基线可保证所有企业至少有可用内容。 |
||||
|
- **Alternatives**:允许企业禁用通用项;允许企业完全替换模板。 |
||||
|
- **Consequences**:MVP 不支持企业裁剪通用项;后续若需要,需新增停用映射而不是修改现有响应语义。 |
||||
|
|
||||
|
### Decision 3: 创建幂等与更新乐观锁分离 |
||||
|
|
||||
|
- **Choice**:创建使用 `idempotencyKey + request_hash`;更新、删除和知识内容使用 `@Version` 乐观锁。 |
||||
|
- **Rationale**:创建需要处理网络重试,更新需要避免管理员或企业多端并发覆盖。 |
||||
|
- **Alternatives**:所有写接口只依赖数据库唯一键;所有接口都使用请求幂等键。 |
||||
|
- **Consequences**:客户端创建时需保存幂等键;更新时必须先取得当前版本。 |
||||
|
|
||||
|
### Decision 4: 公司知识摘要只使用公开评价并执行小样本保护 |
||||
|
|
||||
|
- **Choice**:只聚合 `PUBLISHED` 且审核为 `AUTO_APPROVED/APPROVED` 的评价;少于 3 条不返回均值。 |
||||
|
- **Rationale**:与现有公开评价语义一致,降低小样本隐私和误导风险。 |
||||
|
- **Alternatives**:聚合全部评价;无阈值返回所有均值。 |
||||
|
- **Consequences**:新企业或评价较少企业只能看到基础资料与评价数量,不显示汇总分数。 |
||||
|
|
||||
|
### Decision 5: 学生访问不绑定 Offer |
||||
|
|
||||
|
- **Choice**:任何 STUDENT 可按公开存在的 `companyId` 读取清单和知识,完成状态只属于当前学生。 |
||||
|
- **Rationale**:产品没有要求入职资格校验,企业基础信息和通用清单也不属于敏感业务数据;此设计避免跨模块状态耦合。 |
||||
|
- **Alternatives**:必须存在 ACCEPTED Offer;必须存在投递记录。 |
||||
|
- **Consequences**:后续若要增加资格门槛,需新增权限规则和兼容测试,不改变现有路径。 |
||||
|
|
||||
|
## Error Codes |
||||
|
|
||||
|
新增 97xx: |
||||
|
|
||||
|
```text |
||||
|
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`。 |
||||
|
|
||||
|
## Risks and Verification |
||||
|
|
||||
|
| Risk | Verification | |
||||
|
|---|---| |
||||
|
| V38/V39 与 MySQL/H2 类型不一致 | 迁移集成测试检查三张表、唯一键、外键、种子数量与排序 | |
||||
|
| 企业跨租户访问条目 | Service/MockMvc 测试使用两个企业,断言跨企业返回 404 且数据未变化 | |
||||
|
| 重复创建产生重复项 | 持久化测试断言相同 idempotencyKey 只有一行;不同请求体断言 9704 | |
||||
|
| 并发更新静默覆盖 | 持久化测试和乐观锁冲突测试断言 9702/9706 | |
||||
|
| 完成状态重复或丢失 | 同一学生并发/重复 PUT 测试断言唯一记录、完成率和 completedAt 正确 | |
||||
|
| 评价聚合暴露小样本或内部字段 | DTO 白名单测试、少于 3 条测试、无知识回退测试 | |
||||
|
| 公司知识内容注入 HTML | 请求字段按纯文本存储和返回;长度与内容验证测试 | |
||||
|
| 既有模块回归 | 扩展评价/企业相关测试后执行 `mvn clean verify`,并与 baseline 对比路径数 | |
||||
@ -0,0 +1,165 @@ |
|||||
|
# 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<T>`;请求 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`。 |
||||
@ -0,0 +1,65 @@ |
|||||
|
# n04-onboard 入职清单与公司知识介绍 |
||||
|
|
||||
|
## Why |
||||
|
|
||||
|
产品功能表将 M1.1「入职清单」和 M1.2「公司了解」列为一期 P2 功能,但当前后端没有对应表、服务、API、权限或测试。学生缺少可执行的标准入职准备事项,也无法在一个接口中集中查看企业基础资料、企业文化与制度、员工评价摘要;企业也没有维护自定义清单和公司知识内容的入口。 |
||||
|
|
||||
|
本变更以最小可用闭环补齐后端能力:通用清单提供稳定基线,企业自定义项补足差异,学生的个人完成状态可持续计算完成率;公司知识优先展示企业已维护内容与公开评价摘要,信息不足时合法回退到企业基础信息。 |
||||
|
|
||||
|
## What Changes |
||||
|
|
||||
|
- 新增 `onboarding` 后端模块,提供 9 个接口: |
||||
|
- `GET /onboarding/companies/{companyId}/checklist` |
||||
|
- `PUT /onboarding/companies/{companyId}/checklist/items/{itemId}/completion` |
||||
|
- `GET /onboarding/companies/{companyId}/knowledge` |
||||
|
- `GET /onboarding/company/checklist-items` |
||||
|
- `POST /onboarding/company/checklist-items` |
||||
|
- `PUT /onboarding/company/checklist-items/{itemId}` |
||||
|
- `DELETE /onboarding/company/checklist-items/{itemId}` |
||||
|
- `GET /onboarding/company/knowledge` |
||||
|
- `PUT /onboarding/company/knowledge` |
||||
|
- 新增三类持久化数据:通用/企业自定义清单项、学生对清单项的完成状态、企业知识内容。 |
||||
|
- 通过 Flyway 写入通用入职清单种子数据;企业未配置自定义项时,学生仍获得通用清单。 |
||||
|
- 公司知识聚合企业基础资料、企业维护的文化/制度/入职指南,以及该公司所有公开岗位评价的汇总;有效公开评价少于 3 条时不展示平均分,避免小样本暴露。 |
||||
|
- 企业配置接口仅允许企业维护本公司数据;学生接口仅允许学生读取公司资料并更新本人清单状态。 |
||||
|
- 补齐错误码、幂等键、乐观锁、MockMvc、Service、持久化、迁移、端到端测试、OpenAPI 与前端联调文档。 |
||||
|
|
||||
|
## Scope |
||||
|
|
||||
|
### In Scope |
||||
|
|
||||
|
- 通用清单的数据库种子数据、排序、分类和必填标记。 |
||||
|
- 企业自定义清单项的创建、查询、更新和软删除。 |
||||
|
- 学生按企业读取有效清单、查看个人完成状态、设置完成或未完成。 |
||||
|
- 企业知识内容的读取与 Upsert。 |
||||
|
- 公司知识介绍聚合公司基础资料与公开评价摘要,并支持 `BASIC_ONLY` 回退。 |
||||
|
- COMPANY 所有权校验、STUDENT 角色校验、敏感字段白名单响应。 |
||||
|
- 创建幂等、更新/删除乐观锁、并发冲突错误码和重复请求重放。 |
||||
|
- 模块自动化测试、OpenAPI 静态契约校验和前端联调文档。 |
||||
|
|
||||
|
### Out of Scope |
||||
|
|
||||
|
- ADMIN 管理通用模板、发布审核、版本回滚和内容治理后台。 |
||||
|
- 通知提醒、职场内容推荐、试用期提醒、入职回访和在职反馈。 |
||||
|
- 按岗位、投递或 Offer 生成个性化清单。 |
||||
|
- 第三方 CMS、富文本 HTML、AI 生成公司介绍或 AI 内容审核。 |
||||
|
- 学生端页面、企业端页面和前端状态管理。 |
||||
|
- 修改已有企业、岗位、Offer 或评价接口的响应语义。 |
||||
|
|
||||
|
## Impact |
||||
|
|
||||
|
- `mydreamjob-server/src/main/resources/db/migration/`:新增 V38、V39 迁移,不修改 V1-V37。 |
||||
|
- `mydreamjob-server/src/main/java/com/mydreamjob/onboarding/`:新增领域实体、Repository、Service、Controller 和 DTO。 |
||||
|
- `mydreamjob-server/src/main/java/com/mydreamjob/common/exception/ErrorCode.java`:新增 97xx 错误码。 |
||||
|
- `docs/openapi-rc.json`、`docs/onboarding-api.md`、`docs/frontend-integration.md`、路线图和 README 状态说明。 |
||||
|
- 现有 `Company`、`Job`、`JobReview`、`JobReviewDimensionScore` 只读复用,不改变其表结构和接口。 |
||||
|
- 新增表使用 Flyway,支持 MySQL 8.4 与 H2 `MODE=MySQL` 集成测试。 |
||||
|
|
||||
|
## Proof of Completion |
||||
|
|
||||
|
- 学生可在企业无自定义项时取得通用清单;有自定义项时取得合并后的有效清单。 |
||||
|
- 学生重复设置同一完成状态不产生重复行,完成率和完成时间正确;并发冲突返回明确 409 错误码。 |
||||
|
- 企业只能操作本企业数据;跨企业 itemId、非 COMPANY 或非 STUDENT 请求返回 403/404,不泄露其他企业数据。 |
||||
|
- 公司知识响应只包含白名单字段;无企业知识内容时返回 `BASIC_ONLY` 和企业基础资料;评价少于 3 条时不返回平均分。 |
||||
|
- `mvn -Dtest=Onboarding*Test test`、`mvn clean verify`、`python docs/verify_openapi.py` 全部通过。 |
||||
|
- OpenAPI 契约包含全部 9 个路径、请求体和响应模型,且与运行时文档一致。 |
||||
@ -0,0 +1,125 @@ |
|||||
|
# Company Knowledge |
||||
|
|
||||
|
## ADDED Requirements |
||||
|
|
||||
|
### Requirement: 学生读取公司知识介绍 |
||||
|
|
||||
|
系统 SHALL 允许已登录 STUDENT 通过 `GET /onboarding/companies/{companyId}/knowledge` 读取公司知识介绍。响应 MUST 聚合企业基础资料、企业维护的知识内容以及公开评价摘要,并使用白名单 DTO 排除 `ownerUserId` 等内部字段。 |
||||
|
|
||||
|
#### Scenario: 企业已维护知识内容 |
||||
|
|
||||
|
- **WHEN** STUDENT 请求存在企业且该企业已有知识内容 |
||||
|
- **THEN** 系统返回企业基础资料、文化、工作制度、入职指南和更新时间 |
||||
|
- **AND** `knowledgeState` 为 `CONFIGURED` |
||||
|
- **AND** 不返回企业 `ownerUserId`、员工用户 ID、评价哈希或审核原因 |
||||
|
|
||||
|
#### Scenario: 企业未维护知识内容 |
||||
|
|
||||
|
- **WHEN** STUDENT 请求存在企业且该企业没有知识内容记录 |
||||
|
- **THEN** 系统仍返回企业基础资料 |
||||
|
- **AND** `knowledgeState` 为 `BASIC_ONLY` |
||||
|
- **AND** 知识内容字段为 `null` 或空集合,不返回 404 |
||||
|
|
||||
|
#### Scenario: 企业不存在 |
||||
|
|
||||
|
- **WHEN** STUDENT 请求不存在的 `companyId` |
||||
|
- **THEN** 系统返回 404 `COMPANY_NOT_FOUND` |
||||
|
|
||||
|
### Requirement: 公司知识评价摘要使用公开数据和小样本保护 |
||||
|
|
||||
|
系统 SHALL 聚合该企业全部岗位中状态为 `PUBLISHED` 且审核状态为 `AUTO_APPROVED` 或 `APPROVED` 的评价。公开评价少于 3 条时 MUST 返回 `INSUFFICIENT` 或 `EMPTY`,且不得返回平均分或维度均值。达到 3 条时 MAY 返回平均评分、评分分布、热门标签和八维均值。 |
||||
|
|
||||
|
#### Scenario: 无公开评价 |
||||
|
|
||||
|
- **WHEN** 企业没有任何符合条件的公开评价 |
||||
|
- **THEN** `reviewSummary.state` 为 `EMPTY` |
||||
|
- **AND** `totalReviews` 为 0 |
||||
|
- **AND** `averageRating` 与 `dimensionSummary` 为空 |
||||
|
|
||||
|
#### Scenario: 公开评价少于 3 条 |
||||
|
|
||||
|
- **WHEN** 企业只有 1 或 2 条符合条件的公开评价 |
||||
|
- **THEN** `reviewSummary.state` 为 `INSUFFICIENT` |
||||
|
- **AND** 系统返回真实 `totalReviews` |
||||
|
- **AND** 不返回平均分、评分分布或维度均值 |
||||
|
|
||||
|
#### Scenario: 公开评价达到 3 条 |
||||
|
|
||||
|
- **WHEN** 企业有至少 3 条符合条件的公开评价 |
||||
|
- **THEN** `reviewSummary.state` 为 `SUFFICIENT` |
||||
|
- **AND** 系统返回平均评分、评分分布、热门标签和维度汇总 |
||||
|
- **AND** 每个维度返回样本数及是否达到小样本阈值 |
||||
|
|
||||
|
### Requirement: 企业读取本企业知识配置 |
||||
|
|
||||
|
系统 SHALL 允许 COMPANY 通过 `GET /onboarding/company/knowledge` 读取本企业知识配置。无记录时 MUST 返回 `configured=false` 和空内容,而不是 404;有记录时 MUST 返回内容和当前版本。 |
||||
|
|
||||
|
#### Scenario: 尚未配置 |
||||
|
|
||||
|
- **WHEN** COMPANY 请求本企业知识配置且尚未创建记录 |
||||
|
- **THEN** 系统返回 `configured=false` |
||||
|
- **AND** 内容字段为空,版本为 `0` |
||||
|
|
||||
|
#### Scenario: 已配置 |
||||
|
|
||||
|
- **WHEN** COMPANY 请求本企业已有知识配置 |
||||
|
- **THEN** 系统返回文化、工作制度、入职指南、更新时间和当前版本 |
||||
|
- **AND** `configured=true` |
||||
|
|
||||
|
### Requirement: 企业 Upsert 公司知识内容 |
||||
|
|
||||
|
系统 SHALL 允许 COMPANY 通过 `PUT /onboarding/company/knowledge` 创建或更新本企业知识内容。字段 MUST 使用纯文本并受长度限制,不允许 HTML 富文本。更新已有记录时 MUST 使用乐观锁版本;版本冲突返回 409 `ONBOARDING_KNOWLEDGE_CONCURRENT_UPDATE`。 |
||||
|
|
||||
|
#### Scenario: 首次创建 |
||||
|
|
||||
|
- **WHEN** COMPANY 首次提交合法的知识内容 |
||||
|
- **THEN** 系统创建绑定该企业的知识记录 |
||||
|
- **AND** 返回 `configured=true` 和版本 `0` |
||||
|
|
||||
|
#### Scenario: 使用当前版本更新 |
||||
|
|
||||
|
- **WHEN** COMPANY 使用当前版本提交新的文化、制度或入职指南 |
||||
|
- **THEN** 系统更新记录并递增版本 |
||||
|
- **AND** 返回最新内容 |
||||
|
|
||||
|
#### Scenario: 使用过期版本更新 |
||||
|
|
||||
|
- **WHEN** COMPANY 使用过期版本提交知识内容 |
||||
|
- **THEN** 系统返回 409 `ONBOARDING_KNOWLEDGE_CONCURRENT_UPDATE` |
||||
|
- **AND** 不覆盖数据库中的新内容 |
||||
|
|
||||
|
#### Scenario: 非法内容 |
||||
|
|
||||
|
- **WHEN** 请求字段超出最大长度或包含不受支持的格式 |
||||
|
- **THEN** 系统返回 400 `BAD_REQUEST` |
||||
|
- **AND** 不写入知识记录 |
||||
|
|
||||
|
### Requirement: 公司知识权限与所有权隔离 |
||||
|
|
||||
|
学生读取接口 MUST 仅允许 STUDENT;企业配置接口 MUST 仅允许 COMPANY 且只操作当前用户拥有的企业。任何跨企业读取配置或修改操作 MUST 被拒绝。 |
||||
|
|
||||
|
#### Scenario: 企业访问本企业配置 |
||||
|
|
||||
|
- **WHEN** COMPANY 读取或更新自己的知识记录 |
||||
|
- **THEN** 系统允许操作 |
||||
|
|
||||
|
#### Scenario: 非企业访问企业配置 |
||||
|
|
||||
|
- **WHEN** STUDENT、ADMIN 或未登录用户访问企业知识配置接口 |
||||
|
- **THEN** 系统返回 401 或 403 |
||||
|
- **AND** 不读取或修改企业配置 |
||||
|
|
||||
|
#### Scenario: 不存在的企业 |
||||
|
|
||||
|
- **WHEN** STUDENT 请求不存在的公司知识 |
||||
|
- **THEN** 系统返回 404 `COMPANY_NOT_FOUND` |
||||
|
|
||||
|
### Requirement: 不改变既有评价与企业接口语义 |
||||
|
|
||||
|
本变更 MUST 只读复用既有企业、岗位、评价和维度评分数据,不得修改既有表结构、响应字段或权限语义。 |
||||
|
|
||||
|
#### Scenario: 既有评价接口回归 |
||||
|
|
||||
|
- **WHEN** 执行已有评价与企业模块测试 |
||||
|
- **THEN** 所有既有测试保持通过 |
||||
|
- **AND** OpenAPI 中既有路径的请求与响应语义不变 |
||||
@ -0,0 +1,151 @@ |
|||||
|
# Onboarding Checklist |
||||
|
|
||||
|
## ADDED Requirements |
||||
|
|
||||
|
### Requirement: 学生读取有效入职清单 |
||||
|
|
||||
|
系统 SHALL 允许已登录 STUDENT 按 `companyId` 读取由通用清单与企业活动自定义项组成的有效入职清单。响应 MUST 包含企业、当前学生完成状态、完成数量、总数量、完成率、是否是企业自定义配置,以及按 `sortOrder`、`id` 稳定排序的条目。 |
||||
|
|
||||
|
#### Scenario: 企业未配置自定义项 |
||||
|
|
||||
|
- **WHEN** STUDENT 请求存在企业的清单,且该企业没有活动自定义项 |
||||
|
- **THEN** 系统返回全部活动通用清单项 |
||||
|
- **AND** `customized` 为 `false` |
||||
|
- **AND** 每个条目返回标准来源、分类、标题、说明、必填标记和当前学生的完成状态 |
||||
|
|
||||
|
#### Scenario: 企业已配置自定义项 |
||||
|
|
||||
|
- **WHEN** STUDENT 请求存在企业的清单,且该企业存在活动自定义项 |
||||
|
- **THEN** 系统返回活动通用项与企业自定义项 |
||||
|
- **AND** 通用项与企业项使用稳定排序,响应 `customized` 为 `true` |
||||
|
- **AND** 每个条目的 `itemId` 和 `source` 足以用于后续完成状态更新 |
||||
|
|
||||
|
#### Scenario: 企业不存在 |
||||
|
|
||||
|
- **WHEN** STUDENT 请求不存在的 `companyId` |
||||
|
- **THEN** 系统返回 404 与既有 `COMPANY_NOT_FOUND` 错误码 |
||||
|
- **AND** 不创建完成状态行 |
||||
|
|
||||
|
### Requirement: 学生更新个人清单完成状态 |
||||
|
|
||||
|
系统 SHALL 允许 STUDENT 通过 `PUT /onboarding/companies/{companyId}/checklist/items/{itemId}/completion` 将本人对指定有效条目的状态设置为完成或未完成。请求 MUST 使用绝对状态语义,重复提交同一状态 MUST 不创建重复记录。响应 MUST 返回条目状态、版本和更新后的完成率。 |
||||
|
|
||||
|
#### Scenario: 首次标记完成 |
||||
|
|
||||
|
- **WHEN** STUDENT 对本人可访问的条目提交 `completed=true` |
||||
|
- **THEN** 系统创建该学生与该条目的完成记录 |
||||
|
- **AND** 设置 `completedAt` |
||||
|
- **AND** 返回更新后的完成数量、总数量和完成率 |
||||
|
|
||||
|
#### Scenario: 重复设置相同状态 |
||||
|
|
||||
|
- **WHEN** STUDENT 对同一条目重复提交相同 `completed` 值 |
||||
|
- **THEN** 系统返回幂等成功结果 |
||||
|
- **AND** 数据库仍只有一条学生与条目完成记录 |
||||
|
- **AND** 不改变首次完成后的业务结果 |
||||
|
|
||||
|
#### Scenario: 取消完成 |
||||
|
|
||||
|
- **WHEN** STUDENT 对已完成条目的提交 `completed=false` |
||||
|
- **THEN** 系统将记录置为未完成 |
||||
|
- **AND** 清除 `completedAt` |
||||
|
- **AND** 返回更新后的完成率 |
||||
|
|
||||
|
#### Scenario: 条目不属于该企业 |
||||
|
|
||||
|
- **WHEN** STUDENT 使用属于另一企业自定义项的 `itemId` 调用更新接口 |
||||
|
- **THEN** 系统返回 404 `ONBOARDING_CHECKLIST_ITEM_NOT_FOUND` |
||||
|
- **AND** 不允许创建或修改完成记录 |
||||
|
|
||||
|
### Requirement: 企业创建自定义清单项 |
||||
|
|
||||
|
系统 SHALL 允许 COMPANY 为本企业创建自定义清单项。请求 MUST 校验标题、说明、分类、排序值和必填标记。请求携带 `idempotencyKey` 时,同一企业、同一 key 且请求体相同 MUST 返回原条目;请求体不同 MUST 返回 409 `ONBOARDING_IDEMPOTENCY_CONFLICT`。 |
||||
|
|
||||
|
#### Scenario: 创建成功 |
||||
|
|
||||
|
- **WHEN** COMPANY 提交合法的标题、分类和可选说明 |
||||
|
- **THEN** 系统创建仅属于该企业的活动条目 |
||||
|
- **AND** 返回条目 ID、来源 `COMPANY`、版本、排序值和必填标记 |
||||
|
|
||||
|
#### Scenario: 幂等重放 |
||||
|
|
||||
|
- **WHEN** COMPANY 使用相同 `idempotencyKey` 和相同请求体重复创建 |
||||
|
- **THEN** 系统返回第一次创建的条目 |
||||
|
- **AND** 数据库不产生第二条记录 |
||||
|
|
||||
|
#### Scenario: 幂等键内容冲突 |
||||
|
|
||||
|
- **WHEN** COMPANY 重复使用同一 `idempotencyKey`,但标题、分类、说明、排序或必填标记不同 |
||||
|
- **THEN** 系统返回 409 `ONBOARDING_IDEMPOTENCY_CONFLICT` |
||||
|
|
||||
|
#### Scenario: 超出企业条目上限 |
||||
|
|
||||
|
- **WHEN** COMPANY 已拥有 50 条活动自定义项并继续创建 |
||||
|
- **THEN** 系统返回 409 `ONBOARDING_ITEM_LIMIT_EXCEEDED` |
||||
|
- **AND** 不写入第 51 条活动条目 |
||||
|
|
||||
|
### Requirement: 企业查询和更新自定义清单项 |
||||
|
|
||||
|
系统 SHALL 允许 COMPANY 查询本企业全部自定义项,并通过带版本号的更新请求修改标题、说明、分类、排序、必填标记和活动状态。版本冲突 MUST 返回 409 `ONBOARDING_CHECKLIST_ITEM_CONCURRENT_UPDATE`,不得静默覆盖。 |
||||
|
|
||||
|
#### Scenario: 查询本企业条目 |
||||
|
|
||||
|
- **WHEN** COMPANY 请求本企业自定义条目列表 |
||||
|
- **THEN** 系统返回该企业条目,包括活动与软删除条目及当前版本 |
||||
|
- **AND** 不返回其他企业的条目 |
||||
|
|
||||
|
#### Scenario: 正常更新 |
||||
|
|
||||
|
- **WHEN** COMPANY 使用当前 `version` 更新本企业条目 |
||||
|
- **THEN** 系统保存修改并递增版本 |
||||
|
- **AND** 返回更新后的条目 |
||||
|
|
||||
|
#### Scenario: 版本过期 |
||||
|
|
||||
|
- **WHEN** COMPANY 使用过期 `version` 更新条目 |
||||
|
- **THEN** 系统返回 409 `ONBOARDING_CHECKLIST_ITEM_CONCURRENT_UPDATE` |
||||
|
- **AND** 不覆盖数据库中较新的值 |
||||
|
|
||||
|
### Requirement: 企业软删除自定义清单项 |
||||
|
|
||||
|
系统 SHALL 允许 COMPANY 通过带当前 `version` 的删除请求软删除本企业条目。软删除 MUST 只将条目标记为非活动,不物理删除条目或学生历史完成记录。重复删除已非活动条目 MUST 返回幂等成功结果。 |
||||
|
|
||||
|
#### Scenario: 首次删除 |
||||
|
|
||||
|
- **WHEN** COMPANY 使用当前版本删除本企业活动条目 |
||||
|
- **THEN** 系统将条目 `active` 置为 `false` |
||||
|
- **AND** 该条目不再出现在学生有效清单中 |
||||
|
- **AND** 既有完成记录保留 |
||||
|
|
||||
|
#### Scenario: 重复删除 |
||||
|
|
||||
|
- **WHEN** COMPANY 再次删除已软删除条目 |
||||
|
- **THEN** 系统返回成功 |
||||
|
- **AND** 不产生第二次数据变更 |
||||
|
|
||||
|
#### Scenario: 删除其他企业条目 |
||||
|
|
||||
|
- **WHEN** COMPANY 删除不属于本企业的条目 |
||||
|
- **THEN** 系统返回 404 `ONBOARDING_CHECKLIST_ITEM_NOT_FOUND` |
||||
|
- **AND** 目标条目保持原状态 |
||||
|
|
||||
|
### Requirement: 清单权限和数据最小化 |
||||
|
|
||||
|
学生接口 MUST 仅允许 STUDENT;企业配置接口 MUST 仅允许 COMPANY 且操作绑定当前用户拥有的企业。所有响应 MUST 使用白名单 DTO,不返回用户密码、Token、IP、User-Agent、评价审核原因或其他企业内部的非必要字段。 |
||||
|
|
||||
|
#### Scenario: 非学生访问学生清单 |
||||
|
|
||||
|
- **WHEN** COMPANY、ADMIN 或未登录用户访问学生清单接口 |
||||
|
- **THEN** 系统返回 401 或 403 |
||||
|
- **AND** 不返回清单或完成状态 |
||||
|
|
||||
|
#### Scenario: 非企业访问企业配置 |
||||
|
|
||||
|
- **WHEN** STUDENT、ADMIN 或未登录用户访问企业配置接口 |
||||
|
- **THEN** 系统返回 401 或 403 |
||||
|
- **AND** 不创建或修改企业条目 |
||||
|
|
||||
|
#### Scenario: 企业无绑定资料 |
||||
|
|
||||
|
- **WHEN** COMPANY 用户尚未创建企业资料却调用企业配置接口 |
||||
|
- **THEN** 系统返回 404 `COMPANY_NOT_FOUND` |
||||
@ -0,0 +1,47 @@ |
|||||
|
# n04-onboard 实施任务 |
||||
|
|
||||
|
## Delivery / Proof Map |
||||
|
|
||||
|
| Requirement Area | Tasks | Evidence | |
||||
|
|---|---|---| |
||||
|
| 通用/企业清单数据模型与种子 | 1, 2 | 迁移测试、Repository 集成测试 | |
||||
|
| 学生有效清单与完成状态 | 2, 3, 6 | Service 单元测试、MockMvc、持久化测试 | |
||||
|
| 企业自定义项 CRUD、幂等、并发 | 2, 4, 6 | Service 单元测试、MockMvc、Repository 集成测试 | |
||||
|
| 公司知识配置与基础信息回退 | 1, 2, 5 | Service 单元测试、MockMvc、持久化测试 | |
||||
|
| 公开评价摘要与小样本保护 | 2, 5 | 聚合 Service 测试、评价边界测试 | |
||||
|
| 权限、白名单响应和错误码 | 6, 7 | 未登录、角色、跨企业、脱敏断言 | |
||||
|
| OpenAPI、前端联调和文档 | 8 | `python docs/verify_openapi.py`、文档检查 | |
||||
|
| 全量回归和可发布证明 | 8 | `mvn clean verify`、专项测试汇总 | |
||||
|
|
||||
|
## Tasks |
||||
|
|
||||
|
- [ ] 1. 新增 V38 建表迁移与 V39 通用清单种子,路径为 `mydreamjob-server/src/main/resources/db/migration/`。可观察结果:MySQL/H2 均可创建 `onboarding_checklist_items`、`student_onboarding_progress`、`company_knowledge`,主外键、唯一键和索引存在;通用清单按分类和稳定排序写入。证据:`mvn -Dtest=OnboardingMigrationTest,OnboardingSeedDataIntegrationTest test`。依赖:无。 |
||||
|
- [ ] 2. 新增 `com.mydreamjob.onboarding` 领域实体与 Repository,路径为 `mydreamjob-server/src/main/java/com/mydreamjob/onboarding/`。可观察结果:可查询某企业的活动通用+自定义项、批量查询当前学生完成记录、按企业查询知识记录,并为评价聚合提供跨岗位只读查询。证据:`mvn -Dtest=OnboardingRepositoryIntegrationTest test`。依赖:1。 |
||||
|
- [ ] 3. 实现学生有效清单查询与完成状态更新服务及接口,路径为 `OnboardingChecklistService.java`、`OnboardingChecklistController.java` 和对应 DTO。可观察结果:无企业自定义项时返回通用清单,有自定义项时合并排序;PUT 支持完成/取消完成、重复同状态幂等、完成率与完成时间正确。证据:`mvn -Dtest=OnboardingChecklistServiceTest,OnboardingChecklistControllerTest test`。依赖:2。 |
||||
|
- [ ] 4. 实现企业自定义清单项查询、创建、更新和软删除,路径为 `OnboardingChecklistService.java`、`OnboardingChecklistController.java` 和对应 DTO。可观察结果:仅企业本人可操作;创建支持 `idempotencyKey` 重放与请求体哈希冲突;更新/删除使用版本号;软删除后条目从学生有效清单消失但历史完成记录保留。证据:`mvn -Dtest=OnboardingCompanyChecklistServiceTest,OnboardingCompanyChecklistControllerTest test`。依赖:1, 2。 |
||||
|
- [ ] 5. 实现企业知识内容 Upsert 和学生公司知识聚合,路径为 `CompanyKnowledgeService.java`、`CompanyKnowledgeController.java` 和对应 DTO。可观察结果:企业读取无记录时返回 `configured=false`;学生读取无知识时返回 `BASIC_ONLY`;公开评价少于 3 条不返回均值;达到 3 条时返回平均分、分布、标签和八维摘要。证据:`mvn -Dtest=CompanyKnowledgeServiceTest,CompanyKnowledgeControllerTest test`。依赖:2。 |
||||
|
- [ ] 6. 接入角色、所有权、错误码、幂等和乐观锁,路径为 `SecurityConfig.java`、`ErrorCode.java`、`OnboardingChecklistService.java`、`CompanyKnowledgeService.java`。可观察结果:`/onboarding/**` 仅认证访问;STUDENT/COMPANY 角色和公司所有权严格隔离;9701-9706 在目标冲突场景返回 409/404;响应 DTO 不包含内部身份或审核字段。证据:`mvn -Dtest=OnboardingAuthorizationIntegrationTest,OnboardingConcurrencyIntegrationTest,OnboardingPrivacyTest test`。依赖:3, 4, 5。 |
||||
|
- [ ] 7. 补齐模块测试,路径为 `mydreamjob-server/src/test/java/com/mydreamjob/onboarding/`。可观察结果:Service、MockMvc、持久化、迁移、种子、权限、幂等、并发、回退、小样本和端到端场景全部覆盖;测试断言响应状态、错误码、数据库行数和排序。证据:`mvn -Dtest=Onboarding*Test test`。依赖:3, 4, 5, 6。 |
||||
|
- [ ] 8. 更新 OpenAPI、接口文档、前端联调说明和路线图,路径为 `docs/openapi-rc.json`、`docs/onboarding-api.md`、`docs/frontend-integration.md`、`docs/backend-development-roadmap.md`、`README.md`。可观察结果:9 个路径及 DTO 与运行时一致;前端文档给出请求示例、错误码、版本和幂等说明;路线图将本模块标记为实现完成/待验收。证据:`python docs/verify_openapi.py`,随后执行 `mvn clean verify`。依赖:3, 4, 5, 6, 7。 |
||||
|
|
||||
|
## Requirement-to-Task Coverage |
||||
|
|
||||
|
| Spec Requirement | Covered By | |
||||
|
|---|---| |
||||
|
| 学生读取有效入职清单 | 1, 2, 3 | |
||||
|
| 学生更新个人清单完成状态 | 2, 3, 6, 7 | |
||||
|
| 企业创建自定义清单项 | 1, 2, 4, 6, 7 | |
||||
|
| 企业查询和更新自定义清单项 | 2, 4, 6, 7 | |
||||
|
| 企业软删除自定义清单项 | 2, 4, 6, 7 | |
||||
|
| 清单权限和数据最小化 | 3, 4, 6, 7 | |
||||
|
| 学生读取公司知识介绍 | 1, 2, 5, 7 | |
||||
|
| 公司知识评价摘要与小样本保护 | 2, 5, 7 | |
||||
|
| 企业读取本企业知识配置 | 1, 2, 5, 7 | |
||||
|
| 企业 Upsert 公司知识内容 | 1, 2, 5, 6, 7 | |
||||
|
| 公司知识权限与所有权隔离 | 5, 6, 7 | |
||||
|
| 不改变既有评价与企业接口语义 | 7, 8 | |
||||
|
|
||||
|
## Completion Gate |
||||
|
|
||||
|
- 所有任务完成且专项测试、全量测试、OpenAPI 校验通过后才能进入 release-archivist。 |
||||
|
- 不允许降低既有接口语义、删除业务数据或跳过迁移验证。 |
||||
Loading…
Reference in new issue