# 订单状态流转 — 测试指南

> 本文档面向测试人员和项目经理，描述订单系统在状态重构后的全部业务行为。
> 如有疑问请对照 `specs/006-orderstate/spec.md` 原始需求文档。

---

## 一、背景

系统支持三种订单类型，每种类型有不同的参与角色和完成方式：

| 订单类型 | 谁送餐 | 谁付钱 | 谁完成订单 |
|---------|--------|--------|-----------|
| **外送** (type=0) | 骑手配送 | 在线支付 / 到付 | 骑手点"已送达" |
| **自取** (type=1) | 用户到店取 | 现金（线下收款） | 商家点"完成" 或 用户确认取餐 |
| **堂食** (type=2) | 商家端上 | 现金（线下收款） | 商家点"完成" |

---

## 二、状态字段说明

订单有 4 个独立的状态字段，各自跟踪不同的关注点：

### 2.1 订单状态 (state) — 订单的生命周期

| 值 | 状态名 | 含义 |
|----|--------|------|
| 0 | 待处理 | 订单刚创建，等待商家接单 |
| 1 | 已接单 | 商家已接单，正在备餐 |
| 2 | 已出餐 | 商家备餐完成 |
| 3 | 已完成 | 订单结束（外送=骑手送达，自取=用户取餐，堂食=备餐完成） |
| 4 | 已取消 | 订单被取消 |

### 2.2 支付状态 (payStatus)

| 值 | 状态名 | 含义 |
|----|--------|------|
| 0 | 未支付 | 等待付款 |
| 1 | 已支付 | 付款完成 |
| 2 | 已退款 | 退款完成 |

**关键规则：**
- 外送到付订单：下单时直接标记为"已支付"(1)，骑手送货时收现金
- 自取/堂食现金订单：下单时为"未支付"(0)，商家收款+点完成时才改为"已支付"(1)
- 在线支付订单：下单时为"未支付"(0)，支付成功后改为"已支付"(1)

### 2.3 配送状态 (deliveryStatus) — 仅外送订单

| 值 | 状态名 | 含义 |
|----|--------|------|
| null | 不适用 | 自取/堂食订单没有配送状态 |
| 0 | 待接单 | 等待骑手接单 |
| 1 | 骑手已接单 | 骑手已接单，还没取餐 |
| 2 | 配送中 | 骑手已取餐，正在送 |
| 3 | 已送达 | 骑手已送到 |

### 2.4 售后状态 (afterSaleStatus)

| 值 | 状态名 | 含义 |
|----|--------|------|
| 0 | 无售后 | 正常订单 |
| 1 | 申请中 | 用户申请退款，等商家处理 |
| 2 | 退款中 | 商家同意，退款正在处理 |
| 3 | 已退款 | 退款完成 |
| 4 | 退款拒绝 | 商家拒绝退款 |
| 5 | 客服介入 | 客服正在处理 |
| 6 | 售后完成 | 售后流程结束 |

> **本次不实现售后/退款流程**。afterSaleStatus 字段已预留，但退款相关操作（申请退款、同意退款、拒绝退款、客服介入等）暂未开发。

---

## 三、状态流转场景

### 3.1 外送 — 在线支付

```
用户下单 → 用户付款 → 商家接单 → 商家出餐 → 骑手接单 → 骑手取餐 → 骑手送达
  state:    0         0          1          2         2         2         3
  pay:      0    →    1          1          1         1         1         1
  delivery:  null     null       null       0         1         2         3
```

| 步骤 | 操作者 | 操作 | state | payStatus | deliveryStatus |
|------|--------|------|-------|-----------|----------------|
| 1 | 系统 | 创建订单 | 0 | 0 | null |
| 2 | 用户 | 在线支付 | 0 | **0→1** | null |
| 3 | 商家 | 接单 | **0→1** | 1 | null |
| 4 | 商家 | 出餐 | **1→2** | 1 | **0** |
| 5 | 骑手 | 接单 | 2 | 1 | **0→1** |
| 6 | 骑手 | 取餐出发 | 2 | 1 | **1→2** |
| 7 | 骑手 | 送达 | **2→3** | 1 | **2→3** |

