docs(changelog): order-v3 合同/保险镜像状态 NONE→null 前端变更说明 (#3956)

- 受影响接口:GET /v3/admin/order/{id}、GET /v3/admin/order/{id}/contract-insurance、GET /v3/admin/order/group-batch/{groupBatchId}/orders
- 无合同/无保险时 contractStatus/insuranceStatus 由 "NONE" 改为 null
- Issue #3953 / PR #3956
这个提交包含在:
yaosutu 2026-06-18 10:15:07 +08:00
父节点 cee040f814
当前提交 5a6f8675b6

查看文件

@ -0,0 +1,342 @@
# 合同/保险镜像状态"无记录"时从 `"NONE"` 改为 `null`
- **变更类型**:修改接口
- **端类型**:管理后台
- **日期**2026-06-18
- **Issue**[#3953](https://git.1814.love:8443/wx/HL/issues/3953)
- **PR**[#3956](https://git.1814.love:8443/wx/HL/pulls/3956)
- **Commit**[dc21950a9](https://git.1814.love:8443/wx/HL/commit/dc21950a9)feature/ [41cfd5ad8](https://git.1814.love:8443/wx/HL/commit/41cfd5ad8)merge
- **后端负责人**:腰苏图
---
## ① 接口背景
order-v3 主表 `order_main` 中有两个镜像状态列:`contract_status`(合同状态)和 `insurance_status`(保险状态)。
以往在订单**尚无合同**或**尚无保险**时,这两列的值以字符串 `"NONE"` 写入并通过接口返回给前端。此次统一语义:「无记录」状态改为返回 `null`,不再返回字符串 `"NONE"`
涉及枚举值(有进展时不变):
- 合同:`GENERATING` / `GENERATED` / `SIGNED` / `VOIDED` / `RESIGNING`
- 保险:`INSURING` / `INSURED` / `CANCELLED` / `FAILED`
---
## ② 变更清单
| # | 接口 | 字段路径 | 变更 |
|---|------|----------|------|
| 1 | `GET /v3/admin/order/{id}` | `main.contractStatus` | 无合同时:`"NONE"``null` |
| 2 | `GET /v3/admin/order/{id}` | `main.insuranceStatus` | 无保险时:`"NONE"``null` |
| 3 | `GET /v3/admin/order/{id}/contract-insurance` | `contract.contractStatus` | 无合同时:`"NONE"``null` |
| 4 | `GET /v3/admin/order/{id}/contract-insurance` | `insurance.insuranceStatus` | 无保险时:`"NONE"``null` |
| 5 | `GET /v3/admin/order/group-batch/{groupBatchId}/orders` | `[*].contractStatus` | 无合同时:`"NONE"``null` |
| 6 | `GET /v3/admin/order/group-batch/{groupBatchId}/orders` | `[*].insuranceStatus` | 无保险时:`"NONE"``null` |
> 订单列表 `GET /v3/admin/order/list` 返回的 `OrderListItemRespVO` **不含**这两个字段,不受影响。
---
## ③ 接口详情
### 接口一订单详情9 Tab 聚合)
| 项 | 值 |
|---|---|
| 方法 + 路径 | `GET /v3/admin/order/{id}` |
| 接口名 | 订单详情9 Tab 聚合) |
| 认证 | 需要 JWT管理后台 token |
| 幂等性 | 只读,天然幂等 |
| 限流 | 无特殊限流 |
### 接口二:合同保险 Tab
| 项 | 值 |
|---|---|
| 方法 + 路径 | `GET /v3/admin/order/{id}/contract-insurance` |
| 接口名 | 订单合同保险 Tab |
| 认证 | 需要 JWT管理后台 token |
| 幂等性 | 只读,天然幂等 |
| 限流 | 无特殊限流 |
### 接口三:团期子订单列表
| 项 | 值 |
|---|---|
| 方法 + 路径 | `GET /v3/admin/order/group-batch/{groupBatchId}/orders` |
| 接口名 | 团期子订单列表 |
| 认证 | 需要 JWT管理后台 token |
| 幂等性 | 只读,天然幂等 |
| 限流 | 无特殊限流 |
---
## ④ 接口入参
三个接口均为路径参数,本次变更不影响入参。
| 参数 | 位置 | 类型 | 必填 | 说明 |
|------|------|------|------|------|
| `id` | Path | Long | 是 | 订单 ID接口一、二 |
| `groupBatchId` | Path | Long | 是 | 团期 ID接口三 |
---
## ⑤ 出参字段(受影响字段)
### 接口一 `GET /v3/admin/order/{id}``main` 节点
| 字段路径 | 类型 | 说明 |
|----------|------|------|
| `main.contractStatus` | `String \| null` | 合同状态。**无合同时返回 `null`**(原来返回 `"NONE"`);有合同时为枚举值,见 § ⑥ |
| `main.insuranceStatus` | `String \| null` | 保险状态。**无保险时返回 `null`**(原来返回 `"NONE"`);有保险时为枚举值,见 § ⑥ |
### 接口二 `GET /v3/admin/order/{id}/contract-insurance`
| 字段路径 | 类型 | 说明 |
|----------|------|------|
| `contract.contractStatus` | `String \| null` | 合同状态。**无合同时返回 `null`** |
| `insurance.insuranceStatus` | `String \| null` | 保险状态。**无保险时返回 `null`** |
其余字段(`contractSignedAt``contractFileUrl``events``insurancePolicyNo``insurancePremium` 等)在无合同/无保险时仍返回 `null`,无变化。
### 接口三 `GET /v3/admin/order/group-batch/{groupBatchId}/orders` — 列表项
| 字段路径 | 类型 | 说明 |
|----------|------|------|
| `[*].contractStatus` | `String \| null` | 合同状态。**无合同时返回 `null`** |
| `[*].insuranceStatus` | `String \| null` | 保险状态。**无保险时返回 `null`** |
---
## ⑥ 枚举 / 数据字典
### contractStatus 合同状态
| 枚举值 | 含义 | 备注 |
|--------|------|------|
| `null` | 无合同记录 | **本次新值**(原来是 `"NONE"` |
| `GENERATING` | 合同生成中 | |
| `GENERATED` | 合同已生成(待签署) | |
| `SIGNED` | 已签署 | |
| `VOIDED` | 已作废 | |
| `RESIGNING` | 重新签署中 | |
### insuranceStatus 保险状态
| 枚举值 | 含义 | 备注 |
|--------|------|------|
| `null` | 无保险记录 | **本次新值**(原来是 `"NONE"` |
| `INSURING` | 出单中 | |
| `INSURED` | 已投保 | |
| `CANCELLED` | 已取消 | |
| `FAILED` | 出单失败 | |
---
## ⑦ 错误码
本次变更不新增错误码。以下为接口既有错误码:
| 错误码 | HTTP 状态 | 说明 |
|--------|-----------|------|
| `587000` | 400 | 订单不存在 |
---
## ⑧ 示例
### 8.1 典型成功——订单已签约并已投保
**请求**
```http
GET /v3/admin/order/2000000001
Authorization: Bearer <token>
```
**响应main 节点节选)**
```json
{
"code": 200,
"data": {
"main": {
"id": "2000000001",
"orderNo": "HL20260601001",
"orderStatus": "ON_TRIP",
"contractStatus": "SIGNED",
"insuranceStatus": "INSURED",
"refundStatus": null,
"hasRefund": false
}
}
}
```
**合同保险 Tab`GET /v3/admin/order/2000000001/contract-insurance`**
```json
{
"code": 200,
"data": {
"contract": {
"contractStatus": "SIGNED",
"contractSignedAt": "2026-06-01T10:30:00",
"contractFileUrl": "https://oss.hulalv.com/contract/HL20260601001.pdf",
"events": [
{ "eventType": "GENERATE", "occurredAt": "2026-06-01T10:00:00" },
{ "eventType": "SIGN", "occurredAt": "2026-06-01T10:30:00" }
]
},
"insurance": {
"insuranceStatus": "INSURED",
"insurancePolicyNo": "PICC2026060100123",
"insurancePremium": 88.00,
"events": [
{ "eventType": "ISSUE", "occurredAt": "2026-06-01T10:35:00" }
]
}
}
}
```
---
### 8.2 边界情况——订单刚创建,尚无合同和保险
**响应main 节点节选)**
```json
{
"code": 200,
"data": {
"main": {
"id": "2000000002",
"orderNo": "HL20260602001",
"orderStatus": "CUSTOMIZING",
"contractStatus": null,
"insuranceStatus": null,
"hasRefund": false
}
}
}
```
**合同保险 Tab**
```json
{
"code": 200,
"data": {
"contract": {
"contractStatus": null,
"contractSignedAt": null,
"contractFileUrl": null,
"events": []
},
"insurance": {
"insuranceStatus": null,
"insurancePolicyNo": null,
"insurancePremium": null,
"events": []
}
}
}
```
**团期子订单列表(`GET /v3/admin/order/group-batch/3000000001/orders`,无合同无保险的子订单项节选)**
```json
{
"code": 200,
"data": [
{
"orderId": "2000000002",
"orderNo": "HL20260602001",
"contractStatus": null,
"insuranceStatus": null,
"payStatus": "UNPAID"
}
]
}
```
### 8.3 业务失败——订单 ID 不存在
**请求**
```http
GET /v3/admin/order/9999999999
```
**响应**
```json
{
"code": 587000,
"msg": "订单不存在"
}
```
---
## ⑨ 业务边界
**适用**
- 创建后尚未触发签约的订单:`contractStatus` 返回 `null`
- 创建后尚未触发投保的订单:`insuranceStatus` 返回 `null`
- 合同被作废后重新签署流程未开始的中间态仍走枚举值(`VOIDED``RESIGNING`),不返回 `null`
**不适用(不返回 null**
- 合同/保险流程一旦触发(任意枚举值),即使中途失败(`FAILED`)也返回枚举值而非 `null`
**特殊边界**
- 若历史订单数据库列值为 `"NONE"`(迁移前旧数据),后端读取时统一转换为 `null` 再返回,前端不会收到字符串 `"NONE"`
---
## ⑩ 修改前后对比
### 字段级对比
| 字段 | 无合同/无保险时修改前 | 无合同/无保险时修改后 |
|------|---------------------|---------------------|
| `contractStatus` | `"NONE"` | `null` |
| `insuranceStatus` | `"NONE"` | `null` |
三个接口均适用此对比。有合同/有保险时枚举值**不变**。
### 行为级对比
| 场景 | 修改前 | 修改后 |
|------|--------|--------|
| 新建订单查详情 | `contractStatus: "NONE"` | `contractStatus: null` |
| 查合同保险 Tab | `contract.contractStatus: "NONE"` | `contract.contractStatus: null` |
| 团期子订单列表 | `contractStatus: "NONE"` | `contractStatus: null` |
---
## ⑪ 影响评估 / 回滚
### 破坏兼容
**破坏性变更**。若前端代码以 `contractStatus === "NONE"``insuranceStatus === "NONE"` 判断「无状态」,**需改为 `contractStatus == null`**(或 `!contractStatus`),否则判断会失效。
### 前端同步要求
前端需在本次后端上线前(或同时)完成如下改动:
1. 所有判断 `contractStatus === "NONE"` 的地方改为 `contractStatus == null`(或 `!contractStatus`
2. 所有判断 `insuranceStatus === "NONE"` 的地方改为 `insuranceStatus == null`(或 `!insuranceStatus`
3. 合同 Tab 徽标逻辑:判断是否显示「无合同」状态从 `=== "NONE"` 改为 `=== null`
4. 保险 Tab 徽标逻辑:同上
### 回滚方案
后端可在镜像写入层回退 `null → "NONE"` 兜底逻辑,重新发布即生效,无需 DDL。
---
## ⑫ 注意事项
1. **三个接口同时生效**`GET /v3/admin/order/{id}``GET /v3/admin/order/{id}/contract-insurance``GET /v3/admin/order/group-batch/{groupBatchId}/orders` 同批上线,需一次性适配,不存在分步过渡。
2. **历史数据已处理**:后端上线时同步处理库中存量 `"NONE"` 值,前端永远不会收到字符串 `"NONE"`,无需做兼容两个值的过渡逻辑。
3. **列表接口不受影响**`GET /v3/admin/order/list` 返回的列表项 VO 不含 `contractStatus` / `insuranceStatus`,无需处理。
4. **枚举值语义不变**:除 `"NONE"` 变为 `null` 外,其余枚举值含义和拼写完全不变。
---
## ⑬ 关联 / 联系人
| 项 | 链接 |
|---|------|
| Issue | [#3953 合同/保险镜像状态统一 NONE→null](https://git.1814.love:8443/wx/HL/issues/3953) |
| PR | [#3956 refactor(order-v3): 合同/保险镜像状态统一 NONE→null](https://git.1814.love:8443/wx/HL/pulls/3956) |
| Feature Commit | [dc21950a9](https://git.1814.love:8443/wx/HL/commit/dc21950a9) |
| Merge Commit | [41cfd5ad8](https://git.1814.love:8443/wx/HL/commit/41cfd5ad8) |
| 后端负责人 | 腰苏图yaosutu |