文件
hl-api-changelog/changelogs-v2/2026-09/20_7964_订单取消同事务失活用车需求-内部接口-修复-管理后台.md
T

9.4 KiB
原始文件 Blame 文件历史

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 7964 订单取消同事务失活用车需求,已取消户不再出现在 fleet 待配车池(无字段变更) internal jw(GIT) 修复 deployed not_required not_required 订单取消链路此前只给 fleet 发「取消派车」命令、不碰 order 侧需求行,已取消/已退团户的 order_vehicle_requirement 仍是 is_active=1 / status=PENDING,继续出现在待配车池里——车管会给一个已经退团的客人排车。现改为在订单取消事务内同步失活该单全部活跃用车需求(两个 kind、任意 status 一并失活)。没有新增、修改、删除任何端点,请求与响应字段全不变,故 change_type 取「修复」。⚠️ 对 fleet 侧有两处行为影响,需知会:①待配车池 GET /v3/internal/order/vehicle-requirements 的返回集合变小;②fleet 对已取消单的三个回写入口(status / reopen-after-assignment-cancel / vehicle-assignment/callback)行为经本单专门保护,仍是原先的「静默跳过 / ORDER_CANCELLED_IGNORED」,不会因失活而改抛 582084。后端已合并 dev-v3(e179e09bd)并部署 TEST,三个入口与待配车池均实测通过(工单 #7964)。网关无路由与接口变更,故 gateway_status 取 not_required;本单只改内部端点行为,前端无感知。 前端实证维持 not_required(mmg 2026-09-20):internal 端点不经网关前端不可达,fleet 看板待配车池行变少纯数据面自动受益,三回写入口行为不变。 2026-09-20 dev-v3

order-v3: 订单取消同事务失活用车需求,已取消户不再进 fleet 待配车池

服务: hl-order-service-v3 (端口 8086) PR: #8038 / #8044 Issue: #7964 日期: 2026-09-20 影响范围: fleet 侧待配车需求池与三个用车需求回写入口(内部接口,不经网关)


⚠️ 关键变化

  • 本单没有接口结构变化:没有新增、修改、删除端点,请求参数与响应字段全部不变。
  • 待配车池返回集合变小:GET /v3/internal/order/vehicle-requirements 不再返回已取消 / 已退团订单的需求。此前这些行一直留在池子里,车管会给已经退团的客人排车。
  • 两个 kind、任意 status 一并失活:TRAVEL 与 TRANSFER 都是逐户需求,订单取消时一起失活;PROCESSING / DONE 的行同样失活(订单都取消了,处在哪一态都不该再被派车)。
  • fleet 的三个回写入口行为不变:本单专门保护了它们,对已取消单仍是原先的「静默跳过」/ ORDER_CANCELLED_IGNORED,不会因为需求被失活而改抛 582084。
  • 存量已订正:TEST 库 15 行历史脏数据已随本单清理。

一、行为说明

改前

订单取消(含退团审批、管理员取消、C 端取消、流团、超时未支付、状态机直推等全部九条入口)只做两件事:把订单流转 CANCELLED,并给 fleet 发一条 CANCEL_ACTIVE_BY_REQUIREMENT 取消派车命令。

order 侧的 order_vehicle_requirement 行一个字段都不动:仍是 is_active=1、active_kind 非空、status 保持原值。而待配车池只按 status + requirement_kind + is_active=TRUE 过滤、不 join 订单状态,于是这些行继续出现在 fleet 拉取的池子里。

后果:①车管看到一条本不该存在的待派车;②真派了就会给退团客人占用车辆与司机;③派车行已经 exception 了,需求还 PENDING,两侧状态不一致。

改后

在订单取消事务内(OrderCancelledFleetListener#beforeCommit,全部九条取消入口的唯一漏斗)为每条活跃用车需求:先入队 fleet 取消派车命令,再把该需求行本地失活 —— is_active=0 与 active_kind=NULL 在同一条 UPDATE 里写齐(分两条会在中间态违反 ck_vehicle_requirement_active_kind)。

修的是源头,不是给池子加一层 join 过滤 —— 加过滤只会把不一致藏起来,需求本身仍是活跃态。


二、生效范围

维度 范围
取消入口 全部九条(管理员出行前取消 / C 端本人取消 / C 端删除订单 / 退团审批通过 / 流团已付款户 / 流团待付款户 / 超时未支付 / 管理员状态机直推 CANCEL / E2E 场景推进)
需求类别 TRAVEL 与 TRANSFER 都失活
需求状态 不按 status 过滤,PENDING / PROCESSING / DONE / PENDING_REVIEW 等一律失活
事务 与订单取消同事务;失活失败会回滚整个取消
表 order_vehicle_requirement(无表结构变更、无 Flyway)

三、边界

  • 失活写口不带 CAS / is_active 前置:订单状态机没有从 CANCELLED 出去的边,取消是终态,不存在「期望态被并发改掉所以放弃」这回事;不带前置也是幂等的前提。
  • 返回值只能当留痕:该 UPDATE 每次都刷 update_time,MySQL 的 affected rows 是「逐列比对后确实变化的行数」,所以重放一条早已失活的行返的是 1 而不是 0。返 0 只发生在「行不存在」或「所有列含 update_time 逐列相同」。不要把 rows == 0 当成「重放」的判据。
  • 行中终止(TERMINATED)不在范围:它走 enqueueTerminationTruncate 按终止日截断,且发的是 OrderTransitionedEvent 而不是 OrderCancelledEvent,需求不整条失活。
  • 转期不在范围:GroupBatchTransferService 自己换版,且明确不发 OrderCancelledEvent。
  • 房务侧 HotelRequirement 未查:本单只查了用车侧,不要把结论推广过去;若存在同类问题另起工单。
  • 反向路径确认不存在:订单状态机无 from(CANCELLED) 出边,全仓无 restore*Order / uncancel / revertCancel / reopenOrder 方法,所以不存在「订单恢复了、需求还躺在失活态」的镜像问题。团级配车释放的反向回写(reopenGroupVehicleRequirementsAfterGroupRelease)不会把失活行拉活:它的取锁读自带 is_active=TRUE 守卫,失活行直接跳过,且全程只改 status。

四、测试环境已验证

TEST dev-v3 @ 5d14bc524(行为)与 e179e09bd(语义订正),构建身份探针 BEHIND 0/N。

项 实测
退团审批后离开池子 池子 107 → 105,恰好减该户两条需求
阳性对照 退团前两条需求确在池子里;另有存量对照:8 条陈旧行逐条比对 8/8 都在池子里
不误伤同团其它户 对照户四次快照始终 is_active=1 且始终在池子里
两 kind TRAVEL + TRANSFER 同一次取消中一起失活
非 PENDING 行 PROCESSING ✅ / DONE ✅ 同样失活
三条取消路径 退团审批 / 管理员 cancel/pre-trip / 状态机直推 CANCEL(覆盖两个不同 publish 点)全部生效
库侧一致性 is_active=0 与 active_kind=NULL 同一条 UPDATE 写齐;全库不变式 is_active=0 AND active_kind IS NOT NULL 四次快照恒为 0
存量订正 15 行 → 订正后 0;那 8 条陈旧行复查 0/8 仍在池子,池子 105 → 97
单元测试 新增 17 例,四轮变异(注掉失活调用 / 守卫顺序挪回 / 写口漏清 active_kind / 第三处换回无差别抛)分别令 4 / 1 / 2 / 3 例转红,复绿 97/97
全量回归 A 8265 / B 3992 / C 17 = 12274 例,新增失败 0;唯一失败是 MapperBoundaryArchTest 的既存 payment→refund.mapper 9 条违规,已用精确 merge-base 实跑对照证明与本单零交集

fleet 三个回写入口(本单专门保护,行为不变)

对已取消且需求已失活的单实测:

入口 结果
POST /v3/internal/order/orders/{orderId}/requirement/vehicle/status 200 成功(静默跳过)
POST /v3/internal/order/orders/{orderId}/requirement/vehicle/reopen-after-assignment-cancel 200 成功
POST /v3/internal/order/vehicle-assignment/callback 200 + result: ORDER_CANCELLED_IGNORED

安全边界未放宽(同样已取消的单):传别单的 requirementId → 582085;传不存在的 → 582080。 反向对照:订单未取消 + 需求已失活 → 三个入口仍全抛 582080,即放宽的只有「订单确实已取消」这一个组合。

⚠️ 第三个入口(配车快照回调)原先把 is_active 判定写在 ORDER_CANCELLED_IGNORED 短路之前,本单一并修正。 不修的话 fleet 的 DailyVehicleSnapshotOutboxSender 会因 582084 不在可重试码表里而首次投递即 quarantine(终态,只能人工 requeue),并连带堵住同 requirement 的后续 revision。


五、关联 / 联系人

  • Issue: #7964
  • PR: #8038(主修复)、#8044(返回值语义订正)
  • 后端: jw
  • 收件人: fleet 侧维护者 —— 待配车池返回集合变小,且三个回写入口对已取消单的行为已被本单专门保护(不变)