17 KiB
schema, ticket, title, consumer, author, change_type, backend_status, gateway_status, frontend_status, frontend_owner, frontend_ref, target_release, verified_at, status_note, updated_at, base
| schema | ticket | title | consumer | author | change_type | backend_status | gateway_status | frontend_status | frontend_owner | frontend_ref | target_release | verified_at | status_note | updated_at | base |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| hl-changelog/v2 | 7972 | 接送机用车的免车闸与结算闸改为非对称判据 | admin | wx(GIT) | 修改接口 | deployed | not_required | not_required | 本次改动为后端行为调整(错误码判定条件变化),网关无需变更。gateway_status not_required 意味着网关侧无需部署(网关只转发,错误码处理由后端定义)。 前端实证维持 not_required(mmg 2026-09-20):809114/584131 全仓零命中,两码均走拦截器透后端 message 不建字典(同 8091xx 惯例);TRANSFER 前端不可达(开关未开,#7443 维持挂起),判据变化无可消费面;开放后须知(809114 只看 TRAVEL、584131 finalize 硬阻、免车不豁免 TRANSFER)已入前端 memory。 | 2026-09-20 | dev-v3 |
order-v3: 接送机用车的免车闸与结算闸改为非对称判据
存放目录: changelogs-v2/2026-09/
服务: hl-order-service-v3 PR: #7976 Issue: #7972 日期: 2026-09-20 影响范围: 管理后台团期用车需求编辑、整团确认、订单结算门禁
⚠️ 关键变化
免车判据从「两种需求(TRAVEL+TRANSFER)任一已进行就拒」改为「只看 TRAVEL,TRANSFER 独立判」。
- 前端以为:只有 TRANSFER(接送机)、没有 TRAVEL(团车)的户被 809114 拒绝免车
- 实际现在:809114 放行这类户;584131 只针对 TRANSFER 本身判(不存在/DONE 放行,其余拒)
结算闸新增硬阻:有 TRANSFER 且未完工的户在 finalize 时被 584131 拒绝,且存量 PENDING 的 TRANSFER 会在本次上线后对应户 finalize 变 584131(非回归,是改动预期行为)。
一、背景
只订接送机、不订团车 是合法的在团户(存在真实订单),但被原判据的两支 OR 条件挡住。 免车意在「本团无需用车」,而只有 TRANSFER 的户仍有接送机需求,整团免车不能替接送机放行。 改为非对称判据后:整团免车只豁免 TRAVEL,接送机必须逐户推进到 DONE。
二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|---|---|---|---|---|
| 1 | 保存团期用车需求 | PUT | /admin/group-batch/{groupBatchId}/vehicle-requirement |
行为变化 | 809114 放宽(不挡仅 TRANSFER 户) |
| 2 | 整团确认 | POST | /admin/group-batch/{groupBatchId}/vehicle-requirement/confirm |
行为变化 | 同上 |
| 3 | 订单结算 | POST | /admin/order/{orderId}/submit |
行为变化 | 新增 584131 拒绝条件 |
| 4 | 读团期用车需求 | GET | /admin/group-batch/{groupBatchId}/vehicle-requirement |
行为变化 | 只读,无副作用 |
三、接口详情
1. 保存团期用车需求 PUT /admin/group-batch/{groupBatchId}/vehicle-requirement
VO: GroupVehicleRequirementSaveReqVO → GroupVehicleRequirementRespVO
使用场景
管理员在团期用车需求编辑页保存草稿或点「整团免车」。 本次改动只影响点「整团免车」按钮时的 809114 门禁判定:只有 TRANSFER(接送机)、没有 TRAVEL(团车)的户现在能通过。
入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| groupBatchId | Path | Long | ✅ | - | 团期聚合主键 |
| reason | Body | String | ✅ | 不含空格、不超 200 字 | 免车原因(写入备注留痕) |
| operatorId | 上下文 | String | ✅ | 取决于登录用户 | 操作人 ID(管理员工号) |
出参 Result<GroupVehicleRequirementRespVO>
| 字段 | 类型 | 说明 |
|---|---|---|
| requirementId | Long | 正式需求聚合主键 |
| status | String | 状态(DRAFT/CONFIRMED/DISPATCHED/DONE) |
| groups | Array | 乘车分组列表,免车声明时为空数组 |
| version | Integer | 乐观锁版本号 |
| confirmedBy | String | 确认人 ID(免车时为当前操作人) |
| confirmedAt | LocalDateTime | 确认时刻 |
请求示例
{
"reason": "只订接送机,不需要团车"
}
响应示例
{
"code": 200,
"message": "成功",
"data": {
"requirementId": 7200000000000001,
"groupBatchId": 7100000000000001,
"status": "CONFIRMED",
"groups": [],
"version": 2,
"confirmedBy": "admin001",
"confirmedAt": "2026-09-20 10:30:00",
"remark": "[2026-09-19 15:22:00 撤回] 改房型; [2026-09-20 10:30:00 免车] 只订接送机,不需要团车"
},
"success": true
}
空数据 / 降级响应
无(必返回需求对象)。
错误响应
{
"code": 809114,
"message": "车务已开工(子订单 5100000000000001:PROCESSING),不能再声明整团免车",
"success": false,
"data": null
}
业务边界
- 809114 新判据(仅 TRAVEL 分支):检查全部在团户的 active TRAVEL(非 TRANSFER)需求,若任一已进入 PENDING/PROCESSING/DONE,整团免车被拒
- TRANSFER 不挡免车:即使某户有 active TRANSFER 处于上述状态,809114 不再拦住,转由结算闸(584131)独立硬阻
- 幂等:整团已经是免车态(零分组已确认版本)时重复点免车返回 200,不抛错、不推版本
- 无副作用:本端点不调 fleet,不修改户级需求状态,只操作团期层面的正式需求聚合根
2. 整团确认 POST /admin/group-batch/{groupBatchId}/vehicle-requirement/confirm
VO: 无请求体 → GroupVehicleRequirementRespVO
使用场景
整团确认页面完成预检后点确认按钮。整团确认作为保存需求的上游,同样需要过 809114 门禁(只看 TRAVEL)。
入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| groupBatchId | Path | Long | ✅ | - | 团期聚合主键 |
出参 Result<GroupVehicleRequirementRespVO>
| 字段 | 类型 | 说明 |
|---|---|---|
| requirementId | Long | 正式需求聚合主键 |
| status | String | 状态(DRAFT/CONFIRMED/DISPATCHED/DONE) |
| groups | Array | 乘车分组列表 |
| version | Integer | 乐观锁版本号 |
| confirmedBy | String | 确认人 ID |
| confirmedAt | LocalDateTime | 确认时刻 |
请求示例
{}
响应示例
{
"code": 200,
"message": "成功",
"data": {
"requirementId": 7200000000000001,
"status": "CONFIRMED",
"groups": [
{
"groupCode": "BUS",
"memberCount": 4,
"days": [
{ "tripDate": "2026-10-01", "headcount": 4 }
]
}
],
"version": 1,
"confirmedBy": "admin002",
"confirmedAt": "2026-09-20 11:00:00"
},
"success": true
}
空数据 / 降级响应
无。
错误响应
{
"code": 809114,
"message": "车务已开工(子订单 5100000000000002:DONE),不能再声明整团免车",
"success": false
}
业务边界
- 与保存草稿共用同一把团级锁:concurrency 写入时后到者抛 809102
- 状态转移:确认后正式需求进入 CONFIRMED,版本 +1
3. 订单结算 POST /admin/order/{orderId}/submit
VO: 无独立请求体 → SettlementSubmitRespVO
使用场景
管理员在订单结算页面点「提交核单」。本次改动新增 584131 硬阻:有 TRANSFER(接送机)且未完工的户无法 finalize。
入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| orderId | Path | Long | ✅ | - | 子订单 ID |
出参 Result<SettlementSubmitRespVO>
| 字段 | 类型 | 说明 |
|---|---|---|
| orderId | Long | 子订单 ID |
| status | String | finalize 后的订单状态 |
请求示例
{}
响应示例
{
"code": 200,
"message": "成功",
"data": {
"orderId": 5100000000000001,
"status": "SETTLED"
},
"success": true
}
空数据 / 降级响应
无。
错误响应(新增 584131)
{
"code": 584131,
"message": "接送机:PENDING(团期已声明整团免车,跳过用车户级 TRAVEL 闸;但接送机未安排完成)",
"success": false
}
业务边界
新增判据(584131,非对称):
- TRAVEL 分支:若整团已声明免车(zero-group CONFIRMED 版本),TRAVEL 判定被短路,跳过需求检查
- TRANSFER 分支(独立):
- 不存在 active TRANSFER(未订接送机)→ 放行
- TRANSFER 状态 = DONE → 放行
- TRANSFER 状态 = PENDING/PROCESSING/其他 → 硬阻 584131,不受整团免车影响
存量数据行为:production 中停在 PENDING 的 TRANSFER 需求行(9 条已于 2026-09-19 走真实端点 reject 清掉),所属户的 finalize 在本次上线后会从 200 变 584131——这是本次改动的预期行为,不是回归。
4. 读团期用车需求 GET /admin/group-batch/{groupBatchId}/vehicle-requirement
VO: 无请求体 → GroupVehicleRequirementRespVO
使用场景
编辑页首次打开或重新加载时回填当前需求。本接口是配车刷新状态在 admin 侧唯一的无副作用观测口(#7988),不取锁、不进事务、无任何写操作。
入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| groupBatchId | Path | Long | ✅ | - | 团期聚合主键 |
出参 Result<GroupVehicleRequirementRespVO>
| 字段 | 类型 | 说明 |
|---|---|---|
| requirementId | Long | 正式需求聚合主键 |
| status | String | 状态(DRAFT/CONFIRMED/DISPATCHED/DONE) |
| groups | Array | 乘车分组列表 |
| version | Integer | 乐观锁版本号 |
| confirmedBy | String | 确认人 ID |
| confirmedAt | LocalDateTime | 确认时刻 |
请求示例
{}
响应示例
{
"code": 200,
"message": "成功",
"data": {
"requirementId": 7200000000000001,
"status": "CONFIRMED",
"groups": [],
"version": 2,
"confirmedBy": "admin001",
"confirmedAt": "2026-09-20 10:30:00",
"planRefreshStatus": "FAILED",
"planRefreshFailReason": "fleet timeout"
},
"success": true
}
空数据 / 降级响应
{
"code": 200,
"message": "成功",
"data": null,
"success": true
}
错误响应
{
"code": 589500,
"message": "团期不存在",
"success": false,
"data": null
}
业务边界
- 零写入:不锁、不改库、不进事务、不调 fleet、不推版本
- 存在性:返回 null 表示「尚未形成正式需求」(正常),不区分「本该有但丢了」
四、契约约束与正确调用方式
✅ 正确 / ❌ 错误 payload 对照
| 场景 | 端点 | payload | 结果 |
|---|---|---|---|
| ✅ 仅 TRANSFER,免车通过 | PUT /admin/group-batch/{id}/vehicle-requirement |
{"reason":"只订接送机"} |
200,状态 CONFIRMED,groups=[] |
| ✅ 仅 TRAVEL,整团确认通过 | POST /admin/group-batch/{id}/vehicle-requirement/confirm |
(无体) | 200,状态 CONFIRMED,groups=[{BUS}] |
| ❌ 仅 TRANSFER,TRAVEL 已 PROCESSING,免车拒绝 | PUT /admin/group-batch/{id}/vehicle-requirement |
{"reason":"..."} |
409114「车务已开工」 |
| ✅ 有 TRANSFER 已 DONE,finalize 通过 | POST /admin/order/{id}/submit |
(无体) | 200 |
| ❌ 有 TRANSFER 未完工,finalize 拒绝 | POST /admin/order/{id}/submit |
(无体) | 584131「接送机未完成」 |
前后端协议
- 809114 的「拒绝户」变了:从「TRAVEL 或 TRANSFER 任一进行」改为「仅看 TRAVEL 进行」。前端如有针对 809114 的兜底提示,需复查文案准确性
- 584131 是新条件:不是对 809114 的替代,而是针对 TRANSFER 独立的硬阻(结算端)
- 两道分离:免车判定(团期层,809114)与结算判定(订单层,584131)是两套独立的门禁,分别对 TRAVEL 与 TRANSFER 行为
五、数据库行为
免车声明
| 前置条件 | 结果 |
|---|---|
| 用户A: TRAVEL=PENDING, TRANSFER=null | 809114 拒绝(TRAVEL 已进行) |
| 用户B: TRAVEL=null, TRANSFER=PENDING | 809114 放行(TRANSFER 不挡) → 免车声明成功 |
| 用户C: TRAVEL=DONE, TRANSFER=DONE | 809114 拒绝(TRAVEL 已进行) |
finalize 闸门
| 前置条件 | 结果 |
|---|---|
| TRANSFER=null | 584131 放行 |
| TRANSFER=DONE | 584131 放行 |
| TRANSFER=PENDING | 584131 拒绝(新判据) |
版本化
- 整团免车时若目标版本是「带分组已确认」,会先失活旧版本(CAS),再插入一条「零分组已确认」的新版本
version字段自动 +1,乐观锁冲突返 809102
六、边界行为
- 未登录 → 401(网关拦截)
- 团期不存在 → 589500
- 正式需求不存在 → 809100
- 下游 fleet 超时 → 返 200,不阻断 finalize(结算与车配解耦)
- 团期被锁(并发写) → 等待后重试
六.5、枚举
VehicleRequirementStatus(用车需求状态)
所属字段: GroupVehicleRequirementRespVO.status | 类型: String
| 值 | 中文 | 说明 |
|---|---|---|
DRAFT |
草稿 | 编辑中 |
CONFIRMED |
已确认 | 整团确认/免车声明后 |
DISPATCHED |
已派发 | fleet 配车完成 |
DONE |
已完成 | 订单 finalize 后 |
PENDING_RECONFIRM |
待重确认 | 被打回重新编辑后的中间态 |
VehicleRequirementKind(需求类型)
所属字段: 内部枚举,不直接暴露给前端 | 类型: String
| 值 | 中文 | 说明 |
|---|---|---|
TRAVEL |
团车 | 整团用车需求 |
TRANSFER |
接送机 | 机场接送 |
六.6、修改前后对比
行为级对比
| 行为 | 改前 | 改后 |
|---|---|---|
| 809114 判定 | 检查 TRAVEL 和 TRANSFER,任一进行则拒 | 只检查 TRAVEL,TRANSFER 不挡 |
| 接送机只户的免车 | 被 809114 拒绝 | 809114 放行(若 TRANSFER 未完工则被 584131 拒) |
| finalize 对 TRANSFER | 无专项检查 | 新增 584131:不存在/DONE 放行,其余拒 |
| 整团免车对 TRANSFER | 整户豁免 | 不豁免,TRANSFER 仍需逐户到 DONE |
| flow 推进(行程清单) | 只看 TRAVEL | 追加 TRANSFER 检查:即使整团免车也要看 TRANSFER |
六.7、影响评估
- 是否破坏向后兼容:是(行为变化)
- 前端是否必须同步上线:否(后端改动前兼容,后端改动后错误码体验改善但不影响流程)
- 前端 workaround 清理点:
- 若有针对 809114「TRANSFER 已进行」的兜底提示,改成只提「TRAVEL 已进行」
- 若有假设「整团免车豁免一切需求」的逻辑,补充 TRANSFER 的检查(与结算闸 584131 同口径)
七、不影响范围
- 仅影响:管理后台团期车需求编辑、整团确认、订单结算三处门禁
- 零影响:
- 小程序(MP 端点)
- 户级用车需求的创建/更新(只改 validation 判定,不改流程)
- 价格、保险、酒店等其他模块
- 一期业务(dev 分支无此改动)
八、测试环境已验证
部署版本: 8cdb9ad97(包含提交 572dc4037)
✓ PUT /admin/group-batch/7100000000000001/vehicle-requirement
{"reason":"只订接送机"}
→ 200 + status=CONFIRMED + groups=[]
✓ POST /admin/group-batch/7100000000000001/vehicle-requirement/confirm
→ 200 + CONFIRMED
✓ POST /admin/order/5100000000000001/submit
(TRANSFER=DONE)
→ 200
✓ POST /admin/order/5100000000000002/submit
(TRANSFER=PENDING)
→ 584131 (新判据)
✓ GET /admin/group-batch/7100000000000001/vehicle-requirement
→ 200 + 当前需求
九、相关历史 PR
| PR | Issue | 说明 | 是否仍有效 |
|---|---|---|---|
| #7763 | #7441 PR-3 | 首次引入 584131 硬拦「缺团车需求」 | ✅ 有效(本次改动收紧范围,只看 TRANSFER) |
| #8033 | #7988 | 补只读观测口(本端点) | ✅ 有效 |
| 本 PR #7976 | #7972 | 收口 809114/584131 的 kind 口径,非对称判定 | ✅ 最新 |
十、相关文档
- 关联 Issue: wx/HL#7972
- 关联 PR: wx/HL#7976
- Merge commit: wx/HL@572dc4037
关联 / 联系人
链接
联系人
- 后端负责人: @wx