订单管理 — 技术方案
依据: 《订单管理功能需求.md》v1.0.1
关联: 《会员管理技术方案.md》v1.0、《商品管理技术方案.md》v1.2、《店铺管理技术方案.md》v1.2.1、《关联需求分析.md》v1.2
范围: 本文以 订单库表、平台履约接口、定时关单、跨模块 Facade 为主;C 端下单/支付/确认收货给出 共用表与接口占位;支付渠道、退款、拆单 非本期。
原则: 整单发货;支付成功扣库存;O10 未完成订单;O11 完成后刷新会员消费。
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 模块落位
baqing-shop
├── com.ruoyi.web.controller.agri.OrderController # 平台
└── com.ruoyi.web.controller.api.OrderAppController # C 端占位
ruoyi-agri
├── domain.BizOrder / BizOrderItem / BizOrderLogisticsTrace
├── service.IOrderService
├── job.OrderPayTimeoutJob
└── facade.OrderFacadeImpl → IOrderFacade
1.2 业务链
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 |
本模块 |
店铺、商户 |
hasUnfinishedOrdersByShop(Long shopId) — status∈{0,1,2} |
|
|
|
hasUnfinishedOrdersByMerchant(Long merchantId) — 其下任一店存在未完成 |
|
|
|
pageOrdersByMember(Long memberId, OrderMemberQuery q) → TableDataInfo |
|
|
|
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):
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 |
— |
— |
2. 数据库设计
2.1 本模块表
| 表名 |
说明 |
biz_order |
订单主表 |
biz_order_item |
商品明细(快照) |
biz_order_logistics_trace |
物流节点时间轴 |
2.2 订单状态与字典
order_status(char(1))
| 值 |
含义 |
页签 |
| 0 |
待支付 |
待支付 |
| 1 |
待发货 |
待发货 |
| 2 |
已发货 |
已发货 |
| 3 |
已完成 |
已完成 |
| 4 |
已关闭 |
已关闭 |
| 5 |
已删除 |
已删除 |
close_type(关闭时必填,order_status=4)
| 值 |
含义 |
| 1 |
未支付超时(O8) |
| 2 |
买家取消 |
| 3 |
运营关闭 |
| 4 |
支付失败/退款(另册) |
delivery_type(发货时写入)
biz_order_logistics_trace.trace_type
| 值 |
含义 |
| 1 |
已发货 |
| 2 |
运输更新 |
| 3 |
已送达 |
| 4 |
确认收货 |
检索 shipStatus(Query): 与 order_status 映射 — 0/1/2 直接等值;「运输中」= order_status=2。
2.3 biz_order
| 字段 |
类型 |
说明 |
| order_id |
bigint PK |
|
| order_no |
varchar(32) |
唯一 |
| member_id |
bigint |
买家 |
| shop_id |
bigint |
店铺 |
| merchant_id |
bigint |
冗余,便于商户维度统计 |
| order_status |
char(1) |
§2.2 |
| close_type |
char(1) |
关闭类型 |
| 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) |
实付 = goods + freight(O11 统计字段) |
| consignee_name |
varchar(64) |
收货快照 |
| consignee_mobile |
varchar(20) |
|
| consignee_address |
varchar(512) |
完整地址文本 |
| delivery_type |
char(1) |
发货后填写 |
| logistics_company |
varchar(64) |
物流 |
| tracking_no |
varchar(64) |
快递单号 |
| vehicle_no |
varchar(32) |
商家配送 |
| courier_name |
varchar(64) |
配送员 |
| courier_mobile |
varchar(20) |
|
| ship_remark |
varchar(500) |
发货备注 |
| member_nick_name |
varchar(64) |
下单时会员昵称快照 |
| shop_name |
varchar(128) |
店铺名快照 |
| create_time |
datetime |
下单时间 |
| pay_time |
datetime |
支付时间 |
| ship_time |
datetime |
发货时间 |
| pay_expire_time |
datetime |
支付截止时间 |
| finish_time |
datetime |
确认收货时间 |
| del_flag |
char(1) |
0 存在;已删除 用 status=5,del_flag 仍 0(审计) |
| update_by/time, remark |
|
|
列表默认:order_status != '5';「已删除」页签 order_status='5'。
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) |
unit_price * quantity |
| 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) |
操作人 |
2.6 索引
| 表 |
索引 |
| biz_order |
uk_order_no;idx_member;idx_shop_status;idx_create_time;idx_pay_expire(status, pay_expire_time) |
| biz_order_item |
idx_order_id |
| biz_order_logistics_trace |
idx_order_id_time |
2.7 DDL
CREATE TABLE `biz_order` (
`order_id` bigint(20) NOT NULL AUTO_INCREMENT,
`order_no` varchar(32) NOT NULL COMMENT '订单编号',
`member_id` bigint(20) NOT NULL,
`shop_id` bigint(20) NOT NULL,
`merchant_id` bigint(20) NOT NULL,
`order_status` char(1) NOT NULL DEFAULT '0' COMMENT '0待支付1待发货2已发货3已完成4已关闭5已删除',
`close_type` char(1) DEFAULT NULL,
`close_reason` varchar(500) DEFAULT NULL,
`pay_type` char(1) DEFAULT NULL,
`pay_status` char(1) NOT NULL DEFAULT '0' COMMENT '0未付1已付',
`trade_no` varchar(64) DEFAULT NULL,
`goods_amount` decimal(12,2) NOT NULL DEFAULT '0.00',
`freight_amount` decimal(12,2) NOT NULL DEFAULT '0.00',
`pay_amount` decimal(12,2) NOT NULL DEFAULT '0.00',
`consignee_name` varchar(64) NOT NULL,
`consignee_mobile` varchar(20) NOT NULL,
`consignee_address` varchar(512) NOT NULL,
`delivery_type` char(1) DEFAULT NULL,
`logistics_company` varchar(64) DEFAULT NULL,
`tracking_no` varchar(64) DEFAULT NULL,
`vehicle_no` varchar(32) DEFAULT NULL,
`courier_name` varchar(64) DEFAULT NULL,
`courier_mobile` varchar(20) DEFAULT NULL,
`ship_remark` varchar(500) DEFAULT NULL,
`member_nick_name` varchar(64) DEFAULT NULL,
`shop_name` varchar(128) DEFAULT NULL,
`create_time` datetime NOT NULL,
`pay_time` datetime DEFAULT NULL,
`ship_time` datetime DEFAULT NULL,
`pay_expire_time` datetime NOT NULL,
`finish_time` datetime DEFAULT NULL,
`del_flag` char(1) NOT NULL DEFAULT '0',
`create_by` varchar(64) DEFAULT '',
`update_by` varchar(64) DEFAULT '',
`update_time` datetime DEFAULT NULL,
`remark` varchar(500) DEFAULT NULL,
PRIMARY KEY (`order_id`),
UNIQUE KEY `uk_order_no` (`order_no`),
KEY `idx_member` (`member_id`,`create_time`),
KEY `idx_shop_status` (`shop_id`,`order_status`),
KEY `idx_create_time` (`create_time`),
KEY `idx_pay_expire` (`order_status`,`pay_expire_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单';
CREATE TABLE `biz_order_item` (
`item_id` bigint(20) NOT NULL AUTO_INCREMENT,
`order_id` bigint(20) NOT NULL,
`goods_id` bigint(20) NOT NULL,
`goods_name` varchar(128) NOT NULL,
`goods_spec` varchar(64) DEFAULT '默认',
`goods_image` varchar(512) DEFAULT NULL,
`unit_price` decimal(12,2) NOT NULL,
`quantity` int(11) NOT NULL,
`line_amount` decimal(12,2) NOT NULL,
`sort_no` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`item_id`),
KEY `idx_order_id` (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单明细';
CREATE TABLE `biz_order_logistics_trace` (
`trace_id` bigint(20) NOT NULL AUTO_INCREMENT,
`order_id` bigint(20) NOT NULL,
`trace_type` char(1) NOT NULL,
`trace_time` datetime NOT NULL,
`content` varchar(1000) NOT NULL DEFAULT '',
`create_by` varchar(64) DEFAULT '',
`create_time` datetime DEFAULT NULL,
PRIMARY KEY (`trace_id`),
KEY `idx_order_id_time` (`order_id`,`trace_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='物流节点';
2.8 字典
| dict_type |
用途 |
| biz_order_status |
order_status |
| biz_order_close_type |
close_type |
| biz_delivery_type |
delivery_type |
| biz_order_pay_type |
pay_type |
| biz_order_pay_status |
pay_status |
| biz_logistics_trace_type |
trace_type |
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、logistics(当前单号/配送员)、traceList(时间倒序)、closeInfo。
3.4 发货 POST /{orderId}/ship
前置: order_status=1。
{
"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
{ "traceTime": "2026-05-28 18:00:00", "content": "到达拉萨中转站" }
前置: order_status=2;追加 trace_type=2。
3.6 登记送达 POST /{orderId}/delivered
{ "traceTime": "2026-05-29 10:00:00", "content": "已送达小区门口" }
追加 trace_type=3;不 改 order_status(仍为 2,O7)。
3.7 关闭 PUT /{orderId}/close
{ "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)
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)
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 |
| 商家端订单后台 |
平台代履约 |
| 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 |
| O15 |
delivery_type 分支校验 |
文档版本:v1.0 · MySQL 5.7.39 · RuoYi v3.9.2-springboot2 · 关联《订单管理功能需求.md》v1.0.1、《订单管理测试用例.md》v1.0、《会员管理技术方案.md》v1.0、《关联需求分析.md》v1.2