# 订单管理 — 测试用例

> **依据：** 《订单管理功能需求.md》v1.0.1、《订单管理技术方案.md》v1.0.2  
> **关联：** 《会员管理技术方案.md》v1.0、《店铺管理测试用例.md》（删店 O10）、《商品管理测试用例.md》（库存）  
> **范围：** 平台端 `/agri/order` 列表/详情/发货/物流/送达/关闭/删除；`IOrderFacade`（O10）；定时关单 O8；C 端 `/api/order` 主路径（create/pay/confirmReceive）  
> **代码落位（待实现）：** `baqing-shop/src/main/java/com/ruoyi/web/modules/order/`  
> **排除：** 分批发货拆单、支付渠道对接、退款原路、Excel 批量发货、商家端订单后台、发票售后  
> **环境：** RuoYi v3.9.2；平台 Token；`sys_config.order.pay.timeout.minutes` 可测环境设为 **2** 便于 O8

---

## 用例编号说明

| 前缀 | 类型 | 工具 |
|------|------|------|
| ORD-UT-xxx | 单元测试 | JUnit 5 + Mockito |
| ORD-API-xxx | 接口测试 | Apifox / REST Assured |
| ORD-UI-xxx | 界面测试 | Playwright（Chromium） |
| ORD-JOB-xxx | 定时任务 | 单元/集成触发 Job |

---

## 一、单元测试

### ORD-UT-001 支付成功待支付→待发货并扣库存

| 要素 | 内容 |
|------|------|
| **测试项** | paySuccess |
| **测试目的** | 验证 O4 |
| **前置条件** | 订单 status=0；Mock `IGoodsFacade.deductStock` 成功 |
| **测试步骤** | `paySuccess(orderId)` |
| **预期结果** | status=1；pay_status=1；pay_time 非空；每行 deductStock 调用一次 |

### ORD-UT-002 支付失败不扣库存

| 要素 | 内容 |
|------|------|
| **测试项** | paySuccess |
| **测试目的** | 验证扣库存仅在成功路径 |
| **前置条件** | deductStock 抛异常 |
| **测试步骤** | `paySuccess` |
| **预期结果** | 事务回滚；status 仍为 0 |

### ORD-UT-003 整单发货待发货→已发货

| 要素 | 内容 |
|------|------|
| **测试项** | ship |
| **测试目的** | 验证 O5 |
| **前置条件** | status=1 |
| **测试步骤** | `ship(dto)` deliveryType=1 |
| **预期结果** | status=2；写入 logistics 字段；插入 trace_type=1；**不** 调用 deductStock |

### ORD-UT-004 非待发货不可发货

| 要素 | 内容 |
|------|------|
| **测试项** | ship |
| **测试目的** | 状态机 |
| **前置条件** | status=0/2/3/4 |
| **测试步骤** | `ship` |
| **预期结果** | 失败；msg「当前状态不可发货」 |

### ORD-UT-005 物流配送必填公司与单号

| 要素 | 内容 |
|------|------|
| **测试项** | ship 校验 |
| **测试目的** | 验证 O15 |
| **前置条件** | status=1 |
| **测试步骤** | deliveryType=1，trackingNo 空 |
| **预期结果** | 失败 |

### ORD-UT-006 商家配送必填车辆与配送员

| 要素 | 内容 |
|------|------|
| **测试项** | ship 校验 |
| **测试目的** | 验证 O15 |
| **前置条件** | status=1 |
| **测试步骤** | deliveryType=2，courierName 空 |
| **预期结果** | 失败 |

### ORD-UT-007 更新物流追加节点不改变主状态

| 要素 | 内容 |
|------|------|
| **测试项** | addLogisticsTrace |
| **测试目的** | 验证 O6 |
| **前置条件** | status=2 |
| **测试步骤** | 连续两次 `addLogisticsTrace` |
| **预期结果** | status 仍为 2；trace 表 2 条 type=2 |

### ORD-UT-008 登记送达不改为已完成

| 要素 | 内容 |
|------|------|
| **测试项** | markDelivered |
| **测试目的** | 验证 O7 |
| **前置条件** | status=2 |
| **测试步骤** | `markDelivered` |
| **预期结果** | status=2；trace_type=3 一条 |

### ORD-UT-009 确认收货→已完成并刷新会员消费

| 要素 | 内容 |
|------|------|
| **测试项** | confirmReceive |
| **测试目的** | 验证 O7、O11 |
| **前置条件** | status=2；Mock `IMemberFacade` |
| **测试步骤** | `confirmReceive(orderId, memberId)` |
| **预期结果** | status=3；finish_time 非空；trace_type=4；调用 `refreshConsumption(memberId)` 一次 |

### ORD-UT-010 待发货关闭回滚库存

