- #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>
234 行
7.7 KiB
Markdown
234 行
7.7 KiB
Markdown
# 出行人批量编辑: POST /traveler/batch-edit 真实业务化
|
|
|
|
> **服务**: hl-order-service-v3 (端口 8084 / 二期)
|
|
> **PR**: #2535
|
|
> **Issue**: #2518
|
|
> **日期**: 2026-05-18
|
|
> **影响范围**: 管理后台 F27 出行人补全(B 端定制师代填)
|
|
> **存放目录**: `changelogs-v2/2026-05/`(二期 v3 专属,带 -v2 后缀)
|
|
> **部署 commit**: dev-v3 `83dbeedb4`
|
|
> **测试服已验证**: ✅(/@qa 通过 9443 网关 + 真 admin token round-trip)
|
|
|
|
---
|
|
|
|
## ⚠️ 关键变化
|
|
|
|
1. **请求体字段名**: 文档原稿 §2.2 写的是 `items`,**实际 VO 定义为 `travelers`**,前端按 `travelers` 提交(下文示例为准)。
|
|
2. **从 Mock 占位变真实业务**: 之前是空骨架返回固定假数据,本 PR 接通真实 UPDATE + 12301 校验 + 合同冻结 + 同住分组校验 + 资料状态联动。
|
|
3. **新增 9 个错误码段位**: 581101 / 581102 / 581103 / 581104 / 581105 / 581111 / 581112 / 581113 / 581114(段位安排见下)。
|
|
4. **加幂等 @Idempotent(3s)**: 同订单 3 秒窗口内重复提交直接拒绝,防止快速点击 / 网络重试导致补全双写。
|
|
|
|
---
|
|
|
|
## 一、背景
|
|
|
|
文档 V5.48 §2.2 定义 admin 端批量编辑出行人,任一行 12301 字段(国籍 / 民族)校验失败整体回滚,补全完成后异步重算 `profile_status` 并触发"已完善"系统标签。本 PR 完成真实业务化,以及合同 signed 状态对证件号的冻结约束。
|
|
|
|
---
|
|
|
|
## 二、变更接口清单
|
|
|
|
| # | 接口 | 方法 | 路径 | 变更类型 |
|
|
|---|------|------|------|----------|
|
|
| 1 | 出行人批量编辑 | POST | `/v3/admin/order/{id}/traveler/batch-edit` | 真实业务化 + 9 个新错误码 |
|
|
|
|
---
|
|
|
|
## 三、接口详情
|
|
|
|
### 1. 出行人批量编辑 `POST /v3/admin/order/{id}/traveler/batch-edit`
|
|
|
|
**VO**: `TravelerBatchEditReqVO` + `TravelerEditItem`(嵌套数组每行)
|
|
|
|
#### 入参
|
|
|
|
| 字段 | 位置 | 类型 | 必填 | 说明 |
|
|
|------|------|------|------|------|
|
|
| `id` | Path | Long | ✅ | 订单 ID |
|
|
| `travelers` | Body | List\<TravelerEditItem\> | ✅ | 待修改出行人数组(≤30) |
|
|
|
|
**TravelerEditItem 每行字段**:
|
|
|
|
| 字段 | 类型 | 必填 | 说明 |
|
|
|------|------|------|------|
|
|
| `id` | Long | ✅ | 出行人 ID(必须属于该 orderId) |
|
|
| `name` | String | ❌ | 姓名 |
|
|
| `gender` | String | ❌ | MALE / FEMALE / UNKNOWN |
|
|
| `birthday` | LocalDate | ❌ | 出生日期 |
|
|
| `idType` | String | ❌ | ID_CARD / PASSPORT / BIRTH_CERT |
|
|
| `idNo` | String | ❌ | 证件号(明文传,DB 加密) |
|
|
| `nationality` | String | ❌ | 国籍(默认中国,不可设为空字符串) |
|
|
| `race` | String | ❌ | 民族(默认汉族,不可设为空字符串) |
|
|
| `phone` | String | ❌ | 出行人手机(明文传,DB 加密) |
|
|
| `emergencyContact` | String | ❌ | 紧急联系人姓名 |
|
|
| `emergencyPhone` | String | ❌ | 紧急联系人电话(明文传) |
|
|
| `roomGroupNo` | Integer | ❌ | 同住分组号 |
|
|
|
|
#### 出参 `Result<TravelerBatchEditRespVO>`
|
|
|
|
| 字段 | 类型 | 说明 |
|
|
|------|------|------|
|
|
| `updatedCount` | Integer | 实际更新行数 |
|
|
| `completedCount` | Integer | 本次操作后变为 COMPLETED 的行数 |
|
|
| `pendingCount` | Integer | 仍为 PENDING 的行数 |
|
|
| `allCompleted` | Boolean | 该订单所有出行人是否已完善(联动 5 项 checklist) |
|
|
|
|
#### 请求示例
|
|
|
|
```json
|
|
POST /v3/admin/order/60123456789012/traveler/batch-edit
|
|
Authorization: Bearer {admin_jwt}
|
|
|
|
{
|
|
"travelers": [
|
|
{
|
|
"id": 70123456789012,
|
|
"name": "张三",
|
|
"gender": "MALE",
|
|
"birthday": "1985-08-12",
|
|
"idType": "ID_CARD",
|
|
"idNo": "220103198508121234",
|
|
"nationality": "中国",
|
|
"race": "汉族",
|
|
"phone": "13800002046",
|
|
"roomGroupNo": 1
|
|
},
|
|
{
|
|
"id": 70123456789013,
|
|
"name": "张小宝",
|
|
"gender": "MALE",
|
|
"birthday": "2018-05-01",
|
|
"idType": "BIRTH_CERT",
|
|
"idNo": "J012345678",
|
|
"roomGroupNo": 1
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
#### 响应示例
|
|
|
|
```json
|
|
{
|
|
"code": 200,
|
|
"data": {
|
|
"updatedCount": 2,
|
|
"completedCount": 2,
|
|
"pendingCount": 0,
|
|
"allCompleted": true
|
|
},
|
|
"msg": "success"
|
|
}
|
|
```
|
|
|
|
#### 错误响应清单(本工单新增段位)
|
|
|
|
| 错误码 | 含义 |
|
|
|---|---|
|
|
| `581100` | 出行人不存在 |
|
|
| `581101` | 12301 必报字段缺失(国籍 / 民族不能为空字符串) |
|
|
| `581102` | 订单不存在,无法编辑出行人 |
|
|
| `581103` | 性别枚举不合法(应为 MALE / FEMALE / UNKNOWN) |
|
|
| `581104` | 同住分组号超出订单家庭数上限 |
|
|
| `581105` | 批量大小超限(>30,@Size 已 fallback,Service 层防御性二次校验) |
|
|
| `581110` | 出行人 ID 不属于该订单 |
|
|
| `581111` | 已签电子合同后禁止修改证件号(合同 signed 后 idNo 冻结) |
|
|
| `581112` | 证件号格式不合法(身份证 18 位 / 护照 5-20 位) |
|
|
| `581113` | 手机号格式非法(应为 11 位数字) |
|
|
| `581114` | 出生日期不能晚于今天 |
|
|
| `581119` | 出行人证件号重复(同订单内 idNo 去重) |
|
|
|
|
错误响应体示例:
|
|
|
|
```json
|
|
{
|
|
"code": 581111,
|
|
"msg": "已签电子合同后禁止修改证件号",
|
|
"data": null
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 四、契约约束
|
|
|
|
### 校验顺序(任一失败整事务回滚)
|
|
|
|
1. 批量大小 ≤30(581105)
|
|
2. 订单存在(581102)
|
|
3. 身份证去重(581119,同请求体内自查重)
|
|
4. 姓名硬拦截(异常态字符,跟 TravelerNameValidator 共享口径)
|
|
5. 12301 字段非空字符串(581101)
|
|
6. 格式校验: idNo / phone / birthday(581112 / 581113 / 581114) + gender 枚举(581103) + roomGroupNo 范围(581104)
|
|
7. 归属校验: 每行 id 必须属于该 orderId(581110)
|
|
8. **已签电子合同冻结**: `contract_status = SIGNED` 时禁改证件号(581111)
|
|
|
|
### 并发控制
|
|
|
|
- `@Idempotent(timeout=3s)`: 同订单 3 秒重复提交直接拒绝
|
|
- `@Lock4j(expire=30000ms)`: 同订单批量更新加 30 秒分布式锁,防 admin / mp 同时提交竞态
|
|
|
|
---
|
|
|
|
## 五、数据库行为
|
|
|
|
- UPDATE N 行 `order_traveler`(只更非 null 字段,null 入参不覆盖原值)
|
|
- 派生 `profile_status`(若 idType/idNo/name/birthday/gender 齐全 → COMPLETED 否则 PENDING)
|
|
- INSERT 1 行 `order_status_log`,reason="出行人补全"
|
|
- 触发"已完善"系统标签事件(联动 confirm-checklist 的 TRAVELER_COMPLETE)
|
|
|
|
---
|
|
|
|
## 六、边界行为
|
|
|
|
- 未登录 → 401(网关拦截)
|
|
- 入参 travelers 为 null / 空数组 → 400(@NotEmpty)
|
|
- 入参 travelers.size > 30 → 400(@Size,@Idempotent 之前拦截)
|
|
- 订单不存在 → 581102
|
|
- 重复提交(3 秒内) → 接口直接拒绝(@Idempotent 拦截)
|
|
- 已签电子合同 → 581111(只冻结 idNo,其他字段仍可改)
|
|
|
|
---
|
|
|
|
## 七、不影响范围
|
|
|
|
- **仅影响**: 管理后台 F27 出行人补全表单
|
|
- **零影响**:
|
|
- 单个新增 `/traveler/add`(#2519 独立接口)
|
|
- 软删 `/traveler/{travelerId}/delete`(#2520 独立接口)
|
|
- 列表 `/traveler/list`(只读,#2517)
|
|
- mp 端出行人编辑接口
|
|
- 大交通批次 / 桥接表(本接口不动 transport_plan)
|
|
|
|
---
|
|
|
|
## 八、测试环境已验证
|
|
|
|
```
|
|
POST https://web.test.1814.love:9443/v3/admin/order/60123456789012/traveler/batch-edit
|
|
Body: {"travelers":[{id, name, ...}]}
|
|
→ 200 + updatedCount/completedCount/pendingCount/allCompleted ✓
|
|
反例:
|
|
- 12301 字段为空 → 581101 ✓
|
|
- 出行人不属于订单 → 581110 ✓
|
|
- 合同 SIGNED 改 idNo → 581111 ✓
|
|
- 重复提交(3 秒内) → 接口拒绝 ✓
|
|
```
|
|
|
|
---
|
|
|
|
## 九、相关历史 PR
|
|
|
|
| PR | Issue | 说明 | 是否仍有效 |
|
|
|----|-------|------|------------|
|
|
| #1984 | - | [§2 traveler skeleton] 9 接口空骨架 + Mock ServiceImpl | ❌ 被本 PR 真实化覆盖 |
|
|
| **本 PR #2535** | **#2518** | batch-edit 接通真实业务 + 9 错误码 + 幂等 / 锁 | ✅ 最新 |
|
|
|
|
---
|
|
|
|
## 十、相关文档
|
|
|
|
- API SPEC: `D:/work2/HL-v3/docs/order-v3/api/API-SPEC-V5.48.html` §2.2
|
|
- 关联 Issue: [wx/HL#2518](https://git.1814.love:8443/wx/HL/issues/2518)
|
|
- 关联 PR: [wx/HL#2535](https://git.1814.love:8443/wx/HL/pulls/2535)
|