12 KiB
[修改接口·管理后台] 订单详情枚举统一 + service-standard 修复 (#2867 #2868)
PR: #2869 #2874 | 服务: hl-order-service-v3 | 更新时间: 2026-05-22
1. 接口背景
本次修复合并两个 PR,均针对订单详情页管理后台接口的静默 bug:
-
PR #2869(Issue #2868)枚举统一 + null 兑底:contractStatus / insuranceStatus 两个字段历史上枚举値定义不完整(少了中间过渡态和 NONE 初始态),且在订单还未进入合同/保险流程时后端直接返回 null,前端字典无法匹配导致显示空白或"未知"。本次统一枚举値集并将 null 兑底为 "NONE"。
-
PR #2874(Issue #2867)service-standard Tab 修复:hasServiceStandard 字段原来只判断快照记录是否存在(行存在即返回 true),未校验反序列化是否成功、itinerary 是否非空,导致前端收到 hasServiceStandard=true 后调 GET /service-standard 接口却收到 data=null 的矛盾。根因是快照 itinerary 字段类型 List 与源头 List 不匹配,反序列化失败被静默吞掉。
2. 变更清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|---|---|---|---|---|
| 1 | 订单详情 | GET | /v3/admin/order/{id} |
修改接口 | contractStatus/insuranceStatus 枚举统一;hasServiceStandard 判定逻辑修复 |
| 2 | 合同&保险 Tab | GET | /v3/admin/order/{id}/contract-insurance |
修改接口 | contractStatus/insuranceStatus 枚举统一,null 兑底为 NONE |
| 3 | 服务标准 Tab | GET | /v3/admin/order/{id}/service-standard |
修改接口(行为修复) | 原来 data 永远 null;现在能正常返回完整数据 |
3. 接口详情
3.1 GET /v3/admin/order/{id} —— 订单详情
- 使用场景:管理后台订单详情页首次加载,获取订单主信息
- 认证:需要管理后台 JWT(Bearer token),role=ADMIN
- 幂等性:是(只读)
- 限流:无
本次变更字段:
data.main.contractStatus:枚举値扩展(4→6 値),null →"NONE"data.main.insuranceStatus:枚举値扩展(4→5 値),null →"NONE"data.main.hasServiceStandard:判定逻辑修复(见 §9)
3.2 GET /v3/admin/order/{id}/contract-insurance —— 合同&保险 Tab
- 使用场景:管理后台订单详情页切换到 "合同&保险" Tab 时调用
- 认证:需要管理后台 JWT(Bearer token),role=ADMIN
- 幂等性:是(只读)
- 限流:无
本次变更字段:
data.contract.contractStatus:枚举値扩展(4→6 値),null →"NONE"data.insurance.insuranceStatus:枚举値扩展(4→5 値),null →"NONE"
3.3 GET /v3/admin/order/{id}/service-standard —— 服务标准 Tab
- 使用场景:管理后台订单详情页切换到 "服务标准" Tab 时调用;应在
hasServiceStandard=true时才调此接口 - 认证:需要管理后台 JWT(Bearer token),role=ADMIN
- 幂等性:是(只读)
- 限流:无
本次变更:行为修复。修复前 data 永远为 null;修复后在有效快照时返回完整结构。
4. 接口入参
4.1 路径参数
三个接口入参相同,无变化。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
id |
Long(String) | 是 | 订单 ID(snowflake,JSON 传 String 防精度丢失) |
4.2 请求体字段
三个接口均为 GET,无请求体。
5. 出参(响应)
5.1 GET /v3/admin/order/{id} —— 变更字段
| 字段路径 | 类型 | 说明 |
|---|---|---|
data.main.contractStatus |
String(枚举) | 合同状态,见 §6.1。本次扩展枚举値 + null 兑底 |
data.main.insuranceStatus |
String(枚举) | 保险状态,见 §6.2。本次扩展枚举値 + null 兑底 |
data.main.hasServiceStandard |
Boolean | 是否有服务标准快照。本次修复判定逻辑 |
5.2 GET /v3/admin/order/{id}/contract-insurance —— 变更字段
| 字段路径 | 类型 | 说明 |
|---|---|---|
data.contract.contractStatus |
String(枚举) | 合同状态,见 §6.1。本次扩展枚举値 + null 兑底 |
data.insurance.insuranceStatus |
String(枚举) | 保险状态,见 §6.2。本次扩展枚举値 + null 兑底 |
5.3 GET /v3/admin/order/{id}/service-standard —— ServiceStandardVO(完整)
| 字段 | 类型 | 说明 |
|---|---|---|
data.itinerary |
List | 行程列表,元素格式:"Day{n} {title}",如 "Day1 抵达成都"。修复前恒为 null,修复后正常返回 |
data.notice |
String | 出行须知。可为 null |
data.refundPolicy |
String | 退款政策说明。可为 null |
当订单无服务标准快照时(hasServiceStandard=false),整个 data 为 null。
6. 枚举 / 数据字典
6.1 contractStatus(合同状态)
所属字段:data.main.contractStatus(OrderMainVO)、data.contract.contractStatus(ContractInsuranceVO)| 类型:String
| 値 | 中文 | 说明 |
|---|---|---|
NONE |
无合同 | 订单尚未进入签约流程(修复前此状态返回 null) |
GENERATING |
生成中 | 合同正在后台生成 |
GENERATED |
已生成 | 合同已生成,待客户签署 |
SIGNED |
已签署 | 客户已完成签署 |
VOIDED |
已作废 | 合同已作废 |
RESIGNING |
重签中 | 合同正在重新发起签署 |
修复前仅有4 値:PENDING / GENERATED / SIGNED / VOIDED(PENDING 已废弃,NONE / GENERATING / RESIGNING 为本次新增枚举値)
6.2 insuranceStatus(保险状态)
所属字段:data.main.insuranceStatus(OrderMainVO)、data.insurance.insuranceStatus(ContractInsuranceVO)| 类型:String
| 値 | 中文 | 说明 |
|---|---|---|
NONE |
无保险 | 订单尚未进入投保流程(修复前此状态返回 null) |
ISSUING |
投保中 | 保险正在后台出单 |
ISSUED |
已出单 | 保险已成功出单 |
CANCELLED |
已取消 | 保险已取消 |
FAILED |
出单失败 | 保险出单失败 |
修复前仅有4 値:PENDING / ACTIVE / FAILED / CANCELLED(旧値已废弃重命名,全部替换为上表5 値)
7. 错误码
本次修复无新增错误码。
| code | 含义 | 触发场景 |
|---|---|---|
200 |
成功 | 正常响应 |
1000400 |
参数异常 | 路径参数 id 格式错误 |
1000404 |
订单不存在 | 指定 id 的订单不存在或已删除 |
1000403 |
无权限 | 非管理员 token 访问 |
8. 示例
8.1 典型成功 —— 新建订单 contractStatus 返 NONE(修复前返 null)
请求:
GET /v3/admin/order/1921234567890123456
Authorization: Bearer <admin-jwt-token>
响应:
{
"code": 200,
"data": {
"main": {
"orderId": "1921234567890123456",
"orderNo": "ORD20260522001",
"contractStatus": "NONE",
"insuranceStatus": "NONE",
"hasServiceStandard": false
}
},
"message": "ok",
"success": true
}
修复前:contractStatus: null,insuranceStatus: null,前端字典无法匹配显示空白。
8.2 边界情况 —— service-standard 接口有快照时返完整数据(修复前永远 null)
请求:
GET /v3/admin/order/1921234567890123456/service-standard
Authorization: Bearer <admin-jwt-token>
响应(修复后):
{
"code": 200,
"data": {
"itinerary": [
"Day1 抵达成都,入住民宿",
"Day2 前往峨眉山景区",
"Day3 返程"
],
"notice": "请携带身份证原件,到达后联系导游",
"refundPolicy": "出发7天以上全额退款,7天内收取50%手续费"
},
"message": "ok",
"success": true
}
响应(修复前):
{
"code": 200,
"data": null,
"message": "ok",
"success": true
}
8.3 业务失败 —— hasServiceStandard=false 时 service-standard 返 data=null(正常行为)
场景说明:订单未绑定产品行程快照,调 service-standard 接口返 data=null 是正常行为。
请求:
GET /v3/admin/order/1921111111111111111/service-standard
Authorization: Bearer <admin-jwt-token>
响应:
{
"code": 200,
"data": null,
"message": "ok",
"success": true
}
9. 业务边界
hasServiceStandard 判定逻辑(修复后):
同时满足以下三个条件,hasServiceStandard 才返回 true:
- 订单关联的产品行程快照记录存在
- 快照 JSON 反序列化成功(无类型错误)
- 快照中
itinerary字段非空(至少有1天行程)
任意一个条件不满足,hasServiceStandard 返回 false,此时调 GET /service-standard 将返回 data=null。
contractStatus / insuranceStatus 兑底规则:
- 订单刚创建、尚未发起签约/投保时,两个字段返回 "NONE"(修复前返回 null)
- 字段値随后端状态机流转更新,不需要前端轮询,刷新页面即可获取最新値
10. 修改前后对比
10.1 字段级对比
| 字段 | 修复前 | 修复后 |
|---|---|---|
contractStatus(初始态) |
null | "NONE" |
contractStatus(枚举集合) |
PENDING / GENERATED / SIGNED / VOIDED(4 値,含PENDING已废弃) | NONE / GENERATING / GENERATED / SIGNED / VOIDED / RESIGNING(6 値) |
insuranceStatus(初始态) |
null | "NONE" |
insuranceStatus(枚举集合) |
PENDING / ACTIVE / FAILED / CANCELLED(4 値,均已废弃重命名) | NONE / ISSUING / ISSUED / CANCELLED / FAILED(5 値) |
hasServiceStandard |
只判断快照行存在,partial 快照也返 true | 快照存在 + 反序列化成功 + itinerary 非空,三条件全满足才返 true |
| GET /service-standard data | 永远为 null(反序列化失败被吐) | 有效快照时返回完整 {itinerary, notice, refundPolicy} |
10.2 行为级对比
| 行为 | 修复前 | 修复后 |
|---|---|---|
| 新建订单合同/保险状态 | 返回 null,前端字典无法匹配 | 返回 "NONE",字典正常匹配显示"无合同"/"无保险" |
| hasServiceStandard=true 但 Tab 内容空 | 会出现(快照反序列化失败时) | 不再出现;hasServiceStandard=true 一定能拿到有效数据 |
| insuranceStatus 枚举 ACTIVE / PENDING | 后端可能返回这两个値 | 后端不再返回,统一替换为新枚举値集合 |
11. 影响评估 / 回滚
11.1 影响评估
- 是否破坏向后兼容:是(枚举値重命名)。旧字典如只有 PENDING / ACTIVE(保险)或 PENDING(合同)等旧値,遇到新値会显示 "未知"。
- 前端是否必须同步上线:建议同步更新枚举字典(§6.1 和 §6.2 完整値表)再上线;若前端能容忍旧値显示 "未知" 可先不同步。
11.2 回滚方案
- 回滚方式:revert PR #2869 / PR #2874,重新打包部署 hl-order-service-v3
- 回滚后影响:contractStatus / insuranceStatus 恢复返回 null 和旧枚举値;service-standard Tab 恢复返回 null
12. 注意事项
- 枚举字典必须更新:旧前端字典仅有4 値/4 値(含已废弃的 PENDING / ACTIVE),遇到新枚举値(如 NONE / GENERATING / RESIGNING / ISSUING / ISSUED)会显示 "未知"。请按 §6.1 和 §6.2 完整値表更新本地枚举字典。
- 旧枚举値已废弃:contractStatus 的 PENDING、insuranceStatus 的 PENDING 和 ACTIVE 后端不再返回,前端字典保留无害但不会再出现。
- service-standard workaround 清理:如果前端之前针对 hasServiceStandard=true 但 data=null 的矛盾做过兑底处理(如显示固定占位文案、屏蔽Tab),修复后可以移除该 workaround。
13. 关联 / 联系人
13.1 链接
- Issue(枚举统一): #2868
- Issue(service-standard 修复): #2867
- PR(枚举统一): #2869
- PR(service-standard 修复): #2874
- Merge commit(枚举统一): bb4c41969
- Merge commit(service-standard 修复): 442fdf089
13.2 联系人
- 后端负责人: @yst