| 要素 | 内容 |
|------|------|
| **测试项** | closeOrder |
| **测试目的** | 验证 §10.1 库存回滚 |
| **前置条件** | status=1，已支付 |
| **测试步骤** | `closeOrder(closeType=3)` |
| **预期结果** | status=4；每 item 调用 `restoreStock` |

### ORD-UT-011 待支付关闭不回滚库存

| 要素 | 内容 |
|------|------|
| **测试项** | closeOrder |
| **测试目的** | 验证待支付无扣减 |
| **前置条件** | status=0 |
| **测试步骤** | `closeOrder` |
| **预期结果** | status=4；**不** 调用 restoreStock |

### ORD-UT-012 仅已关闭可删除为已删除

| 要素 | 内容 |
|------|------|
| **测试项** | deleteOrder |
| **测试目的** | 验证 O9 |
| **前置条件** | status=4 / status=1 |
| **测试步骤** | 分别 `deleteOrder` |
| **预期结果** | 前者 status=5；后者失败 |

### ORD-UT-013 hasUnfinishedOrdersByShop O10

| 要素 | 内容 |
|------|------|
| **测试项** | IOrderFacade |
| **测试目的** | 验证 O10 |
| **前置条件** | 店 A 存在 status=2 订单；店 B 仅 status=4 |
| **测试步骤** | `hasUnfinishedOrdersByShop` |
| **预期结果** | 店 A true；店 B false |

### ORD-UT-014 已关闭/已完成不算未完成

| 要素 | 内容 |
|------|------|
| **测试项** | IOrderFacade |
| **测试目的** | 验证 O10 边界 |
| **前置条件** | 仅 status=3、4、5 |
| **测试步骤** | `hasUnfinishedOrdersByShop` |
| **预期结果** | false |

### ORD-UT-015 超时关单 Job

| 要素 | 内容 |
|------|------|
| **测试项** | OrderPayTimeoutJob |
| **测试目的** | 验证 O8 |
| **前置条件** | status=0 且 pay_expire_time&lt;now |
| **测试步骤** | 执行 Job |
| **预期结果** | status=4；close_type=1 |

### ORD-UT-016 未超时订单 Job 不关闭

| 要素 | 内容 |
|------|------|
| **测试项** | OrderPayTimeoutJob |
| **测试目的** | 验证 O8 边界 |
| **前置条件** | pay_expire_time&gt;now |
| **测试步骤** | 执行 Job |
| **预期结果** | status 仍为 0 |

### ORD-UT-017 pageOrdersByMember 强制 memberId

| 要素 | 内容 |
|------|------|
| **测试项** | pageOrdersByMember |
| **测试目的** | 验证 M9 |
| **前置条件** | 会员 1、2 各有订单 |
| **测试步骤** | `pageOrdersByMember(1, q)` |
| **预期结果** | 仅返回 member_id=1 |

### ORD-UT-018 getOrderDetail 归属校验

| 要素 | 内容 |
|------|------|
| **测试项** | getOrderDetail |
| **测试目的** | 会员只读边界 |
| **前置条件** | 订单属会员 2 |
| **测试步骤** | `getOrderDetail(orderId, memberId=1)` |
| **预期结果** | 抛错；无权查看 |

### ORD-UT-019 创建订单写入 pay_expire_time

| 要素 | 内容 |
|------|------|
| **测试项** | createOrder |
| **测试目的** | 验证 §5 定时关单字段 |
| **前置条件** | timeout=1440 分钟 |
| **测试步骤** | `createOrder` |
| **预期结果** | pay_expire_time = create_time + 1440min |

### ORD-UT-020 订单号生成唯一

| 要素 | 内容 |
|------|------|
| **测试项** | generateOrderNo |
| **测试目的** | 验证 O2 |
| **前置条件** | — |
| **测试步骤** | 连续生成 100 个 |
| **预期结果** | 无重复；格式 O+日期+序号 |

---

## 二、接口测试

### ORD-API-001 列表默认排除已删除

| 要素 | 内容 |
|------|------|
| **测试项** | GET /agri/order/list |
| **测试目的** | 验证 §5.4 |
| **前置条件** | 存在 status=5 与 status=1 各一单 |
| **测试步骤** | 不传 orderStatus |
| **预期结果** | rows 不含 status=5 |

### ORD-API-002 页签待支付筛选

| 要素 | 内容 |
|------|------|
| **测试项** | GET /list?orderStatus=0 |
| **测试目的** | 验证页签 |
| **前置条件** | 多状态订单 |
| **测试步骤** | orderStatus=0 |
| **预期结果** | 仅待支付 |

### ORD-API-003 页签待发货/已发货/已完成/已关闭/已删除

