# 订单管理 — 技术方案

> **依据：** 《订单管理功能需求.md》v1.0.1  
> **关联：** 《会员管理技术方案.md》v1.0、《商品管理技术方案.md》v1.2、《店铺管理技术方案.md》v1.2.1、《关联需求分析.md》v1.2  
> **范围：** 本文以 **订单库表、平台履约接口、定时关单、跨模块 Facade** 为主；C 端下单/支付/确认收货给出 **共用表与接口占位**；支付渠道、退款、拆单 **非本期**。  
> **原则：** 整单发货；支付成功扣库存；**O10** 未完成订单；**O11** 完成后刷新会员消费。  
> **v1.0.1：** 完善 §2 表字段说明；**权威 DDL** 迁至 [`sql/biz_order.sql`](../../../sql/biz_order.sql)（全字段 COMMENT）；商家端见《全部订单技术方案》v1.0.1。  
> **v1.0.2：** 后端统一落位 **`com.ruoyi.web.modules.order`**（**待实现**）；废止原 `ruoyi-agri` / `controller.agri` 写法。

---

## 1. 技术架构

| 项 | 选型 |
|----|------|
| 基础框架 | RuoYi **v3.9.2**（`springboot2` 分支） |
| 数据库 | **MySQL 5.7.39** |
| ORM / 权限 / 响应 | MyBatis；`@PreAuthorize`；`AjaxResult` / `TableDataInfo` |
| 订单号 | Redis 或 DB 序列：`O` + `yyyyMMdd` + 6 位序号（应用保证唯一） |
| 定时任务 | RuoYi `Quartz`：`OrderPayTimeoutJob`（O8） |
| 配置 | `sys_config`：`order.pay.timeout.minutes`，默认 **1440** |
| 日志 | 发货/关闭/删除 `@Log` |

### 1.1 模块落位

**包路径（定稿 · 待实现）：** `com.ruoyi.web.modules.order`  
**工程目录：** `baqing-shop/src/main/java/com/ruoyi/web/modules/order/`  
**MyBatis XML：** `baqing-shop/src/main/resources/mapper/order/`

```text
com.ruoyi.web.modules.order/
├── controller/
│   ├── OrderController.java           # 平台 /agri/order
│   ├── OrderAppController.java        # C 端 /api/order（占位）
│   └── SellerOrderController.java     # 商家 /agri/seller/order（见《全部订单技术方案》）
├── service/
│   ├── IOrderService.java
│   └── impl/OrderServiceImpl.java     # 平台 + 商家共用
├── support/OrderSupport.java
├── constant/OrderConstants.java
├── domain/
│   ├── BizOrder.java
│   ├── BizOrderItem.java
│   └── BizOrderLogisticsTrace.java
├── dto/、vo/
├── mapper/
│   ├── BizOrderMapper.java
│   ├── BizOrderItemMapper.java
│   └── BizOrderLogisticsTraceMapper.java
├── facade/
│   ├── IOrderFacade.java              # O10、会员只读订单
│   └── impl/OrderFacadeImpl.java
└── job/OrderPayTimeoutJob.java        # O8
```

| 维度 | 说明 |
|------|------|
| 平台 API | `OrderController` → `/agri/order` |
| 商家 API | `SellerOrderController` → `/agri/seller/order` |
| DDL | [`sql/biz_order.sql`](../../../sql/biz_order.sql) |
| 实现状态 | **代码暂未创建**；见 §1.5 |

> **过渡期桩（实现前）：** `account.facade.IOrderFacade` + `DefaultOrderFacade`（会员订单空列表）；`store.facade.IShopOrderFacade` + `DefaultShopOrderFacade`（删店 O10 恒 false）。**接入后迁入** `order.facade` 并删除桩。

### 1.2 业务链

```text
C 端下单 → biz_order(待支付) + biz_order_item(快照)
    ↓ 支付成功
待发货 + IGoodsFacade.deductStock（按行）
    ↓ 平台发货
已发货 + biz_order_logistics_trace
    ↓ C 端确认收货
已完成 + IMemberFacade.refreshConsumption(memberId)
```

| 模块 | 关系 |
|------|------|
| **会员** | `member_id`；`IMemberFacade.isMemberEnabled`；完成后 `refreshConsumption` |
| **店铺** | `shop_id`；`IShopFacade.isShopOpenForOrder`（C 端下单） |
| **商品** | `IGoodsFacade.canPurchase` / `deductStock` / `restoreStock`（关闭回滚） |
| **商户/店铺删除** | `IOrderFacade.hasUnfinishedOrdersByShop/Merchant`（O10） |

