# 微信小程序 · 订单到达/离开信息接口(5 个 CRUD)

**日期**:2026-04-21
**影响**:**微信小程序** 行程前·出发准备页(原型 `ew2Kk`)、订单详情到达信息弹窗(原型 `6pfmI / uq0UL / K2s5H / L4xYN / oVMYy / AQ21V / amizC`)
**PR**:#1000(Closes #998)

---

## 概述

订单"分批到达/离开"模型,用户在行程前填写:

- **到达(ARRIVAL)**:从住所抵达目的地
- **离开(DEPARTURE)**:从目的地返程

每个订单可有多批(带小孩/家人分几趟飞),每批一条 `MpArrivalPlanRespVO` 记录,包含交通方式、班次、站点、时间、本批涉及的出行人。

共 **5 个 CRUD** 接口:查全量、新增、全量替换、修改、删除。

---

## 1. 查全量批次

```
GET /mp/order/{orderId}/arrival
```

**鉴权**:Bearer token。

### 入参

| 参数 | 位置 | 类型 | 必填 |
|---|---|---|---|
| orderId | Path | Long | ✅ |

### 出参 `Result<MpArrivalListRespVO>`

| 字段 | 类型 | 说明 |
|---|---|---|
| orderId | Long | 订单ID |
| arrivals | `List<MpArrivalPlanRespVO>` | 到达批次列表 |
| departures | `List<MpArrivalPlanRespVO>` | 离开批次列表 |

`MpArrivalPlanRespVO` 字段见第 2 节出参。

### 响应示例

```json
{
  "code": 200,
  "message": "成功",
  "data": {
    "orderId": 700001,
    "arrivals": [
      {
        "planId": 800001,
        "orderId": 700001,
        "direction": "ARRIVAL",
        "directionLabel": "到达",
        "transportType": "FLIGHT",
        "transportTypeLabel": "飞机",
        "transportNo": "CZ6255",
        "carrier": "南方航空",
        "departStation": "北京首都T3",
        "arriveStation": "海拉尔东山",
        "departTime": "2026-07-10 08:30:00",
        "arriveTime": "2026-07-10 11:00:00",
        "selfDrivePeriod": null,
        "selfDrivePeriodLabel": null,
        "selfDriveEta": null,
        "remark": "带小孩,需婴儿座椅",
        "creatorType": "USER",
        "createTime": "2026-07-01 10:00:00",
        "travelers": [
          {"travelerId":10001,"name":"张三","travelerType":"ADULT"},
          {"travelerId":10002,"name":"张小三","travelerType":"CHILD"}
        ]
      }
    ],
    "departures": []
  },
  "success": true
}
```

---

## 2. 新增批次

```
POST /mp/order/{orderId}/arrival
```

**鉴权**:Bearer token。
**创建者类型**:`creatorType=USER`(用户自填)。

### 入参

| 参数 | 位置 | 类型 | 必填 |
|---|---|---|---|
| orderId | Path | Long | ✅ |

Body `MpArrivalPlanSaveReqVO`:

| 字段 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|
| direction | String | ✅ | - | `ARRIVAL` / `DEPARTURE`(字典 `transport_direction`) |
| transportType | String | ✅ | - | `FLIGHT` / `TRAIN` / `SELF_DRIVE`(字典 `transport_type` 子集) |
| transportNo | String | FLIGHT/TRAIN 必填 | ≤32 | 航班号/车次号,如 `CZ6255` |
| carrier | String | 否 | ≤64 | 航司/铁路公司,如 `南方航空` |
| departStation | String | 否 | ≤64 | 出发站/机场 |
| arriveStation | String | 否 | ≤64 | 到达站/机场 |
| departTime | LocalDateTime | 否 | - | 出发时间 |
| arriveTime | LocalDateTime | 否 | - | 到达时间 |
| selfDrivePeriod | String | SELF_DRIVE 必填 | - | `MORNING` / `AFTERNOON` / `EVENING`(字典 `self_drive_period`) |
| selfDriveEta | LocalDateTime | 否 | - | 自驾预计到达精确时间 |
| remark | String | 否 | ≤255 | 用户备注 |
| travelerIds | `List<Long>` | ✅ 非空 | - | 本批次涉及的出行人 ID 列表;必须属于本订单 |

