# 全部订单 — 技术方案

> **依据：** 《全部订单功能需求.md》v1.1  
> **关联：** 平台《订单管理技术方案》**v1.0.1**、《订单管理功能需求.md》v1.0.1、《关联需求分析.md》v1.6；商家《商家端店铺上下文接口说明》、《员工管理技术方案》v1.2  
> **v1.0.1：** §2 补全字段说明；权威 DDL 见 [`sql/biz_order.sql`](../../../../sql/biz_order.sql)（与平台同源）。  
> **v1.0.2：** 明确后端落位 **`com.ruoyi.web.modules.order`**（**暂不实现**，§1.3 为目录规划）；与平台技术方案 v1.0.2 对齐。
> **范围：** 商家端 **当前店铺** 订单列表、检索、详情、整单发货、物流更新、送达登记、删除已关闭单；**不** 创建订单、**不** 手工关闭、**不** 维护会员消费统计。  
> **原则：** **无新建表**；与平台 **同源** `biz_order` / `biz_order_item` / `biz_order_logistics_trace`；履约逻辑 **共用** `IOrderService`；`shop_id = X-Shop-Id` 强制隔离；整单发货（O5）。  
> **命名区分：** 本方案为 **商家端 · 全部订单**（`/agri/seller/order`）；平台 **`/agri/order`** 见平台专册。

---

## 1. 技术架构

| 项 | 选型 |
|----|------|
| 基础框架 | RuoYi **v3.9.2**（`springboot2` 分支） |
| 数据库 | **MySQL 5.7.39** |
| ORM / 权限 / 响应 | MyBatis；`@PreAuthorize`；`AjaxResult` / `TableDataInfo` |
| 店铺上下文 | `SellerShopContextInterceptor` + 请求头 **`X-Shop-Id`**；Controller **`requireShopId()`** |
| 脱敏 | 列表/摘要 `MemberMobileUtils.mask`（收货手机） |
| 日志 | 发货、物流、送达、删除 `@Log` |
| 字典 | 与平台共用 `biz_order_status`、`biz_delivery_type` 等 |

### 1.1 与平台侧边界

| 维度 | 平台 · 订单管理 | 商家端 · 全部订单（本方案） |
|------|-----------------|---------------------------|
| 路径 | `/agri/order` | **`/agri/seller/order`** |
| 数据范围 | 全平台 | **`shop_id = 当前店铺`** |
| 表 | `biz_order` 等 | **同表**，无商家副本 |
| 履约 | 发货/物流/送达/关闭/删除 | 发货/物流/送达/删除；**无** `close` |
| 权限前缀 | `agri:order:*` | **`agri:seller:order:*`** |
| Service | `IOrderService` | **同一实现**；入参带 `shopId` 做归属校验 |

> **定稿：** 商家端与平台 **同一套** `OrderServiceImpl`，避免双轨状态机（对齐商品模块 `IGoodsService` 模式）。

### 1.2 协作链

```text
【C 端】下单/支付 → biz_order（shop_id=本店）
            ↓
【系统】OrderPayTimeoutJob（O8，平台模块）
            ↓
【登录】GET /agri/seller/context → X-Shop-Id
            ↓ SellerShopContextInterceptor.assertShopAccessible
【本模块】GET  /agri/seller/order/list        → shop_id 过滤
           GET  /agri/seller/order/{id}        → 归属校验
           POST /agri/seller/order/{id}/ship   → status 1→2 + trace
           POST .../logistics / .../delivered  → 追加 trace
           DELETE .../{id}                     → status 4→5
            ↓
【平台】/agri/order/** 可代运营同一订单（状态同步，不可重复发货）
【C 端】confirmReceive → status 2→3 + IMemberFacade.refreshConsumption（O11）
【删店】IShopOrderFacade.hasUnfinishedOrdersByShop（O10）
```