### 1.3 跨模块 Facade

| 接口 | 提供方 | 调用方 | 方法 |
|------|--------|--------|------|
| **`IOrderFacade`** | **`com.ruoyi.web.modules.order`**（待实现） | 店铺、商户、会员 | 见下表 |

**`IOrderFacade` 方法：**

| 方法 | 说明 |
|------|------|
| `hasUnfinishedOrdersByShop(Long shopId)` | O10；status∈{0,1,2} |
| `hasUnfinishedOrdersByMerchant(Long merchantId)` | 其下任一店存在未完成 |
| `pageOrdersByMember(Long memberId, MemberOrderQueryDTO q)` | 会员管理只读列表 |
| `getOrderDetail(Long orderId, Long memberId)` | `memberId=null` 平台；非 null 校验归属 |

| 接口 | 提供方 | 调用方 | 方法 |
|------|--------|--------|------|
| **`IMemberFacade`** | 会员 | **本模块** | `isMemberEnabled`、`getMember`（下单） |
| | | | `refreshConsumption(Long memberId)` — **已完成** 后重算 O11 |
| **`IGoodsFacade`** | 商品 | **本模块** | `canPurchase(goodsId, qty)`；`deductStock`；`restoreStock`（已支付关闭） |
| **`IShopFacade`** | 店铺 | **本模块** | `isShopOpenForOrder(shopId)` |

**未完成订单 SQL（O10）：**

```sql
SELECT 1 FROM biz_order
WHERE shop_id = #{shopId} AND order_status IN ('0','1','2') AND del_flag = '0'
LIMIT 1
```

### 1.4 状态联动

| 事件 | 订单 | 库存 | 会员消费 |
|------|------|------|----------|
| 支付成功 | 0→1 | 扣减 | — |
| 待发货关闭 | →4 | 回滚 | — |
| 超时未付 | 0→4 | 无 | — |
| 确认收货 | 2→3 | — | refreshConsumption |
| 已关闭删除 | 4→5 | — | — |

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

| 项 | 状态 |
|----|------|
| DDL `sql/biz_order.sql` | **已提供**（待执行） |
| `com.ruoyi.web.modules.order` 包 | **待创建**（本文 §1.1 为目录规划） |
| 平台 `OrderController` / C 端占位 | **待实现** |
| `OrderServiceImpl` / `OrderPayTimeoutJob` | **待实现** |
| `IOrderFacade` 正式实现 | **待实现**（当前见 account/store 桩） |
| 商家 `SellerOrderController` | **待实现**（见《全部订单技术方案》） |
| 前端 | **未实现** |

---

## 2. 数据库设计

> **权威 DDL：** [`sql/biz_order.sql`](../../../sql/biz_order.sql) — 三表 **全字段 COMMENT**、索引与本文一致；**以 SQL 脚本为准** 建表，下文为设计说明。

### 2.1 本模块表

| 表名 | 说明 | 读写方 |
|------|------|--------|
| `biz_order` | 订单主表：状态、金额、收货快照、履约字段 | 平台/C 端/商家端 **同源** |
| `biz_order_item` | 商品明细：**下单快照**，不随商品后续变更 | 创建订单写入；列表/详情只读 |
| `biz_order_logistics_trace` | 物流节点时间轴：**追加**不覆盖 | 发货/物流/送达/确认收货写入 |

### 2.2 枚举与字典

**`order_status`（char(1)）— 字段 `biz_order.order_status`**

| 值 | 含义 | 平台页签 | 商家页签 |
|----|------|----------|----------|
| 0 | 待支付 | 待支付 | **全部**（无独立页签） |
| 1 | 待发货 | 待发货 | 待发货 |
| 2 | 已发货 | 已发货 | 已发货 |
| 3 | 已完成 | 已完成 | 已完成 |
| 4 | 已关闭 | 已关闭 | 已关闭 |
| 5 | 已删除 | 已删除 | 已删除 |

> **逻辑删除：** `order_status='5'` 表示已删除；`del_flag` **仍保持 `'0'`** 供审计；列表默认 `order_status != '5'`。

**`close_type` — 仅 `order_status=4` 时必填**

| 值 | 含义 | 典型触发 |
|----|------|----------|
| 1 | 未支付超时 | O8 定时任务 |
| 2 | 买家取消 | C 端 |
| 3 | 运营关闭 | 平台手工 |
| 4 | 支付失败/退款 | 另册 |

