changelog(order-v3): 大交通 edit/delete 严格化 follow-up
破坏性变更前端必同步: - #2524 delete 错误码 581121 → 581144(跨订单/不存在 plan 都统一) - #2523 edit 补 4 项业务校验(581140/581141/581142/581143)+ @Idempotent(3s) + @Lock4j(30s) - #2526 错误码注释笔误修正(无 API 影响) 测试服 9443 真测 9/9 PASS 来源:/@arch + /@cr 双重审查 → follow-up PR #2560(commit 08deec4d1) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
这个提交包含在:
父节点
9dfe5b270d
当前提交
fa843a7ba8
@ -0,0 +1,236 @@
|
||||
# 大交通批次 edit/delete 严格化 follow-up: 错误码 581121 → 581144 + edit 4 项业务校验补齐
|
||||
|
||||
> **存放目录**: 二期(v3,`order-v3` 标签)→ `changelogs-v2/2026-05/`
|
||||
>
|
||||
> **服务**: hl-order-v3 (端口 8084)
|
||||
> **PR**: #2560 (commit `08deec4d1`)
|
||||
> **Issue**: 无新增工单(审查 follow-up,语义对齐 #2523 / #2524 / #2526,原工单已 closed)
|
||||
> **日期**: 2026-05-19
|
||||
> **影响范围**: 管理后台「行程安排 - 接送站」区块 - 大交通批次编辑/删除接口
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 关键变化(前端必同步两点)
|
||||
|
||||
1. **#2524 delete 错误码变更(破坏性,小幅)**:大交通批次软删接口的「plan 不存在/跨单/已删」错误码 **`581121` → `581144`**。前端若对 `581121` 做了特殊 catch,需改为 `581144`,或合并到通用错误处理。错误消息文案同步改为「大交通批次不存在或不属于该订单」。
|
||||
2. **#2523 edit 严格化(行为收紧)**:之前 edit 接口只校验 `direction` / `transportType` 两个枚举,本次补齐 add 同款 4 项业务校验(581140 字段组合 / 581141 出行人归属 / 581142 时间顺序 / 581143 同方向同一出行人占用),且 edit 加 `@Idempotent(3s)`,3 秒内重复提交直接返 `100502 处理中`。前端 add 已有的错误处理代码可直接复用到 edit。
|
||||
|
||||
> 这是 **#2546(#2523) / #2547(#2524) / #2554(#2526)** 三个原 PR 合并后,/@arch + /@cr 审查发现的尾巴一次性收完,不开新工单。
|
||||
|
||||
---
|
||||
|
||||
## 一、背景
|
||||
|
||||
V5.48 §2.6 大交通模块四接口在 #2521/#2522/#2523/#2524 完成路径风格 + Method 迁移后,审查发现两处实质语义偏差和一处注释笔误:
|
||||
|
||||
| 维度 | 原 PR 状态 | 本 follow-up 修正后 |
|
||||
|------|-----------|---------------------|
|
||||
| `editTransportPlan` 业务校验 | 仅枚举 direction/transportType,**不校验字段组合/出行人归属/时间顺序/同方向占用** | 全 4 项校验,完全对齐 add |
|
||||
| `editTransportPlan` 防并发 | 仅 `@Lock4j(30s)`,**无幂等** | `@Lock4j(30s)` + `@Idempotent(3s)` |
|
||||
| `deleteTransportPlan` 错误码 | `581121`(沿用 §2 baseline) | **`581144`**(对齐 §2.6.4 文档 + #2523 命名) |
|
||||
| `TravelerErrorCode` 错误码区间注释 | "581131-581135" 整体看作启用 | "581131-581134 启用 + 581135 预留" |
|
||||
|
||||
错误码 `581121` 是 §2 出行人模块的"归属校验"基线值,但 §2.6 大交通批次模块已在 add/edit 中使用 `581140-581144` 段位,delete 也应统一使用 `581144`,前端可用同一段位错误处理覆盖全部 §2.6 接口。
|
||||
|
||||
---
|
||||
|
||||
## 二、变更接口清单
|
||||
|
||||
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|
||||
|---|------|------|------|----------|------|
|
||||
| 1 | 大交通批次编辑 | POST | `/v3/admin/order/{id}/transport-plan/{planId}/edit` | 行为收紧 + 注解新增 | 补 4 项业务校验 + `@Idempotent(3s)` |
|
||||
| 2 | 大交通批次软删 | POST | `/v3/admin/order/{id}/transport-plan/{planId}/delete` | 错误码变更(破坏性) | `581121` → `581144`,消息文案同步 |
|
||||
|
||||
---
|
||||
|
||||
## 三、接口详情
|
||||
|
||||
### 1. 大交通批次编辑 `POST /v3/admin/order/{id}/transport-plan/{planId}/edit`
|
||||
|
||||
入参 / 出参 / 路径 / Method **均与 PR #2546 一致**,仅以下行为变化:
|
||||
|
||||
#### 新增/补齐校验(对齐 add `POST .../transport-plan/add`)
|
||||
|
||||
校验顺序(从上到下,任一不通过立刻返错):
|
||||
|
||||
| 步骤 | 校验项 | 错误码 | 说明 |
|
||||
|------|--------|--------|------|
|
||||
| 1 | plan 存在 + 归属订单 + 未软删 | `581144` | (本 follow-up 改造点,见接口 2)|
|
||||
| 2 | 订单存在性 | `581100` | |
|
||||
| 3 | **字段组合合法性** | `581140` | FLIGHT/TRAIN/COACH 缺 `transportNo`,SELF_DRIVE 错带 `transportNo`/站点 等 |
|
||||
| 4 | **时间顺序** | `581142` | `departTime > arriveTime` 拒绝(SELF_DRIVE 用 `selfDriveEta` 单点时间不触发此校验)|
|
||||
| 5 | **travelerIds 归属订单** | `581141` | 任一 ID 不在 `order.travelerIds` → 整体拒绝 |
|
||||
| 6 | **同方向同一出行人已被占用**(排除自己)| `581143` | 同 `direction` + 同 `travelerId` 在其他 active plan 中存在 → 拒绝;**自己的旧关联不算冲突** |
|
||||
|
||||
#### 新增注解
|
||||
|
||||
```java
|
||||
@Idempotent(timeout = 3) // 新增:3 秒内重复 POST 同 path+Body 返首次结果
|
||||
@Lock4j(keys = "#id", expire = 30000) // 已有
|
||||
```
|
||||
|
||||
#### 错误响应示例
|
||||
|
||||
```json
|
||||
// 字段组合非法(FLIGHT 缺 transportNo)
|
||||
{ "code": 581140, "msg": "transportNo 必填(运输方式=FLIGHT)", "data": null }
|
||||
|
||||
// travelerIds 含订单外的出行人
|
||||
{ "code": 581141, "msg": "出行人不属于该订单: travelerIds=[70123456789099]", "data": null }
|
||||
|
||||
// 时间顺序错误
|
||||
{ "code": 581142, "msg": "departTime 不能晚于 arriveTime", "data": null }
|
||||
|
||||
// 同方向同 traveler 占用(其他 plan)
|
||||
{ "code": 581143, "msg": "出行人在同方向已有另一批次: direction=ARRIVAL, travelerId=70123456789012, conflictPlanId=80012999", "data": null }
|
||||
|
||||
// 幂等命中(3 秒内重复 POST)
|
||||
{ "code": 100502, "msg": "请求处理中,请稍后重试", "data": null }
|
||||
```
|
||||
|
||||
> **edit 排除自身规则**:校验 `581143` 时,SQL `WHERE plan_id != #{currentPlanId}`,改自己不算冲突。该排除是 edit 接口独有,add 接口不存在 currentPlanId 概念。
|
||||
|
||||
---
|
||||
|
||||
### 2. 大交通批次软删 `POST /v3/admin/order/{id}/transport-plan/{planId}/delete`
|
||||
|
||||
入参 / 出参 / 路径 / Method / 数据库行为 **均与 PR #2547 一致**,仅错误码变化:
|
||||
|
||||
#### 错误响应变更
|
||||
|
||||
| 场景 | 变更前(PR #2547) | 变更后(PR #2560,**当前**) |
|
||||
|------|------------------|---------------------------|
|
||||
| plan 不存在 | `581121` "大交通批次不属于该订单" | **`581144` "大交通批次不存在或不属于该订单"** |
|
||||
| plan 跨单(orderId 不匹配) | `581121` 同上 | **`581144`** 同上 |
|
||||
| plan 已软删(deleted=1)再 delete | `581121` 同上 | **`581144`** 同上 |
|
||||
| 订单不存在 | `581100` | `581100`(**不变**)|
|
||||
|
||||
示例响应:
|
||||
|
||||
```json
|
||||
{ "code": 581144, "msg": "大交通批次不存在或不属于该订单: planId=80099999, orderId=60123456789012", "data": null }
|
||||
```
|
||||
|
||||
> `581121` 在 §2.6 模块**不再返回**(仍保留在 §2 出行人模块的其他接口中)。
|
||||
|
||||
---
|
||||
|
||||
## 四、契约约束与正确调用方式
|
||||
|
||||
### edit 接口(前端 actionable)
|
||||
|
||||
| 场景 | payload | 结果 |
|
||||
|------|---------|------|
|
||||
| ✅ 合法 edit | FLIGHT + transportNo + 站点 + travelerIds 都属本单 + 时间顺序对 | 200 |
|
||||
| ✅ 改自己旧关联的 traveler | edit Body `travelerIds=[A,B]`,A 在原 plan 已有 → 不触发 581143 | 200 |
|
||||
| ❌ 缺 transportNo(FLIGHT) | `{"transportType":"FLIGHT","transportNo":null}` | `581140` |
|
||||
| ❌ 时间倒挂 | `departTime=11:00, arriveTime=09:00` | `581142` |
|
||||
| ❌ traveler 跨单 | `travelerIds=[订单外的 ID]` | `581141` |
|
||||
| ❌ 同方向 A 已在别 plan | direction=ARRIVAL + travelerA 在 plan #B 占用 | `581143` |
|
||||
| ❌ 3 秒内双击 | 第二次同 path+Body POST | `100502` 处理中 |
|
||||
|
||||
### delete 接口(前端 actionable)
|
||||
|
||||
| 错误码 | 前端建议 |
|
||||
|--------|---------|
|
||||
| 旧 `581121` | **移除**或合并到通用错误处理 |
|
||||
| 新 `581144` | 提示「该批次不存在或已被删除」,刷新列表 |
|
||||
|
||||
### 前端代码迁移建议
|
||||
|
||||
```js
|
||||
// 旧
|
||||
if (resp.code === 581121) showToast('批次不属于该订单');
|
||||
|
||||
// 新(推荐合并 581121 + 581144,兼容过渡期)
|
||||
if (resp.code === 581144 || resp.code === 581121) showToast('批次不存在或已被删除');
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、数据库行为
|
||||
|
||||
**无变化**。本 follow-up 仅修改 Service 层校验顺序 + Controller 注解 + 错误码常量值,DB schema / 表行为完全沿用 PR #2546 / #2547。
|
||||
|
||||
---
|
||||
|
||||
## 六、边界行为
|
||||
|
||||
### edit 接口
|
||||
|
||||
- 新校验全部在事务开始前完成,失败不写库
|
||||
- `@Idempotent(3s)` 缓存键 = path + 用户 ID + Body hash,3 秒内重复同 Body 直接拿首次结果
|
||||
- 同方向同 traveler 占用判定 SQL: `WHERE direction = ? AND traveler_id IN (?) AND plan_id != #{currentPlanId} AND deleted = 0`
|
||||
- 已软删的 plan 不参与 `581143` 占用判定
|
||||
|
||||
### delete 接口
|
||||
|
||||
- `@Idempotent(3s)` + `@Lock4j(30s)` 沿用,不变
|
||||
- 错误码常量从 `TRANSPORT_PLAN_NOT_BELONG_TO_ORDER(581121)` 改为 `TRANSPORT_PLAN_NOT_FOUND_OR_NOT_BELONG(581144)`
|
||||
|
||||
### 通用
|
||||
|
||||
- 未登录 → 401(网关拦截,不变)
|
||||
- 历史 plan(v2 迁移数据)edit/delete 行为完全一致
|
||||
|
||||
---
|
||||
|
||||
## 七、不影响范围
|
||||
|
||||
- **仅影响**: 管理后台 F21 「接送站」编辑弹窗 + 删除按钮
|
||||
- **零影响**:
|
||||
- C 端订单详情 / mp 抵达计划接口
|
||||
- §2.6.1 list 接口 / §2.6.2 add 接口(本 follow-up 不动)
|
||||
- 出行人 CRUD(traveler 表本身不动)
|
||||
- 订单状态机
|
||||
- `581121` 在 §2 出行人模块其他接口仍正常返回,语义未变
|
||||
- `581131-581134` 出行人模块错误码无功能变化,仅注释笔误修正
|
||||
|
||||
---
|
||||
|
||||
## 八、测试环境已验证
|
||||
|
||||
测试服 9443 网关 + 真 admin token round-trip **9/9 PASS**(/@qa 报告):
|
||||
|
||||
```
|
||||
edit 接口严格化:
|
||||
✓ FLIGHT 缺 transportNo → 581140
|
||||
✓ travelerIds 含订单外 ID → 581141
|
||||
✓ departTime > arriveTime → 581142
|
||||
✓ 同方向 traveler 在别 plan 占用 → 581143
|
||||
✓ 同方向 traveler 在自己 plan(改自己)→ 200(排除自身生效)
|
||||
✓ 3 秒内重复 POST 同 Body → 100502 幂等命中
|
||||
|
||||
delete 接口错误码:
|
||||
✓ planId 不存在 → 581144(原 581121)
|
||||
✓ planId 跨单 → 581144(原 581121)
|
||||
✓ 重复 delete 已软删 plan → 581144(原 581121)
|
||||
```
|
||||
|
||||
本地单测 109/109 全绿(5 单测 + 7 IT,注解反射断言验证 `@Idempotent.timeout=3` / `@Lock4j.keys="#id"`)。
|
||||
|
||||
---
|
||||
|
||||
## 九、相关历史 PR
|
||||
|
||||
| PR | Issue | 说明 | 是否仍有效 |
|
||||
|----|-------|------|------------|
|
||||
| #2546 | #2523 | edit 接口 Method+路径迁移(原版只校验 2 枚举) | ✅ 仍有效,**本 follow-up 在其上补 4 校验 + 幂等** |
|
||||
| #2547 | #2524 | delete 接口 Method+路径迁移(原版用 `581121`) | ✅ 仍有效,**本 follow-up 改错误码 → `581144`** |
|
||||
| #2554 | #2526 | 出行人 smart-parse 新增接口(原版注释笔误)| ✅ 仍有效,**本 follow-up 仅修注释,无 API 变化** |
|
||||
| **本 PR #2560** | (审查 follow-up,无新工单) | edit 严格化 + delete 错误码 + 注释笔误 | ✅ **最新** |
|
||||
|
||||
---
|
||||
|
||||
## 十、相关文档
|
||||
|
||||
- 关联 PR: [wx/HL#2560](https://git.1814.love:8443/wx/HL/pulls/2560)
|
||||
- 原 PR(本 follow-up 的修正基础):
|
||||
- [wx/HL#2546](https://git.1814.love:8443/wx/HL/pulls/2546)(关联 [#2523](https://git.1814.love:8443/wx/HL/issues/2523))
|
||||
- [wx/HL#2547](https://git.1814.love:8443/wx/HL/pulls/2547)(关联 [#2524](https://git.1814.love:8443/wx/HL/issues/2524))
|
||||
- [wx/HL#2554](https://git.1814.love:8443/wx/HL/pulls/2554)(关联 [#2526](https://git.1814.love:8443/wx/HL/issues/2526))
|
||||
- API 文档: `docs/order-v3/api/API-SPEC-V5.48.html` §2.6.3(edit)/ §2.6.4(delete)
|
||||
- 同月配套 changelog:
|
||||
- `18_#2523_transport-plan-edit.md`
|
||||
- `18_#2524_transport-plan-delete.md`
|
||||
- `18_#2526_traveler-smart-parse.md`
|
||||
正在加载...
x
在新工单中引用
屏蔽一个用户