hl-api-changelog/changelogs-v2/2026-06/26_4460_合同保险Tab无合同无保险返null-修改接口-管理后台.md

251 行
8.1 KiB
Markdown

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

# 【修改接口·管理后台】订单详情「合同·保险」Tab无合同/无保险时子对象返 null (#4460)
> **PR**: #4461 | **服务**: hl-order-service-v3 | **更新时间**: 2026-06-26
## 1. 接口背景
订单详情「合同·保险」Tab 懒加载接口,在订单**从未签约 / 从未投保**时,原本仍返回一个字段全为 `null``contract` / `insurance` 壳对象,前端要判断"这单到底有没有合同/保险"只能逐字段判空,体验差且容易误判。
本次改为:合同、保险**各自独立**判断是否存在,不存在则整个子对象直接返 `null`,前端一层判断即可。
## 2. 变更清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|------|------|------|----------|------|
| 1 | 合同·保险 Tab | GET | `/v3/admin/order/{id}/contract-insurance` | 修改接口 | 无合同时 `contract` 返 null、无保险时 `insurance` 返 null原为字段全 null 的壳对象) |
## 3. 接口详情
### 3.1 获取订单合同·保险 Tab 数据
- **使用场景**管理后台订单详情页,点开「合同·保险」Tab 时懒加载
- **认证**:需要 JWT管理后台登录态
- **幂等性**:是(纯查询)
- **限流**:无
## 4. 接口入参
### 4.1 路径参数
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `id` | String | ✅ | 订单 ID雪花 ID,字符串传递 |
### 4.2 请求体字段
GET 请求)。
## 5. 出参(响应)
### 5.1 顶层结构
| 字段 | 类型 | 说明 |
|------|------|------|
| `contract` | Object \| null | 合同信息;该订单**从未签约**时为 `null` |
| `insurance` | Object \| null | 保险信息;该订单**从未投保**时为 `null` |
> **判定口径**
> - `contract` 为 null合同状态为空 **且** 无合同方案头 **且** 无合同事件
> - `insurance` 为 null保险聚合状态为空 **且** 无保单
> 三者/两者任一非空即视为"存在",照常返回完整对象。
### 5.2 `contract` 对象字段(存在时)
| 字段 | 类型 | 说明 |
|------|------|------|
| `contractStatus` | String | 合同状态,见 §6.1 |
| `contractSignedAt` | String | 签署时间(`yyyy-MM-dd HH:mm:ss`),未签为 null |
| `contractFileUrl` | String | 合同文件地址,未生成为 null |
| `contractSchemeName` | String | 合同方案名 |
| `signerName` | String | 签署人 |
| `signMethod` | String | 签署方式 |
| `events` | Array | 合同流程事件(固定 5 步模板),见 §5.4 |
### 5.3 `insurance` 对象字段(存在时)
| 字段 | 类型 | 说明 |
|------|------|------|
| `insuranceStatus` | String | 保险聚合状态,见 §6.2 |
| `insuranceStatusName` | String | 保险状态中文名 |
| `totalPremium` | Number | 总保费 |
| `policyCount` | Number | 保单数量 |
| `policies` | Array | 逐张保单列表(存在时;可能为空数组) |
### 5.4 `events` / `policies[].events` 事件字段
| 字段 | 类型 | 说明 |
|------|------|------|
| `eventType` / `stepCode` | String | 步骤码 |
| `eventName` | String | 步骤名 |
| `actorType` | String | 操作方类型,见 §6.3 |
| `actorTypeName` | String | 操作方中文名 |
| `actorLabel` | String | 操作方标签 |
| `statusLabel` | String | 状态标签(如 已完成/进行中/待处理) |
| `description` | String | 描述 |
| `occurredAt` | String | 发生时间 |
## 6. 枚举 / 数据字典
### 6.1 contractStatus合同状态
**所属字段**`contract.contractStatus` | **类型**`String`
| 值 | 中文 | 说明 |
|----|------|------|
| `SIGNED` | 已签约 | 合同已签署完成 |
| 其他业务态 | — | 按合同流程取值(生成/报备/作废等) |
### 6.2 insuranceStatus保险聚合状态
**所属字段**`insurance.insuranceStatus` | **类型**`String`
| 值 | 中文insuranceStatusName | 说明 |
|----|------|------|
| `INSURED` | 已出单 | — |
| `INSURING` | 出单中 | — |
| `CANCELLED` | 已取消 | — |
| `FAILED` | 出单失败 | — |
### 6.3 actorType操作方类型
**所属字段**`events[].actorType` / `policies[].events[].actorType` | **类型**`String`
| 值 | 中文actorTypeName | 说明 |
|----|------|------|
| `SYSTEM` | 系统 | — |
| `THIRD_PARTY` | 第三方 | 如电子合同平台 / 保险公司 |
| `CUSTOMER` | 客户 | — |
## 7. 错误码
| code | 含义 | 触发场景 |
|------|------|----------|
| `581007` | 订单不存在 | 路径 `id` 对应订单不存在 |
## 8. 示例
### 8.1 典型成功(无合同无保险订单)
**请求**
```
GET /v3/admin/order/2070039058075017217/contract-insurance
Authorization: Bearer <token>
无请求体
```
**响应**
```json
{
"code": 200,
"message": "成功",
"data": {
"contract": null,
"insurance": null
},
"success": true
}
```
### 8.2 边界(有合同·有保险订单)
**场景说明**订单已签约SIGNED且已投保,两子对象照常返回完整结构。
**响应**
```json
{
"code": 200,
"message": "成功",
"data": {
"contract": {
"contractStatus": "SIGNED",
"contractSignedAt": "2026-04-18 17:12:00",
"contractFileUrl": "https://oss.example.com/contract/xxx.pdf",
"contractSchemeName": "标准国内单团签约方案",
"signerName": "张三",
"signMethod": "电子签约",
"events": [
{ "stepCode": "GENERATE", "eventName": "生成合同", "actorType": "SYSTEM", "actorTypeName": "系统", "statusLabel": "已完成", "occurredAt": "2026-04-18 17:10:00" }
]
},
"insurance": {
"insuranceStatus": "INSURED",
"insuranceStatusName": "已出单",
"totalPremium": 88.00,
"policyCount": 1,
"policies": [
{ "policyNo": "PICC-001", "status": "INSURED", "statusName": "已出单", "events": [] }
]
}
},
"success": true
}
```
### 8.3 业务失败(订单不存在)
**请求**
```
GET /v3/admin/order/88888888/contract-insurance
Authorization: Bearer <token>
```
**响应**
```json
{
"code": 581007,
"message": "订单不存在",
"success": false
}
```
## 9. 业务边界
-**返完整对象**:合同侧状态/方案头/事件任一存在 → `contract` 完整返回;保险侧聚合状态或保单任一存在 → `insurance` 完整返回
- ⚠️ **返 null**:从未签约 → `contract` 整体为 `null`;从未投保 → `insurance` 整体为 `null`
- 两者相互独立:可能出现「有合同无保险」(`contract` 非 null、`insurance` 为 null或反之
## 10. 修改前后对比
### 10.1 字段级对比
| 字段 | 改前 | 改后 |
|------|------|------|
| `data.contract`(无合同时) | 字段全为 null 的对象 `{contractStatus:null, ..., events:[]}` | `null` |
| `data.insurance`(无保险时) | 字段全为 null 的对象 `{insuranceStatus:null, ..., policyCount:0, policies:[]}` | `null` |
### 10.2 行为级对比
| 行为 | 改前 | 改后 |
|------|------|------|
| 前端判断有无合同/保险 | 逐字段判空(如 `contract.contractStatus == null && ...` | 一层判断 `if (data.contract)` / `if (data.insurance)` |
## 11. 影响评估 / 回滚
### 11.1 影响评估
- **是否破坏向后兼容**:是(无合同/无保险订单的 `contract`/`insurance` 由对象变为 null,前端需调整判空逻辑
- **前端是否必须同步上线**:是(原逐字段判空逻辑需改为判子对象是否为 null
### 11.2 回滚方案
- **回滚方式**revert PR #4461
- **回滚耗时**:预计 5 分钟(重新部署)
## 12. 注意事项
- 前端 workaround 清理点:原先「逐字段判空来识别是否有合同/保险」的逻辑可改为「判 `data.contract` / `data.insurance` 是否为 null」
- 「有合同·有保险」「有合同·无保险」「无合同·有保险」「无合同·无保险」四种组合均需正确处理
## 13. 关联 / 联系人
### 13.1 链接
- **Issue**: [#4460](https://git.1814.love:8443/wx/HL/issues/4460)
- **PR**: [#4461](https://git.1814.love:8443/wx/HL/pulls/4461)
- **Merge commit**: [8ae5d58](https://git.1814.love:8443/wx/HL/commit/8ae5d5819268a01910a8ec93908489d728ad7005)
### 13.2 联系人
- **后端负责人**: @yst
- **前端对接(管理后台)**: —