- #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>
8.8 KiB
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+ BodyTransportPlanReqVO - 实际现在是什么:
POST /v3/admin/order/{id}/transport-plan/add+ BodyTransportPlanReqVO(字段不变)
配套破坏性: §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 |
大交通字段组合不合法 | FLIGHT 缺 transportNo / SELF_DRIVE 错带 transportNo 或 departTime |
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} |
校验顺序(任一失败即抛业务异常,事务回滚):
- 订单存在性 →
581100 - 字段组合校验(类型 × 字段必填矩阵)→
581140 - 时间顺序校验 →
581142 - travelerIds 归属校验(SELECT order_traveler WHERE order_id 比对)→
581141 - 同方向同 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)
- C 端订单详情 / 抵达计划(
八、测试环境已验证
测试服 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