**`pay_status`**

| 值 | 含义 |
|----|------|
| 0 | 未支付 |
| 1 | 已支付 |

**`delivery_type` — 发货时写入主表**

| 值 | 含义 | 发货必填字段 |
|----|------|--------------|
| 1 | 物流配送 | logistics_company + tracking_no |
| 2 | 商家配送 | vehicle_no + courier_name + courier_mobile |

**`biz_order_logistics_trace.trace_type`**

| 值 | 含义 | 写入时机 |
|----|------|----------|
| 1 | 已发货 | POST ship |
| 2 | 运输更新 | POST logistics（可多次） |
| 3 | 已送达 | POST delivered |
| 4 | 确认收货 | C 端 confirmReceive |

**检索 `shipStatus`（Query）：** 映射 `order_status` 的 `0/1/2`；「运输中」= `order_status=2`。

### 2.3 `biz_order` 字段说明

| 字段 | 类型 | 必填 | 说明 |
|------|------|:----:|------|
| order_id | bigint PK | 是 | 主键自增 |
| order_no | varchar(32) | 是 | 全平台唯一；`O`+日期+序号 |
| member_id | bigint | 是 | 买家 `biz_member.member_id` |
| shop_id | bigint | 是 | 店铺；删店 O10、商家端隔离键 |
| merchant_id | bigint | 是 | 商户冗余；删商户校验 |
| order_status | char(1) | 是 | §2.2；默认 `0` |
| close_type | char(1) | 关单时 | status=4 时填 |
| close_reason | varchar(500) | 否 | 关闭说明 |
| pay_type | char(1) | 否 | 支付方式字典 |
| pay_status | char(1) | 是 | 0未付 1已付 |
| trade_no | varchar(64) | 否 | 支付流水号 |
| goods_amount | decimal(12,2) | 是 | 商品合计 |
| freight_amount | decimal(12,2) | 是 | 运费，默认 0 |
| pay_amount | decimal(12,2) | 是 | 实付；**O11** 统计 |
| consignee_name | varchar(64) | 是 | 收货人快照 |
| consignee_mobile | varchar(20) | 是 | 收货手机快照；列表脱敏 |
| consignee_address | varchar(512) | 是 | 收货地址快照 |
| delivery_type | char(1) | 发货时 | 1物流 2商家配送 |
| logistics_company | varchar(64) | 物流时 | 物流公司 |
| tracking_no | varchar(64) | 物流时 | 快递单号 |
| vehicle_no | varchar(32) | 商家配送时 | 车牌/车号 |
| courier_name | varchar(64) | 商家配送时 | 配送员 |
| courier_mobile | varchar(20) | 商家配送时 | 配送员手机 |
| ship_remark | varchar(500) | 否 | 发货备注 |
| shipper_name | varchar(64) | 发货时 | 发货人快照（自配送设置） |
| shipper_mobile | varchar(20) | 发货时 | 发货人联系方式快照 |
| ship_region_code | varchar(64) | 发货时 | 发货地区编码快照 |
| ship_region_name | varchar(128) | 发货时 | 发货地区名称快照 |
| ship_detail_address | varchar(256) | 发货时 | 发货详细地址快照 |
| member_nick_name | varchar(64) | 否 | 会员昵称快照 |
| shop_name | varchar(128) | 否 | 店铺名快照 |
| create_time | datetime | 是 | 下单时间 |
| pay_time | datetime | 支付后 | 支付成功时间 |
| ship_time | datetime | 发货后 | 发货时间 |
| pay_expire_time | datetime | 是 | 支付截止；O8 扫描 |
| finish_time | datetime | 完成后 | 确认收货时间 |
| del_flag | char(1) | 是 | 固定 `0`；删单用 status=5 |
| create_by / update_by / update_time / remark | | | RuoYi 审计 |

### 2.4 `biz_order_item` 字段说明

| 字段 | 类型 | 必填 | 说明 |
|------|------|:----:|------|
| item_id | bigint PK | 是 | 主键 |
| order_id | bigint | 是 | 所属订单 |
| goods_id | bigint | 是 | 原商品 ID；库存扣减/回滚 |
| goods_name | varchar(128) | 是 | 名称快照 |
| goods_spec | varchar(64) | 否 | 规格快照，默认「默认」 |
| goods_image | varchar(512) | 否 | 主图 URL 快照 |
| unit_price | decimal(12,2) | 是 | 单价快照 |
| quantity | int | 是 | 数量 |
| line_amount | decimal(12,2) | 是 | 行小计 |
| sort_no | int | 是 | 展示排序 |