### 出参 `Result<MpArrivalPlanRespVO>`

| 字段 | 类型 | 说明 |
|---|---|---|
| planId | Long | 批次ID |
| orderId | Long | 订单ID |
| direction | String | 方向 |
| directionLabel | String | 方向中文标签 |
| transportType | String | 交通方式 |
| transportTypeLabel | String | 交通方式中文标签 |
| transportNo | String | 航班号/车次号 |
| carrier | String | 航司/铁路公司 |
| departStation | String | 出发站 |
| arriveStation | String | 到达站 |
| departTime | LocalDateTime | 出发时间 |
| arriveTime | LocalDateTime | 到达时间 |
| selfDrivePeriod | String | 自驾时段 |
| selfDrivePeriodLabel | String | 自驾时段中文标签,如 `下午(12:00-18:00)` |
| selfDriveEta | LocalDateTime | 自驾预计到达 |
| remark | String | 备注 |
| creatorType | String | `USER`(用户自填)/`ADMIN`(定制师代录)(字典 `creator_type`) |
| createTime | LocalDateTime | 创建时间 |
| travelers | `List<MpArrivalPlanTravelerSimpleVO>` | 本批次涉及的出行人(脱敏) |

**`MpArrivalPlanTravelerSimpleVO`**:
| 字段 | 类型 | 说明 |
|---|---|---|
| travelerId | Long | 出行人ID |
| name | String | 姓名 |
| travelerType | String | `ADULT` / `CHILD` / `YOUNG_CHILD` / `BABY`(字典 `traveler_type`) |

### 请求示例

```json
{
  "direction": "ARRIVAL",
  "transportType": "FLIGHT",
  "transportNo": "CZ6255",
  "carrier": "南方航空",
  "departStation": "北京首都T3",
  "arriveStation": "海拉尔东山",
  "departTime": "2026-07-10 08:30:00",
  "arriveTime": "2026-07-10 11:00:00",
  "remark": "带小孩,需婴儿座椅",
  "travelerIds": [10001, 10002]
}
```

---

## 3. 全量替换

```
POST /mp/order/{orderId}/arrival/batch
```

**鉴权**:Bearer token。

先软删当前所有批次,再按请求体批量创建。空数组 `[]` = 清空订单的所有到达/离开批次。

### 入参

| 参数 | 位置 | 类型 | 必填 |
|---|---|---|---|
| orderId | Path | Long | ✅ |

Body:`List<MpArrivalPlanSaveReqVO>`(每项结构同第 2 节)。

### 出参

`Result<MpArrivalListRespVO>` — 替换完成后的全量数据,结构同第 1 节。

---

## 4. 修改批次

```
PUT /mp/order/{orderId}/arrival/plan/{planId}
```

**鉴权**:Bearer token。

**覆盖语义**:按字段全量覆盖,包括 `travelerIds`(替换旧关联)。

### 入参

| 参数 | 位置 | 类型 | 必填 |
|---|---|---|---|
| orderId | Path | Long | ✅ |
| planId | Path | Long | ✅ |

Body:`MpArrivalPlanSaveReqVO`(同第 2 节)。

### 出参

`Result<MpArrivalPlanRespVO>` — 修改后的批次,结构同第 2 节。

---

## 5. 删除批次

```
DELETE /mp/order/{orderId}/arrival/plan/{planId}
```

**鉴权**:Bearer token。

**级联**:软删关联的出行人绑定行。

### 入参

| 参数 | 位置 | 类型 | 必填 |
|---|---|---|---|
| orderId | Path | Long | ✅ |
| planId | Path | Long | ✅ |

### 出参

`Result<Void>`:

```json
{ "code": 200, "message": "成功", "data": null, "success": true }
```

---

## 边界行为

