hl-api-changelog/changelogs-v2/2026-05/18_#2523_transport-plan-edit.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.3 KiB

大交通批次编辑: Method 破坏性变更 (PUT → POST) + 错误码 581144

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

服务: hl-order-v3 (端口 8084) PR: #2546 (commit 1106b9d7f) + hotfix #2550 (commit 15e2c7eeb 修 conflict markers) Issue: #2523 日期: 2026-05-18 影响范围: 管理后台「行程安排 - 接送站」区块 - 大交通批次编辑弹窗


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

一行红字说清:

  • 本次变了什么: 编辑大交通批次接口 Method 从 PUT 改为 POST + 路径子段从 /transport-plans/{planId} 改为 /transport-plan/{planId}/edit
  • 前端以前以为的是什么: PUT /v3/admin/order/{id}/transport-plans/{planId}
  • 实际现在是什么: POST /v3/admin/order/{id}/transport-plan/{planId}/edit

Method 变化是头号陷阱: 前端若沿用 axios.put(...) 会直接 404 / 405,必须改 axios.post(...)

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


一、背景

V5.48 §2.6 统一管理后台所有变更动作走 POST /{资源}/{动词} 风格(对齐订单核心模块、出行人模块):

旧风格 (REST 经典) 新风格 (V5.48 统一)
PUT /transport-plans/{id} POST /transport-plan/{id}/edit
DELETE /transport-plans/{id} POST /transport-plan/{id}/delete

POST + 动词路径的好处: 网关 RBAC 配置统一只看 path 不看 Method、操作审计日志埋点统一、防误触缓存。

#2550 hotfix 修复合并 dev-v3 时 IDE 残留的 <<<<<<< conflict markers(扫描全代码确保零残留)。


二、变更接口清单

# 接口 方法 路径 变更类型 说明
1 大交通批次编辑 POST /v3/admin/order/{id}/transport-plan/{planId}/edit Method + 路径双破坏 PUT /transport-plans/{planId} 下线;新增错误码 581144

三、接口详情

1. 大交通批次编辑 POST /v3/admin/order/{id}/transport-plan/{planId}/edit

VO: TransportPlanReqVO(入参,与 add 同 VO) / TransportPlanVO(出参)

入参