### 3.2 外送 — 到付

```
与在线支付基本相同，唯一区别：下单时 payStatus 直接设为 1（已支付）
```

| 步骤 | 操作者 | 操作 | state | payStatus | deliveryStatus |
|------|--------|------|-------|-----------|----------------|
| 1 | 系统 | 创建订单 | 0 | **1**（直接已支付） | null |
| 2 | 商家 | 接单 | **0→1** | 1 | null |
| 3 | 商家 | 出餐 | **1→2** | 1 | **0** |
| 4 | 骑手 | 接单→取餐→送达 | 2→2→3 | 1 | 0→1→2→3 |

### 3.3 自取 — 现金

```
用户下单 → 商家接单 → 商家出餐 → 商家收款+完成（或用户确认取餐）
  state:    0         1          2         3
  pay:      0         0          0         1（商家收款时设为1）
  delivery: 无        无         无        无
```

| 步骤 | 操作者 | 操作 | state | payStatus |
|------|--------|------|-------|-----------|
| 1 | 系统 | 创建订单 | 0 | 0 |
| 2 | 商家 | 接单 | **0→1** | 0 |
| 3 | 商家 | 出餐 | **1→2** | 0 |
| 4a | 商家 | 完成（收款+完成） | **2→3** | **0→1** |
| 4b | 用户 | 确认取餐（仅已支付时可用） | **2→3** | 1（不变） |

> **重要规则**：用户"确认取餐"仅对已支付(payStatus=1)的自取订单有效。现金订单(payStatus=0)的用户无法自己确认取餐，只能等商家点"完成"。

### 3.4 堂食 — 现金

与自取流程完全一致，只是 type=2。

---

## 四、取消订单场景

### 4.1 用户取消

| 条件 | state | 结果 |
|------|-------|------|
| 商家未接单 | state=0 | state→4（已取消） |
| 商家已接单 | state=1 或更高 | **不允许取消**（需走退款流程） |

**测试步骤：**
1. 用户下单（state=0）
2. 用户点击"取消订单"
3. 预期：订单变为"已取消"(state=4)

### 4.2 商家取消

| 条件 | state | 结果 |
|------|-------|------|
| 待处理或已接单 | state=0 或 1 | state→4（已取消） |
| 已出餐或之后 | state=2 或更高 | **不允许取消** |

### 4.3 系统自动取消

| 场景 | 条件 | 结果 |
|------|------|------|
| 超时未接单 | 商家在规定时间内未接单 | state→4 |
| 超时未完成 | 商家出餐后长时间未完成 | state→3（自动完成） |

---

## 五、各端订单列表测试

### 5.1 用户端

接口：`GET /system/userOrder/orderList`

| Tab 名称 | tab 参数 | 应出现的订单 | 不应出现的订单 |
|----------|---------|-------------|---------------|
| 待付款 | `unpaid` | 外送未支付订单(payStatus=0, type=0) | 自取/堂食的未支付订单、已支付的外送订单 |
| 进行中 | `active` | 所有正在进行的订单 | 已完成、已取消、有售后的订单 |
| 已完成 | `completed` | state=3 的订单 | 其他状态的订单 |
| 已取消 | `cancelled` | state=4 且无售后的订单 | 有售后的已取消订单 |
| 退款/售后 | `refund` | afterSaleStatus>0 的订单 | 正常订单 |

**重点测试用例：**

| # | 场景 | 预期结果 |
|---|------|---------|
| 1 | 自取订单(type=1)刚创建，payStatus=0 | 应出现在"进行中"Tab，**不应**出现在"待付款"Tab |
| 2 | 堂食订单(type=2)刚创建，payStatus=0 | 同上，出现在"进行中"，不在"待付款" |
| 3 | 外送订单(type=0)未支付 | 应出现在"待付款"Tab |
| 4 | 外送到付订单刚创建，payStatus=1 | 应出现在"进行中"Tab |
| 5 | 传入 type=1 筛选 | 只显示自取订单 |
| 6 | 不传 type | 显示所有类型的订单 |

### 5.2 商家端