- **字段校验**:`transportType=FLIGHT/TRAIN` 时 `transportNo` 必填;`transportType=SELF_DRIVE` 时 `selfDrivePeriod` 必填
- **出行人校验**:`travelerIds` 中的 ID 必须属于本订单,否则 400
- **订单不存在/无权限**:500,`message` 含 `orderId`
- 用户填的批次 `creatorType=USER`;定制师后台代录的 `creatorType=ADMIN`

---

## 辅助接口 · 航班/火车动态查询(填写表单时用,来自同一批交付)

数据源:飞常准官方 API,resource-service 侧做 Redis 缓存后透传。返回为**列表**(同航班号可能有共享航班、同车次日间可能多班次)。

### 航班查询(3 个)

| 路径 | 入参 | 场景 |
|---|---|---|
| `GET /mp/transport/flight` | `flightNo`(如 `CA1133`)、`date`(`yyyy-MM-dd`) | 按航班号 + 日期查 |
| `GET /mp/transport/flight/by-airport` | `dep`、`arr`(机场三字码,如 `PEK`/`HLD`)、`date` | 按出发/到达机场三字码 + 日期查 |
| `GET /mp/transport/flight/by-city` | `depCity`、`arrCity`(城市三字码,如 `BJS`/`SHA`)、`date` | 按出发/到达城市 + 日期查(覆盖多机场) |

**出参**:`Result<List<MpFlightInfoVO>>`

| 字段 | 类型 | 说明 |
|---|---|---|
| flightNo | String | 航班号,如 `CA1133` |
| airline | String | 航空公司名称 |
| category | String | 航班属性:`国内`/`国际`/`地区` |
| departAirportCode | String | 出发机场三字码,如 `PEK` |
| arriveAirportCode | String | 到达机场三字码,如 `HLD` |
| departAirport | String | 出发机场名称,如 `北京首都` |
| arriveAirport | String | 到达机场名称,如 `呼伦贝尔海拉尔` |
| departCity | String | 出发城市,如 `北京` |
| arriveCity | String | 到达城市,如 `海拉尔` |
| planDepartTime | String | 计划起飞时间(`yyyy-MM-dd HH:mm:ss`) |
| planArriveTime | String | 计划到达时间(`yyyy-MM-dd HH:mm:ss`) |
| actualDepartTime | String | 实际起飞时间(未起飞为 null) |
| actualArriveTime | String | 实际到达时间(未到达为 null) |
| status | String | 航班状态:`计划`/`起飞`/`到达`/`延误`/`取消`/`返航`/`备降` 等 |
| stopFlag | String | 是否经停:`0`=不经停,`1`=经停 1 次,`n`=经停 n 次 |
| shareFlag | String | 是否共享航班:`0`=否,`1`=是 |
| shareFlightNo | String | 共享航班号(实际承运航班号) |
| departTerminal | String | 出发航站楼,如 `T2` |
| arriveTerminal | String | 到达航站楼 |
| boardGate | String | 登机口 |
| arriveExit | String | 到达出口 |
| departTimezone | String | 出发时区偏移(秒) |
| arriveTimezone | String | 到达时区偏移(秒) |

**响应示例**:

```json
{
  "code": 200,
  "message": "成功",
  "data": [
    {
      "flightNo": "CA1133",
      "airline": "中国国际航空股份有限公司",
      "category": "国内",
      "departAirportCode": "PEK",
      "arriveAirportCode": "HLD",
      "departAirport": "北京首都",
      "arriveAirport": "呼伦贝尔海拉尔",
      "departCity": "北京",
      "arriveCity": "海拉尔",
      "planDepartTime": "2026-07-10 06:45:00",
      "planArriveTime": "2026-07-10 08:55:00",
      "actualDepartTime": null,
      "actualArriveTime": null,
      "status": "计划",
      "stopFlag": "0",
      "shareFlag": "0",
      "shareFlightNo": null,
      "departTerminal": "T2",
      "arriveTerminal": null,
      "boardGate": null,
      "arriveExit": null,
      "departTimezone": "28800",
      "arriveTimezone": "28800"
    }
  ],
  "success": true
}
```

### 火车查询(4 个)

