# Onboarding Checklist Specification ## Purpose 定义学生入职清单读取、个人完成状态、企业自定义项管理和通用清单回退行为。 ## 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`