接口：`GET /system/orderShOprate/orderList`

| Tab 名称 | tab 参数 | 应出现的订单 |
|----------|---------|-------------|
| 待受理 | `pending` | state=0 且（已支付 OR 自取/堂食） |
| 待出餐 | `preparing` | state=1 |
| 已出餐 | `ready` | state=2 且无售后 |
| 已完成 | `completed` | state=3 且无售后 |
| 已取消 | `cancelled` | state=4 且无售后 |
| 退款/售后 | `refund` | 有售后记录的订单 |

**重点测试用例：**

| # | 场景 | 预期结果 |
|---|------|---------|
| 1 | 外送未支付订单(state=0, payStatus=0) | **不应**出现在"待受理"（用户还没付款） |
| 2 | 自取订单刚创建(state=0, payStatus=0) | **应**出现在"待受理"（现金线下收） |
| 3 | 外送到付订单(state=0, payStatus=1) | 应出现在"待受理" |
| 4 | 外送在线支付成功(state=0, payStatus=1) | 应出现在"待受理" |
| 5 | 商家接单后 | 订单从"待受理"移到"待出餐" |
| 6 | 商家出餐后 | 订单从"待出餐"移到"已出餐" |

### 5.3 骑手端

接口：`GET /system/orderQsOprate/orderList`

| Tab 名称 | tab 参数 | 应出现的订单 |
|----------|---------|-------------|
| 新任务 | `newTask` | 外送订单(type=0)、已出餐(state=2)、等待骑手(deliveryStatus=0)、无售后 |
| 待取货 | `toPickup` | 当前骑手已接单(deliveryStatus=1)、无售后 |
| 配送中 | `delivering` | 当前骑手正在配送(deliveryStatus=2)、无售后 |
| 已完成 | `completed` | 当前骑手已完成的订单 |
| 已取消 | `cancelled` | 当前骑手关联的已取消订单 |
| 退款/售后 | `refund` | 当前骑手关联的有售后的订单 |

**重点测试用例：**

| # | 场景 | 预期结果 |
|---|------|---------|
| 1 | 商家还没出餐(state=1)的外送订单 | **不应**出现在"新任务"Tab |
| 2 | 自取订单(type=1) | **不应**出现在骑手端任何Tab |
| 3 | 新任务列表排序 | 按距离从近到远排列（需要传入经纬度参数） |
| 4 | 其他骑手已接单的订单 | **不应**出现在当前骑手的"新任务"中 |

---

## 六、用户端操作测试

### 6.1 确认取餐

接口：`POST /system/userOrder/confirmPickup`

| # | 前置条件 | 操作 | 预期结果 |
|---|---------|------|---------|
| 1 | 自取订单、已出餐(state=2)、已支付(payStatus=1) | 点击确认取餐 | 成功，state→3 |
| 2 | 自取订单、已出餐(state=2)、未支付(payStatus=0) | 点击确认取餐 | 失败，提示"订单未支付" |
| 3 | 外送订单(type=0) | 点击确认取餐 | 失败，提示"仅自取订单可确认取餐" |
| 4 | 堂食订单(type=2) | 点击确认取餐 | 失败，提示"仅自取订单可确认取餐" |
| 5 | 自取订单、待处理(state=0) | 点击确认取餐 | 失败，提示"当前订单状态不可确认取餐" |
| 6 | 自取订单、已接单(state=1) | 点击确认取餐 | 失败，提示"当前订单状态不可确认取餐" |
| 7 | 自取订单、已完成(state=3) | 点击确认取餐 | 失败，提示"当前订单状态不可确认取餐" |
| 8 | 别人的订单 | 点击确认取餐 | 失败，提示"订单不存在" |

### 6.2 取消订单

接口：`POST /system/userOrder/cancelOrder`

| # | 前置条件 | 操作 | 预期结果 |
|---|---------|------|---------|
| 1 | 任何类型、待处理(state=0) | 点击取消订单 | 成功，state→4 |
| 2 | 任何类型、已接单(state=1) | 点击取消订单 | 失败，提示"仅待处理状态的订单可取消" |
| 3 | 任何类型、已出餐(state=2) | 点击取消订单 | 失败，同上 |
| 4 | 别人的订单 | 点击取消订单 | 失败，提示"订单不存在" |
| 5 | 自取订单(state=0) | 点击取消 | 成功 |
| 6 | 堂食订单(state=0) | 点击取消 | 成功 |
| 7 | 外送订单(state=0) | 点击取消 | 成功 |