### 2.5 `biz_order_logistics_trace` 字段说明

| 字段 | 类型 | 必填 | 说明 |
|------|------|:----:|------|
| trace_id | bigint PK | 是 | 主键 |
| order_id | bigint | 是 | 所属订单 |
| trace_type | char(1) | 是 | §2.2 节点类型 |
| trace_time | datetime | 是 | 业务发生时间 |
| content | varchar(1000) | 是 | 描述/摘要 |
| create_by | varchar(64) | 否 | 操作人 |
| create_time | datetime | 否 | 写入时间 |

### 2.6 索引

| 表 | 索引 | 用途 |
|----|------|------|
| biz_order | uk_order_no | 订单号唯一 |
| biz_order | idx_member (member_id, create_time) | 会员订单列表 |
| biz_order | idx_shop_status (shop_id, order_status) | **商家端页签** + 删店 O10 |
| biz_order | idx_create_time | 时间排序/区间检索 |
| biz_order | idx_pay_expire (order_status, pay_expire_time) | O8 超时关单扫描 |
| biz_order_item | idx_order_id | 明细/商品名检索 |
| biz_order_logistics_trace | idx_order_id_time | 详情物流轴 |

### 2.7 DDL

**执行：** `sql/biz_order.sql`（与 §2.3～2.6 一致，含完整 `COMMENT`）。

```text
sql/biz_order.sql
├── biz_order                  COMMENT='订单主表（平台+商家端同源）'
├── biz_order_item             COMMENT='订单商品明细（下单快照）'
└── biz_order_logistics_trace  COMMENT='订单物流节点时间轴'
```

### 2.8 字典（sys_dict_type）

| dict_type | 绑定字段 | 说明 |
|-----------|----------|------|
| biz_order_status | order_status | 0～5 |
| biz_order_close_type | close_type | 1～4 |
| biz_delivery_type | delivery_type | 1物流 2商家配送 |
| biz_order_pay_type | pay_type | 支付方式 |
| biz_order_pay_status | pay_status | 0/1 |
| biz_logistics_trace_type | trace_type | 1～4 |

---

## 3. 平台端接口设计

**基路径：** `/agri/order`  
**权限：** `agri:order:list|query|ship|edit|remove`

### 3.1 接口一览

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

### 3.2 列表 `GET /list`

| Query | 说明 |
|-------|------|
| pageNum, pageSize | |
| orderStatus | 页签：空=全部（排除5）；0～5 |
| orderNo | 订单编号 |
| goodsName | 关联 item 模糊 |
| shipStatus | 同 orderStatus 履约态（0/1/2） |
| consigneeName, consigneeMobile, consigneeAddress | |
| deliveryType | 1/2（已发货后有值） |
| payType, payStatus | |
| tradeNo | |
| memberKeyword | 会员昵称/手机号 |
| shopName | 店铺名模糊 |
| logisticsCompany | |
| beginTime, endTime | create_time |

**排序：** `create_time DESC`。  
**默认过滤：** 未传 `orderStatus` 时 `order_status != '5'`。

**rows（摘要）：**

| 字段 | 说明 |
|------|------|
| orderId, orderNo, createTime | |
| orderStatus, orderStatusLabel | |
| payAmount, freightAmount | |
| memberNickName, consigneeName, consigneeMobile | 手机脱敏 |
| consigneeAddress, deliveryTypeLabel | |
| shopName | |
| items | 数组：goodsImage, goodsName, goodsSpec, quantity, unitPrice（首行+itemCount） |

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

含 §需求 6.2 各分区：`base`、`items`、`consignee`、**`shipper`（发货快照）**、`logistics`（当前单号/配送员）、`traceList`（时间倒序）、`closeInfo`。

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

**前置：** `order_status=1`。

