hl-api-changelog/changelogs-v2/2026-05/18_2518_traveler-batch-edit.md
API Changelog Bot e9117da736 changelog(order-v3): 出行人 4 接口 (#2517/#2518/#2519/#2520)
- #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>
2026-05-18 19:31:03 +08:00

7.7 KiB

出行人批量编辑: 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)

请求示例

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
    }
  ]
}

响应示例

{
  "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 去重)

错误响应体示例:

{
  "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
  • 关联 PR: wx/HL#2535