hl-api-changelog/changelogs/2026-04/2026-04-19_mp-order-insurance-popup-and-improvements.md

8.0 KiB

小程序订单 - 保险弹窗接口 + hotels/serviceItems 字段修复

  • 日期: 2026-04-19
  • PR:
    • #885 hotels coverUrl 回填
    • #936 保险弹窗 + clickable=true + 过滤 CANCELLED + 被保人
  • 类型: FEATURE + BUGFIX
  • 状态: 已合并到 dev + 测试环境部署中
  • 服务: hl-mp-service + hl-order-service-v2

一、接口变更清单

# 方法 路径 变更类型 影响位置 变更内容
1 GET /mp/order/{orderId}/insurance 🟢 新增接口 保险详情弹窗
2 GET /mp/order/{orderId}/hotels 🔴 出参修复 data.items[].coverUrl 已分配路径此前硬编码 null, 现从产品快照反查返回图片 URL
3 POST /mp/order/create
GET /mp/order/{id}
PUT /mp/order/{id}/edit
🔴 出参字段值变化 data.serviceItems[type=INSURANCE].clickable 此前 INCLUDED 时返回 false, 现统一返回 true

所有接口入参无变化


二、接口详情

1. 🟢 新增: GET /mp/order/{orderId}/insurance

保险详情弹窗数据。

鉴权

  • Header: Authorization: Bearer {mp-token}
  • 订单必须属于当前用户, 否则返回 code=403

路径参数

参数 位置 类型 必填 说明
orderId path Long 订单 ID

Query 参数

无。

Request Body

无。

响应 Schema

{
  code: number;           // 200 成功 / 403 无权 / 404 订单不存在
  message: string;
  data: MpInsuranceDetailVO | null;   // 订单无保险时为 null
  success: boolean;
}

interface MpInsuranceDetailVO {
  // ===== 方案基础信息 =====
  schemeId: string;             // 方案 ID (String 防 JS 大数丢精度)
  schemeName: string;           // 方案名
  description: string | null;   // 方案描述
  isOverseas: boolean;          // 境内/境外
  totalDays: number;            // 适用行程天数
  notice: 'INCLUDED' | 'OPTIONAL';   // INCLUDED=必含, OPTIONAL=可选

  // ===== 保障分段(方案配置, 与订单无关) =====
  segments: CoverageSegment[];

  // ===== 投保记录(订单付款后才有) =====
  policies: PolicyItem[];
}

interface CoverageSegment {
  segmentName: string;     // 分段名 "低风险"/"高风险"
  dayOffsetStart: number;  // 起始天(1 起)
  dayOffsetEnd: number;    // 结束天(-1 表示最后一天)
  productName: string;     // 保险产品名
  planName: string;        // 保障计划名
}

interface PolicyItem {
  insuranceOrderId: string;     // 保险订单 ID (String, 用于下载单张保单)
  policyNo: string;             // 保单号
  productName: string;
  planName: string;
  premium: number;              // 保费(元)
  insuredCount: number;         // 被保人数
  coverageStartDate: string;    // 起保日 yyyy-MM-dd
  coverageEndDate: string;      // 截止日
  status: 'PENDING' | 'INSURING' | 'INSURED' | 'FAILED';  // CANCELLED 不返回
  statusLabel: string;          // 状态中文
  insuredPersons: InsuredPerson[];
}

interface InsuredPerson {
  name: string;                   // 姓名
  idCardType: 'ID_CARD' | 'PASSPORT';
  idCardNo: string;               // 证件号(后端已脱敏: 110***********1234)
  birthday: string;               // 生日 yyyy-MM-dd
  gender: 'MALE' | 'FEMALE';
  phone: string;                  // 手机号(后端已脱敏: 138****5678)
}

响应示例