| 关联模块 | 协作 |
|----------|------|
| **平台 · 订单管理** | 同源表与状态字典；C 端/定时关单/平台关闭 **不在本 Controller** |
| **会员管理** | 只读 `member_nick_name` 快照；**不调用** `refreshConsumption` |
| **商品管理** | 明细读 `biz_order_item` 快照；发货 **不** 再扣库存（O4） |
| **店铺管理** | `shop_id` 隔离；`IShopOrderFacade` 删店校验 O10 |
| **店铺上下文** | 与商品/员工/店铺设置共用 `X-Shop-Id` |

### 1.3 模块落位

**包（定稿 · 暂不实现）：** `com.ruoyi.web.modules.order`  
**目录：** `baqing-shop/src/main/java/com/ruoyi/web/modules/order/`  
与平台 **同一模块**；商家仅增加 `SellerOrderController`，业务共用 `IOrderService` / `OrderServiceImpl`。

> **说明：** 下文目录树为 **实现规划**；当前仓库 **尚未创建** order 包源码；删店/会员订单仍走 account、store 模块 **桩 Facade**。

```text
baqing-shop/src/main/java/com/ruoyi/web/modules/order/
├── controller/
│   ├── OrderController.java              # 平台 /agri/order（平台专册）
│   └── SellerOrderController.java        # 商家 /agri/seller/order（本方案）
├── service/
│   ├── IOrderService.java
│   └── impl/OrderServiceImpl.java        # 列表/详情/履约（平台+商家共用）
├── support/OrderSupport.java             # 状态校验、归属校验、trace 摘要
├── domain/
│   ├── BizOrder.java
│   ├── BizOrderItem.java
│   └── BizOrderLogisticsTrace.java
├── dto/
│   ├── OrderQuery.java                   # 平台列表 Query
│   ├── SellerOrderQuery.java             # 商家列表（无 shopName）
│   ├── OrderShipDTO.java
│   ├── OrderLogisticsDTO.java
│   └── OrderDeliveredDTO.java
├── vo/
│   ├── OrderListRowVO.java               # 列表行（含 items 摘要）
│   └── OrderDetailVO.java                # 详情分区
├── mapper/
│   ├── BizOrderMapper.java
│   ├── BizOrderItemMapper.java
│   └── BizOrderLogisticsTraceMapper.java
├── facade/
│   ├── IOrderFacade.java                 # 会员订单只读（扩展）
│   └── OrderFacadeImpl.java              # hasUnfinishedOrdersByShop/Merchant
└── job/OrderPayTimeoutJob.java           # O8（平台侧部署，商家无）

resources/mapper/order/BizOrderMapper.xml

sql/biz_order.sql                         # 权威 DDL（全字段 COMMENT，与平台一致）
sql/agri_seller_order_menu.sql            # 商家菜单（待配置）
```

> **DDL 说明：** 三表定义 **唯一权威来源** 为 [`sql/biz_order.sql`](../../../../sql/biz_order.sql)；平台《订单管理技术方案》§2 为对照说明。

**前端（建议）：**

| 项 | 值 |
|----|-----|
| 页面 | `ruoyi-ui/src/views/agri/seller/order/index.vue` |
| API | `ruoyi-ui/src/api/agri/seller/order.js` |

### 1.4 跨模块 Facade

| 接口 | 提供方 | 本模块用法 |
|------|--------|------------|
| **`IOrderFacade`** | 本模块 | 对外：`hasUnfinishedOrdersByShop`（O10）；会员只读分页 |
| **`IShopOrderFacade`** | 店铺模块 | 删店调用 **本模块** `IOrderFacade` |
| **`IGoodsOrderFacade`** | 商品模块 | 下架阻断（O10 占位，待订单接入） |
| **`IMemberFacade`** | 会员 | **商家端不调用**；C 端确认收货后由平台/C 端链路调用 |

---

## 2. 数据库设计

**无新建表。** 与平台 **完全同源**；字段 COMMENT、索引见 **权威脚本** [`sql/biz_order.sql`](../../../../sql/biz_order.sql) 及平台《订单管理技术方案》§2（v1.0.1）。

### 2.1 表与商家端用法

