QA 验收发现 §2 changelog 写的 4 个 traveler 接口路径与 hl-order-service-v3 实际 Controller 不一致:
| § | 原 changelog | 实际 Controller |
|---|---|---|
| 2.1 列表 | GET /travelers | GET /traveler/list |
| 2.3 新增 | POST /travelers | POST /traveler/add |
| 2.4 软删 | DELETE /travelers/{id} | POST /traveler/{id}/delete |
前端按 changelog 调会 404/405。本次修 changelog 对齐实际 Controller,保留动词式风格。
关联:HL Issue #2533 / PR #2534(QA 暴露)
Co-Authored-By: Claude <noreply@anthropic.com>
32 KiB
【新增接口·管理后台】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<List<TravelerVO>>,每条 17 字段)
| 字段 | 类型 | 说明 |
|---|---|---|
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<Long> | 关联的大交通批次 ID 列表 |
错误码
| code | 含义 |
|---|---|
581020 |
订单不存在 |
581021 |
无权访问该订单(公司隔离) |
示例
典型 - 请求:
GET /v3/admin/order/60123456789012/travelers
Authorization: Bearer {admin_jwt}
典型 - 响应:
{
"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 | ❌ | 出行人 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<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信号:所有出行人字段齐 → 触发"已完善"系统标签 / 进入下一阶段
示例
典型 - 请求:
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"
}
]
}
典型 - 响应:
{
"code": 200,
"data": {
"createdCount": 1,
"updatedCount": 1,
"completedCount": 1,
"pendingCount": 1,
"allCompleted": false
},
"msg": "success"
}
异常(证件号格式非法 581101) - 响应:
{ "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<TravelerVO>)
字段同 §3.1 列表项,含新建行完整字段。
错误码
| code | 含义 |
|---|---|
581020 |
订单不存在 |
581115 |
实际人数已等于 order_main 声明人数(v4.8 取消占位模型规则) |
581116 |
订单状态禁止加人(已结算 / 已取消) |
581117 |
travelerType 与现有同住分组冲突 |
业务边界
- ✅ 创单后单独添加:v4.8 取消占位模型,按需 INSERT 新行
- ⚠️ 人数同步:自动 UPDATE
order_main.<type>Count+1 - ⚠️ 应用层数量检查:实际人数 ≥ 声明人数 →
581115,需要先调整人数声明再加人
示例
典型 - 请求:
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
}
典型 - 响应:
{
"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) - 响应:
{ "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<Boolean>)
返回 true 表示软删成功。
错误码
| code | 含义 |
|---|---|
581020 |
订单不存在 |
581110 |
出行人不属于该订单 |
581119 |
已签电子合同,禁止删除出行人 |
581120 |
出行人是订单最后 1 位成人,禁止删除 |
业务边界
- ✅ 软删:UPDATE
order_traveler.deleted=1,不真删 - ✅ 级联解除桥接:UPDATE
order_transport_plan_traveler.deleted=1 - ❌ 已签合同禁删:合同强约束 →
581119 - ❌ 最后成人禁删:保留至少 1 位成人 →
581120
示例
典型 - 请求:
POST /v3/admin/order/60123456789012/traveler/70123456789013/delete
Authorization: Bearer {admin_jwt}
典型 - 响应:
{ "code": 200, "data": true, "msg": "success" }
异常(已签合同 581119) - 响应:
{ "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.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 |
无权访问该订单 |
示例
典型 - 请求:
GET /v3/admin/order/60123456789012/transport-plans
Authorization: Bearer {admin_jwt}
典型 - 响应:
{
"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) - 请求:
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": "需要接机举牌"
}
典型 - 响应:
{
"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) - 响应:
{ "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<TransportPlanVO>)
字段同 §3.5。
错误码
| code | 含义 |
|---|---|
581140 ~ 581143 |
同 §3.6 字段组合校验 |
581144 |
plan 不存在 / 不属于该订单 |
业务边界
- ⚠️ 全量覆盖语义:所有 Req 字段都会替换原值,包括
travelerIds - ⚠️ 桥接表重建:旧
order_transport_plan_traveler行 DELETE,按新travelerIdsINSERT
示例
典型 - 请求:
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": "改签后的航班"
}
典型 - 响应:
{
"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<Boolean>)
返回 true 表示软删成功。
错误码
| code | 含义 |
|---|---|
581144 |
plan 不存在 / 不属于该订单 |
示例
典型 - 请求:
DELETE /v3/admin/order/60123456789012/transport-plans/80012345
Authorization: Bearer {admin_jwt}
典型 - 响应:
{ "code": 200, "data": true, "msg": "success" }
3.9 §2.8 出行人智能批量解析 ⏳
路径:POST /v3/admin/order/{id}/traveler/smart-parse
使用场景:F27 出行人补全 / 一键导入弹窗。定制师粘贴多行文本(姓名+证件号+手机号),后端解析为结构化出行人列表,校验后批量入库
状态:⏳ wx 待实现(Issue #2526)。本节字段先定,wx 实现完即可对接
入参(TravelerSmartParseReqVO)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
id |
Long | ✅ | 订单 ID(path) |
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+ UPDATEorder_main.<type>Count同事务
示例
典型 - 请求:
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
}
典型 - 响应:
{
"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) - 响应:
{ "code": 581122, "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<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
示例
典型(未通过) - 请求:
GET /v3/admin/order/60123456789012/traveler/validate
Authorization: Bearer {admin_jwt}
典型(未通过) - 响应:
{
"code": 200,
"data": {
"passed": false,
"declaredCount": 3,
"actualCount": 2,
"countMismatch": true,
"incompleteList": [
{"travelerId": "70123456789013", "name": "张*", "missingFields": ["idNo", "phone"]}
]
},
"msg": "success"
}
典型(通过) - 响应:
{
"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):前端可先按本文字段对接 UI,wx 实现完接口立即可调;接口字段已确定不会变
- 敏感字段明文返回:§2.1 / §2.3 / §2.8 / §2.9 admin 接口返回
idNo/phone/emergencyPhone明文,前端拿到后不要写本地 log / 不要塞 URL query;§2.9incompleteList[].name已脱敏 - 桥接表约束:§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.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 ~ #2527 11 个(含 §2.7 #2525 internal feign 单独走后端 changelog),全部 assign wx;§2.8 #2526 为 0→1 新建
- 后端负责人: @yaosutu / 实施 @wx