Files
huangping df91ab0673 docs(i4): add I4 design for M3 delivery and M4 license SN
Describe REST contracts, validation, routing, and I4 sync checklist
aligned with V4 schema and parallel iteration index.

Made-with: Cursor
2026-04-06 21:48:55 +08:00

300 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 迭代 I4 设计说明 — M3 交付批次与清单、M4 许可 SN 台账
> **迭代定位**:与 [并行迭代索引](../PARALLEL_ITERATION_INDEX.md) 中 **I4** 一致 — 平台后端 **M3 交付** + **M4 SN 录入/绑定/状态/手工回写**;前端 **交付页 + SN 页**;本仓库(SDK 工作区)以 **OpenAPI 契约与文档口径** 与 BP-10 对齐。
> **分支**`develop`。
> **已有实现锚点**(勿从零重设计,仅对齐与补全):Flyway `V4__delivery_batch_and_license_sn.sql``cn.craftlabs.platform.api.domain.DeliveryBatchStatus` / `LicenseSnStatus``web/dto` 下 `Delivery*`、`LicenseSn*`;审计常量 `AuditEntityTypes`、`AuditActions` 已含 `DELIVERY_BATCH`、`LICENSE_SN` 及对应动作。
---
## 1. I4 范围与 I3 / I5 边界
### 1.1 I4 **纳入**(本迭代 DoD
| 域 | 说明 |
|----|------|
| **M3 P0** | 交付批次(项目/可选合同、批次号、计划日、备注);交付清单行(描述、数量、可选合同行关联);批次状态 **PENDING → DELIVERED / CANCELLED** 及完成时间等侧写。对应产品:[M3-F01F05 P0](../../chuangfei-platform-product-modules.md#4-m3-交付管理)。 |
| **M4 P0** | SN 台账:全局唯一 `sn_code`**`project_id` 与/或 `contract_line_id` 绑定路径**;生命周期状态子集;激活备注/手工回写字段。对应产品:[M4-F01F05 P0](../../chuangfei-platform-product-modules.md#5-m4-授权与许可运营)。 |
| **M10-F01** | 交付批次、交付行、SN 的关键变更与状态迁移写入审计(与 I3 合同审计模式一致;实体类型见 §4)。 |
| **跨轨口径** | [I4 末同步点](../PARALLEL_ITERATION_INDEX.md#3-跨轨同步点必须对齐):**SN 绑定与「孤儿 SN」规则**文档化并三轨对齐;**交付门禁(M3-F07)与「孤儿 SN」强校验(M4-F02)** 在 **M11-F20 系统参数** 中预留为 **未来可配置项**(I4 可实现默认策略 + 配置占位,**不阻塞** I4 闭环)。 |
### 1.2 I3 **留给上游的契约**(I4 只消费,不重复建设)
- **合同 / 合同行**`project_id``contract_id`、行项主键;合同状态机已在 I3 冻结。交付行上的 `contract_line_id` 必须解析到合法合同行及其所属项目。
- **客户 / 项目**:批次必填 `project_id`;可选 `contract_id` 须属于同一项目。
### 1.3 I5 **明确不纳入 I4**(避免范围蔓延)
- **M5 Callback Inbox**、**M6 集成配置** 的持久化与页面(I5 起)。
- Webhook **生产级** 投递、幂等落库与平台 Inbox 全链路 E2E。
- **设备(M7)**、**比特控制台摘要链接(M4-F06)** 等可后续挂接;I4 仅保证 SN 主数据与绑定字段可关联到合同行/项目。
---
## 2. 数据模型锚点(与迁移一致)
表与字段以 `services/delivery-platform-api/src/main/resources/db/migration/V4__delivery_batch_and_license_sn.sql` 为准:
- **`platform_delivery_batch`**`project_id`(必填)、`contract_id`(可选)、`batch_code`(唯一)、`planned_delivery_date``status`(默认 `PENDING`)、`finished_at``remarks`
- **`platform_delivery_line`**:归属 `batch_id``description``quantity``contract_line_id`(可选),`sort_order`
- **`platform_license_sn`**`sn_code`(全局唯一)、`project_id` / `contract_line_id`(均可空于 DB 层,**业务校验见 §4**)、`status`(默认 `REGISTERED`)、`activation_remark`
### 2.1 状态枚举(API JSON 使用枚举名字符串)
**交付批次** `DeliveryBatchStatus`
| 值 | 说明 |
|----|------|
| `PENDING` | 未交付(默认) |
| `DELIVERED` | 已交付 |
| `CANCELLED` | 已取消 |
**许可 SN** `LicenseSnStatus`P0 子集,与代码枚举一致):
| 值 | 说明 |
|----|------|
| `REGISTERED` | 已登记 |
| `ISSUED` | 已发放 |
| `ACTIVATED` | 已激活 |
| `SUSPENDED` | 已冻结 |
| `REVOKED` | 已回收 |
非法状态迁移返回 **409**,错误码建议与合同类似:`DELIVERY_BATCH_ILLEGAL_STATUS``LICENSE_SN_ILLEGAL_STATUS`(具体以 OpenAPI 与实现为准)。
---
## 3. REST API 提案(前缀 `/api/v1`
与现有 Controller 风格一致:**`@RequestMapping("/api/v1/...")`**。下列路径为 I4 计划形态;JSON 字段名与当前 DTO **camelCase** 对齐(`projectId``contractId``batchCode` 等)。
### 3.1 交付批次 `delivery-batches`
| 方法 | 路径 | 说明 |
|------|------|------|
| `GET` | `/api/v1/delivery-batches` | 分页列表;查询参数建议:`projectId``contractId``status``keyword`(批次号)、`page``size`。 |
| `POST` | `/api/v1/delivery-batches` | 创建批次(体见下);**不含**行时可后续用行接口追加。 |
| `GET` | `/api/v1/delivery-batches/{id}` | 详情;可通过 `?includeLines=true` 或默认嵌套返回 `lines`(与 `DeliveryBatchResponse` 一致)。 |
| `PUT` | `/api/v1/delivery-batches/{id}` | 更新计划交付日、备注等非状态字段(`DeliveryBatchUpdateRequest`)。 |
| `PATCH` | `/api/v1/delivery-batches/{id}/status` | **仅**变更状态:`PENDING``DELIVERED``CANCELLED`;服务端可在此写入 `finishedAt`(如 `DELIVERED`)。 |
**嵌套 — 交付行 `lines`**
| 方法 | 路径 | 说明 |
|------|------|------|
| `GET` | `/api/v1/delivery-batches/{batchId}/lines` | 清单列表。 |
| `POST` | `/api/v1/delivery-batches/{batchId}/lines` | 新增一行。 |
| `PUT` | `/api/v1/delivery-batches/{batchId}/lines/{lineId}` | 更新行。 |
| `DELETE` | `/api/v1/delivery-batches/{batchId}/lines/{lineId}` | 删除行。 |
**创建批次请求体示例**
```json
{
"projectId": 1001,
"contractId": 2002,
"batchCode": "DLV-2026-0001",
"plannedDeliveryDate": "2026-04-15",
"remarks": "首批现场交付"
}
```
**更新批次请求体示例**`PUT /api/v1/delivery-batches/{id}`
```json
{
"plannedDeliveryDate": "2026-04-20",
"remarks": "延期一周"
}
```
**状态 PATCH 示例**`PATCH /api/v1/delivery-batches/{id}/status`
```json
{
"status": "DELIVERED"
}
```
**交付行写入示例**`POST` / `PUT` body`DeliveryLineRequest`
```json
{
"sortOrder": 1,
"description": "AI 推理节点 × 生产环境",
"quantity": 2,
"contractLineId": 3003
}
```
**详情响应片段**`DeliveryBatchResponse`,含行)
```json
{
"id": 1,
"projectId": 1001,
"contractId": 2002,
"batchCode": "DLV-2026-0001",
"plannedDeliveryDate": "2026-04-15",
"status": "PENDING",
"finishedAt": null,
"remarks": "首批现场交付",
"createdAt": "2026-04-06T08:00:00Z",
"updatedAt": "2026-04-06T08:00:00Z",
"lines": [
{
"id": 10,
"batchId": 1,
"sortOrder": 1,
"description": "AI 推理节点 × 生产环境",
"quantity": 2,
"contractLineId": 3003,
"createdAt": "2026-04-06T08:05:00Z",
"updatedAt": "2026-04-06T08:05:00Z"
}
]
}
```
### 3.2 许可 SN `license-sns`
| 方法 | 路径 | 说明 |
|------|------|------|
| `GET` | `/api/v1/license-sns` | 分页列表;建议查询:`projectId``contractLineId``status``snCode`(精确或前缀按产品定)。 |
| `POST` | `/api/v1/license-sns` | 创建 SN`LicenseSnCreateRequest`);须满足 §4 绑定规则。 |
| `GET` | `/api/v1/license-sns/{id}` | 详情。 |
| `PUT` | `/api/v1/license-sns/{id}` | **全量/部分更新绑定字段**`projectId``contractLineId``activationRemark``LicenseSnUpdateRequest`);用于纠正绑定或手工回写备注。 |
| `PATCH` | `/api/v1/license-sns/{id}/status` | 变更 `LicenseSnStatus``LicenseSnStatusPatchRequest`);须校验合法迁移。 |
**创建 SN 请求体示例**
```json
{
"snCode": "SN-CRAFT-8F3A-0001",
"projectId": 1001,
"contractLineId": 3003,
"activationRemark": null
}
```
**更新绑定 / 备注示例**`PUT`
```json
{
"projectId": 1001,
"contractLineId": 3003,
"activationRemark": "客户现场激活成功,凭证号 xxx"
}
```
**状态 PATCH 示例**
```json
{
"status": "ACTIVATED"
}
```
**详情响应示例**`LicenseSnResponse`
```json
{
"id": 501,
"snCode": "SN-CRAFT-8F3A-0001",
"projectId": 1001,
"contractLineId": 3003,
"status": "ACTIVATED",
"activationRemark": "客户现场激活成功,凭证号 xxx",
"createdAt": "2026-04-06T09:00:00Z",
"updatedAt": "2026-04-06T10:00:00Z"
}
```
---
## 4. 校验规则与审计
### 4.1 交付批次
- **`projectId`**:必填;项目须存在。
- **`contractId`**:可选;若提供,合同须存在且 **`contract.project_id == batch.project_id`**。
- **`batchCode`**:全平台唯一(与表 `uq_platform_delivery_batch_code` 一致);冲突 **409**
- **状态**:仅允许自 `PENDING` 转至 `DELIVERED``CANCELLED``DELIVERED`/`CANCELLED` 视为终态,**禁止**再次变更(除非产品后续另定「重开」流程,不在 I4 P0)。
### 4.2 交付行
- **`contractLineId`**:可选;若提供,合同行须存在,且其所属合同的 **`project_id` 须与父批次 `project_id` 一致**(从而与批次可选 `contract_id` 兼容:若批次已指定合同,可额外校验行所属合同与批次合同一致,建议 **强一致**:行上合同行必须属于 `batch.contract_id``contract_id` 非空时)。
- **`quantity`**> 0(与 `DeliveryLineRequest``@DecimalMin` 一致)。
### 4.3 许可 SN
- **`snCode`**:必填;**全局唯一**;冲突 **409**
- **绑定路径****至少具备 `projectId``contractLineId` 之一**(可同时具备)。
- 若仅提供 **`contractLineId`**:服务端**派生** `project_id` = 该合同行所属合同的 `project_id`,并持久化(便于列表按项目过滤)。
- 若同时提供两者:须校验 **`contractLine` 派生出的 `project_id` 与请求 `projectId` 一致**,否则 **400**
- **孤儿 SN(与 I4 末同步对齐)**:
- **产品理想态(M4-F02)**:禁止无项目且无合同行路径的「裸 SN」。
- **M11-F20P1**:「孤儿 SN」**强校验**开关、**交付门禁**(M3-F07,例如仅已交付范围可发放/绑定)作为**系统参数**在架构上预留;I4 建议 **默认策略**:创建/更新时 **拒绝** 零绑定(与 P0 一致);若需「先录入后绑定」,可通过 **配置** 降级为 **警告 + 允许保存**(实现可放在应用服务层读取参数表,**表结构可 Mid 再做**,I4 先在文档与 OpenAPI `description` 中固定语义)。
- **状态迁移**:按 §2.1 枚举定义允许边(细表可在实现中维护;**禁止**随意跳转到任意状态)。
### 4.4 审计(M10-F01
沿用 I3 模式:**实体类型 + 动作 + 旧值/新值摘要 + 操作者 + 时间**。常量已存在于:
- `AuditEntityTypes.DELIVERY_BATCH``AuditEntityTypes.LICENSE_SN`
- `AuditActions``DELIVERY_BATCH_CREATED` / `UPDATED` / `STATUS_CHANGED``DELIVERY_LINE_ADDED` / `UPDATED` / `DELETED``LICENSE_SN_CREATED` / `UPDATED` / `STATUS_CHANGED`
若持久化审计行需扩展子类型或 payload 结构,**保持与合同审计同一表结构**,仅扩展 `entity_type` / `action` 枚举值;**无需**为 I4 另起实体类型常量,除非后续拆分「交付行」为独立可检索实体(当前可用 `DELIVERY_LINE_*` 动作挂 `entity_id` = line id`batch_id` 放上下文 JSON)。
---
## 5. 前端路由(Vue 3,布局子路由)
与 [轨道 B — I4](../tracks/02-frontend-platform-ui.md) 一致,路径挂在 **AppLayout** 之下(懒加载、`meta.title` / 权限码略)。
| 路由 | 页面职责 |
|------|----------|
| `/deliveries` | 交付批次列表、筛选、跳转新建/详情。 |
| `/deliveries/new` | 新建批次(项目/可选合同、批次号、计划日、备注);可内嵌或分步添加行。 |
| `/deliveries/:id` | 批次详情:行清单 CRUD;**状态按钮** 调用 `PATCH .../status`PENDING → DELIVERED/CANCELLED)。 |
| `/licenses/sn` | SN 台账列表;**孤儿 SN** 列表筛选或醒目标记(与后端配置/字段一致)。 |
| `/licenses/sn/new` | 新建 SN(录入 `snCode`、绑定项目/合同行)。 |
| `/licenses/sn/:id` | SN 详情:**PUT** 调整绑定与 `activationRemark`;**PATCH** 调整状态;展示简要时间线(可仅读审计或本地状态历史 Mid 增强)。 |
**契约顺序提醒**[轨道 B §4](../tracks/02-frontend-platform-ui.md)):Auth → Customer/Project → Contract → **Delivery/SN** → CallbackI4 页面依赖 I2/I3 主数据与合同行选择器。
---
## 6. I4 末同步点 Checklist(后端 + 前端 + SDK 文档)
以下为 [并行索引 I4 末](../PARALLEL_ITERATION_INDEX.md#3-跨轨同步点必须对齐) 的落地核对项。
### 6.1 后端(platform-api
- [ ] Flyway V4 表与索引已在各环境应用;DTO 与 OpenAPI 字段一致。
- [ ] §3 路径已实现或通过兼容别名暴露;错误码与 409 语义与合同模块一致。
- [ ] §4.1~§4.3 校验全覆盖(含合同行与项目一致性、SN 全局唯一、绑定派生)。
- [ ] **孤儿 SN**:默认策略与(可选)M11-F20 配置占位行为**文档化并在代码注释或配置类中可定位**。
- [ ] **交付门禁(M3-F07**:与 SN 创建/绑定相关的规则在代码中**可插拔**或明确「I4 硬编码默认 + I5+ 读配置」的 TODO 与 Owner。
- [ ] 审计:`DELIVERY_BATCH` / `LICENSE_SN` / 交付行动作写入 M10-F01 存储。
- [ ] `contracts/openapi/delivery-platform-api.json` 更新并通过 `OpenApiContractSnapshotTest`
### 6.2 前端(delivery-platform-ui
- [ ] §5 路由与菜单可达;RBAC 权限码与后端对齐。
- [ ] 交付详情状态操作仅展示合法迁移;错误态与 409 提示一致。
- [ ] SN 新建/编辑:合同行选择器与项目联动;**孤儿**场景 UI 与后端策略一致(禁止或警告)。
- [ ] E2E P0`交付 → SN 录入/绑定 → 状态/备注回写`(与轨道 B I4 DoD 一致)。
### 6.3 客户端 SDK 工作区(本仓库)
- [ ] OpenAPI 快照与 [contracts/README.md](../../contracts/README.md) 说明含 Delivery/SN 标签。
- [ ] [tracks/03-client-sdk.md](../tracks/03-client-sdk.md) 或等价文档中 **BP-10 与平台对象口径** 补充:交付批次、SN 在集成叙事中的位置(**不实现**平台 REST 客户端亦可,但**文档**须与 I4 契约一致)。
- [ ] **M11-F20**:在 SDK/集成文档中标注为 **后续配置项**(门禁、孤儿强校验),避免集成方误假设 I4 已暴露该 HTTP API。
---
## 7. 修订记录
| 日期 | 说明 |
|------|------|
| 2026-04-06 | 初版:I4 范围与边界、REST 提案、校验与审计、前端路由、I4 末三轨 checklist。 |