| 要素 | 内容 |
|------|------|
| **测试项** | GET /list |
| **测试目的** | 验证 O3 页签 |
| **前置条件** | 各状态均有样本 |
| **测试步骤** | orderStatus=1～5 各请求一次 |
| **预期结果** | 筛选正确 |

### ORD-API-004 按订单编号检索

| 要素 | 内容 |
|------|------|
| **测试项** | GET /list |
| **测试目的** | 验证 O16 |
| **前置条件** | 已知 orderNo |
| **测试步骤** | orderNo 模糊 |
| **预期结果** | 命中目标单 |

### ORD-API-005 按商品名称检索

| 要素 | 内容 |
|------|------|
| **测试项** | GET /list |
| **测试目的** | 验证 O16 |
| **前置条件** | 订单含商品名「尿素」 |
| **测试步骤** | goodsName=尿素 |
| **预期结果** | 命中 |

### ORD-API-006 按收货人手机号检索

| 要素 | 内容 |
|------|------|
| **测试项** | GET /list |
| **测试目的** | 验证 O16 |
| **前置条件** | — |
| **测试步骤** | consigneeMobile |
| **预期结果** | 命中 |

### ORD-API-007 按会员 keyword 检索

| 要素 | 内容 |
|------|------|
| **测试项** | GET /list |
| **测试目的** | 扩展检索 |
| **前置条件** | memberNickName 已知 |
| **测试步骤** | memberKeyword |
| **预期结果** | 命中 |

### ORD-API-008 详情含明细与物流轴

| 要素 | 内容 |
|------|------|
| **测试项** | GET /{orderId} |
| **测试目的** | 验证 O14 |
| **前置条件** | 已发货且有多条 trace |
| **测试步骤** | GET 详情 |
| **预期结果** | items、traceList 非空；trace 时间倒序 |

### ORD-API-009 发货成功

| 要素 | 内容 |
|------|------|
| **测试项** | POST /{id}/ship |
| **测试目的** | 验证 §7 |
| **前置条件** | status=1；权限 agri:order:ship |
| **测试步骤** | POST 合法物流 Body |
| **预期结果** | code=200；再查 status=2 |

### ORD-API-010 更新物流

| 要素 | 内容 |
|------|------|
| **测试项** | POST /{id}/logistics |
| **测试目的** | 验证 §8 |
| **前置条件** | status=2 |
| **测试步骤** | POST content |
| **预期结果** | 详情 trace 增加 type=2 |

### ORD-API-011 登记送达

| 要素 | 内容 |
|------|------|
| **测试项** | POST /{id}/delivered |
| **测试目的** | 验证 §9.1 |
| **前置条件** | status=2 |
| **测试步骤** | POST |
| **预期结果** | status 仍为 2；trace type=3 |

### ORD-API-012 运营关闭待发货

| 要素 | 内容 |
|------|------|
| **测试项** | PUT /{id}/close |
| **测试目的** | 验证 §10.1 |
| **前置条件** | status=1 已支付 |
| **测试步骤** | closeType=3 |
| **预期结果** | status=4；商品库存已回滚（查 goods.stock） |

### ORD-API-013 删除已关闭订单

| 要素 | 内容 |
|------|------|
| **测试项** | DELETE /{id} |
| **测试目的** | 验证 O9 |
| **前置条件** | status=4 |
| **测试步骤** | DELETE |
| **预期结果** | status=5；默认列表不可见 |

### ORD-API-014 非已关闭不可删除

| 要素 | 内容 |
|------|------|
| **测试项** | DELETE /{id} |
| **测试目的** | 验证 O9 负向 |
| **前置条件** | status=1 |
| **测试步骤** | DELETE |
| **预期结果** | 失败 |

### ORD-API-015 无权限发货 403

| 要素 | 内容 |
|------|------|
| **测试项** | POST /ship |
| **测试目的** | 权限 |
| **前置条件** | 仅 list 权限 Token |
| **测试步骤** | POST ship |
| **预期结果** | 403 |

### ORD-API-016 C 端创建订单

| 要素 | 内容 |
|------|------|
| **测试项** | POST /api/order/create |
| **测试目的** | C 端主路径 |
| **前置条件** | 会员启用；店开业；商品出售中库存足 |
| **测试步骤** | POST 下单 |
| **预期结果** | status=0；有 items 快照；pay_expire_time 有值 |

### ORD-API-017 C 端支付成功

| 要素 | 内容 |
|------|------|
| **测试项** | POST /api/order/{id}/pay |
| **测试目的** | C 端支付 |
| **前置条件** | status=0 |
| **测试步骤** | pay |
| **预期结果** | status=1 |

### ORD-API-018 C 端确认收货

| 要素 | 内容 |
|------|------|
| **测试项** | POST /api/order/{id}/confirmReceive |
| **测试目的** | 完成 + O11 |
| **前置条件** | status=2；会员 Token |
| **测试步骤** | confirmReceive |
| **预期结果** | status=3；biz_member.order_count/total_amount 更新 |