---

## 七、商家端操作测试

| 操作 | 接口 | 前置条件 | 预期结果 |
|------|------|---------|---------|
| 接单 | `POST /system/orderShOprate/acceptOrder` | state=0 | state→1 |
| 出餐 | `GET /system/orderShOprate/dispatchOrder` | state=1 | state→2；外送额外设 deliveryStatus=0 |
| 完成（自取/堂食） | `POST /system/orderShOprate/completeOrder` | type=1或2，state=2 | state→3，payStatus→1 |
| 取消 | `POST /system/orderShOprate/cancelOrder` | state=0或1 | state→4 |

**重点测试用例：**

| # | 场景 | 预期结果 |
|---|------|---------|
| 1 | 商家对外送订单出餐 | state→2，**同时 deliveryStatus=0**（等待骑手） |
| 2 | 商家对自取订单出餐 | state→2，deliveryStatus 保持 null |
| 3 | 商家对自取订单点"完成" | state→3，payStatus→1（收款+完成一步到位） |
| 4 | 商家对外送订单点"完成" | **不允许**（外送订单由骑手完成） |
| 5 | 商家对已出餐(state=2)的订单接单 | **不允许**（只能对 state=0 的订单接单） |

---

## 八、骑手端操作测试

| 操作 | 接口 | 前置条件 | 预期结果 |
|------|------|---------|---------|
| 接单 | `POST /system/orderQsOprate/acceptOrder` | type=0, deliveryStatus=0, afterSaleStatus=0 | deliveryStatus→1，绑定骑手ID |
| 取餐出发 | `POST /system/orderQsOprate/pickupOrder` | deliveryStatus=1, afterSaleStatus=0 | deliveryStatus→2 |
| 送达 | `POST /system/orderQsOprate/deliverOrder` | deliveryStatus=2 | deliveryStatus→3，**state→3** |

**重点测试用例：**

| # | 场景 | 预期结果 |
|---|------|---------|
| 1 | 骑手对自取订单(type=1)接单 | **不允许**（只有外送订单需要骑手） |
| 2 | 骑手对已有人接的订单接单 | **不允许**（防止重复接单） |
| 3 | 骑手送达后 | state=3, deliveryStatus=3，订单在骑手端移到"已完成"Tab |
| 4 | 骑手接单时有活跃退款(afterSaleStatus>0) | 允许操作但需提示 |

---

## 九、用户看到的文字

用户端页面不直接显示 state 数字，而是根据状态组合显示中文文字。

### 9.1 外送订单 (type=0) 用户看到的

| 用户看到 | 对应的订单状态 |
|---------|--------------|
| 待支付 | 还没付款(payStatus=0) |
| 商家已接单 | 已付款，等商家处理(state=0或1) |
| 配送中 | 商家出餐，骑手在送(state=2, deliveryStatus=0/1/2) |
| 已送达 | 骑手送到(state=2, deliveryStatus=3) |
| 已完成 | 订单结束(state=3) |
| 已取消 | 订单取消(state=4) |
| 退款中/已退款 | 有退款处理(afterSaleStatus>0) |

### 9.2 自取订单 (type=1) 用户看到的

| 用户看到 | 对应的订单状态 |
|---------|--------------|
| 待支付 | 还没付款(payStatus=0) |
| 商家已接单 | 等商家处理(state=0或1) |
| 待取餐 | 商家出餐了，等用户去取(state=2) |
| 已完成 | 订单结束(state=3) |
| 已取消 | 订单取消(state=4) |

### 9.3 堂食订单 (type=2) 用户看到的

| 用户看到 | 对应的订单状态 |
|---------|--------------|
| 待支付 | 还没付款(payStatus=0) |
| 商家已接单 | 等商家处理(state=0或1) |
| 备餐完成 | 商家出餐了(state=2) |
| 已完成 | 订单结束(state=3) |
| 已取消 | 订单取消(state=4) |

