# 管理后台 · 订单交通信息(接送站)5 个 CRUD + 飞常准查询 7 个 **日期**:2026-04-23 **影响**:**管理后台** 订单详情·行程安排·交通信息(接送站)板块 **PR**:#1285(/transport 路径别名) **关联 Issue**:#1284(404 修复) --- ## 概述 统一管理后台「交通信息」板块的接口契约。**管理后台前端之前对接的是一份已废弃的 v1 小程序契约**(`legType / departureStation / travelDate / ...`),和后端现有新契约(`direction / departStation / departTime + arriveTime / ...`)完全不匹配,导致: 1. `POST /admin/order/{orderId}/transport` **404**(#1285 已修:Controller 同时支持 `/transport` 和 `/arrival` 两个前缀) 2. 字段反序列化不上 → **400 `方向不能为空`、`出行人列表不能为空`** 本次梳理:管理后台前端请**完全对齐小程序端 `/mp/order/{orderId}/arrival` 的 DTO 结构**(字段名、必填规则都一样,仅路径前缀不同)。同时后端已开放 `/admin/transport/*` 共 7 个飞常准查询接口,供前端自动补全航班/车次时刻。 --- ## 一、订单交通批次 5 个 CRUD ### 基础信息 - **鉴权**:Admin Bearer token - **创建者**:`creatorType=ADMIN`(定制师代录;C 端小程序同样接口 `creatorType=USER`) - **DTO**:后端 `ArrivalPlanSaveReqVO`(和小程序 `MpArrivalPlanSaveReqVO` 完全一致,字段名一一对应) ### 路径表 **URL 支持两个前缀别名(PR #1285)**,任选其一,推荐 `/transport`(与 DB 表 `order_transport_plan` 对齐): | 功能 | 方法 | 路径 | |---|---|---| | 查全量批次 | GET | `/admin/order/{orderId}/transport` 或 `/admin/order/{orderId}/arrival` | | 新增单批次 | POST | `/admin/order/{orderId}/transport` 或 `/admin/order/{orderId}/arrival` | | 全量替换(多批次一次性提交) | POST | `/admin/order/{orderId}/transport/batch` 或 `/admin/order/{orderId}/arrival/batch` | | 修改单批次 | PUT | `/admin/order/{orderId}/transport/plan/{planId}` 或 `/admin/order/{orderId}/arrival/plan/{planId}` | | 删除单批次 | DELETE | `/admin/order/{orderId}/transport/plan/{planId}` 或 `/admin/order/{orderId}/arrival/plan/{planId}` | --- ### 1. 查全量批次 ``` GET /admin/order/{orderId}/transport ``` #### 入参 | 参数 | 位置 | 类型 | 必填 | |---|---|---|---| | orderId | Path | Long | ✅ | #### 出参 `Result` | 字段 | 类型 | 说明 | |---|---|---| | orderId | Long | 订单ID | | arrivals | `List` | 到达批次(接站) | | departures | `List` | 离开批次(送站) | `ArrivalPlanRespVO` 字段结构见第 2 节出参。 --- ### 2. 新增单批次 ``` POST /admin/order/{orderId}/transport ``` **创建者类型**:`creatorType=ADMIN`。 #### 入参 | 参数 | 位置 | 类型 | 必填 | |---|---|---|---| | orderId | Path | Long | ✅ | **Body `ArrivalPlanSaveReqVO`**(完整字段表): | 字段 | 类型 | 必填规则 | 约束 | 说明 | |---|---|---|---|---| | **direction** | String | ✅ 始终必填 | - | 方向。`ARRIVAL`=到达/接站;`DEPARTURE`=离开/送站。字典 `transport_direction` | | **transportType** | String | ✅ 始终必填 | - | 交通方式。`FLIGHT`=飞机,`TRAIN`=火车,`SELF_DRIVE`=自驾。字典 `transport_type`(本模块子集) | | transportNo | String | FLIGHT/TRAIN 必填;SELF_DRIVE 不填 | ≤32 | 航班号/车次号,如 `CA1234` / `G71` | | carrier | String | 可选 | ≤64 | 航司/铁路公司,如 `中国国航` | | departStation | String | DEPARTURE+FLIGHT/TRAIN 必填;其他场景可选 | ≤64 | 出发站/机场 | | arriveStation | String | ARRIVAL+FLIGHT/TRAIN 必填;其他场景可选 | ≤64 | 到达站/机场 | | departTime | LocalDateTime | DEPARTURE+FLIGHT/TRAIN 必填;其他场景可选 | - | 出发时间,`yyyy-MM-dd HH:mm:ss` | | arriveTime | LocalDateTime | ARRIVAL+FLIGHT/TRAIN 必填;其他场景可选 | - | 到达时间,`yyyy-MM-dd HH:mm:ss` | | selfDrivePeriod | String | SELF_DRIVE 必填 | - | 自驾时段:`MORNING` / `AFTERNOON` / `EVENING`。字典 `self_drive_period` | | selfDriveEta | LocalDateTime | 可选 | - | 自驾预计到达精确时间 | | remark | String | 可选 | ≤255 | 用户备注 | | **travelerIds** | `List` | ✅ 始终非空 | 至少 1 人 | 本批次涉及的出行人 ID 列表;每个 ID **必须属于本订单** | > ⚠️ **字段间联动**:字段必填规则由 Service 层按 `transportType + direction` 组合校验,不是单注解表达式。前端请按上表规则做前置校验,否则会 500。 #### 出参 `Result` | 字段 | 类型 | 说明 | |---|---|---| | 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 | 自驾时段中文标签 | | selfDriveEta | LocalDateTime | 自驾预计到达 | | remark | String | 备注 | | creatorType | String | `USER` / `ADMIN` | | createTime | LocalDateTime | 创建时间 | | travelers | `List` | 本批次涉及的出行人 | **`ArrivalPlanTravelerSimpleVO`**: | 字段 | 类型 | 说明 | |---|---|---| | travelerId | Long | 出行人 ID | | name | String | 姓名 | | travelerType | String | `ADULT` / `CHILD` / `YOUNG_CHILD` / `BABY` | #### 请求示例(接站 ARRIVAL) ```json { "direction": "ARRIVAL", "transportType": "FLIGHT", "transportNo": "CA1234", "carrier": "中国国航", "departStation": "上海虹桥T2", "arriveStation": "海拉尔东山", "departTime": "2026-07-10 08:30:00", "arriveTime": "2026-07-10 11:00:00", "remark": "带小孩,需要婴儿座椅", "travelerIds": [10001, 10002] } ``` #### 请求示例(送站 DEPARTURE) ```json { "direction": "DEPARTURE", "transportType": "FLIGHT", "transportNo": "CA5678", "carrier": "中国国航", "departStation": "海拉尔东山", "arriveStation": "上海虹桥T2", "departTime": "2026-07-15 14:00:00", "arriveTime": "2026-07-15 17:00:00", "travelerIds": [10001, 10002] } ``` #### 请求示例(自驾 SELF_DRIVE) ```json { "direction": "ARRIVAL", "transportType": "SELF_DRIVE", "selfDrivePeriod": "AFTERNOON", "selfDriveEta": "2026-07-10 15:30:00", "remark": "自驾从漠河过来", "travelerIds": [10001] } ``` --- ### 3. 全量替换(多批次一次性提交) ``` POST /admin/order/{orderId}/transport/batch ``` 先软删当前所有批次,再按请求体批量创建。空数组 `[]` = 清空订单全部交通批次。 #### 入参 | 参数 | 位置 | 类型 | 必填 | |---|---|---|---| | orderId | Path | Long | ✅ | Body:`List`(每项结构同第 2 节)。 #### 出参 `Result`,结构同第 1 节。 #### 使用场景 管理后台「一次性代录全部交通」按钮。例如定制师拿到客户的去程和回程航班信息,一次性把 2 条(或多条,如一家分两批)全部提交。 --- ### 4. 修改单批次 ``` PUT /admin/order/{orderId}/transport/plan/{planId} ``` **覆盖语义**:按字段全量覆盖,包括 `travelerIds`(替换旧关联)。 #### 入参 | 参数 | 位置 | 类型 | 必填 | |---|---|---|---| | orderId | Path | Long | ✅ | | planId | Path | Long | ✅ | Body:`ArrivalPlanSaveReqVO`(结构同第 2 节)。 #### 出参 `Result`。 --- ### 5. 删除单批次 ``` DELETE /admin/order/{orderId}/transport/plan/{planId} ``` **级联**:软删关联的出行人绑定行。 #### 入参 | 参数 | 位置 | 类型 | 必填 | |---|---|---|---| | orderId | Path | Long | ✅ | | planId | Path | Long | ✅ | #### 出参 `Result`。 --- ## 二、航班/火车查询 7 个(`/admin/transport/*`) 数据源:飞常准官方 API,`resource-service` 侧做 Redis 缓存后透传。与小程序端 `/mp/transport/*` **底层共享同一批数据**。 **用途**:前端表单里用户输入航班号后,后端返回精确的起飞/到达时间、机场/航站楼等,**前端自动回填 `departTime / arriveTime / departStation / arriveStation`**,用户不必手动填时分秒。 ### 路径表 | 功能 | 路径 | 核心入参 | |---|---|---| | 按航班号查航班 | `GET /admin/transport/flight/by-flight-no` | `flightNo`、`date` | | 按机场三字码查航班 | `GET /admin/transport/flight/by-airport` | `dep`、`arr`、`date` | | 按城市三字码查航班 | `GET /admin/transport/flight/by-city` | `depCity`、`arrCity`、`date` | | 按车次号查火车(含经停站) | `GET /admin/transport/train/by-train-no` | `trainNo`、`date` | | 按车次+出发/到达站查火车 | `GET /admin/transport/train/by-train-no-stations` | `trainNo`、`dep`、`arr`、`date` | | 按出发/到达站查火车 | `GET /admin/transport/train/by-station` | `dep`、`arr`、`date` | | 按出发/到达城市查火车 | `GET /admin/transport/train/by-city` | `depCity`、`arrCity`、`date` | > ⚠️ 注意:admin 端路径风格和 mp 端**略有差异**(admin 端按航班号的叫 `/flight/by-flight-no`,mp 端叫 `/flight`;按车次号的叫 `/train/by-train-no`,mp 端叫 `/train`)。其他 5 个路径完全一致。 ### 航班返回 `FlightVO` 字段 | 字段 | 类型 | 说明 | |---|---|---| | flightNo | String | 航班号 | | airline | String | 航空公司名称 | | category | String | `国内` / `国际` / `地区` | | departAirportCode | String | 出发机场三字码 | | arriveAirportCode | String | 到达机场三字码 | | departAirport | String | 出发机场名称 | | arriveAirport | String | 到达机场名称 | | departCity | String | 出发城市 | | arriveCity | String | 到达城市 | | planDepartTime | String | 计划起飞时间 | | planArriveTime | String | 计划到达时间 | | actualDepartTime | String | 实际起飞时间(未起飞为 null) | | actualArriveTime | String | 实际到达时间(未到达为 null) | | status | String | `计划` / `起飞` / `到达` / `延误` / `取消` / `返航` / `备降` 等 | | stopFlag | String | `0`=不经停,`1`=经停 1 次,`n`=经停 n 次 | | shareFlag | String | `0`=否,`1`=是(共享航班) | | shareFlightNo | String | 共享航班号 | | departTerminal | String | 出发航站楼 | | arriveTerminal | String | 到达航站楼 | | boardGate | String | 登机口 | | arriveExit | String | 到达出口 | | departTimezone | String | 出发时区偏移(秒) | | arriveTimezone | String | 到达时区偏移(秒) | ### 火车返回 `TrainVO` 字段 | 字段 | 类型 | 说明 | |---|---|---| | trainNo | String | 车次 | | 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` | 经停站列表(仅 `by-train-no` 返回) | **`TrainStopVO`**: | 字段 | 类型 | 说明 | |---|---|---| | stationName | String | 车站名称 | | arriveTime | String | 到站时间 | | departTime | String | 发车时间 | ### 边界 - 查不到数据(航班号无效/停运):返回 `data: []` - 飞常准上游异常/超时:500,`message` 含上游错误码 - 日期格式:`yyyy-MM-dd`,格式错误返回 500 - 所有三字码不区分大小写(内部 `toUpperCase`) --- ## 三、字典依赖 | 字典类型 | 值 | |---|---| | `transport_direction` | `ARRIVAL` / `DEPARTURE` | | `transport_type` | `FLIGHT` / `TRAIN` / `SELF_DRIVE`(本模块子集) | | `self_drive_period` | `MORNING` / `AFTERNOON` / `EVENING` | | `creator_type` | `USER` / `ADMIN` | | `traveler_type` | `ADULT` / `CHILD` / `YOUNG_CHILD` / `BABY` | 所有 `xxxLabel` 字段由后端透传中文展示值,前端无需再次映射。 --- ## 四、前端迁移指南(重要 ⚠️) ### 字段改名对照表 管理后台前端**之前在用 v1 小程序遗留契约**,请按下表迁移: | 旧契约(v1 遗留) | 新契约(当前) | 说明 | |---|---|---| | `legType` (`OUTBOUND` / `RETURN`) | **`direction`** (`ARRIVAL` / `DEPARTURE`) | **字段名+值域都变了**。`OUTBOUND` 去程=客户到达目的地 → `ARRIVAL`;`RETURN` 回程=客户从目的地离开 → `DEPARTURE` | | `departureStation` | **`departStation`** | 去掉 `ure` 词尾 | | `arrivalStation` | **`arriveStation`** | 去掉 `al` 词尾 | | `travelDate`(一个日期) | **`departTime` + `arriveTime`**(两个 LocalDateTime) | 拆成出发时间和到达时间,两者都要有时分秒 | | 缺失 | **`travelerIds`(必填)** | 新增必填字段,至少 1 人,必须属于本订单 | ### 推荐 UX 用户填写交通信息时: 1. **方向**:单选 `接站 (ARRIVAL)` / `送站 (DEPARTURE)` 2. **交通方式**:单选 `飞机 (FLIGHT)` / `火车 (TRAIN)` / `自驾 (SELF_DRIVE)` 3. **航班号/车次号**:输入后立即调 `GET /admin/transport/flight/by-flight-no` 或 `GET /admin/transport/train/by-train-no` 4. 从返回列表让用户选具体班次,**自动回填** `departStation / arriveStation / departTime / arriveTime / carrier` 5. 自驾场景:改为输入 `selfDrivePeriod` + 可选 `selfDriveEta` 6. **出行人选择**:多选框,默认全选订单所有出行人(用户可取消部分),最少 1 人 7. 提交时按上表 DTO 校验格式 ### 参考实现 小程序端已上线该 UI,表单交互和字段校验可**直接参考 `MpArrivalController` 的使用**(changelog: `2026-04-21_mp-order-arrival.md`)。唯一差别是路径前缀 `/admin` 和 `creatorType=ADMIN`。 --- ## 五、关联 PR 与历史 - #1285(本次):Controller 同时支持 `/transport` 和 `/arrival` 两个前缀别名。修 404。 - #1262(2026-04-23):配房差价 execute 自动写 `order_surcharge` / `order_discount`(source=ROOM_UPGRADE),与本模块无关但同日相邻。 - 2026-04-21 MP 端 `/mp/order/{orderId}/arrival` 已上线,DTO 结构与本次 admin 端一致。 **待废弃**:`MpOrderTransportController`(`/mp/order/{orderId}/transport`,v1 遗留)已标 `@Deprecated`,请**不要再参考它的字段命名**。管理后台前端之前误以此为契约,本次必须迁移。