stale-order-solution.md 3.3 KB

洗车订单僵死问题 — 三道防线方案

问题描述

用户点击「结算」后,服务器通过阿里云 IoT RRpc 向设备发送 close_order 命令。正常情况下,设备关机后会通过 AMQP 推送 order_close 事件,服务器收到后完成扣款和状态更新。

异常场景order_close 事件未到达平台时:

  • 订单一直处于「开机 + 未支付」状态
  • 设备一直处于「忙碌」状态
  • 其他用户无法使用该设备

方案概览

三道防线逐层递进,确保订单最终能被结算、设备最终能被释放:

| 防线 | 触发时机 | 策略 | |------|----------|------| | 防线一 | 用户点击结算时 | RRpc 失败后重试 + 主动查询设备回退 | | 防线二 | 定时任务(每5分钟) | 扫描超时订单,查询设备兜底结算 | | 防线三 | 用户启动设备时 | 检查设备是否有僵死订单,有则先清理 |

防线一:closeOrder 增强

文件: WashOrderServiceImpl.closeOrder()

用户点击结算
  ├─ 发送 close_order RRpc
  ├─ 失败 → 重试 1 次
  └─ 重试仍失败 → queryOrder() 主动查询
       ├─ 设备已关闭 → 直接结算
       ├─ 设备仍在运行 → forceCloseOrder() 强制关闭
       └─ 设备不可达 → 提示用户稍后重试

防线二:僵死订单定时对账

文件: StaleOrderReconciliationJob.java
模块: car-wash-admin
执行频率: 每 5 分钟
阈值配置: kym.stale-order-threshold-seconds(默认 1800 秒 = 30 分钟)

扫描条件:

  • order_status = 0(开机)
  • pay_status = 0(未支付)
  • start_time < now - threshold

处理策略:

  1. 查询设备订单状态 — 调用 queryOrder() RRpc
    • 设备返回已关闭 → 用返回数据执行结算
    • 设备返回仍在运行 → 跳过(正常进行中)
  2. 设备不可达,检查 DB 中设备状态
    • 设备已空闲 → 用最后一次 order_update 保存的数据兜底结算,close_type 标记为 reconcile
  3. 无法自动处理 → 标记 stopReason 为异常,等待人工介入

防线三:启动前设备检查

文件: WashOrderServiceImpl.createOrder()checkAndResolveStaleOrder()

在创建新订单前:

  1. 查询该设备是否有未完结订单
  2. 如有,调用 queryOrder() 确认设备实际状态
  3. 设备实际已关闭 → 先执行结算,再创建新订单

新增/修改文件清单

文件 操作 说明
car-wash-service/.../OrderSettlementService.java 新增 结算服务接口
car-wash-service/.../OrderSettlementServiceImpl.java 新增 结算逻辑提取(从 OrderCloseEventHandler 抽取)
car-wash-service/.../OrderCloseEventHandler.java 修改 简化为委托 OrderSettlementService
car-wash-service/.../AwoaraEventHandlerFactory.java 修改 适配新构造参数
car-wash-service/.../WashOrderServiceImpl.java 修改 closeOrder 增强 + createOrder 预检
car-wash-admin/.../StaleOrderReconciliationJob.java 新增 僵死订单定时对账任务

配置项

# application.yml(可选,默认 1800 秒 = 30 分钟)
kym:
  stale-order-threshold-seconds: 1800

建议设置为设备 operationTimeout(默认 3600 秒)的一半,既不会误伤正常订单,也能及时处理异常。