# 【新增接口·管理后台】v3 traveler 模块 §2(出行人 + 大交通) > **更新时间**: 2026-05-18 > **端类型**: 管理后台 > **设计文档版本**: v5.50(API-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 动词类操作** | §2.8 smart-parse(⚠️ 待新建,Issue #2526)/ §2.9 validate | **2** | §2.9 ✅,§2.8 ⏳ wx 实现中 | | **§2 合计** | — | **10** | ✅ 本次推送(§2.8 字段先定,wx 实现后接口可立即对接) | > **不在本 changelog 范围内**:§2.7 `GET /v3/internal/order/orders/{orderId}/travelers`(internal Feign 跨服务查出行人 + 解密审计)属于**后端 changelog 范畴**(受众=其他后端服务/运维,不是前端),将单独推到 `hl-backend-changelog` 仓库。 --- ## 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 模块管理后台 10 个接口,对应管理后台原型 F20 / F21 / F27(详情概览 + 出行人补全 + 接送站 + 大交通登记弹窗)。`§2.7 internal feign` 不在本文档范围(见 §0 说明)。 --- ## 2. 变更清单 | # | § | 接口名 | 方法 | 路径 | |---|---|--------|------|------| | 1 | 2.1 | 出行人列表 | GET | `/v3/admin/order/{id}/traveler/list` | | 2 | 2.2 | 出行人批量编辑(upsert) | POST | `/v3/admin/order/{id}/traveler/batch-edit` | | 3 | 2.3 | 单个出行人新增 | POST | `/v3/admin/order/{id}/traveler/add` | | 4 | 2.4 | 出行人软删 | POST | `/v3/admin/order/{id}/traveler/{travelerId}/delete` | | 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.8 ⏳ | 出行人智能批量解析 | POST | `/v3/admin/order/{id}/traveler/smart-parse` | | 10 | 2.9 | 出行人信息校验 | GET | `/v3/admin/order/{id}/traveler/validate` | > §2.7 internal feign 接口不在本表(属后端 changelog)。 --- ## 3. 接口详情 > 每个接口自包含:使用场景 / 入参 / 出参 / 错误码 / 业务边界 / 示例。 > 跨接口共享枚举集中在 §6。 --- ### 3.1 §2.1 出行人列表 **路径**:`GET /v3/admin/order/{id}/traveler/list` **使用场景**:详情页 Tab 1 出行人区块 / F27 出行人补全页 **认证**:JWT(admin 角色 + 公司隔离) **敏感字段**:`idNo` / `phone` / `emergencyPhone` **明文返回**(admin JWT 鉴权保护) #### 入参 `id` (path, Long) — 订单 ID #### 出参(`Result>`,每条 16 字段) | 字段 | 类型 | 说明 | |------|------|------| | `id` | String | 出行人 ID | | `orderId` | String | 订单 ID | | `travelerType` | String | 枚举见 §6.1(ADULT / CHILD / YOUNG_CHILD / BABY) | | `name` | String? | 姓名(占位行为 null) | | `gender` | String | 枚举见 §6.2(MALE / FEMALE / UNKNOWN) | | `birthday` | LocalDate? | 出生日期 | | `idType` | String | 枚举见 §6.3(ID_CARD / PASSPORT / BIRTH_CERT) | | `idNo` | String? | 证件号(admin 明文) | | `nationality` | String | 国籍(默认中国) | | `race` | String | 民族(默认汉族) | | `phone` | String? | 出行人手机(admin 明文) | | `emergencyContact` | String? | 紧急联系人姓名 | | `emergencyPhone` | String? | 紧急联系人电话(admin 明文) | | `roomGroupNo` | Integer? | 同住分组号 | | `profileStatus` | String | 枚举见 §6.4(PENDING / COMPLETED) | | `transportPlanIds` | List\ | 关联的大交通批次 ID 列表 | > ⚠️ **transportPlanIds 序列化规则**:后端字段类型 `List`,但 HL 全局 Jackson `NumberSerializer` 会自动把超过 JS 安全整数(2^53-1)的 Long 转 String 输出。大交通批次 ID 是雪花 ID(19 位),**实际响应中一定是 String 数组**;下面示例值 `80012345`(8 位短整数)仅为可读性目的,**生产值类似 `"80012345678901234"`**。前端按 String 数组接收。 #### 错误码 | code | 含义 | |------|------| | `581020` | 订单不存在 | | `581021` | 无权访问该订单(公司隔离) | #### 示例 **典型 - 请求**: ```http GET /v3/admin/order/60123456789012/traveler/list 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": ["80012345678901234", "80012345678901235"] }, { "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\ | ✅ | 待修改/新增出行人数组 | `@NotEmpty` `@Size(max=30)` | `TravelerEditItem`(12 字段): | 字段 | 类型 | 必填 | 说明 | |------|------|:----:|------| | `id` | Long | ❌ | 出行人 ID(null=新增 / 非 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`) | 字段 | 类型 | 说明 | |------|------|------| | `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}/traveler/add` **使用场景**: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`) 字段同 §3.1 列表项,含新建行完整字段。 #### 错误码 | code | 含义 | |------|------| | `581020` | 订单不存在 | | `581115` | 实际人数已等于 `order_main` 声明人数(v4.8 取消占位模型规则) | | `581116` | 订单状态禁止加人(已结算 / 已取消) | | `581117` | travelerType 与现有同住分组冲突 | #### 业务边界 - ✅ **创单后单独添加**:v4.8 取消占位模型,按需 INSERT 新行 - ⚠️ **人数同步**:自动 UPDATE `order_main.Count` +1 - ⚠️ **应用层数量检查**:实际人数 ≥ 声明人数 → `581115`,需要先调整人数声明再加人 #### 示例 **典型 - 请求**: ```http POST /v3/admin/order/60123456789012/traveler/add 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 出行人软删 **路径**:`POST /v3/admin/order/{id}/traveler/{travelerId}/delete` **使用场景**:F27 改人数场景(如取消同行 1 人) **关联**:同步 UPDATE `order_main` 对应人数 -1 + 解除其在 `order_transport_plan_traveler` 桥接表中所有关联 **约束**:**已签电子合同的订单禁止删人** #### 入参 | 字段 | 类型 | 必填 | 说明 | |------|------|:----:|------| | `id` | Long | ✅ | 订单 ID(path) | | `travelerId` | Long | ✅ | 出行人 ID(path) | #### 出参(`Result`) 返回 `true` 表示软删成功。 #### 错误码 | code | 含义 | |------|------| | `581020` | 订单不存在 | | `581110` | 出行人不属于该订单 | | `581106` | 已签电子合同,禁止删除出行人(DB 段位让位:581119 已被 `TRAVELER_ID_CARD_DUPLICATE` 占用,文档原 581119 让位至此) | | `581107` | 出行人是订单最后 1 位成人,禁止删除(DB 段位让位:581120 已被 `TRANSPORT_PLAN_NOT_FOUND` 占用,文档原 581120 让位至此) | #### 业务边界 - ✅ **软删**:UPDATE `order_traveler.deleted=1`,不真删 - ✅ **级联解除桥接**:UPDATE `order_transport_plan_traveler.deleted=1` - ❌ **已签合同禁删**:合同强约束 → `581106` - ❌ **最后成人禁删**:保留至少 1 位成人 → `581107` #### 示例 **典型 - 请求**: ```http POST /v3/admin/order/60123456789012/traveler/70123456789013/delete Authorization: Bearer {admin_jwt} ``` **典型 - 响应**: ```json { "code": 200, "data": true, "msg": "success" } ``` **异常(已签合同 581106) - 响应**: ```json { "code": 581106, "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>`) 每条 16 字段 + travelers 嵌套: | 字段 | 类型 | 说明 | |------|------|------| | `id` | String | 批次 ID | | `orderId` | String | 订单 ID | | `direction` | String | 枚举见 §6.5(ARRIVAL / DEPARTURE) | | `mode` | String | 枚举见 §6.6(TOGETHER / SEPARATE) | | `transportType` | String | 枚举见 §6.7(FLIGHT / 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\ | ✅ | 关联出行人 ID(至少 1 个) | | `remark` | String | ❌ | 备注(≤500) | #### 出参(`Result`) 字段同 §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 | ✅ | 订单 ID(path) | | `planId` | Long | ✅ | 批次 ID(path) | | Body | `TransportPlanReqVO` | ✅ | 同 §3.6 字段(全量覆盖语义) | #### 出参(`Result`) 字段同 §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 | ✅ | 订单 ID(path) | | `planId` | Long | ✅ | 批次 ID(path) | #### 出参(`Result`) 返回 `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.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 | ✅ | 订单 ID(path) | | `rawText` | String | ✅ | 粘贴的多行原文(≤ 20000 字符) | | `dryRun` | Boolean | ❌ | true=仅解析不入库(默认 false=解析+入库) | #### 出参(`Result`) | 字段 | 类型 | 说明 | |------|------|------| | `successCount` | Integer | 解析成功并入库的行数 | | `failCount` | Integer | 解析失败的行数 | | `successList` | List\ | 成功创建的出行人列表(结构同 §3.1);dryRun=true 时不含 id | | `failures` | List\ | 失败明细:`{lineIndex, maskedSnippet, reason}` | #### 错误码 | code | 含义 | |------|------| | `581131` | 原文超长(> 20000 字符)(DB 段位让位:原文档 581120 已被 `TRANSPORT_PLAN_NOT_FOUND` 占用) | | `581132` | 原文为空或全行无法解析(DB 段位让位:原文档 581121 改至此) | | `100501` | 限流触发(10 次/分钟)—— 走 HL 全局 `CommonErrorCode.RATE_LIMITED`,不在 traveler 段位 | | `581134` | 订单状态不允许批量导入(已结算 / 已取消)(DB 段位让位:原文档 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.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" } ``` **异常(限流 100501) - 响应**: ```json { "code": 100501, "data": null, "msg": "请求过于频繁,请稍后再试" } ``` --- ### 3.10 §2.9 出行人信息校验 **路径**:`GET /v3/admin/order/{id}/traveler/validate` **使用场景**:支付前 / 锁单前置校验(同 §1.8 confirm-checklist 的 `TRAVELER_COMPLETE` 项底层依赖) **校验维度**:(1) 实际出行人数 = 订单声明人数;(2) 每个出行人字段齐全(姓名+身份证+手机+证件类型) #### 入参 `id` (path, Long) — 订单 ID #### 出参(`Result`) | 字段 | 类型 | 说明 | |------|------|------| | `passed` | Boolean | 是否全部通过 | | `declaredCount` | Integer | 订单声明人数(含所有人群类型) | | `actualCount` | Integer | 实际 `order_traveler` 行数(未软删) | | `countMismatch` | Boolean | 人数是否不一致 | | `incompleteList` | List\ | 字段不完整的出行人 | `IncompleteTravelerVO`: | 字段 | 类型 | 说明 | |------|------|------| | `travelerId` | String | 出行人 ID | | `name` | String | 姓名(脱敏,如 `张*`) | | `missingFields` | List\ | 缺失字段名列表(如 `["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 missingFields(缺失字段名) **使用字段**:§3.10 出参 `incompleteList[].missingFields[]` 可能值(与 §3.1 字段名一致):`name` / `idType` / `idNo` / `phone` --- ## 11. 影响评估 - **是否破坏向后兼容**:否(v3 全新二期,前端 v3 项目仓库首次消费) - **前端是否必须同步上线**:是 - **本次推送范围**:§2 模块管理后台 10 接口(含 §2.8 smart-parse 字段先定,wx 实现后立即可对接)。§2.7 internal feign 走后端 changelog 仓库,不在本表。 --- ## 12. 注意事项 - **§2.8 smart-parse 待 wx 新建(Issue [#2526](https://git.1814.love:8443/wx/HL/issues/2526))**:前端可先按本文字段对接 UI,wx 实现完接口立即可调;接口字段已确定不会变 - **敏感字段明文返回**:§2.1 / §2.3 / §2.8 / §2.9 admin 接口返回 `idNo` / `phone` / `emergencyPhone` 明文,前端拿到后**不要**写本地 log / 不要塞 URL query;§2.9 `incompleteList[].name` 已脱敏 - **桥接表约束**:§3.6 / §3.7 大交通批次新增/编辑时同方向同一出行人只能在 1 个 plan(错则 `581143`) - **删人合同强约束**:§3.4 已签电子合同后禁止删人 → `581106` - **`/v3/admin/**` 公司隔离**:所有 admin 接口均带跨公司隔离校验,跨公司访问返 `581021`,前端无需自行过滤 --- ## 13. 关联 - **API 设计文档**: `docs/order-v3/api/API-SPEC-V5.50.html` §2.1 ~ §2.9(v5.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 字段口径) - **§2.7 internal feign 单独走后端 changelog**: 待推 `hl-backend-changelog/.../18_§2.7_traveler-internal-feign-新增接口.md`(受众=合同 / 保险服务) - **关联 Issue**:[#2517](https://git.1814.love:8443/wx/HL/issues/2517) ~ [#2527](https://git.1814.love:8443/wx/HL/issues/2527) 11 个(含 §2.7 [#2525](https://git.1814.love:8443/wx/HL/issues/2525) internal feign 单独走后端 changelog),全部 assign wx;§2.8 [#2526](https://git.1814.love:8443/wx/HL/issues/2526) 为 0→1 新建 - **后端负责人**: @yaosutu / 实施 @wx