8.1 KiB
8.1 KiB
【修改接口·管理后台】订单详情「合同·保险」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>
无请求体
响应:
{
"code": 200,
"message": "成功",
"data": {
"contract": null,
"insurance": null
},
"success": true
}
8.2 边界(有合同·有保险订单)
场景说明:订单已签约(SIGNED)且已投保,两子对象照常返回完整结构。
响应:
{
"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>
响应:
{
"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 链接
13.2 联系人
- 后端负责人: @yst
- 前端对接(管理后台): —