# C端订单详情页接口变更 — 2026-04-17

> **服务** hl-order-service-v2（端口8094）· **类型** feat
> **前端调用路径**: 网关(8080) → hl-mp-service(8085) → hl-order-service-v2(8094)
> **前端实际请求前缀**: `GET /mp/order/`（mp-service 需同步新增 BFF 转发层）

---

## 一、已改接口（1个）

### ✏️ `GET /mp/order/{orderId}` — 订单详情

**请求参数**：不变（路径参数 orderId）

**返回值变更**：整体结构从 `OrderDetailVO` 改为 `MpOrderDetailVO`

#### 新增字段

| 字段 | 类型 | 必有 | 说明 | 示例 |
| --- | --- | --- | --- | --- |
| `serviceItems` | `List` | ✅ | 服务包含摘要列表，每项含 type/title/summary/clickable | 见下方 |
| `notIncluded` | `String` | 否 | 不含说明 | `"往返大交通、个人消费"` |
| `priceBreakdown` | `Object` | 否 | 价格明细（新订单有值，历史订单null） | 见下方 |
| `balancePayMethodLabel` | `String` | 否 | 尾款方式标签 | `"落地后由领队收取"` |
| `refundProgress` | `Object` | 否 | 退款进度（退款中/已取消时有值） | 见下方 |
| `tripProgress` | `Object` | 否 | 行程进度（仅行程中状态有值） | 见下方 |
| `invoiceStatus` | `String` | ✅ | 发票状态 | `"AFTER_TRIP"` |
| `productTypeLabel` | `String` | ✅ | 产品类型中文 | `"核心产品"` |

#### 删除的字段（前端不再使用）

`totalCost`, `surchargeAmount`, `pendingUpgradeAmount`, `depositRatio`, `balanceProofUrl`, `balancePayMethod`, `remark`, `checklistConfirmed`, `unlockRequestedAt`, `confirmedAt`, `readyAt`, `creatorAdminId`, `creatorName`, `mchId`, `mchName`, `sharerOpenid`, `productSnapshot`, `expiryMinutes`, `timeline`, `processStatus`, `processStatusLabel`, `discountReason`, `discounts`, `surcharges`, `transportSegments`, `roomInfo`, `hotelAssignments`, `hotelAssignmentDetails`, `vehicleInfo`, `refundPolicy`, `refundPolicyId`, `contactName`, `contactPhone`

#### 保留不变的字段

`orderId`, `orderNo`, `groupCode`, `productId`, `productName`, `productCoverUrl`, `productType`, `departureDate`, `returnDate`, `tripDays`, `tripNights`, `adultCount`, `childCount`, `youngChildCount`, `babyCount`, `createTime`, `status`, `statusLabel`, `displayStatus`, `displayStatusLabel`, `cancelReason`, `cancelledAt`, `completedAt`, `expiryTime`, `totalPrice`, `depositAmount`, `balanceAmount`, `paidAmount`, `paymentType`, `paymentTypeLabel`, `paidAt`, `payMethodLabel`, `discountAmount`, `refundAmount`, `contracts`, `insurances`, `travelers`, `todos`, `reviewed`, `customizerId`, `customizerName`, `customizerAvatarUrl`, `customizerQrUrl`

---

## 二、新增接口（6个弹窗）

> 用户点击 `serviceItems` 列表中某项时按需调用，不需要在详情页一次性请求。
> 每个接口都需要传 userId（mp-service 从 token 中获取后透传）。

### 🆕 `GET /mp/order/{orderId}/tickets` — 门票清单

**请求参数**：路径参数 `orderId`

**返回值** `MpTicketListVO`：

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `hint` | `String` | 顶部提示语 |
| `items` | `List` | 门票列表 |
| `items[].seq` | `Integer` | 序号（1开始） |
| `items[].scenicName` | `String` | 景点名称 |
| `items[].coverUrl` | `String` | 景点封面图（可能为空） |
| `items[].dayNumber` | `Integer` | 第几天 |
| `items[].originalPrice` | `BigDecimal` | 门票原价/人（可能为null） |
| `items[].included` | `Boolean` | 是否已含 |
| `totalValue` | `BigDecimal` | 门票总价值/人（可能为null） |
| `footerText` | `String` | 底部文案 |

**响应示例**：
```json
{
  "code": 200,
  "data": {
    "hint": "以下景点门票已包含在套餐内，无需另购",
    "items": [
      { "seq": 1, "scenicName": "莫日格勒河", "coverUrl": null, "dayNumber": 2, "originalPrice": 80.00, "included": true },
      { "seq": 2, "scenicName": "根河湿地", "coverUrl": null, "dayNumber": 3, "originalPrice": 120.00, "included": true }
    ],
    "totalValue": 200.00,
    "footerText": "全部已含·无需再付"
  }
}
```

