hl-api-changelog/changelogs-v2/2026-05/18_§2_traveler模块-新增接口-管理后台.md
yaosutu 757a5dd60b docs(order-v3): §2 traveler 模块 11 接口首推(§2.8 smart-parse 字段先定待 wx 实现)
v3 traveler 模块(出行人 + 大交通)全 11 接口完整契约:

§2A 出行人 admin CRUD(4 接口):
- §2.1 GET    /v3/admin/order/{id}/travelers              出行人列表
- §2.2 POST   /v3/admin/order/{id}/traveler/batch-edit    批量编辑(upsert)
- §2.3 POST   /v3/admin/order/{id}/travelers              单个新增
- §2.4 DELETE /v3/admin/order/{id}/travelers/{travelerId} 软删

§2B 大交通批次 admin CRUD(4 接口):
- §2.6.1 GET    /v3/admin/order/{id}/transport-plans            列表
- §2.6.2 POST   /v3/admin/order/{id}/transport-plans            新增
- §2.6.3 PUT    /v3/admin/order/{id}/transport-plans/{planId}   编辑
- §2.6.4 DELETE /v3/admin/order/{id}/transport-plans/{planId}   软删

§2C internal Feign(1 接口):
- §2.7 GET /v3/internal/order/orders/{orderId}/travelers  跨服务查(含明文 + 解密审计)

