- #2517 list 路径迁移 /travelers → /traveler/list - #2518 batch-edit 真实业务+9错误码 - #2519 add 路径迁移+4错误码+@Idempotent+@Lock4j - #2520 delete 方法+路径迁移+4错误码+整事务 测试服 9443 真测全 ✅ Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
189 行
6.7 KiB
Markdown
189 行
6.7 KiB
Markdown
# 出行人软删: DELETE → POST /traveler/{travelerId}/delete 路径迁移 + 真实业务化
|
|
|
|
> **服务**: hl-order-service-v3 (端口 8084 / 二期)
|
|
> **PR**: #2537
|
|
> **Issue**: #2520
|
|
> **日期**: 2026-05-18
|
|
> **影响范围**: 管理后台 F27 改人数场景(临时取消同行 1 人)
|
|
> **存放目录**: `changelogs-v2/2026-05/`(二期 v3 专属,带 -v2 后缀)
|
|
> **部署 commit**: dev-v3 `34c294465`
|
|
> **测试服已验证**: ✅(/@qa 通过 9443 网关 + 真 admin token round-trip)
|
|
|
|
---
|
|
|
|
## ⚠️ 关键变化
|
|
|
|
1. **破坏性路径 + 方法迁移**:
|
|
- **方法**: DELETE → POST(对齐 v3 风格,所有写操作统一 POST)
|
|
- **路径**: `/travelers/{travelerId}` → `/traveler/{travelerId}/delete`(对齐 V5.48 §2.4)
|
|
2. **从 Mock 占位变真实业务**: 4 道校验 + 软删 + 桥接表级联软删 + count-1 + status_log。
|
|
3. **新增 2 个错误码**: `581106`(已签合同禁删) / `581107`(最后 1 成人禁删)。
|
|
- ⚠️ 注意码段: **不是 581140/581141**,大交通段位 581120+ 已被占用,出行人主段位空挡是 581106/581107 紧贴主块(文档原稿写的 581119/581120 已被 TRAVELER_ID_CARD_DUPLICATE / TRANSPORT_PLAN_NOT_FOUND 占用)。
|
|
4. **加幂等 + 锁整事务**: `@Idempotent(3s) + @Lock4j(30s)`。
|
|
|
|
---
|
|
|
|
## 一、背景
|
|
|
|
文档 V5.48 §2.4 定义: 软删除单行 `order_traveler`,同步:
|
|
1. UPDATE `order_main.<type>Count` -1
|
|
2. UPDATE `order_transport_plan_traveler.deleted=1`(级联解除其在大交通桥接表的所有关联)
|
|
3. INSERT `order_status_log`,reason="删除出行人"
|
|
|
|
并有 2 条业务硬约束:
|
|
- **已签电子合同(contract_status = SIGNED)的订单禁止删人**(合同已固定参与人,删除会破坏合同人员一致性)
|
|
- **订单最后 1 位成人禁止删除**(无成人订单逻辑上无效,会影响保险 / 大交通 / 房型分配)
|
|
|
|
---
|
|
|
|
## 二、变更接口清单
|
|
|
|
| # | 接口 | 方法 | 老路径 | 新路径 | 变更类型 |
|
|
|---|------|------|--------|--------|----------|
|
|
| 1 | 出行人软删 | DELETE → **POST** | `/v3/admin/order/{id}/travelers/{travelerId}` | `/v3/admin/order/{id}/traveler/{travelerId}/delete` | 破坏性方法 + 路径迁移 + 真实业务化 |
|
|
|
|
---
|
|
|
|
## 三、接口详情
|
|
|
|
### 1. 出行人软删 `POST /v3/admin/order/{id}/traveler/{travelerId}/delete`
|
|
|
|
#### 入参
|
|
|
|
| 字段 | 位置 | 类型 | 必填 | 说明 |
|
|
|------|------|------|------|------|
|
|
| `id` | Path | Long | ✅ | 订单 ID |
|
|
| `travelerId` | Path | Long | ✅ | 出行人 ID |
|
|
|
|
(请求体: 无)
|
|
|
|
#### 出参 `Result<Boolean>`
|
|
|
|
```json
|
|
{
|
|
"code": 200,
|
|
"data": true,
|
|
"msg": "success"
|
|
}
|
|
```
|
|
|
|
#### 请求示例
|
|
|
|
```
|
|
POST /v3/admin/order/60123456789012/traveler/70123456789013/delete
|
|
Authorization: Bearer {admin_jwt}
|
|
```
|
|
|
|
#### 错误响应清单(本工单新增段位)
|
|
|
|
| 错误码 | 含义 |
|
|
|---|---|
|
|
| `581100` | 出行人不存在 |
|
|
| `581102` | 订单不存在,无法删除出行人 |
|
|
| `581106` | **已签电子合同,禁止删除出行人**(本 PR 新增) |
|
|
| `581107` | **出行人是订单最后 1 位成人,禁止删除**(本 PR 新增) |
|
|
| `581110` | 出行人 ID 不属于该订单 |
|
|
|
|
> 段位说明: 文档原稿 §2.4 期望 `581119 / 581120`,但 581119 已被 TRAVELER_ID_CARD_DUPLICATE 占用,581120 已被 TRANSPORT_PLAN_NOT_FOUND 占用。本 PR 落到出行人主段位空挡 **581106 / 581107**,紧贴主块,保留 581140-581144 给 §2.6 大交通后续工单。
|
|
|
|
错误响应体示例:
|
|
|
|
```json
|
|
{
|
|
"code": 581106,
|
|
"msg": "已签电子合同,禁止删除出行人",
|
|
"data": null
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 四、契约约束
|
|
|
|
### 校验顺序(任一失败整事务回滚,4 道闸)
|
|
|
|
1. **订单存在性**(581102): 查 `order_main` by `id`
|
|
2. **出行人归属**(581110): 出行人的 `order_id` 必须等于 path `id`(防越权 / 错传)
|
|
3. **合同冻结**(581106): 订单 `contract_status = SIGNED` → 拒绝
|
|
4. **最后 1 成人**(581107): 当前操作是 ADULT 且订单未软删的 ADULT 行数 = 1 → 拒绝
|
|
|
|
### 并发控制
|
|
|
|
- `@Idempotent(timeout=3s)`: 同 `(orderId, travelerId)` 3 秒重复提交直接拒绝(防快速双击)
|
|
- `@Lock4j(expire=30000ms)`: 同订单 30 秒锁,与 batch-edit / add 互斥,防 count 字段 race
|
|
|
|
---
|
|
|
|
## 五、数据库行为
|
|
|
|
整事务:
|
|
|
|
| 操作 | 表 | 说明 |
|
|
|------|------|------|
|
|
| UPDATE | `order_traveler` | `deleted=1` `deleted_at=NOW()` |
|
|
| UPDATE | `order_transport_plan_traveler` | 该出行人所有未软删的桥接行 `deleted=1`(级联解除大交通关联) |
|
|
| UPDATE | `order_main.<type>Count` | -1(按 `travelerType` 选择: adultCount / childCount / youngChildCount / babyCount) |
|
|
| INSERT | `order_status_log` | reason="删除出行人",flowStatus 不变(from == to) |
|
|
|
|
---
|
|
|
|
## 六、边界行为
|
|
|
|
- 未登录 → 401(网关拦截)
|
|
- 订单不存在 → 581102
|
|
- 出行人不属于订单(越权) → 581110
|
|
- 合同 SIGNED → 581106(优先于人数校验)
|
|
- 删最后 1 成人 → 581107(允许删 CHILD / YOUNG_CHILD / BABY 即使他们也是最后 1 个)
|
|
- 出行人有大交通关联 → 自动级联软删桥接表,不阻断主流程
|
|
- 出行人已被软删过(重复删) → 581100(查不到行)
|
|
|
|
---
|
|
|
|
## 七、不影响范围
|
|
|
|
- **仅影响**: 管理后台 F27 改人数(删除)场景
|
|
- **零影响**:
|
|
- 批量编辑 `/traveler/batch-edit`(#2518 独立接口)
|
|
- 新增 `/traveler/add`(#2519 独立接口)
|
|
- 列表 `/traveler/list`(只读)
|
|
- 大交通错误码段位 581120-581129(保留)
|
|
- 老路径 `DELETE /travelers/{travelerId}` 已下线,前端调用必须改用 `POST /traveler/{travelerId}/delete`
|
|
|
|
---
|
|
|
|
## 八、测试环境已验证
|
|
|
|
```
|
|
POST https://web.test.1814.love:9443/v3/admin/order/60123456789012/traveler/70123456789013/delete
|
|
Authorization: Bearer {admin_jwt}
|
|
→ 200 + data: true ✓
|
|
→ order_traveler.deleted=1 ✓
|
|
→ order_transport_plan_traveler 级联 deleted=1 ✓
|
|
→ order_main.child_count -1 ✓
|
|
→ order_status_log 新增 1 行 reason=删除出行人 ✓
|
|
反例:
|
|
- 合同 SIGNED 删人 → 581106 ✓
|
|
- 删最后 1 成人 → 581107 ✓
|
|
- travelerId 不属于该订单 → 581110 ✓
|
|
- DELETE 老方法老路径 → 404 / 405 ✓
|
|
```
|
|
|
|
---
|
|
|
|
## 九、相关历史 PR
|
|
|
|
| PR | Issue | 说明 | 是否仍有效 |
|
|
|----|-------|------|------------|
|
|
| #1984 | - | [§2 traveler skeleton] 空骨架 Mock | ❌ 被本 PR 真实化覆盖 |
|
|
| #2535 | #2518 | batch-edit 真实化(同期工单,共用 581100-581114) | ✅ 有效 |
|
|
| #2536 | #2519 | add 真实化(同期工单,581115-581118) | ✅ 有效 |
|
|
| **本 PR #2537** | **#2520** | delete 接通真实业务 + 方法+路径迁移 + 2 错误码(581106/581107) | ✅ 最新 |
|
|
|
|
---
|
|
|
|
## 十、相关文档
|
|
|
|
- API SPEC: `D:/work2/HL-v3/docs/order-v3/api/API-SPEC-V5.48.html` §2.4
|
|
- 关联 Issue: [wx/HL#2520](https://git.1814.love:8443/wx/HL/issues/2520)
|
|
- 关联 PR: [wx/HL#2537](https://git.1814.love:8443/wx/HL/pulls/2537)
|