---

### 🆕 `GET /mp/order/{orderId}/hotels` — 住宿清单

**请求参数**：路径参数 `orderId`

**返回值** `MpHotelListVO`：

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `hint` | `String` | 顶部提示语（如"5晚精选当地酒店"） |
| `items` | `List` | 酒店列表 |
| `items[].hotelName` | `String` | 酒店名称 |
| `items[].coverUrl` | `String` | 酒店封面图（可能为空） |
| `items[].dayNumber` | `Integer` | 第几天入住 |
| `items[].hotelType` | `String` | 酒店类型（如"5星"/"特色民宿"，可能为空） |
| `items[].roomType` | `String` | 房型（如"大床房"，可能为空） |
| `footerNote` | `String` | 底部备注 |

**响应示例**：
```json
{
  "code": 200,
  "data": {
    "hint": "5晚精选当地酒店",
    "items": [
      { "hotelName": "海拉尔铂尔曼大酒店", "coverUrl": null, "dayNumber": 1, "hotelType": "", "roomType": "大床房" }
    ],
    "footerNote": "实际入住酒店以出行前确认为准"
  }
}
```

---

### 🆕 `GET /mp/order/{orderId}/meals` — 餐饮清单

**请求参数**：路径参数 `orderId`

**返回值** `MpMealListVO`：

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `hint` | `String` | 顶部提示语（如"已含5早8正餐"） |
| `specialMeals` | `List` | 特色正餐列表 |
| `specialMeals[].dayNumber` | `Integer` | 第几天 |
| `specialMeals[].mealName` | `String` | 餐名 |
| `specialMeals[].mealType` | `String` | 餐型（午餐/晚餐） |
| `specialMeals[].description` | `String` | 菜品描述 |
| `stats` | `Object` | 餐饮统计 |
| `stats.breakfast` | `String` | 早餐统计（如"5餐·酒店自助"） |
| `stats.specialDinner` | `String` | 特色正餐统计（如"3餐"） |
| `stats.regularDinner` | `String` | 日常正餐统计（如"5餐·当地特色餐厅"） |
| `footerNote` | `String` | 底部备注 |

**响应示例**：
```json
{
  "code": 200,
  "data": {
    "hint": "已含1早2正餐",
    "specialMeals": [
      { "dayNumber": 2, "mealName": "手把肉宴", "mealType": "午餐", "description": "含奶茶/手把肉" }
    ],
    "stats": { "breakfast": "1餐·酒店自助", "specialDinner": "1餐", "regularDinner": "1餐·当地特色餐厅" },
    "footerNote": "对食物有过敏请提前告知领队"
  }
}
```

---

### 🆕 `GET /mp/order/{orderId}/vehicle` — 用车详情

**请求参数**：路径参数 `orderId`

**返回值** `MpVehicleDetailVO`：

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `hint` | `String` | 顶部提示语 |
| `vehicleName` | `String` | 车型名称 |
| `coverUrl` | `String` | 车型图片（可能为空） |
| `description` | `String` | 车型描述/特点（可能为空） |
| `matchRule` | `String` | 匹配规则说明 |

**响应示例**：
```json
{
  "code": 200,
  "data": {
    "hint": "一家一车不拼团·全程同一辆车",
    "vehicleName": "商务车",
    "coverUrl": null,
    "description": "",
    "matchRule": "匹配规则：根据出行人数自动配车"
  }
}
```

---

### 🆕 `GET /mp/order/{orderId}/guide` — 领队详情

**请求参数**：路径参数 `orderId`

**返回值** `MpGuideInfoVO`：

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `name` | `String` | 姓名（可能为null） |
| `avatarUrl` | `String` | 头像（可能为null） |
| `role` | `String` | 角色标签（固定"主领队"或"领队"） |
| `phone` | `String` | 电话（可能为null） |
| `remark` | `String` | 简介（可能为null） |

> ⚠️ 产品服务快照扩展完成前，除 `role` 外所有字段可能为 null

---

### 🆕 `GET /mp/order/{orderId}/photographer` — 摄影详情

**请求参数**：路径参数 `orderId`

**返回值** `MpPhotographerInfoVO`：

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `name` | `String` | 姓名（可能为null） |
| `avatarUrl` | `String` | 头像（可能为null） |
| `role` | `String` | 角色标签（固定"摄影师"） |
| `phone` | `String` | 电话（可能为null） |
| `remark` | `String` | 简介（可能为null） |