§2D 动词类操作(2 接口):
- §2.8 POST /v3/admin/order/{id}/traveler/smart-parse   wx 实现中(Issue #2526)
- §2.9 GET  /v3/admin/order/{id}/traveler/validate     出行人信息校验

按 SKILL.md 新约定(多接口 changelog 按接口自含 + 示例必含请求/响应)组织:
- §3.1~§3.11 共 11 子节,每节含使用场景/入参/出参/错误码/业务边界/请求+响应示例
- §6 枚举集中 10 个(travelerType / gender / idType / profileStatus / direction
  / mode / transportType / selfDrivePeriod / purpose / missingFields),按字段
  分组 + 标使用接口位置

设计文档同步:API-SPEC v5.50(§2 章节路径全部 RESTful 复数化,与 v3 代码对齐)。
1125 行 / 13 节齐全 / §2.8 字段先定标  待 wx 实现立即可对接。
2026-05-18 17:08:21 +08:00

1126 行
34 KiB
Markdown

此文件含有不可见的 Unicode 字符

此文件含有人类无法区分的不可见的 Unicode 字符,但可以由计算机进行不同的处理。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

# 【新增接口·管理后台】v3 traveler 模块 §2出行人 + 大交通)
> **更新时间**: 2026-05-18
> **端类型**: 管理后台
> **设计文档版本**: v5.50API-SPEC / SRS / DETAIL-DESIGN / DATABASE-SCHEMA 4 份 HTML 同步)
---
## 0. 模块全貌
| 子模块 | 含接口 | 接口数 | 状态 |
|---|---|---|---|
| **§2A 出行人 admin CRUD** | §2.1 列表 / §2.2 批量编辑 / §2.3 新增 / §2.4 软删 | **4** | ✅ |
| **§2B 大交通批次 admin CRUD** | §2.6.1 列表 / §2.6.2 新增 / §2.6.3 编辑 / §2.6.4 软删 | **4** | ✅ |
| **§2C internal Feign** | §2.7 跨服务查出行人(含明文+解密审计) | **1** | ✅ |
| **§2D 动词类操作** | §2.8 smart-parse 待新建,Issue #2526/ §2.9 validate | **2** | §2.9 ✅,§2.8 ⏳ wx 实现中 |
| **§2 合计** | — | **11** | ✅ 本次推送§2.8 字段先定,wx 实现后接口可立即对接) |
---
## 1. 接口背景
订单服务 v3 traveler 模块hl-order-service-v3管理订单关联的**出行人信息**(含敏感字段:身份证 / 手机 / 紧急联系人)+ **大交通批次**(接送站 / 航班 / 火车 / 自驾)。
业务边界:
- 出行人不在 §1.1 创单接口里传,创单后通过 §2 单独添加v4.8 取消占位行模型)
- admin 端明文返回 idNo / phone / emergencyPhone仅 B 端定制师可见,JWT 鉴权保护)
- 大交通通过 `order_transport_plan_traveler` 桥接表M:N关联出行人,支持「一家分两批到达」场景
- §2.7 internal feign 解密返回明文,**写入审计流水** `order_decrypt_audit_log`
本次推送 §2 模块全 11 接口,对应管理后台原型 F20 / F21 / F27详情概览 + 出行人补全 + 接送站 + 大交通登记弹窗)。
---
## 2. 变更清单
| # | § | 接口名 | 方法 | 路径 |
|---|---|--------|------|------|
| 1 | 2.1 | 出行人列表 | GET | `/v3/admin/order/{id}/travelers` |
| 2 | 2.2 | 出行人批量编辑upsert | POST | `/v3/admin/order/{id}/traveler/batch-edit` |
| 3 | 2.3 | 单个出行人新增 | POST | `/v3/admin/order/{id}/travelers` |
| 4 | 2.4 | 出行人软删 | DELETE | `/v3/admin/order/{id}/travelers/{travelerId}` |
| 5 | 2.6.1 | 大交通批次列表 | GET | `/v3/admin/order/{id}/transport-plans` |
| 6 | 2.6.2 | 大交通批次新增 | POST | `/v3/admin/order/{id}/transport-plans` |
| 7 | 2.6.3 | 大交通批次编辑 | PUT | `/v3/admin/order/{id}/transport-plans/{planId}` |
| 8 | 2.6.4 | 大交通批次软删 | DELETE | `/v3/admin/order/{id}/transport-plans/{planId}` |
| 9 | 2.7 | 跨服务查出行人Feign | GET | `/v3/internal/order/orders/{orderId}/travelers` |
| 10 | 2.8 ⏳ | 出行人智能批量解析 | POST | `/v3/admin/order/{id}/traveler/smart-parse` |
| 11 | 2.9 | 出行人信息校验 | GET | `/v3/admin/order/{id}/traveler/validate` |
---
## 3. 接口详情
> 每个接口自包含:使用场景 / 入参 / 出参 / 错误码 / 业务边界 / 示例。
> 跨接口共享枚举集中在 §6。
---
### 3.1 §2.1 出行人列表
**路径**`GET /v3/admin/order/{id}/travelers`
**使用场景**:详情页 Tab 1 出行人区块 / F27 出行人补全页
**认证**JWTadmin 角色 + 公司隔离)
**敏感字段**`idNo` / `phone` / `emergencyPhone` **明文返回**admin JWT 鉴权保护)
#### 入参
`id` (path, Long) — 订单 ID
#### 出参(`Result<List<TravelerVO>>`,每条 17 字段)
| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | String | 出行人 ID |
| `orderId` | String | 订单 ID |
| `travelerType` | String | 枚举见 §6.1ADULT / CHILD / YOUNG_CHILD / BABY |
| `name` | String? | 姓名(占位行为 null |
| `gender` | String | 枚举见 §6.2MALE / FEMALE / UNKNOWN |
| `birthday` | LocalDate? | 出生日期 |
| `idType` | String | 枚举见 §6.3ID_CARD / PASSPORT / BIRTH_CERT |
| `idNo` | String? | 证件号admin 明文) |
| `nationality` | String | 国籍(默认中国) |
| `race` | String | 民族(默认汉族) |
| `phone` | String? | 出行人手机admin 明文) |
| `emergencyContact` | String? | 紧急联系人姓名 |
| `emergencyPhone` | String? | 紧急联系人电话admin 明文) |
| `roomGroupNo` | Integer? | 同住分组号 |
| `profileStatus` | String | 枚举见 §6.4PENDING / COMPLETED |
| `transportPlanIds` | List\<Long\> | 关联的大交通批次 ID 列表 |
#### 错误码
| code | 含义 |
|------|------|
| `581020` | 订单不存在 |
| `581021` | 无权访问该订单(公司隔离) |
#### 示例
**典型 - 请求**
```http
GET /v3/admin/order/60123456789012/travelers
Authorization: Bearer {admin_jwt}
```
**典型 - 响应**
```json
{
"code": 200,
"data": [
{
"id": "70123456789012",
"orderId": "60123456789012",
"travelerType": "ADULT",
"name": "张三",
"gender": "MALE",
"birthday": "1985-08-12",
"idType": "ID_CARD",
"idNo": "220103198508121234",
"nationality": "中国",
"race": "汉族",
"phone": "13800002046",
"emergencyContact": "李四",
"emergencyPhone": "13900008888",
"roomGroupNo": 1,
"profileStatus": "COMPLETED",
"transportPlanIds": [80012345, 80012346]
},
{
"id": "70123456789013",
"orderId": "60123456789012",
"travelerType": "CHILD",
"name": null,
"gender": "UNKNOWN",
"birthday": null,
"idType": null,
"idNo": null,
"nationality": "中国",
"race": "汉族",
"phone": null,
"emergencyContact": null,
"emergencyPhone": null,
"roomGroupNo": null,
"profileStatus": "PENDING",
"transportPlanIds": []
}
],
"msg": "success"
}
```
---
### 3.2 §2.2 出行人批量编辑upsert 语义)
**路径**`POST /v3/admin/order/{id}/traveler/batch-edit`
**使用场景**F27 出行人补全 B 端代填,定制师一次性提交订单内 N 行出行人的字段修改/新增(`id=null` 新增 / `id` 非 null 更新)
**整体事务**:任一行字段校验失败 → 全部回滚
#### 入参(`TravelerBatchEditReqVO`
| 字段 | 类型 | 必填 | 说明 | 校验规则 |
|------|------|:----:|------|----------|
| `travelers` | List\<TravelerEditItem\> | ✅ | 待修改/新增出行人数组 | `@NotEmpty` `@Size(max=30)` |
`TravelerEditItem`12 字段):
| 字段 | 类型 | 必填 | 说明 |
|------|------|:----:|------|
| `id` | Long | ❌ | 出行人 IDnull=新增 / 非 null=更新) |
| `travelerType` | String | 新增必填 | 枚举见 §6.1(更新时 null 保留原值) |
| `name` | String | ❌ | 姓名 |
| `gender` | String | ❌ | 枚举见 §6.2 |
| `birthday` | LocalDate | ❌ | 出生日期 |
| `idType` | String | ❌ | 枚举见 §6.3 |
| `idNo` | String | ❌ | 证件号(明文传) |
| `nationality` | String | ❌ | 国籍(不可为空字符串) |
| `race` | String | ❌ | 民族(不可为空字符串) |
| `phone` | String | ❌ | 手机(明文传) |
| `emergencyContact` | String | ❌ | 紧急联系人姓名 |
| `emergencyPhone` | String | ❌ | 紧急联系人电话(明文传) |
| `roomGroupNo` | Integer | ❌ | 同住分组号 |
#### 出参(`Result<TravelerBatchEditRespVO>`
| 字段 | 类型 | 说明 |
|------|------|------|
| `createdCount` | Integer | 本次新增的出行人数(`id=null` 行) |
| `updatedCount` | Integer | 本次更新的出行人数(`id` 非 null 行) |
| `completedCount` | Integer | 本次操作后变为 COMPLETED 的行数 |
| `pendingCount` | Integer | 仍为 PENDING 的行数 |
| `allCompleted` | Boolean | 该订单所有出行人是否已完善 |
#### 错误码
| code | 含义 |
|------|------|
| `581020` | 订单不存在 |
| `581100` | travelers 列表为空 / 超过 30 |
| `581101` | 字段格式校验失败idNo 校验和 / phone 格式) |
| `581110` | 更新时出行人 ID 不属于该订单 |
| `581111` | 已签电子合同后禁止改证件号 |
| `581112` | 新增时缺 travelerType |
#### 业务边界
-**upsert 语义**:单次请求可混合新增 + 更新
-**整体事务**:任一行失败回滚
- ⚠️ **size 上限 30**:超出走分批多次调用
- ⚠️ **`allCompleted=true` 信号**:所有出行人字段齐 → 触发"已完善"系统标签 / 进入下一阶段
#### 示例
**典型 - 请求**
```http
POST /v3/admin/order/60123456789012/traveler/batch-edit
Authorization: Bearer {admin_jwt}
Content-Type: application/json
{
"travelers": [
{
"id": 70123456789013,
"name": "王小明",
"gender": "MALE",
"birthday": "2018-06-20",
"idType": "ID_CARD",
"idNo": "220103201806201234",
"nationality": "中国",
"race": "汉族",
"phone": "13812342046",
"roomGroupNo": 1
},
{
"id": null,
"travelerType": "BABY",
"name": "王小宝",
"gender": "FEMALE",
"birthday": "2024-01-15"
}
]
}
```
**典型 - 响应**
```json
{
"code": 200,
"data": {
"createdCount": 1,
"updatedCount": 1,
"completedCount": 1,
"pendingCount": 1,
"allCompleted": false
},
"msg": "success"
}
```
**异常(证件号格式非法 581101 - 响应**
```json
{ "code": 581101, "data": null, "msg": "证件号校验失败idNo 校验位错误)" }
```
---
### 3.3 §2.3 单个出行人新增(占位补充)
**路径**`POST /v3/admin/order/{id}/travelers`
**使用场景**F27 改人数场景(如临时加 1 个未声明儿童)。**会同步 UPDATE `order_main` 对应人数字段**adultCount / childCount 等)+ 触发后续:保险重算 / 房车需求需重提示
#### 入参(`TravelerCreateReqVO`,12 字段)
| 字段 | 类型 | 必填 | 说明 |
|------|------|:----:|------|
| `travelerType` | String | ✅ | 枚举见 §6.1 |
| `name` | String | ❌ | 姓名(可后填) |
| `gender` | String | ❌ | 枚举见 §6.2 |
| `birthday` | LocalDate | ❌ | 出生日期 |
| `idType` | String | ❌ | 枚举见 §6.3 |
| `idNo` | String | ❌ | 证件号(明文传) |
| `nationality` | String | ❌ | 国籍(默认中国) |
| `race` | String | ❌ | 民族(默认汉族) |
| `phone` | String | ❌ | 出行人手机(明文传) |
| `emergencyContact` | String | ❌ | 紧急联系人姓名 |
| `emergencyPhone` | String | ❌ | 紧急联系人电话 |
| `roomGroupNo` | Integer | ❌ | 同住分组号 |
#### 出参(`Result<TravelerVO>`
字段同 §3.1 列表项,含新建行完整字段。
#### 错误码
| code | 含义 |
|------|------|
| `581020` | 订单不存在 |
| `581115` | 实际人数已等于 `order_main` 声明人数v4.8 取消占位模型规则) |
| `581116` | 订单状态禁止加人(已结算 / 已取消) |
| `581117` | travelerType 与现有同住分组冲突 |
#### 业务边界
-**创单后单独添加**v4.8 取消占位模型,按需 INSERT 新行
- ⚠️ **人数同步**:自动 UPDATE `order_main.<type>Count` +1
- ⚠️ **应用层数量检查**:实际人数 ≥ 声明人数 → `581115`,需要先调整人数声明再加人
#### 示例
**典型 - 请求**
```http
POST /v3/admin/order/60123456789012/travelers
Authorization: Bearer {admin_jwt}
Content-Type: application/json
{
"travelerType": "CHILD",
"name": "王小明",
"gender": "MALE",
"birthday": "2018-06-20",
"idType": "ID_CARD",
"idNo": "220103201806201234",
"phone": "13812342046",
"roomGroupNo": 1
}
```
**典型 - 响应**
```json
{
"code": 200,
"data": {
"id": "70123456789013",
"orderId": "60123456789012",
"travelerType": "CHILD",
"name": "王小明",
"gender": "MALE",
"birthday": "2018-06-20",
"idType": "ID_CARD",
"idNo": "220103201806201234",
"nationality": "中国",
"race": "汉族",
"phone": "13812342046",
"emergencyContact": null,
"emergencyPhone": null,
"roomGroupNo": 1,
"profileStatus": "COMPLETED",
"transportPlanIds": []
},
"msg": "success"
}
```
**异常(人数已满 581115 - 响应**
```json
{ "code": 581115, "data": null, "msg": "实际人数已达声明上限,请先调整人数再添加" }
```
---
### 3.4 §2.4 出行人软删
**路径**`DELETE /v3/admin/order/{id}/travelers/{travelerId}`
**使用场景**F27 改人数场景(如取消同行 1 人)
**关联**:同步 UPDATE `order_main` 对应人数 -1 + 解除其在 `order_transport_plan_traveler` 桥接表中所有关联
**约束****已签电子合同的订单禁止删人**
#### 入参
| 字段 | 类型 | 必填 | 说明 |
|------|------|:----:|------|
| `id` | Long | ✅ | 订单 IDpath |
| `travelerId` | Long | ✅ | 出行人 IDpath |
#### 出参(`Result<Boolean>`
返回 `true` 表示软删成功。
#### 错误码
| code | 含义 |
|------|------|
| `581020` | 订单不存在 |
| `581110` | 出行人不属于该订单 |
| `581119` | 已签电子合同,禁止删除出行人 |
| `581120` | 出行人是订单最后 1 位成人,禁止删除 |
#### 业务边界
-**软删**UPDATE `order_traveler.deleted=1`,不真删
-**级联解除桥接**UPDATE `order_transport_plan_traveler.deleted=1`
-**已签合同禁删**:合同强约束 → `581119`
-**最后成人禁删**:保留至少 1 位成人 → `581120`
#### 示例
**典型 - 请求**
```http
DELETE /v3/admin/order/60123456789012/travelers/70123456789013
Authorization: Bearer {admin_jwt}
```
**典型 - 响应**
```json
{ "code": 200, "data": true, "msg": "success" }
```
**异常(已签合同 581119 - 响应**
```json
{ "code": 581119, "data": null, "msg": "订单已签电子合同,禁止删除出行人" }
```
---
### 3.5 §2.6.1 大交通批次列表
**路径**`GET /v3/admin/order/{id}/transport-plans`
**使用场景**F21 行程安排 Tab "接送站"区块
**业务模型**:一个订单可有 N 个批次(实现"一家分两批到达"),按 `direction` 分 ARRIVAL / DEPARTURE
#### 入参
`id` (path, Long) — 订单 ID
#### 出参(`Result<List<TransportPlanVO>>`
每条 16 字段 + travelers 嵌套:
| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | String | 批次 ID |
| `orderId` | String | 订单 ID |
| `direction` | String | 枚举见 §6.5ARRIVAL / DEPARTURE |
| `mode` | String | 枚举见 §6.6TOGETHER / SEPARATE |
| `transportType` | String | 枚举见 §6.7FLIGHT / TRAIN / SELF_DRIVE |
| `transportNo` | String? | 航班号 / 车次号;SELF_DRIVE 时为空 |
| `carrier` | String? | 航司 / 铁路公司 |
| `departStation` | String? | 出发站 |
| `arriveStation` | String? | 到达站 |
| `departTime` | LocalDateTime? | 出发时间FLIGHT/TRAIN 必有;SELF_DRIVE 为空) |
| `arriveTime` | LocalDateTime? | 到达时间(同上) |
| `selfDrivePeriod` | String? | 枚举见 §6.8(仅 SELF_DRIVE |
| `selfDriveEta` | LocalDateTime? | 自驾预计到达时间(仅 SELF_DRIVE 可选) |
| `pickupRequired` | Boolean? | 是否需要接送 |
| `pickupRemark` | String? | 接送备注 |
| `travelers` | List\<{id, name}\> | 关联出行人简要(桥接表 JOIN |
| `remark` | String? | 备注 |
#### 错误码
| code | 含义 |
|------|------|
| `581020` | 订单不存在 |
| `581021` | 无权访问该订单 |
#### 示例
**典型 - 请求**
```http
GET /v3/admin/order/60123456789012/transport-plans
Authorization: Bearer {admin_jwt}
```
**典型 - 响应**
```json
{
"code": 200,
"data": [
{
"id": "80012345",
"orderId": "60123456789012",
"direction": "ARRIVAL",
"mode": "TOGETHER",
"transportType": "FLIGHT",
"transportNo": "CA1234",
"carrier": "中国国际航空",
"departStation": "北京首都T3",
"arriveStation": "长春龙嘉",
"departTime": "2026-06-01T08:30:00",
"arriveTime": "2026-06-01T10:15:00",
"selfDrivePeriod": null,
"selfDriveEta": null,
"pickupRequired": true,
"pickupRemark": "需在 T3 出口举牌接机",
"travelers": [
{"id": "70123456789012", "name": "张三"},
{"id": "70123456789013", "name": "王小明"}
],
"remark": "需要接机举牌"
},
{
"id": "80012346",
"orderId": "60123456789012",
"direction": "DEPARTURE",
"mode": "TOGETHER",
"transportType": "SELF_DRIVE",
"transportNo": null,
"carrier": null,
"departStation": null,
"arriveStation": null,
"departTime": null,
"arriveTime": null,
"selfDrivePeriod": "AFTERNOON",
"selfDriveEta": "2026-06-05T15:00:00",
"pickupRequired": false,
"pickupRemark": null,
"travelers": [
{"id": "70123456789012", "name": "张三"}
],
"remark": null
}
],
"msg": "success"
}
```
---
### 3.6 §2.6.2 大交通批次新增
**路径**`POST /v3/admin/order/{id}/transport-plans`
**使用场景**F21 大交通登记弹窗 - 新增
**桥接表维护**:同事务 INSERT `order_transport_plan_traveler`(每个 plan 必须关联至少 1 名出行人)
#### 入参(`TransportPlanReqVO`,11 字段)
| 字段 | 类型 | 必填 | 说明 |
|------|------|:----:|------|
| `direction` | String | ✅ | 枚举见 §6.5 |
| `transportType` | String | ✅ | 枚举见 §6.7 |
| `transportNo` | String | 条件 | 航班号 / 车次号;SELF_DRIVE 时为空 |
| `carrier` | String | ❌ | 航司 / 铁路公司 |
| `departStation` / `arriveStation` | String | ❌ | 出发 / 到达站 |
| `departTime` / `arriveTime` | LocalDateTime | 条件 | FLIGHT/TRAIN 必填;SELF_DRIVE 为空 |
| `selfDrivePeriod` | String | 条件 | 枚举见 §6.8,仅 SELF_DRIVE |
| `selfDriveEta` | LocalDateTime | ❌ | 仅 SELF_DRIVE 可选 |
| `travelerIds` | List\<Long\> | ✅ | 关联出行人 ID至少 1 个) |
| `remark` | String | ❌ | 备注≤500 |
#### 出参(`Result<TransportPlanVO>`
字段同 §3.5 列表项。
#### 错误码
| code | 含义 |
|------|------|
| `581020` | 订单不存在 |
| `581140` | 字段组合非法FLIGHT 缺航班号 / SELF_DRIVE 错带 transportNo 等) |
| `581141` | `travelerIds` 含订单外的出行人 |
| `581142` | 出发时间晚于到达时间 |
| `581143` | 同方向同一出行人已在另一 plan |
#### 业务边界
-**字段组合校验**FLIGHT/TRAIN 需 transportNo + 出发/到达时间;SELF_DRIVE 不传 transportNo,可传 selfDrivePeriod + selfDriveEta
- ⚠️ **桥接约束**:同方向同一出行人只能在 1 个 plan,不同方向各 1 个
#### 示例
**典型(航班 ARRIVAL - 请求**
```http
POST /v3/admin/order/60123456789012/transport-plans
Authorization: Bearer {admin_jwt}
Content-Type: application/json
{
"direction": "ARRIVAL",
"transportType": "FLIGHT",
"transportNo": "CA1234",
"carrier": "中国国际航空",
"departStation": "北京首都T3",
"arriveStation": "长春龙嘉",
"departTime": "2026-06-01T08:30:00",
"arriveTime": "2026-06-01T10:15:00",
"travelerIds": [70123456789012, 70123456789013],
"remark": "需要接机举牌"
}
```
**典型 - 响应**
```json
{
"code": 200,
"data": {
"id": "80012345",
"orderId": "60123456789012",
"direction": "ARRIVAL",
"mode": "TOGETHER",
"transportType": "FLIGHT",
"transportNo": "CA1234",
"carrier": "中国国际航空",
"departStation": "北京首都T3",
"arriveStation": "长春龙嘉",
"departTime": "2026-06-01T08:30:00",
"arriveTime": "2026-06-01T10:15:00",
"selfDrivePeriod": null,
"selfDriveEta": null,
"pickupRequired": null,
"pickupRemark": null,
"travelers": [
{"id": "70123456789012", "name": "张三"},
{"id": "70123456789013", "name": "王小明"}
],
"remark": "需要接机举牌"
},
"msg": "success"
}
```
**异常(自驾错带 transportNo 581140 - 响应**
```json
{ "code": 581140, "data": null, "msg": "SELF_DRIVE 类型不应传 transportNo" }
```
---
### 3.7 §2.6.3 大交通批次编辑
**路径**`PUT /v3/admin/order/{id}/transport-plans/{planId}`
**使用场景**F21 大交通登记弹窗 - 编辑(全量覆盖含 travelerIds
**事务**UPDATE plan + DELETE+INSERT 重建桥接表
#### 入参
| 字段 | 类型 | 必填 | 说明 |
|------|------|:----:|------|
| `id` | Long | ✅ | 订单 IDpath |
| `planId` | Long | ✅ | 批次 IDpath |
| Body | `TransportPlanReqVO` | ✅ | 同 §3.6 字段(全量覆盖语义) |
#### 出参(`Result<TransportPlanVO>`
字段同 §3.5。
#### 错误码
| code | 含义 |
|------|------|
| `581140` ~ `581143` | 同 §3.6 字段组合校验 |
| `581144` | plan 不存在 / 不属于该订单 |
#### 业务边界
- ⚠️ **全量覆盖语义**:所有 Req 字段都会替换原值,包括 `travelerIds`
- ⚠️ **桥接表重建**:旧 `order_transport_plan_traveler` 行 DELETE,按新 `travelerIds` INSERT
#### 示例
**典型 - 请求**
```http
PUT /v3/admin/order/60123456789012/transport-plans/80012345
Authorization: Bearer {admin_jwt}
Content-Type: application/json
{
"direction": "ARRIVAL",
"transportType": "FLIGHT",
"transportNo": "CA1235",
"carrier": "中国国际航空",
"departStation": "北京首都T3",
"arriveStation": "长春龙嘉",
"departTime": "2026-06-01T10:30:00",
"arriveTime": "2026-06-01T12:15:00",
"travelerIds": [70123456789012],
"remark": "改签后的航班"
}
```
**典型 - 响应**
```json
{
"code": 200,
"data": {
"id": "80012345",
"direction": "ARRIVAL",
"transportType": "FLIGHT",
"transportNo": "CA1235",
"departTime": "2026-06-01T10:30:00",
"arriveTime": "2026-06-01T12:15:00",
"travelers": [{"id": "70123456789012", "name": "张三"}],
"remark": "改签后的航班"
},
"msg": "success"
}
```
---
### 3.8 §2.6.4 大交通批次软删
**路径**`DELETE /v3/admin/order/{id}/transport-plans/{planId}`
**使用场景**F21 大交通登记弹窗 - 删除
**事务**UPDATE `order_transport_plan.deleted=1` + UPDATE 桥接表 deleted=1
#### 入参
| 字段 | 类型 | 必填 | 说明 |
|------|------|:----:|------|
| `id` | Long | ✅ | 订单 IDpath |
| `planId` | Long | ✅ | 批次 IDpath |
#### 出参(`Result<Boolean>`
返回 `true` 表示软删成功。
#### 错误码
| code | 含义 |
|------|------|
| `581144` | plan 不存在 / 不属于该订单 |
#### 示例
**典型 - 请求**
```http
DELETE /v3/admin/order/60123456789012/transport-plans/80012345
Authorization: Bearer {admin_jwt}
```
**典型 - 响应**
```json
{ "code": 200, "data": true, "msg": "success" }
```
---
### 3.9 §2.7 跨服务查出行人internal Feign
**路径**`GET /v3/internal/order/orders/{orderId}/travelers`
**使用场景**:合同域签约 / 保险域投保 / mp-service 聚合等跨服务调用
**敏感字段****明文返回**idNo / phone / emergencyPhone 解密,仅内网 Feign + 签名校验保护)
**审计**:每次调用 INSERT `order_decrypt_audit_log`(解密审计永久流水)
#### 入参
| 字段 | 类型 | 必填 | 说明 |
|------|------|:----:|------|
| `orderId` | Long | ✅ | 订单 IDpath |
| `purpose` | String | ✅ | Query 参数。枚举见 §6.9CONTRACT_SIGN / INSURANCE_ISSUE / OTHER |
#### 出参(`Result<List<TravelerInternalVO>>`
字段同 §3.1 + 多 1 个 `decryptedAt`
| 字段 | 类型 | 说明 |
|------|------|------|
| ... | | 同 §3.1 全部字段(含敏感明文) |
| `decryptedAt` | LocalDateTime | 本次解密时间戳,写入审计流水 |
#### 错误码
| code | 含义 |
|------|------|
| `589100` | orderId 不存在 |
| `589101` | purpose 枚举非法 |
#### 业务边界
-**内网 Feign 专用**Gateway 不暴露 `/internal/**` 至公网
-**审计流水**:每次调用都写 `order_decrypt_audit_log`,含 `orderId` / `purpose` / `decryptedAt` / 调用方服务(取自 Feign 签名头)
- ⚠️ **purpose 必填**:用于审计 / 合规追溯,前端 / 上层调用方必须正确传
#### 示例
**典型 - 请求**
```http
GET /v3/internal/order/orders/60123456789012/travelers?purpose=CONTRACT_SIGN
```
**典型 - 响应**
```json
{
"code": 200,
"data": [
{
"id": "70123456789012",
"orderId": "60123456789012",
"travelerType": "ADULT",
"name": "张三",
"gender": "MALE",
"birthday": "1985-08-12",
"idType": "ID_CARD",
"idNo": "220103198508121234",
"nationality": "中国",
"race": "汉族",
"phone": "13812342046",
"emergencyContact": "李四",
"emergencyPhone": "13988888888",
"roomGroupNo": 1,
"profileStatus": "COMPLETED",
"transportPlanIds": [80012345, 80012346],
"decryptedAt": "2026-05-18T22:30:25"
}
],
"msg": "success"
}
```
**异常purpose 非法 589101 - 响应**
```json
{ "code": 589101, "data": null, "msg": "purpose 枚举非法" }
```
---
### 3.10 §2.8 出行人智能批量解析 ⏳
**路径**`POST /v3/admin/order/{id}/traveler/smart-parse`
**使用场景**F27 出行人补全 / 一键导入弹窗。定制师粘贴多行文本(姓名+证件号+手机号),后端解析为结构化出行人列表,校验后批量入库
**状态**:⏳ **wx 待实现**Issue [#2526](https://git.1814.love:8443/wx/HL/issues/2526)。本节字段先定,wx 实现完即可对接
#### 入参(`TravelerSmartParseReqVO`
| 字段 | 类型 | 必填 | 说明 |
|------|------|:----:|------|
| `id` | Long | ✅ | 订单 IDpath |
| `rawText` | String | ✅ | 粘贴的多行原文(≤ 20000 字符) |
| `dryRun` | Boolean | ❌ | true=仅解析不入库(默认 false=解析+入库) |
#### 出参(`Result<TravelerSmartParseRespVO>`
| 字段 | 类型 | 说明 |
|------|------|------|
| `successCount` | Integer | 解析成功并入库的行数 |
| `failCount` | Integer | 解析失败的行数 |
| `successList` | List\<TravelerVO\> | 成功创建的出行人列表(结构同 §3.1;dryRun=true 时不含 id |
| `failures` | List\<FailureVO\> | 失败明细:`{lineIndex, maskedSnippet, reason}` |
#### 错误码
| code | 含义 |
|------|------|
| `581120` | 原文超长(> 20000 字符) |
| `581121` | 原文为空或全行无法解析 |
| `581122` | 限流触发10 次/分钟) |
| `581123` | 订单状态不允许批量导入(已结算 / 已取消) |
#### 业务边界 + 审计安全
-**限流**:每 admin 10 次/分钟(`@RateLimiter(count=10, time=60)`
-**PII 不落审计**:接口**不挂** `@OperationLog`(避免明文 idNo/phone 写入 audit_log
-**错误行脱敏**`failures[].maskedSnippet` 严格脱敏idNo 前 6 后 4、phone 前 3 后 4
- ⚠️ **dryRun=true**:纯解析不落库,前端可用作"试解析预览"
- ⚠️ **dryRun=false**:解析+入库一次完成,INSERT N 行 `order_traveler` + UPDATE `order_main.<type>Count` 同事务
#### 示例
**典型 - 请求**
```http
POST /v3/admin/order/60123456789012/traveler/smart-parse
Authorization: Bearer {admin_jwt}
Content-Type: application/json
{
"rawText": "张三 350101199001011234 13800138000\n李四 13900139000 110101199201021235\n王五 110101****1234 138****8765",
"dryRun": false
}
```
**典型 - 响应**
```json
{
"code": 200,
"data": {
"successCount": 2,
"failCount": 1,
"successList": [
{"id": "70123456789014", "name": "张三", "travelerType": "ADULT", "profileStatus": "COMPLETED"},
{"id": "70123456789015", "name": "李四", "travelerType": "ADULT", "profileStatus": "COMPLETED"}
],
"failures": [
{"lineIndex": 3, "maskedSnippet": "王五 110101****1234 138****8765", "reason": "身份证号校验失败"}
]
},
"msg": "success"
}
```
**异常(限流 581122 - 响应**
```json
{ "code": 581122, "data": null, "msg": "智能导入解析过于频繁,请稍后再试" }
```
---
### 3.11 §2.9 出行人信息校验
**路径**`GET /v3/admin/order/{id}/traveler/validate`
**使用场景**:支付前 / 锁单前置校验(同 §1.8 confirm-checklist 的 `TRAVELER_COMPLETE` 项底层依赖)
**校验维度**(1) 实际出行人数 = 订单声明人数;(2) 每个出行人字段齐全(姓名+身份证+手机+证件类型)
#### 入参
`id` (path, Long) — 订单 ID
#### 出参(`Result<TravelerValidateRespVO>`
| 字段 | 类型 | 说明 |
|------|------|------|
| `passed` | Boolean | 是否全部通过 |
| `declaredCount` | Integer | 订单声明人数(含所有人群类型) |
| `actualCount` | Integer | 实际 `order_traveler` 行数(未软删) |
| `countMismatch` | Boolean | 人数是否不一致 |
| `incompleteList` | List\<IncompleteTravelerVO\> | 字段不完整的出行人 |
`IncompleteTravelerVO`
| 字段 | 类型 | 说明 |
|------|------|------|
| `travelerId` | String | 出行人 ID |
| `name` | String | 姓名(脱敏,如 `张*` |
| `missingFields` | List\<String\> | 缺失字段名列表(如 `["idNo", "phone"]` |
#### 错误码
| code | 含义 |
|------|------|
| `581124` | 订单不存在 |
#### 业务边界
-**只读校验**:不入库、不改状态
- ⚠️ **`incompleteList[].name` 脱敏**:响应里姓名仅留首字(如 `张*`),不暴露完整姓名
- ⚠️ **必填判定**:缺 `name` / `idType` / `idNo` / `phone` 任一即进 `incompleteList`
#### 示例
**典型(未通过) - 请求**
```http
GET /v3/admin/order/60123456789012/traveler/validate
Authorization: Bearer {admin_jwt}
```
**典型(未通过) - 响应**
```json
{
"code": 200,
"data": {
"passed": false,
"declaredCount": 3,
"actualCount": 2,
"countMismatch": true,
"incompleteList": [
{"travelerId": "70123456789013", "name": "张*", "missingFields": ["idNo", "phone"]}
]
},
"msg": "success"
}
```
**典型(通过) - 响应**
```json
{
"code": 200,
"data": {
"passed": true,
"declaredCount": 3,
"actualCount": 3,
"countMismatch": false,
"incompleteList": []
},
"msg": "success"
}
```
---
## 6. 枚举 / 数据字典
> 跨接口共享枚举集中列出,按字段分组,标注「使用字段在哪里」。
### 6.1 travelerType出行人类型
**使用字段**§3.1 / §3.2 / §3.3 / §3.9 出入参 `travelerType`
| 值 | 说明 |
|----|------|
| `ADULT` | 成人 |
| `CHILD` | 儿童(有床) |
| `YOUNG_CHILD` | 幼儿(无床/占座) |
| `BABY` | 婴儿(无座) |
### 6.2 gender性别
**使用字段**§3.1 / §3.2 / §3.3 / §3.9 出入参 `gender`
| 值 | 说明 |
|----|------|
| `MALE` | 男 |
| `FEMALE` | 女 |
| `UNKNOWN` | 未填 |
### 6.3 idType证件类型
**使用字段**§3.1 / §3.2 / §3.3 / §3.9 出入参 `idType`
| 值 | 说明 |
|----|------|
| `ID_CARD` | 居民身份证 |
| `PASSPORT` | 护照 |
| `BIRTH_CERT` | 出生证明 |
### 6.4 profileStatus资料完善状态
**使用字段**§3.1 / §3.3 / §3.9 出参 `profileStatus`
| 值 | 说明 |
|----|------|
| `PENDING` | 字段未齐全 |
| `COMPLETED` | 字段已齐全 |
### 6.5 direction大交通方向
**使用字段**§3.5 / §3.6 / §3.7 出入参 `direction`
| 值 | 说明 |
|----|------|
| `ARRIVAL` | 到达(去程) |
| `DEPARTURE` | 返程(离开) |
### 6.6 mode大交通模式
**使用字段**§3.5 出参 `mode`
| 值 | 说明 |
|----|------|
| `TOGETHER` | 一起到达 / 返程 |
| `SEPARATE` | 分批到达 / 返程(一家分两批场景) |
### 6.7 transportType交通工具类型
**使用字段**§3.5 / §3.6 / §3.7 出入参 `transportType`
| 值 | 说明 |
|----|------|
| `FLIGHT` | 航班 |
| `TRAIN` | 火车 |
| `SELF_DRIVE` | 自驾 |
### 6.8 selfDrivePeriod自驾时段
**使用字段**§3.5 / §3.6 / §3.7 出入参 `selfDrivePeriod`(仅 SELF_DRIVE 类型)
| 值 | 说明 |
|----|------|
| `MORNING` | 上午 |
| `AFTERNOON` | 下午 |
| `EVENING` | 晚上 |
### 6.9 purposeFeign 调用目的)
**使用字段**§3.9 入参 `purpose`Query
| 值 | 说明 |
|----|------|
| `CONTRACT_SIGN` | 合同签约 |
| `INSURANCE_ISSUE` | 保险出单 |
| `OTHER` | 其他场景(需在审计流水里另行说明) |
### 6.10 missingFields缺失字段名
**使用字段**§3.11 出参 `incompleteList[].missingFields[]`
可能值(与 §3.1 字段名一致):`name` / `idType` / `idNo` / `phone`
---
## 11. 影响评估
- **是否破坏向后兼容**v3 全新二期,前端 v3 项目仓库首次消费)
- **前端是否必须同步上线**:是
- **本次推送范围**§2 模块全 11 接口(含 §2.8 smart-parse 字段先定,wx 实现后立即可对接)
---
## 12. 注意事项
- **§2.8 smart-parse 待 wx 新建Issue [#2526](https://git.1814.love:8443/wx/HL/issues/2526)**:前端可先按本文字段对接 UI,wx 实现完接口立即可调;接口字段已确定不会变
- **敏感字段明文返回**§2.1 / §2.3 / §2.7 / §2.9 / §2.8 admin/internal 接口返回 `idNo` / `phone` / `emergencyPhone` 明文,前端拿到后**不要**写本地 log / 不要塞 URL query;§2.9 `incompleteList[].name` 已脱敏
- **`/v3/internal/**` 不暴露公网**§3.9 跨服务 Feign 接口只在内网调用,前端不调
- **桥接表约束**§3.6 / §3.7 大交通批次新增/编辑时同方向同一出行人只能在 1 个 plan错则 `581143`
- **删人合同强约束**§3.4 已签电子合同后禁止删人 → `581119`
- **`/v3/admin/**` 公司隔离**:所有 admin 接口均带跨公司隔离校验,跨公司访问返 `581021`,前端无需自行过滤
---
## 13. 关联
- **API 设计文档**: `docs/order-v3/api/API-SPEC-V5.50.html` §2.1 ~ §2.9v5.50 阶段已对齐 v3 代码现状)
- **SRS 业务规格**: `docs/order-v3/srs/order-cloud-v3-srs-v5.50.html` §1.0i 出行人模型 / §2 大交通
- **数据库 Schema**: `docs/order-v3/database/DATABASE-SCHEMA-V5.50.html``order_traveler` / `order_transport_plan` / `order_transport_plan_traveler` / `order_decrypt_audit_log`
- **§1 总 changelog**(订单核心模块): `changelogs-v2/2026-05/18_§1_订单核心模块-新增接口-管理后台.md`(详情主聚合 §1.3.1 引用本模块 §2.1 TravelerVO 字段口径)
- **关联 Issue**[#2517](https://git.1814.love:8443/wx/HL/issues/2517) ~ [#2527](https://git.1814.love:8443/wx/HL/issues/2527) 11 个,全部 assign wx;§2.8 [#2526](https://git.1814.love:8443/wx/HL/issues/2526) 为 0→1 新建
- **后端负责人**: @yaosutu / 实施 @wx