```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 |

**事务：** `order_status=2`；写 ship 字段；插入 trace_type=1；**不** 再扣库存。

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

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

**前置：** `order_status=2`；追加 trace_type=2。

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

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

追加 trace_type=3；**不** 改 order_status（仍为 2，O7）。

### 3.7 关闭 `PUT /{orderId}/close`

```json
{ "closeType": "3", "closeReason": "运营协商关闭" }
```

| 前置 status | 副作用 |
|-------------|--------|
| 0 | 无库存操作 |
| 1 | `IGoodsFacade.restoreStock` 按 item 行 |
| 2 | 同上（若已发货关闭，本期仅记原因，退款另册） |

→ `order_status=4`，写 close_type/reason。

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

**前置：** `order_status=4` → `order_status=5`。

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

| 场景 | msg |
|------|-----|
| 状态不允许发货 | 当前状态不可发货 |
| 非已关闭删单 | 仅已关闭订单可删除 |
| 库存回滚失败 | 库存回滚失败，请稍后重试 |

---

## 4. C 端接口（占位）

**基路径：** `/api/order`（会员 Token）

| 方法 | 路径 | 说明 |
|------|------|------|
| POST | `/create` | 校验 canPurchase + isMemberEnabled + isShopOpen；写 order/item；status=0；`pay_expire_time=now+timeout` |
| POST | `/{orderId}/pay` | 模拟/渠道回调：status→1，pay_status=1，deductStock，pay_time |
| POST | `/{orderId}/confirmReceive` | status→2 时：→3，trace_type=4，refreshConsumption |
| GET | `/list` | 当前会员订单 |
| GET | `/{orderId}` | 详情（校验 member_id） |

| 规则 | 说明 |
|------|------|
| 待支付关闭后 | 支付接口拒绝，msg 订单已关闭 |
| 拆单 | **不支持** |

---

## 5. 定时任务（O8）

```text
OrderPayTimeoutJob（如每 5 分钟）
  SELECT order_id FROM biz_order
   WHERE order_status='0' AND pay_expire_time < NOW() AND del_flag='0'
  FOR EACH → close_type=1, order_status=4（批量 UPDATE）
```

`pay_expire_time` 在 **创建订单** 时：`create_time + ConfigService.getInt("order.pay.timeout.minutes", 1440)` 分钟。

---

## 6. 会员模块协作（O11）

```text
confirmReceive / 平台不代确认
  @Transactional
  → UPDATE biz_order SET order_status='3', finish_time=NOW()
  → INSERT trace_type=4
  → IMemberFacade.refreshConsumption(memberId)
```

`refreshConsumption` 实现见《会员管理技术方案》§5.2（按 member_id 重算已完成 SUM）。

**`pageOrdersByMember` / `getOrderDetail`：** 与 §3.2、§3.3 共用 Service；`memberId` 强制 `WHERE member_id=?`。

---

## 7. 菜单与权限

| 权限 | 说明 |
|------|------|
| agri:order:list | 列表 |
| agri:order:query | 详情 |
| agri:order:ship | 发货/物流/送达 |
| agri:order:edit | 关闭 |
| agri:order:remove | 删除 |

---

## 8. 非本期

| 项 | 说明 |
|----|------|
| 分批发货/拆单 | 需求 §17 |
| 支付渠道、退款 | 另册；`pay` 可先 Mock |
| 商家端订单后台 | 见《全部订单技术方案》v1.0.1 |
| Excel 批量发货/打印 | 需求 §17 |

---

## 9. 需求规则映射

| 规则 | 实现 |
|------|------|
| O1～O3 | biz_order + 字典 |
| O4 | pay 成功 deductStock |
| O5 | ship 整单一次 → status=2 |
| O6～O7 | logistics trace；完成仅 C 端 confirm |
| O8 | Job + pay_expire_time |
| O9 | DELETE status 4→5 |
| O10 | Facade IN (0,1,2) |
| O11 | refreshConsumption |
| O12 | 写接口：`OrderController`（平台）+ `SellerOrderController`（商家，带 shopId） |
| O15 | delivery_type 分支校验 |

---

## 10. 修订记录

| 版本 | 说明 |
|------|------|
| **v1.0** | 首版 |
| **v1.0.1** | §2 字段说明补全；权威 DDL 迁至 `sql/biz_order.sql` |
| **v1.0.2** | 后端落位定稿 `com.ruoyi.web.modules.order`（**待实现**）；废止 ruoyi-agri；§1.5 实现状态 |
| **v1.0.3** | `biz_order` 新增发货人/地址快照字段；升级脚本 `biz_order_ship_snapshot.sql` |

---

*文档版本：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)、[`sql/biz_order_ship_snapshot.sql`](../../../sql/biz_order_ship_snapshot.sql) · 关联《订单管理功能需求.md》v1.0.1、《全部订单技术方案.md》v1.0.2*