### 9.4 商家端订单卡片显示

**外送订单：**

| 条件 | 显示 | 颜色建议 |
|------|------|---------|
| state=0, payStatus=0 | 待支付 | 灰色 |
| state=0, payStatus=1 | 待接单 | 橙色 |
| state=1 | 制作中 | 蓝色 |
| state=2, deliveryStatus=0/1 | 待骑手取餐 | 蓝色 |
| state=2, deliveryStatus=2 | 配送中 | 蓝色 |
| state=2, deliveryStatus=3 | 已送达 | 绿色 |
| state=3 | 已完成 | 灰色 |
| state=4 | 已取消 | 红色 |

**自取/堂食订单：**

| 条件 | 显示 | 颜色建议 |
|------|------|---------|
| state=0 | 待接单 | 橙色 |
| state=1 | 制作中 | 蓝色 |
| state=2 | 备餐完成 | 橙色 |
| state=3 | 已完成 | 灰色 |
| state=4 | 已取消 | 红色 |

**售后状态覆盖规则**：当 afterSaleStatus>0 时，不管上面的规则，统一显示售后状态文字（退款申请中/退款中/已退款/退款已拒绝），优先级最高。

---

## 十、边界场景和注意事项

| # | 场景 | 说明 |
|---|------|------|
| 1 | 已取消的订单应出现在哪个Tab | 三个端都有独立的"已取消"Tab 承接 |
| 2 | 自取/堂食订单的 deliveryStatus | 始终为 null，不应出现在骑手端 |
| 3 | 外送订单商家出餐后 | deliveryStatus 应自动设为 0（等待骑手），不是 null |
| 4 | 现金自取订单的支付 | 下单时 payStatus=0，只有商家点"完成"时才变为 1 |
| 5 | 到付外送订单的支付 | 下单时 payStatus=1（直接确认支付方式） |
| 6 | 用户确认取餐前提 | 必须是自取(type=1) + 已支付(payStatus=1) + 已出餐(state=2) |
| 7 | 退款流程 | **本次不实现**，afterSaleStatus 字段已预留但无操作入口 |
| 8 | 旧接口 | 旧的订单列表接口保留不删，但不再维护，前端应切换到新接口 |
| 9 | 平台管理端 | 订单列表页需从单状态下拉改为四个独立下拉筛选（state/payStatus/deliveryStatus/afterSaleStatus） |
| 10 | 定时任务 | 商家出餐后超时未完成的订单，系统自动设为已完成(state=3) |

---

## 十一、实现状态总览

| 功能 | 状态 | 说明 |
|------|------|------|
| 订单创建（四字段初始化） | ✅ 已实现 | UserOrderController.createOrder |
| 用户端订单列表 | ✅ 已实现 | UserOrderController.orderList |
| 商家端订单列表 | ✅ 已实现 | PosOrderShOprateController.orderList |
| 骑手端订单列表 | ✅ 已实现 | PosOrderQsOprateController.orderList |
| 用户确认取餐 | ✅ 已实现 | UserOrderController.confirmPickup |
| 用户取消订单（新） | ✅ 已实现 | UserOrderController.cancelOrder |
| 用户取消订单（旧） | ⚠️ 已废弃 | OrderAppealController.userCancelpOrder 已注释 |
| 商家接单/出餐/完成/取消 | ✅ 已实现 | PosOrderShOprateController |
| 骑手接单/取餐/送达 | ✅ 已实现 | PosOrderQsOprateController |
| 支付回调更新 payStatus | ✅ 已实现 | PayController |
| 定时任务自动完成 | 待确认 | TestTask.java |
| 用户申请退款 | ❌ 未实现 | 留待接入在线支付后开发 |
| 商家同意/拒绝退款 | ❌ 未实现 | 留待接入在线支付后开发 |
| 客服介入 | ❌ 未实现 | 留待接入在线支付后开发 |
| 平台管理端四字段筛选 | ❌ 未实现 | foodie-admin-vue 待改造 |