### ORD-API-019 已关闭订单不可再支付

| 要素 | 内容 |
|------|------|
| **测试项** | POST /pay |
| **测试目的** | 验证 §3.4 C 端 |
| **前置条件** | status=4（超时关闭） |
| **测试步骤** | pay |
| **预期结果** | 失败；订单已关闭 |

### ORD-API-020 删店预检存在未完成订单

| 要素 | 内容 |
|------|------|
| **测试项** | GET /agri/shop/{id}/deleteCheck |
| **测试目的** | 跨模块 O10 |
| **前置条件** | 店下 status=0 订单 |
| **测试步骤** | deleteCheck |
| **预期结果** | canDelete=false；含未完成订单文案 |

---

## 三、界面测试

### ORD-UI-001 订单列表页签切换

| 要素 | 内容 |
|------|------|
| **测试项** | 列表 UI |
| **测试工具** | Playwright |
| **测试步骤** | 登录平台 → 订单管理 → 点击「待发货」页签 |
| **预期结果** | 表格仅待发货；角标数量合理 |

### ORD-UI-002 待发货展示去发货按钮

| 要素 | 内容 |
|------|------|
| **测试项** | 操作列 |
| **测试步骤** | 待发货行查看操作 |
| **预期结果** | 有「去发货」；无删除 |

### ORD-UI-003 发货弹窗配送方式切换字段显隐

| 要素 | 内容 |
|------|------|
| **测试项** | 发货表单 |
| **测试步骤** | 打开发货 → 选物流/商家配送 |
| **预期结果** | 物流显示公司+单号；商家显示车辆+配送员 |

### ORD-UI-004 发货成功状态变更

| 要素 | 内容 |
|------|------|
| **测试项** | 发货 E2E |
| **前置条件** | 待发货样本单 |
| **测试步骤** | 填表提交发货 |
| **预期结果** | 提示成功；行状态变已发货 |

### ORD-UI-005 详情物流时间轴倒序

| 要素 | 内容 |
|------|------|
| **测试项** | 详情 |
| **测试步骤** | 打开已发货单详情 |
| **预期结果** | 时间轴最新在上 |

### ORD-UI-006 关闭二次确认

| 要素 | 内容 |
|------|------|
| **测试项** | 关闭 |
| **测试步骤** | 点击关闭 |
| **预期结果** | 确认框；可填原因 |

### ORD-UI-007 已删除页签可见逻辑删除单

| 要素 | 内容 |
|------|------|
| **测试项** | 已删除 |
| **测试步骤** | 切换「已删除」页签 |
| **预期结果** | 可见 status=5 订单；只读 |

### ORD-UI-008 列表手机号脱敏

| 要素 | 内容 |
|------|------|
| **测试项** | 脱敏 |
| **测试步骤** | 查看列表收货手机 |
| **预期结果** | 中间 4 位 * |

---

## 四、定时任务测试

### ORD-JOB-001 超时自动关闭 E2E

| 要素 | 内容 |
|------|------|
| **测试项** | O8 集成 |
| **前置条件** | timeout=2 分钟；创建待支付单并等待过期 |
| **测试步骤** | 触发 Job 或等待调度 |
| **预期结果** | 订单变已关闭；close_type=未支付超时 |

---

## 五、测试数据准备

| 数据 | 说明 |
|------|------|
| 会员 M1 | status=0；用于下单 |
| 会员 M2 | status=1 禁用；用于登录/下单阻断 |
| 店铺 S1 | 开业；S2 停业 |
| 商品 G1 | 出售中 stock=100 |
| 订单样本 | 各 status 0～5 至少 1 条 |
| 配置 | `order.pay.timeout.minutes` 测试值 |

---

## 六、用例统计

| 类型 | 数量 |
|------|------|
| 单元测试 ORD-UT | 20 |
| 接口测试 ORD-API | 20 |
| 界面测试 ORD-UI | 8 |
| 定时任务 ORD-JOB | 1 |
| **合计** | **49** |

---

## 七、自动化映射（待实现）

| 用例 | 建议代码位置 |
|------|--------------|
| ORD-UT-001～020 | `baqing-shop/src/test/java/com/ruoyi/web/modules/order/service/OrderServiceImplTest.java` |
| ORD-API-001～020 | `.../controller/OrderControllerTest.java` |
| ORD-JOB-001 | `.../job/OrderCloseJobTest.java` |
| ORD-UI-001～008 | `e2e/platform/order.spec.ts`（Playwright） |

---

*文档版本：v1.0 · 关联《订单管理功能需求.md》v1.0.1、《订单管理技术方案.md》v1.0.2 · 代码落位 `com.ruoyi.web.modules.order`（**待实现**）*