{
  "code": 200,
  "message": "成功",
  "data": {
    "schemeId": "2028762847782060034",
    "schemeName": "测试3日方案",
    "description": null,
    "isOverseas": false,
    "totalDays": 3,
    "notice": "INCLUDED",
    "segments": [
      { "segmentName": "低风险", "dayOffsetStart": 1, "dayOffsetEnd": 2,
        "productName": "山河令(太保山东新)", "planName": "10万计划" },
      { "segmentName": "高风险", "dayOffsetStart": 3, "dayOffsetEnd": 3,
        "productName": "平安保游万里行马术专属保险(互联网)", "planName": "计划一" }
    ],
    "policies": [
      {
        "insuranceOrderId": "9100000000001001",
        "policyNo": "BY202604190001",
        "productName": "山河令(太保山东新)",
        "planName": "10万计划",
        "premium": 50.00,
        "insuredCount": 2,
        "coverageStartDate": "2026-05-28",
        "coverageEndDate": "2026-05-29",
        "status": "INSURED",
        "statusLabel": "已承保",
        "insuredPersons": [
          { "name": "张三", "idCardType": "ID_CARD",
            "idCardNo": "110***********2222", "birthday": "1990-01-01",
            "gender": "MALE", "phone": "138****8001" },
          { "name": "李四", "idCardType": "ID_CARD",
            "idCardNo": "110***********3333", "birthday": "1995-02-02",
            "gender": "FEMALE", "phone": "138****8002" }
        ]
      }
    ]
  },
  "success": true
}

几种特殊返回

场景 返回
订单不存在 code=404, data=null, message="订单不存在"
订单不属于当前用户 code=403, data=null, message="无权查看此订单"
订单未配保险 (insuranceNotice=EXCLUDED) 或无 INSURANCE 服务项 code=200, data=null
订单配了保险但未付款 code=200, data.policies=[] (只有方案+分段, 无保单)
订单已付款, 正在投保中 code=200, data.policiesstatus=PENDINGINSURING 的条目
一订单多保单 (分段投保 / 退保重保) code=200, data.policies 返回多条, CANCELLED 已过滤不返回
方案在数据库被删 code=200, data=null (兜底)

2. 🔴 修复: GET /mp/order/{orderId}/hotels

变更位置

出参 data.items[].coverUrl

变更前

已分配路径 (订单已确认实际入住酒店时) 返回:

{
  "items": [
    { "hotelId": "xxx", "hotelName": "某酒店", "coverUrl": null,
      "assignmentDate": "2026-05-28", "roomType": "大床房", "hotelType": "HOTEL" }
  ]
}

变更后

{
  "items": [
    { "hotelId": "xxx", "hotelName": "某酒店",
      "coverUrl": "https://cdn.../hotel-cover.jpg",
      "assignmentDate": "2026-05-28", "roomType": "大床房", "hotelType": "HOTEL" }
  ]
}

变更原因

订单分配表 order_hotel_assignment 不存 coverUrl, 此前硬编码 null. 现按 hotelId 从产品快照 hotels[] 里反查图片 URL. 未分配路径行为不变.

其他字段

无变化 (hotelId/hotelName/assignmentDate/roomType/hotelType 及同级 nightCount/roomTypeStats 均无改动)。


3. 🔴 字段值变化: serviceItems[type=INSURANCE].clickable

影响接口

  • POST /mp/order/create 返回 data.serviceItems
  • GET /mp/order/{orderId} 返回 data.serviceItems
  • PUT /mp/order/{orderId}/edit 返回 data.serviceItems

变更位置

data.serviceItems 数组中 type === 'INSURANCE' 的元素的 clickable 字段。

变更前

insuranceNotice clickable
EXCLUDED 条目不返回
INCLUDED (必含保险) false
OPTIONAL (可选加保) true

变更后

insuranceNotice clickable
EXCLUDED 条目不返回 (不变)
INCLUDED true (与 OPTIONAL 统一)
OPTIONAL true (不变)

变更原因

新增了保险弹窗接口 /mp/order/{id}/insurance 后, INCLUDED 也有点击交互需求 (查看方案 + 保单列表), 不再需要前端禁用这个条目。

历史订单

在本次改动之前已下单的订单, order_service_item 表里固化的 clickable 仍是原值 (可能 false), 新下单的订单起是 true。如果需要批量修正历史数据, 可提 Issue 加补偿脚本。


三、不兼容变更

  • 新增接口: 老前端不调用不影响
  • hotels coverUrl: 从 null 变为有值, 前端原本 null 态处理代码仍然正确
  • clickable: 从 false 变为 true 只是让条目变可点, 旧代码不点它也不会出错