| 表 | 表 COMMENT | 商家端用法 |
|----|------------|------------|
| `biz_order` | 订单主表（平台+商家端同源） | 列表/详情/发货写主表；**WHERE shop_id = #{shopId}** |
| `biz_order_item` | 订单商品明细（下单快照） | 详情明细、列表首行；按 `order_id` 关联 |
| `biz_order_logistics_trace` | 订单物流节点时间轴 | 详情时间轴；发货/物流/送达 **追加** trace |

### 2.2 枚举（与平台 §2.2 一致）

**`order_status`**

| 值 | 含义 | 商家页签 |
|----|------|----------|
| 0 | 待支付 | **全部**（只读，不可发货） |
| 1 | 待发货 | 待发货 · 可 ship |
| 2 | 已发货 | 已发货 · 可 logistics/delivered |
| 3 | 已完成 | 已完成 · 只读 |
| 4 | 已关闭 | 已关闭 · 可 delete |
| 5 | 已删除 | 已删除 · 只读 |

**`delivery_type`：** `1` 物流（logistics_company+tracking_no）· `2` 商家配送（vehicle_no+courier_name+courier_mobile）

**`trace_type`：** `1` 已发货 · `2` 运输更新 · `3` 已送达 · `4` 确认收货（C 端）

> **del_flag：** 固定 `'0'`；逻辑删除用 `order_status='5'`，**勿** 用 del_flag 表示删单。

### 2.3 商家端重点字段

| 字段 | 列表 | 详情 | 商家写 | 说明 |
|------|:----:|:----:|:------:|------|
| shop_id | — | — | — | 隔离键；须等于 X-Shop-Id |
| order_no, create_time, order_status | ✓ | ✓ | — | 状态字典 biz_order_status |
| pay_amount, freight_amount | ✓ | ✓ | — | O11 统计用 pay_amount |
| member_nick_name | ✓ | ✓ | — | 买家展示名快照 |
| consignee_name/mobile/address | ✓ | ✓ | — | 下单快照；手机脱敏 |
| delivery_type | 发货后 | ✓ | 发货 | biz_delivery_type |
| logistics_company, tracking_no | — | ✓ | 发货(物流) | delivery_type=1 |
| vehicle_no, courier_name, courier_mobile | — | ✓ | 发货(商家) | delivery_type=2 |
| ship_remark | — | ✓ | 发货 | 选填 |
| pay_status, pay_time | — | ✓ | — | 区分待支付/待发货 |
| close_type, close_reason | — | ✓ | — | 平台/系统关闭原因只读 |
| ship_time, finish_time | — | ✓ | — | 发货/完成时间 |

明细表 `biz_order_item`：goods_name/spec/image/unit_price/quantity/line_amount — **全部只读**（快照）。

物流表 `biz_order_logistics_trace`：trace_type/trace_time/content/create_by — 商家 **追加** type 1/2/3。

### 2.4 页签与 SQL 过滤

| 前端页签 | Query `orderStatus` | SQL 条件 |
|----------|---------------------|----------|
| 全部 | 空或不传 | `shop_id=? AND order_status != '5'`（**含 0 待支付**） |
| 待发货 | `1` | `shop_id=? AND order_status='1'` |
| 已发货 | `2` | `shop_id=? AND order_status='2'` |
| 已完成 | `3` | `shop_id=? AND order_status='3'` |
| 已关闭 | `4` | `shop_id=? AND order_status='4'` |
| 已删除 | `5` | `shop_id=? AND order_status='5'` |

### 2.5 高级检索 `shipStatus`

与平台一致：**直接映射 `order_status`**，仅允许 `0/1/2`（待支付/待发货/已发货）。与页签 **AND** 组合。

| shipStatus | 等价 |
|------------|------|
| 0 | `order_status='0'` |
| 1 | `order_status='1'` |
| 2 | `order_status='2'` |

> **说明：** 不另建履约子状态表；「运输中」= `order_status=2`。

### 2.6 索引（商家列表）

