修 PR #1285 的 404 问题后,把管理后台交通信息这块接口契约完整梳理一遍,给前端迁移指南: - 订单交通批次 5 个 CRUD (/admin/order/{orderId}/transport 或 /arrival,两个前缀别名): 查全量 / 新增 / 全量替换 / 修改 / 删除 - 飞常准查询 7 个 (/admin/transport/flight/* + /train/*): 航班号/机场/城市 + 车次/站点/城市 - 重点标出前端之前用的 v1 遗留契约(legType/departureStation/travelDate)与新契约的字段对照 - 补 FlightVO/TrainVO 完整字段表 + 三种交通方式的请求示例 - 推荐 UX: 航班号输入后自动调飞常准回填时间站点,避免用户手填时分秒
15 KiB
管理后台 · 订单交通信息(接送站)5 个 CRUD + 飞常准查询 7 个
日期:2026-04-23 影响:管理后台 订单详情·行程安排·交通信息(接送站)板块 PR:#1285(/transport 路径别名) 关联 Issue:#1284(404 修复)
概述
统一管理后台「交通信息」板块的接口契约。管理后台前端之前对接的是一份已废弃的 v1 小程序契约(legType / departureStation / travelDate / ...),和后端现有新契约(direction / departStation / departTime + arriveTime / ...)完全不匹配,导致:
POST /admin/order/{orderId}/transport404(#1285 已修:Controller 同时支持/transport和/arrival两个前缀)- 字段反序列化不上 → 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<ArrivalListRespVO>
| 字段 | 类型 | 说明 |
|---|---|---|
| orderId | Long | 订单ID |
| arrivals | List<ArrivalPlanRespVO> |
到达批次(接站) |
| departures | List<ArrivalPlanRespVO> |
离开批次(送站) |
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<Long> |
✅ 始终非空 | 至少 1 人 | 本批次涉及的出行人 ID 列表;每个 ID 必须属于本订单 |
⚠️ 字段间联动:字段必填规则由 Service 层按
transportType + direction组合校验,不是单注解表达式。前端请按上表规则做前置校验,否则会 500。
出参 Result<ArrivalPlanRespVO>
| 字段 | 类型 | 说明 |
|---|---|---|
| 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> |
本批次涉及的出行人 |
ArrivalPlanTravelerSimpleVO:
| 字段 | 类型 | 说明 |
|---|---|---|
| travelerId | Long | 出行人 ID |
| name | String | 姓名 |
| travelerType | String | ADULT / CHILD / YOUNG_CHILD / BABY |
请求示例(接站 ARRIVAL)
{
"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)
{
"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)
{
"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<ArrivalPlanSaveReqVO>(每项结构同第 2 节)。
出参
Result<ArrivalListRespVO>,结构同第 1 节。
使用场景
管理后台「一次性代录全部交通」按钮。例如定制师拿到客户的去程和回程航班信息,一次性把 2 条(或多条,如一家分两批)全部提交。
4. 修改单批次
PUT /admin/order/{orderId}/transport/plan/{planId}
覆盖语义:按字段全量覆盖,包括 travelerIds(替换旧关联)。
入参
| 参数 | 位置 | 类型 | 必填 |
|---|---|---|---|
| orderId | Path | Long | ✅ |
| planId | Path | Long | ✅ |
Body:ArrivalPlanSaveReqVO(结构同第 2 节)。
出参
Result<ArrivalPlanRespVO>。
5. 删除单批次
DELETE /admin/order/{orderId}/transport/plan/{planId}
级联:软删关联的出行人绑定行。
入参
| 参数 | 位置 | 类型 | 必填 |
|---|---|---|---|
| orderId | Path | Long | ✅ |
| planId | Path | Long | ✅ |
出参
Result<Void>。
二、航班/火车查询 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<TrainStopVO> |
经停站列表(仅 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
用户填写交通信息时:
- 方向:单选
接站 (ARRIVAL)/送站 (DEPARTURE) - 交通方式:单选
飞机 (FLIGHT)/火车 (TRAIN)/自驾 (SELF_DRIVE) - 航班号/车次号:输入后立即调
GET /admin/transport/flight/by-flight-no或GET /admin/transport/train/by-train-no - 从返回列表让用户选具体班次,自动回填
departStation / arriveStation / departTime / arriveTime / carrier - 自驾场景:改为输入
selfDrivePeriod+ 可选selfDriveEta - 出行人选择:多选框,默认全选订单所有出行人(用户可取消部分),最少 1 人
- 提交时按上表 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,请不要再参考它的字段命名。管理后台前端之前误以此为契约,本次必须迁移。