字段 位置 类型 必填 说明
id Path Long 订单 ID
planId Path Long 批次 ID
Body Body TransportPlanReqVO 字段同 add(见 #2522 入参表)

出参 Result<TransportPlanVO>

字段同 #2521 列表元素结构。

请求示例

POST /v3/admin/order/60123456789012/transport-plan/80012345/edit

{
  "direction": "ARRIVAL",
  "transportType": "FLIGHT",
  "transportNo": "CA1234",
  "carrier": "中国国际航空",
  "departStation": "北京首都T3",
  "arriveStation": "长春龙嘉",
  "departTime": "2026-06-01T09:00:00",
  "arriveTime": "2026-06-01T11:00:00",
  "selfDrivePeriod": null,
  "selfDriveEta": null,
  "travelerIds": [70123456789012, 70123456789013, 70123456789014],
  "remark": "改时间,加一人"
}

响应示例

{
  "code": 200,
  "data": {
    "id": "80012345",
    "orderId": "60123456789012",
    "direction": "ARRIVAL",
    "transportType": "FLIGHT",
    "transportNo": "CA1234",
    "carrier": "中国国际航空",
    "departStation": "北京首都T3",
    "arriveStation": "长春龙嘉",
    "departTime": "2026-06-01T09:00:00",
    "arriveTime": "2026-06-01T11:00:00",
    "selfDrivePeriod": null,
    "selfDriveEta": null,
    "travelers": [
      {"id": "70123456789012", "name": "张三"},
      {"id": "70123456789013", "name": "王小明"},
      {"id": "70123456789014", "name": "李四"}
    ],
    "remark": "改时间,加一人"
  },
  "msg": "success"
}

错误响应

复用 add 接口的 4 个错误码(581140-581143),并新增:

错误码 含义 触发场景
581144 plan 不存在 / 不属于该订单 planId 无效 / 已软删 / order_id 与 path 不匹配

示例 581144:

{ "code": 581144, "msg": "大交通批次不存在或不属于该订单: planId=80099999, orderId=60123456789012", "data": null }

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

正确 / 错误调用对照

场景 请求 结果
新调用 POST /transport-plan/80012345/edit + Body 200
沿用旧 PUT PUT /transport-plans/80012345 + Body 404 / 405
planId 跨单 edit path orderId=A,planId 属于 orderId=B 581144
planId 已软删 edit 已 deleted=1 的 plan 581144

全量重建语义

  • edit 不是"增量改字段": Body 提交什么就最终持久化什么(包括 travelerIds 全量)
  • 想去掉 1 个 traveler: 在 travelerIds 数组中删掉该 ID 再提交,后端会 DELETE+INSERT 重建桥接表
  • 不能用 edit "局部改 1 个字段而保留其他字段不动"的语义,前端必须先 GET list 取完整 plan 再改

并发与幂等

  • @Lock4j(keys = "#id"): 同订单 30 秒锁,串行化 add/edit/delete
  • 编辑接口不加 @Idempotent(每次编辑都应被尊重,允许相同 Body 重复提交以触发 update_time 刷新)

五、数据库行为

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

步骤 动作
1 order_transport_plan UPDATE 该 plan 行所有业务字段(direction / transportType / transportNo / carrier / 各 station / 各 time / selfDrive* / remark),update_time 自动刷新
2 order_transport_plan_traveler DELETE(物理删除桥接行 WHERE plan_id = ?)
3 order_transport_plan_traveler INSERT N 行(N = 新 travelerIds.size())

桥接表用「全量重建」而非 diff 算法,简化业务逻辑;桥接表本身无业务历史价值,物理删除不留痕。order_transport_plan 主表的 update_time 会随 edit 刷新供前端展示「最后修改时间」。

校验顺序(同 add,附加 plan 归属校验):

  1. plan 存在性 + 归属 → 581144
  2. 订单存在性 → 581100
  3. 字段组合 → 581140
  4. 时间顺序 → 581142
  5. travelerIds 归属 → 581141
  6. 同方向同 traveler 占用(排除自己)→ 581143

六、边界行为

  • 未登录 → 401(网关拦截)
  • planId 不存在 / 跨单 / 已软删 → 581144
  • 同方向同 traveler 占用判定排除当前 plan 自己(改自己不算冲突,只跟其他 active plan 比对)
  • Body 与原 plan 完全相同(无字段变化)→ 200 + 桥接表 DELETE+INSERT(等价 no-op 但 update_time 仍刷新)
  • 老数据(v2 历史 plan 字段缺失)→ edit 会以 Body 全量值覆盖

七、不影响范围

  • 仅影响: 管理后台 F21 「接送站」编辑弹窗
  • 零影响:
    • C 端订单详情 / mp 抵达计划接口
    • 出行人 CRUD(traveler 表本身不动)
    • 订单状态机
    • 操作日志(本接口暂不挂 @OperationLog,与 v2 行为一致)

八、测试环境已验证

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

POST https://web.test.1814.love:9443/v3/admin/order/{orderId}/transport-plan/{planId}/edit
  → 200 + 返回更新后 plan(travelers 重建)✓
  → planId 不存在 → 581144 ✓
  → planId 属于别单 → 581144 ✓
  → 业务校验复用 581140/581141/581142/581143 全覆盖 ✓
  → 同方向同 traveler 占用判定排除自己 → 200 ✓
  → 桥接表全量 DELETE+INSERT 重建生效 ✓

hotfix #2550(commit 15e2c7eeb)清理了合并 dev-v3 时残留的 <<<<<<< / ======= / >>>>>>> conflict markers(本接口实现文件曾受影响,hotfix 后 mvn compile 通过 + 9443 验证通过)。


九、相关历史 PR

PR Issue 说明 是否仍有效
本 PR #2546 #2523 Method PUT→POST + 路径迁移 + 错误码 581144 最新
#2550 (hotfix) 修复 conflict markers,无功能变化 配套

十、相关文档

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