| 索引 | 商家列表用途 |
|------|--------------|
| `idx_shop_status (shop_id, order_status)` | 页签 + 店铺过滤 |
| `idx_create_time` | 下单时间排序/区间 |
| `uk_order_no` | 订单编号检索 |

---

## 3. 商家端接口设计

**基路径：** `/agri/seller/order`  
**上下文：** 请求头 **`X-Shop-Id`**（必填）；`SellerOrderController.requireShopId()`  
**拦截：** `/agri/seller/**` → `SellerShopContextInterceptor`

### 3.1 接口一览

| 方法 | 路径 | 权限 | 说明 |
|------|------|------|------|
| GET | `/list` | `agri:seller:order:list` | 分页列表（页签+检索） |
| GET | `/{orderId}` | `agri:seller:order:query` | 详情（明细+物流轴） |
| POST | `/{orderId}/ship` | `agri:seller:order:ship` | 整单发货 → 已发货 |
| POST | `/{orderId}/logistics` | `agri:seller:order:ship` | 更新物流（已发货） |
| POST | `/{orderId}/delivered` | `agri:seller:order:ship` | 登记送达 |
| DELETE | `/{orderId}` | `agri:seller:order:remove` | 已关闭 → 已删除 |

> **不提供：** `PUT /close`（商家端手工关闭，见功能需求 §12）。

### 3.2 列表 `GET /list`

**Query（SellerOrderQuery）：**

| 参数 | 说明 |
|------|------|
| pageNum, pageSize | 分页 |
| orderStatus | 页签：空=全部（排除5）；`1`～`5` |
| orderNo | 订单编号（模糊） |
| goodsName | 关联 `biz_order_item.goods_name` 模糊 |
| shipStatus | `0/1/2`，见 §2.4 |
| consigneeName, consigneeMobile, consigneeAddress | 模糊 |
| deliveryType | `1/2`（已发货后有值） |
| beginTime, endTime | `create_time` 区间 |

**固定条件：** `shop_id = requireShopId()`；`del_flag='0'`。  
**排序：** `create_time DESC`。

**rows（OrderListRowVO）：**

| 字段 | 说明 |
|------|------|
| orderId, orderNo, createTime | |
| orderStatus, orderStatusLabel | 字典翻译 |
| payAmount, freightAmount | |
| memberNickName | 会员名称 |
| consigneeName, consigneeMobile | 手机 **脱敏** |
| consigneeAddress | |
| deliveryType, deliveryTypeLabel | 未发货可为 null |
| itemCount | 商品行数 |
| items | 数组（首行展示）：goodsImage, goodsName, goodsSpec, quantity, unitPrice |

### 3.3 详情 `GET /{orderId}`

**前置：** `biz_order.shop_id = 当前 shopId`，否则 `订单不存在或无权访问`。

**data（OrderDetailVO）：**

| 分区 | 字段 |
|------|------|
| base | orderId, orderNo, createTime, orderStatus, payAmount, freightAmount, memberNickName, deliveryType, payStatus, payTime, shipTime, finishTime |
| items | itemId, goodsImage, goodsName, goodsSpec, unitPrice, quantity, lineAmount |
| consignee | consigneeName, consigneeMobile, consigneeAddress |
| logistics | logisticsCompany, trackingNo, vehicleNo, courierName, courierMobile, shipRemark |
| traceList | traceType, traceTypeLabel, traceTime, content, createBy（**时间倒序**） |
| closeInfo | closeType, closeTypeLabel, closeReason（status=4/5 时有值） |

### 3.4 发货 `POST /{orderId}/ship`

**前置：** `shop_id` 匹配；`order_status='1'`。

**Body（OrderShipDTO）：**

```json
{
  "shipTime": "2026-05-28 14:00:00",
  "deliveryType": "1",
  "logisticsCompany": "顺丰",
  "trackingNo": "SF123456",
  "vehicleNo": null,
  "courierName": null,
  "courierMobile": null,
  "shipRemark": ""
}
```

