diff --git a/changelogs-v2/2026-06/26_4460_合同保险Tab无合同无保险返null-修改接口-管理后台.md b/changelogs-v2/2026-06/26_4460_合同保险Tab无合同无保险返null-修改接口-管理后台.md new file mode 100644 index 0000000..9d1dc1c --- /dev/null +++ b/changelogs-v2/2026-06/26_4460_合同保险Tab无合同无保险返null-修改接口-管理后台.md @@ -0,0 +1,250 @@ +# 【修改接口·管理后台】订单详情「合同·保险」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 +无请求体 +``` + +**响应**: +```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 +``` + +**响应**: +```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 +- **前端对接(管理后台)**: —