hl-api-changelog/changelogs-v2/2026-05/18_#2522_transport-plan-add.md
API Changelog Bot 1001a07f44 changelog(order-v3): 大交通批次 4 接口 (#2521-#2524)
- #2521 list 路径迁移 /transport-plans → /transport-plan/list
- #2522 add 路径 + 错误码 581140-581143 + @Idempotent + @Lock4j
- #2523 edit Method PUT → POST + 错误码 581144
- #2524 delete Method DELETE → POST + 桥接表级联软删

测试服 9443 真测全  (commit 15e2c7eeb 含 hotfix #2550)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 20:24:44 +08:00

8.8 KiB

大交通批次新增: 接口路径破坏性迁移 + 4 个新错误码

存放目录: 二期(v3,order-v3 标签)→ changelogs-v2/2026-05/

服务: hl-order-v3 (端口 8084) PR: #2545 (commit 2c66137c7) Issue: #2522 日期: 2026-05-18 影响范围: 管理后台「行程安排 - 接送站」区块 - 大交通批次新增弹窗


⚠️ 关键变化(破坏性 - 前端必同步)

一行红字说清:

  • 本次变了什么: 新增大交通批次接口路径从 /transport-plans 改为 /transport-plan/add,并新增 4 个错误码 581140-581143
  • 前端以前以为的是什么: POST /v3/admin/order/{id}/transport-plans + Body TransportPlanReqVO
  • 实际现在是什么: POST /v3/admin/order/{id}/transport-plan/add + Body TransportPlanReqVO(字段不变)

配套破坏性: §2.6 大交通批次模块 4 个接口路径风格 + 动词 + Method 全部统一迁移,见 #2521/#2523/#2524 changelog。


一、背景

V5.48 §2.6.2 将原 POST /transport-plans 拆为 POST /transport-plan/add,并补齐"字段组合 / 出行人合法性 / 时间顺序 / 同方向冲突"4 类业务校验错误码,与文档 §2.6 错误码段位 581140-581143 一一对应。

orderId 从 path 取,Body 不传;操作人从 JWT 派生(对齐 §2.6 通用约定)。


二、变更接口清单

# 接口 方法 路径 变更类型 说明
1 大交通批次新增 POST /v3/admin/order/{id}/transport-plan/add 路径迁移 + 错误码新增 /transport-plans 下线;4 个业务错误码上线

三、接口详情

1. 大交通批次新增 POST /v3/admin/order/{id}/transport-plan/add

VO: TransportPlanReqVO(入参) / TransportPlanVO(出参)

入参 Body TransportPlanReqVO

字段 类型 必填 约束 说明
direction String 枚举 ARRIVAL / DEPARTURE
transportType String 枚举 FLIGHT / TRAIN / SELF_DRIVE
transportNo String 条件 ≤50 航班号 / 车次号;SELF_DRIVE 时必须为 null
carrier String ≤50 航司 / 铁路公司
departStation String ≤100 出发站
arriveStation String ≤100 到达站
departTime LocalDateTime 条件 - FLIGHT/TRAIN 必填;SELF_DRIVE 必须为 null
arriveTime LocalDateTime 条件 - FLIGHT/TRAIN 必填;SELF_DRIVE 必须为 null;必须 ≥ departTime
selfDrivePeriod String 条件 枚举 SELF_DRIVE 必填: MORNING / AFTERNOON / EVENING
selfDriveEta LocalDateTime - SELF_DRIVE 可选
travelerIds List size≥1 关联出行人 ID,至少 1 个
remark String ≤500 备注

出参 Result<TransportPlanVO>

字段同 #2521 列表的元素结构(含 travelers: [{id, name}] 嵌套)。

请求示例

POST /v3/admin/order/60123456789012/transport-plan/add

{
  "direction": "ARRIVAL",
  "transportType": "FLIGHT",
  "transportNo": "CA1234",
  "carrier": "中国国际航空",
  "departStation": "北京首都T3",
  "arriveStation": "长春龙嘉",
  "departTime": "2026-06-01T08:30:00",
  "arriveTime": "2026-06-01T10:15:00",
  "selfDrivePeriod": null,
  "selfDriveEta": null,
  "travelerIds": [70123456789012, 70123456789013],
  "remark": "需要接机举牌"
}

响应示例

{
  "code": 200,
  "data": {
    "id": "80012345",
    "orderId": "60123456789012",
    "direction": "ARRIVAL",
    "transportType": "FLIGHT",
    "transportNo": "CA1234",
    "carrier": "中国国际航空",
    "departStation": "北京首都T3",
    "arriveStation": "长春龙嘉",
    "departTime": "2026-06-01T08:30:00",
    "arriveTime": "2026-06-01T10:15:00",
    "selfDrivePeriod": null,
    "selfDriveEta": null,
    "travelers": [
      {"id": "70123456789012", "name": "张三"},
      {"id": "70123456789013", "name": "王小明"}
    ],
    "remark": "需要接机举牌"
  },
  "msg": "success"
}

错误响应

错误码 含义 触发场景示例
581140 大交通字段组合不合法 FLIGHTtransportNo / SELF_DRIVE 错带 transportNodepartTime
581141 travelerIds 含订单外的出行人 提交的 traveler ID 不属于当前订单或已软删
581142 出发时间晚于到达时间 departTime > arriveTime
581143 同方向同一出行人已在另一 plan 张三已在 ARRIVAL plan A,再为 ARRIVAL plan B 提交张三
581100 订单不存在 path 中 orderId 无效

示例 581140:

{ "code": 581140, "msg": "大交通字段组合不合法: FLIGHT 类型必须填 transportNo", "data": null }

示例 581143:

{ "code": 581143, "msg": "同方向同一出行人已在另一 plan: 张三(70123456789012) 已存在 ARRIVAL 批次 80012340", "data": null }

四、契约约束与正确调用方式

正确 / 错误 payload 对照

场景 payload 关键字段 结果
FLIGHT 完整 transportType:FLIGHT, transportNo:CA1234, departTime/arriveTime 有值 201
SELF_DRIVE 完整 transportType:SELF_DRIVE, transportNo:null, departTime:null, arriveTime:null, selfDrivePeriod:MORNING 201
FLIGHT 缺航班号 transportType:FLIGHT, transportNo:null 581140
SELF_DRIVE 带航班号 transportType:SELF_DRIVE, transportNo:CA1234 581140
时间倒挂 departTime:10:00, arriveTime:08:00 581142
travelerIds 含他人 travelerIds:[别单出行人] 581141
同方向重复占用 同方向同 traveler 在另一 plan 581143

并发与幂等

  • @Idempotent(timeout = 3): 同 admin 3 秒内重复 POST 同请求体直接拿首次结果,防双击双提
  • @Lock4j(keys = "#id"): 同订单 30 秒锁,串行化 add/edit/delete,保证桥接表一致

同方向冲突规则

  • 「同方向」= direction 相同的所有 plan(同 ARRIVAL 之间冲突,同 DEPARTURE 之间冲突)
  • 「同一出行人」= 同一 traveler_id
  • 不同方向不冲突(同一人可同时在 1 个 ARRIVAL plan + 1 个 DEPARTURE plan)
  • 软删的 plan / 桥接行不参与冲突判定

五、数据库行为

单事务执行,失败整体回滚:

步骤 动作
1 order_transport_plan INSERT 新行,雪花 ID,deleted=0,create_time/update_time 自动填
2 order_transport_plan_traveler INSERT N 行(N = travelerIds.size()),每行 {plan_id, traveler_id, deleted:0}

校验顺序(任一失败即抛业务异常,事务回滚):

  1. 订单存在性 → 581100
  2. 字段组合校验(类型 × 字段必填矩阵)→ 581140
  3. 时间顺序校验 → 581142
  4. travelerIds 归属校验(SELECT order_traveler WHERE order_id 比对)→ 581141
  5. 同方向同 traveler 占用校验(SELECT bridge WHERE direction = ? AND traveler_id IN ?)→ 581143

六、边界行为

  • 未登录 → 401(网关拦截)
  • 订单不存在 → 581100
  • travelerIds 重复值 → 后端去重后再校验(不报错)
  • SELF_DRIVE 仅传 selfDrivePeriod 不传 selfDriveEta → 通过(ETA 选填)
  • 一次提交超过单订单合理上限(>10 plan)→ 不在此层拦截,业务上仅靠订单状态自然限制

七、不影响范围

  • 仅影响: 管理后台 F21 「接送站」新增弹窗
  • 零影响:
    • C 端订单详情 / 抵达计划(order_arrival_plan 表完全独立,见 detail 文档 §10.2 共存边界)
    • 出行人模块自身 CRUD
    • 订单状态机(plan 增删不直接变 order_main.status)

八、测试环境已验证

测试服 9443 网关 + 真 admin token round-trip 验证:

POST https://web.test.1814.love:9443/v3/admin/order/{orderId}/transport-plan/add
  → 200 + 返回新 plan(含 travelers 嵌套)✓
  → FLIGHT 缺 transportNo → 581140 ✓
  → SELF_DRIVE 错带 transportNo → 581140 ✓
  → departTime > arriveTime → 581142 ✓
  → travelerIds 含别单 → 581141 ✓
  → 同方向同 traveler 重复 → 581143 ✓
  → 重复 POST 3s 内同请求体 → 幂等返同结果 ✓

(commit 15e2c7eeb 含 hotfix #2550,4 接口一并 9443 真测过)


九、相关历史 PR

PR Issue 说明 是否仍有效
本 PR #2545 #2522 路径迁移 + 错误码 581140-581143 上线 最新

十、相关文档

  • 关联 Issue: wx/HL#2522
  • 关联 PR: wx/HL#2545
  • API 文档: docs/order-v3/api/API-SPEC-V5.48.html §2.6.2
  • 配套破坏性: 见 18_#2521_transport-plan-list-path.md / 18_#2523_transport-plan-edit.md / 18_#2524_transport-plan-delete.md