| deliveryType | 必填字段 |
|--------------|----------|
| `1` | logisticsCompany + trackingNo |
| `2` | vehicleNo + courierName + courierMobile |

**事务（OrderServiceImpl.shipOrder）：**

```text
校验 shop_id + status=1
→ UPDATE biz_order SET order_status='2', delivery_type, 物流/配送员字段, ship_time
→ INSERT biz_order_logistics_trace(trace_type='1', content=摘要)
（不调用 deductStock）
```

### 3.5 更新物流 `POST /{orderId}/logistics`

**Body（OrderLogisticsDTO）：**

```json
{ "traceTime": "2026-05-28 18:00:00", "content": "到达拉萨中转站" }
```

**前置：** `shop_id` 匹配；`order_status='2'`。  
**效果：** 追加 `trace_type='2'`；**不改** order_status。

### 3.6 登记送达 `POST /{orderId}/delivered`

**Body（OrderDeliveredDTO）：**

```json
{ "traceTime": "2026-05-29 10:00:00", "content": "已送达小区门口" }
```

**前置：** `order_status='2'`。  
**效果：** 追加 `trace_type='3'`；**不改** order_status（O7）。

### 3.7 删除 `DELETE /{orderId}`

**前置：** `shop_id` 匹配；`order_status='4'`。  
**效果：** `order_status='5'`（逻辑删除，O9）。

### 3.8 业务错误 msg（示例）

| 场景 | msg |
|------|-----|
| 未选店铺 | 请先选择当前店铺（`ShopConstants.MSG_SHOP_CONTEXT_REQUIRED`） |
| 越权/不存在 | 订单不存在或无权访问 |
| 非待发货发货 | 当前状态不可发货 |
| 非已发货更新物流 | 当前状态不可更新物流 |
| 非已关闭删除 | 仅已关闭订单可删除 |
| 平台已发货 | 当前状态不可发货（并发） |

---

## 4. Service 分层（平台 + 商家共用）

```text
SellerOrderController
    → IOrderService.selectSellerList(shopId, query)
    → IOrderService.selectSellerDetail(shopId, orderId)
    → IOrderService.shipOrder(shopId, orderId, dto, operator)
    → IOrderService.appendLogistics(shopId, orderId, dto, operator)
    → IOrderService.registerDelivered(shopId, orderId, dto, operator)
    → IOrderService.deleteClosedOrder(shopId, orderId, operator)

OrderController（平台）
    → 同上方法，shopId=null，不校验店铺；closeOrder 仅平台
```

**归属校验（OrderSupport.assertOrderBelongsToShop）：**

```java
BizOrder order = orderMapper.selectById(orderId);
if (order == null || !shopId.equals(order.getShopId())) {
    throw new ServiceException("订单不存在或无权访问");
}
```

**列表 SQL 片段（BizOrderMapper.selectSellerList）：**

```sql
SELECT o.* FROM biz_order o
WHERE o.shop_id = #{shopId} AND o.del_flag = '0'
  <if test="orderStatus == null or orderStatus == ''">AND o.order_status != '5'</if>
  <if test="orderStatus != null and orderStatus != ''">AND o.order_status = #{orderStatus}</if>
  <!-- goodsName: EXISTS (SELECT 1 FROM biz_order_item i WHERE i.order_id=o.order_id AND i.goods_name LIKE ...) -->
ORDER BY o.create_time DESC
```

---

## 5. 菜单与权限

| 菜单 | 路由 | 组件 | 权限前缀 |
|------|------|------|----------|
| 全部订单 | `/agri/seller/order` | `agri/seller/order/index` | `agri:seller:order` |

| 权限 | 说明 |
|------|------|
| `agri:seller:order:list` | 列表、页签 |
| `agri:seller:order:query` | 详情 |
| `agri:seller:order:ship` | 发货、更新物流、登记送达 |
| `agri:seller:order:remove` | 删除已关闭单 |

> 员工账号须角色绑定上述权限；经营账号默认拥有商户侧菜单（按角色配置）。

---

