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

## 问题描述

用户点击「结算」后，服务器通过阿里云 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` | 新增 | 僵死订单定时对账任务 |

## 配置项

```yaml
# application.yml（可选，默认 1800 秒 = 30 分钟）
kym:
  stale-order-threshold-seconds: 1800
```

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