> ⚠️ 同领队，产品服务快照扩展完成前字段可能为 null

---

## 三、新增结构体详细说明

### serviceItems（服务包含摘要）

详情接口返回的 `serviceItems` 是一个数组，每项代表一个服务类别：

```json
[
  { "type": "HOTEL", "title": "住宿", "summary": "5晚精选当地酒店", "clickable": true },
  { "type": "MEAL", "title": "餐饮", "summary": "5早 8正餐", "clickable": true },
  { "type": "VEHICLE", "title": "用车", "summary": "专属车辆 + 司机", "clickable": true },
  { "type": "GUIDE", "title": "领队", "summary": "全程陪同", "clickable": true },
  { "type": "PHOTOGRAPHER", "title": "摄影", "summary": "全程跟拍 + 精修照片", "clickable": true },
  { "type": "TICKET", "title": "门票", "summary": "4处景点门票", "clickable": true },
  { "type": "INSURANCE", "title": "保险", "summary": "旅行意外险", "clickable": false }
]
```

`clickable=true` 时点击应调用对应弹窗接口：`type=HOTEL` → `GET /mp/order/{orderId}/hotels`，以此类推。

### priceBreakdown（价格明细）

```json
{
  "items": [
    { "label": "成人", "unitPrice": 2300.00, "quantity": 2, "amount": 4600.00 },
    { "label": "儿童", "unitPrice": 1800.00, "quantity": 1, "amount": 1800.00 }
  ],
  "subtotal": 6400.00,
  "discounts": [
    { "tag": "早鸟优惠", "description": "提前30天", "amount": -400.00 }
  ],
  "totalDiscount": -400.00,
  "totalPayable": 6000.00
}
```

> ⚠️ 仅新创建的订单有值，历史订单为 null，前端需做空判断

### refundProgress（退款进度）

```json
{
  "steps": [
    { "title": "提交退款申请", "description": "用户发起退款", "status": "COMPLETED", "time": "2026-06-25T16:32:00" },
    { "title": "商家审核中", "description": "客服正在审核您的退款申请", "status": "ACTIVE", "time": null },
    { "title": "退款处理", "description": "原路退回您的微信账户", "status": "PENDING", "time": null },
    { "title": "退款到账", "description": "预计1-3个工作日", "status": "PENDING", "time": null }
  ],
  "currentStep": 1,
  "refundAmount": 1000.00,
  "refundMethod": null,
  "refundArrivalTime": null
}
```

step.status 取值：`COMPLETED`=已完成 / `ACTIVE`=进行中 / `PENDING`=待处理

> ⚠️ 仅退款中/已取消且有退款时有值，其他状态为 null

### tripProgress（行程进度）

```json
{
  "currentDay": 2,
  "totalDays": 6,
  "progressPercent": 33,
  "todayDestination": "莫日格勒河"
}
```

> ⚠️ 仅行程中(displayStatus=TRAVELLING)状态有值，其他状态为 null

### invoiceStatus 取值

| 值 | 说明 |
| --- | --- |
| `ISSUED` | 已开票 |
| `PENDING` | 开票中 |
| `NOT_ISSUED` | 未开票（已完成订单可申请） |
| `AFTER_TRIP` | 出行后可开 |

---

## 四、mp-service BFF 层需同步新增

以上6个弹窗接口目前在 order-service-v2 的 `/internal/mp/order/` 路径下已就绪。
mp-service（hl-mp-service）需要新增对应的 BFF 转发层，路径映射如下：

| 前端调用 | mp-service 转发到 |
| --- | --- |
| `GET /mp/order/{orderId}/tickets` | `GET /internal/mp/order/{orderId}/tickets?userId=xxx` |
| `GET /mp/order/{orderId}/hotels` | `GET /internal/mp/order/{orderId}/hotels?userId=xxx` |
| `GET /mp/order/{orderId}/meals` | `GET /internal/mp/order/{orderId}/meals?userId=xxx` |
| `GET /mp/order/{orderId}/vehicle` | `GET /internal/mp/order/{orderId}/vehicle?userId=xxx` |
| `GET /mp/order/{orderId}/guide` | `GET /internal/mp/order/{orderId}/guide?userId=xxx` |
| `GET /mp/order/{orderId}/photographer` | `GET /internal/mp/order/{orderId}/photographer?userId=xxx` |

> mp-service 从用户 token 中提取 userId，作为查询参数透传给 order-service。