## 6. 与平台方案对照

| 平台能力 | 平台路径 | 商家端 |
|----------|----------|--------|
| 列表 | `GET /agri/order/list` | `GET /agri/seller/order/list` + **shop_id** |
| 详情 | `GET /agri/order/{id}` | 同上 + 归属校验 |
| 发货 | `POST .../ship` | **同 Body/规则** |
| 物流/送达 | `POST .../logistics`、`/delivered` | 同上 |
| 关闭 | `PUT .../close` | **无** |
| 删除 | `DELETE .../{id}` | 同上 + shop_id |
| O8 定时关单 | `OrderPayTimeoutJob` | 商家 **只读** 结果 |
| O11 消费统计 | C 端 confirmReceive | 商家 **不参与** |

---

## 7. 测试要点

| 类型 | 要点 |
|------|------|
| 单测 | `OrderServiceImplTest`：shop 隔离、发货状态机、删除前置、双端并发 |
| 接口 | `SellerOrderControllerTest`：X-Shop-Id、越权、各状态操作、权限 |
| 详册 | 《全部订单测试用例.md》SAO-UT / SAO-API / SAO-UI（61 用例） |

---

## 8. 实现状态（v1.0.2）

| 项 | 状态 |
|----|------|
| DDL `sql/biz_order.sql` | **已提供**（待执行） |
| `com.ruoyi.web.modules.order` 包 | **待创建**（§1.3 目录规划） |
| `OrderServiceImpl` / 平台 `OrderController` | **待实现** |
| `SellerOrderController` | **待实现** |
| `IOrderFacade` | **桩**（`account.DefaultOrderFacade` / `store.DefaultShopOrderFacade`） |
| 菜单 SQL `agri_seller_order_menu.sql` | **待配置** |
| 单测 / 接口测 | **待实现**（见《全部订单测试用例》） |
| 前端页 | **未实现** |

---

## 9. 需求规则映射

| 规则 | 实现 |
|------|------|
| SO1 | 所有 SQL/Service 带 `shop_id` |
| SO2～SO7 | 共用平台状态机与 `OrderServiceImpl` |
| SO8 | `IOrderFacade` / `IShopOrderFacade` O10 |
| SO9 | 列表 VO 脱敏 |
| SO10 | 前端切换店铺 + `X-Shop-Id` 刷新 |
| SO11 | `SellerOrderQuery` 字段 |
| SO12 | `traceList` 倒序 |
| SO13 | `OrderShipDTO` 分支校验 |
| SO14 | 单表双 Controller，无副本 |
| SO15 | `@PreAuthorize` + 菜单 |

---

## 10. 非本期

| 项 | 说明 |
|----|------|
| 分批发货/拆单 | 平台 O5 整单；功能需求 §17 |
| 商家 close | 功能需求 §12 |
| 代确认收货 | C 端专责 |
| 支付/退款 | C 端 / 另册 |
| 发货管理独立页 | 可深链本模块 `orderStatus=1` |

---

## 11. 修订记录

| 版本 | 说明 |
|------|------|
| **v1.0.3** | 关联《全部订单功能需求》v1.1；对齐《发货管理功能需求》v1.2 整单履约表述 |
| **v1.0.2** | 明确代码落位 `com.ruoyi.web.modules.order` **暂不实现**；§8 实现状态与平台 v1.0.2 对齐 |
| **v1.0.1** | §2 字段说明补全；DDL 权威脚本 `sql/biz_order.sql` |
| **v1.0** | 首版；无新建表；商家 `/agri/seller/order` 六接口；共用 IOrderService |

---

*文档版本：v1.0.3 · MySQL 5.7.39 · RuoYi v3.9.2-springboot2 · 代码落位：`baqing-shop/src/main/java/com/ruoyi/web/modules/order/`（**待实现**）· DDL：[`sql/biz_order.sql`](../../../../sql/biz_order.sql) · 关联《全部订单功能需求.md》v1.1 · 平台侧见《订单管理技术方案.md》v1.0.2*