| 路径 | 入参 | 场景 |
|---|---|---|
| `GET /mp/transport/train` | `trainNo`(如 `G1`)、`date` | 按车次号查,返回含 `stops` 全部经停站 |
| `GET /mp/transport/train/by-train-no-stations` | `trainNo`、`dep`、`arr`(车站中文名)、`date` | 按车次 + 出发站 + 到达站,比按车次多返回 `departStatus`/`arriveStatus`,**不返回 `stops`** |
| `GET /mp/transport/train/by-station` | `dep`、`arr`(车站中文名)、`date` | 按出发站 + 到达站,返回经过该区间的所有车次 |
| `GET /mp/transport/train/by-city` | `depCity`、`arrCity`(城市中文名,如 `北京`)、`date` | 按出发城市 + 到达城市(含所有车站) |

**出参**:`Result<List<MpTrainInfoVO>>`

| 字段 | 类型 | 说明 |
|---|---|---|
| trainNo | String | 车次,如 `G1` |
| departStation | String | 出发车站,如 `北京南` |
| arriveStation | String | 到达车站,如 `上海虹桥` |
| shutdown | String | 是否停运:`0`=否,`1`=是 |
| planDepartTime | String | 计划出发时间 |
| planArriveTime | String | 计划到达时间 |
| estimatedDepartTime | String | 预计出发时间(仅 `by-train-no-stations` 返回) |
| estimatedArriveTime | String | 预计到达时间(仅 `by-train-no-stations` 返回) |
| actualDepartTime | String | 实际出发时间(仅 `by-train-no-stations` 返回) |
| actualArriveTime | String | 实际到达时间(仅 `by-train-no-stations` 返回) |
| departStatus | String | 出发状态:`计划`/`正点`/`晚点`/`出发`(仅 `by-train-no-stations` 返回) |
| arriveStatus | String | 到达状态:`计划`/`正点`/`晚点`/`到达`(仅 `by-train-no-stations` 返回) |
| duration | Integer | 运行时长(分钟) |
| stops | `List<MpTrainStopVO>` | 经停站列表(仅按车次号 `/mp/transport/train` 返回) |

**`MpTrainStopVO`**:

| 字段 | 类型 | 说明 |
|---|---|---|
| stationName | String | 车站名称,如 `沧州西` |
| arriveTime | String | 到站时间 |
| departTime | String | 发车时间 |

**响应示例**(按车次号):

```json
{
  "code": 200,
  "message": "成功",
  "data": [
    {
      "trainNo": "G1",
      "departStation": "北京南",
      "arriveStation": "上海虹桥",
      "shutdown": "0",
      "planDepartTime": "2026-07-10 06:30",
      "planArriveTime": "2026-07-10 11:24",
      "duration": 294,
      "stops": [
        {"stationName": "北京南", "arriveTime": null, "departTime": "2026-07-10 06:30"},
        {"stationName": "沧州西", "arriveTime": "2026-07-10 07:18", "departTime": "2026-07-10 07:20"},
        {"stationName": "上海虹桥", "arriveTime": "2026-07-10 11:24", "departTime": null}
      ]
    }
  ],
  "success": true
}
```

### 边界

- 查不到数据(航班号无效/停运):返回 `data: []`
- 非常准上游异常/超时:返回 500,`message` 含上游错误码
- 日期格式:`yyyy-MM-dd`,校验不通过返回 500 `日期格式错误，应为yyyy-MM-dd`
- 所有三字码/城市码/车站名不区分大小写(三字码内部自动 `toUpperCase`)

---

## 字典依赖

| 字典类型 | 说明 |
|---|---|
| `transport_direction` | `ARRIVAL` / `DEPARTURE` |
| `transport_type` | `FLIGHT` / `TRAIN` / `SELF_DRIVE`(本场景只用这 3 个子集) |
| `self_drive_period` | `MORNING` / `AFTERNOON` / `EVENING` |
| `creator_type` | `USER` / `ADMIN` |
| `traveler_type` | `ADULT` / `CHILD` / `YOUNG_CHILD` / `BABY` |

所有 `xxxLabel` 字段均由 BFF 透传自 order-v2,已经是中文展示值。
