- #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>
7.5 KiB
7.5 KiB
出行人新增: POST /traveler/add 路径迁移 + 真实业务化
服务: hl-order-service-v3 (端口 8084 / 二期) PR: #2536 Issue: #2519 日期: 2026-05-18 影响范围: 管理后台 F27 改人数场景(临时加 1 个出行人) 存放目录:
changelogs-v2/2026-05/(二期 v3 专属,带 -v2 后缀) 部署 commit: dev-v316e861a7d测试服已验证: ✅(/@qa 通过 9443 网关 + 真 admin token round-trip)
⚠️ 关键变化
- 破坏性路径迁移: 老
POST /v3/admin/order/{id}/travelers→ 新POST /v3/admin/order/{id}/traveler/add(对齐 V5.48 §2.3 命名规范) - 从 Mock 占位变真实业务: 三道校验 + INSERT + UPDATE order_main 人数 + INSERT order_status_log,而不是直接 insert 不校验。
- 新增 4 个错误码: 581115 / 581116 / 581117 / 581118
- 加幂等 @Idempotent(3s) + 分布式锁 @Lock4j(30s): 防止与 batch-edit / 自身重复提交并发,导致 order_main 人数计数 race。
一、背景
文档 V5.48 §2.3 定义: 当订单实际出行人数 > 创单声明人数(典型场景: 客户带了未声明的孩子)时,定制师 B 端临时加人。后端必须同步 UPDATE order_main.<type>Count,且不允许已结算 / 已取消 / 退款中的订单加人(避免影响财务封账与退款冲账)。
二、变更接口清单
| # | 接口 | 方法 | 老路径 | 新路径 | 变更类型 |
|---|---|---|---|---|---|
| 1 | 单个出行人新增 | POST | /v3/admin/order/{id}/travelers |
/v3/admin/order/{id}/traveler/add |
破坏性路径迁移 + 真实业务化 |
三、接口详情
1. 单个出行人新增 POST /v3/admin/order/{id}/traveler/add
VO: TravelerCreateReqVO(请求体) / TravelerVO(响应,16 字段)
入参
| 字段 | 位置 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
id |
Path | Long | ✅ | 订单 ID |
travelerType |
Body | String | ✅ | ADULT / CHILD / YOUNG_CHILD / BABY |
name |
Body | String | ❌ | 姓名(可后填) |
gender |
Body | String | ❌ | MALE / FEMALE / UNKNOWN |
birthday |
Body | LocalDate | ❌ | 出生日期 |
idType |
Body | String | ❌ | ID_CARD / PASSPORT / BIRTH_CERT |
idNo |
Body | String | ❌ | 证件号(明文传,DB 加密) |
nationality |
Body | String | ❌ | 国籍(默认"中国") |
race |
Body | String | ❌ | 民族(默认"汉族") |
phone |
Body | String | ❌ | 出行人手机(明文传) |
emergencyContact |
Body | String | ❌ | 紧急联系人姓名 |
emergencyPhone |
Body | String | ❌ | 紧急联系人电话 |
roomGroupNo |
Body | Integer | ❌ | 同住分组号 |
出参 Result<TravelerVO>
与 §2.1 列表接口完全相同的 16 字段 VO(含 id / orderId / travelerType / name / gender / birthday / idType / idNo / nationality / race / phone / emergencyContact / emergencyPhone / roomGroupNo / profileStatus / transportPlanIds)。
请求示例
POST /v3/admin/order/60123456789012/traveler/add
Authorization: Bearer {admin_jwt}
{
"travelerType": "CHILD",
"name": "王小明",
"gender": "MALE",
"birthday": "2018-06-20",
"idType": "BIRTH_CERT",
"idNo": "J012345678",
"nationality": "中国",
"race": "汉族",
"roomGroupNo": 1
}
响应示例
{
"code": 200,
"data": {
"id": 70123456789014,
"orderId": 60123456789012,
"travelerType": "CHILD",
"name": "王小明",
"gender": "MALE",
"birthday": "2018-06-20",
"idType": "BIRTH_CERT",
"idNo": "J012345678",
"nationality": "中国",
"race": "汉族",
"phone": null,
"emergencyContact": null,
"emergencyPhone": null,
"roomGroupNo": 1,
"profileStatus": "COMPLETED",
"transportPlanIds": []
},
"msg": "success"
}
错误响应清单(本工单新增段位)
| 错误码 | 含义 |
|---|---|
581100 |
出行人主段位—— 订单不存在 |
581102 |
订单不存在,无法编辑出行人 |
581115 |
实际人数已等于声明人数,请先调整订单人数再新增出行人 |
581116 |
当前订单状态禁止新增出行人(已结算 / 已取消 / 退款中) |
581117 |
出行人类型与现有同住分组冲突 |
581118 |
新增出行人缺少必填字段(travelerType 必填) |
错误响应体示例:
{
"code": 581116,
"msg": "当前订单状态禁止新增出行人(已结算 / 已取消 / 退款中)",
"data": null
}
四、契约约束
校验顺序(任一失败整事务回滚)
- 订单存在(581102)
- 订单状态白名单: 拒绝 SETTLED / CANCELLED / REFUNDING(581116)
- 实际 vs 声明人数:
count(order_traveler 未软删) >= sum(adultCount + childCount + youngChildCount + babyCount)时拒绝(581115,要求先调订单人数) - 同住分组冲突(581117,可选字段校验)
- 必填: travelerType(581118)
并发控制
@Idempotent(timeout=3s): 同订单 3 秒重复提交直接拒绝@Lock4j(expire=30000ms,keys="order:traveler:add:" + #id): 同订单 30 秒锁,防与 batch-edit 并发导致 order_main 人数计数 race
五、数据库行为
- INSERT 1 行
order_traveler(派生profile_status) - UPDATE
order_main.<type>Count+1(按travelerType选择: adultCount / childCount / youngChildCount / babyCount) - INSERT 1 行
order_status_log,reason="新增出行人",flowStatus 不变(from == to)
| travelerType | order_main 字段 | delta |
|---|---|---|
| ADULT | adult_count |
+1 |
| CHILD | child_count |
+1 |
| YOUNG_CHILD | young_child_count |
+1 |
| BABY | baby_count |
+1 |
六、边界行为
- 未登录 → 401(网关拦截)
- 订单不存在 → 581102
- 订单 status = SETTLED / CANCELLED / REFUNDING → 581116(优先于人数校验)
- 实际人数已等于声明 → 581115(典型场景: 客户原本说 2 大 1 小现已 2 大 1 小,要先把声明改成 2 大 2 小再加人)
- travelerType 未识别 → 防御性 return(不报错也不更新人数,实际由前置 @NotBlank 拦截)
七、不影响范围
- 仅影响: 管理后台 F27 改人数(新增)场景
- 零影响:
- 批量编辑
/traveler/batch-edit(#2518 独立接口) - 软删
/traveler/{travelerId}/delete(#2520 独立接口) - 列表
/traveler/list(只读) - mp 端出行人接口
- 老路径
POST /travelers已下线,前端调用必须改用/traveler/add
- 批量编辑
八、测试环境已验证
POST https://web.test.1814.love:9443/v3/admin/order/60123456789012/traveler/add
Body: {"travelerType":"CHILD","name":"王小明","birthday":"2018-06-20",...}
→ 200 + TravelerVO(含 id / profileStatus / transportPlanIds) ✓
→ order_main.child_count +1 ✓
→ order_status_log 新增 1 行 reason=新增出行人 ✓
反例:
- 订单 status=SETTLED → 581116 ✓
- 实际人数 = 声明人数 → 581115 ✓
- 老路径 POST /travelers → 404 ✓
九、相关历史 PR
| PR | Issue | 说明 | 是否仍有效 |
|---|---|---|---|
| #1984 | - | [§2 traveler skeleton] 空骨架 Mock | ❌ 被本 PR 真实化覆盖 |
| #2535 | #2518 | batch-edit 真实业务化(同期工单,共用 581100-581114 段位) | ✅ 有效 |
| 本 PR #2536 | #2519 | add 接通真实业务 + 路径迁移 + 4 错误码(581115-581118) | ✅ 最新 |
十、相关文档
- API SPEC:
D:/work2/HL-v3/docs/order-v3/api/API-SPEC-V5.48.html§2.3 - 关联 Issue: wx/HL#2519
- 关联 PR: wx/HL#2536