9.4 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 | 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。