跳到主要内容

OMS 订单管理系统设计

一、系统概述

1.1 系统简介

OMS(Order Management System)订单管理系统是一个专注于订单全生命周期管理的系统,涵盖订单接收、订单处理、库存分配、订单履约、售后服务等核心环节,支持多渠道、多仓库、多商家的复杂业务场景。

1.2 核心价值

价值点说明预期效果
高并发处理支持秒杀大促场景峰值 10000+ TPS
🎯 智能分单智能订单分配策略履约效率提升 45%
🔄 全链路追踪订单状态实时同步客户满意度提升 40%
📊 数据一致性强一致性保证数据准确率 99.99%
🚀 快速响应毫秒级订单处理响应时间 < 100ms

1.3 业务需求

核心功能

  • 订单接收:多渠道订单接入、订单验证、订单拆分合并
  • 订单处理:订单审核、风控检查、支付确认、库存锁定
  • 订单履约:订单分配、拣货打包、物流发货、配送跟踪
  • 售后服务:退货退款、换货补发、售后审核
  • 订单查询:订单状态查询、物流跟踪、订单报表
  • 异常处理:超时处理、异常订单、补偿机制

非功能需求

  • 高并发:支持秒杀、大促场景,峰值 10000+ TPS
  • 高可用:系统可用性 99.99%
  • 实时性:订单状态实时更新
  • 可扩展:支持多渠道、多仓库扩展
  • 数据一致性:保证订单数据强一致性

二、系统架构

2.1 技术架构

2.2 订单全流程

分库分表策略

订单表分库分表

  • 分库:按用户ID取模分 8 个库
  • 分表:每个库按订单创建时间分 12 张表(按月)
  • 路由规则db = userId % 8, table = month % 12
sql
1-- 示例:订单表分表
2order_0, order_1, order_2, ..., order_11 (12张表)

核心服务

1. 订单服务 (oms-order-service)

java
1- 订单创建 (Order Creation)
2- 订单拆分 (Order Split)
3- 订单合并 (Order Merge)
4- 订单验证 (Order Validation)
5- 订单查询 (Order Query)
6- 订单取消 (Order Cancel)

2. 履约服务 (oms-fulfillment-service)

java
1- 订单分配 (Order Allocation)
2- 库存锁定 (Inventory Lock)
3- 拣货管理 (Picking)
4- 打包管理 (Packing)
5- 发货管理 (Shipping)
6- 配送跟踪 (Delivery Tracking)

3. 售后服务 (oms-aftersale-service)

java
1- 退货申请 (Return Request)
2- 退款申请 (Refund Request)
3- 换货申请 (Exchange Request)
4- 售后审核 (After-sale Approval)
5- 补偿处理 (Compensation)

4. 支付服务 (oms-payment-service)

java
1- 支付创建 (Payment Creation)
2- 支付回调 (Payment Callback)
3- 退款处理 (Refund)
4- 支付查询 (Payment Query)

数据模型设计

核心表结构

1. 订单主表 (order_main)

sql
1CREATE TABLE order_main_{month} (
2 id BIGINT PRIMARY KEY COMMENT '订单ID(雪花算法)',
3 order_no VARCHAR(50) NOT NULL UNIQUE COMMENT '订单号',
4 parent_order_no VARCHAR(50) COMMENT '父订单号(拆单场景)',
5 user_id BIGINT NOT NULL COMMENT '用户ID',
6 channel TINYINT NOT NULL COMMENT '订单渠道:1-PC,2-H5,3-小程序,4-APP,5-第三方平台',
7 channel_order_no VARCHAR(50) COMMENT '渠道订单号',
8 order_type TINYINT DEFAULT 1 COMMENT '订单类型:1-普通订单,2-预售订单,3-秒杀订单,4-拼团订单',
9 order_source TINYINT COMMENT '订单来源:1-自营,2-京东,3-天猫,4-拼多多',
10 total_amount DECIMAL(15,2) NOT NULL COMMENT '订单总额',
11 discount_amount DECIMAL(15,2) DEFAULT 0 COMMENT '优惠金额',
12 freight_amount DECIMAL(15,2) DEFAULT 0 COMMENT '运费',
13 pay_amount DECIMAL(15,2) NOT NULL COMMENT '实付金额',
14 order_status TINYINT DEFAULT 1 COMMENT '订单状态:1-待付款,2-待发货,3-待收货,4-已完成,5-已取消,6-售后中',
15 pay_status TINYINT DEFAULT 0 COMMENT '支付状态:0-未支付,1-已支付,2-退款中,3-已退款',
16 pay_type TINYINT COMMENT '支付方式:1-微信,2-支付宝,3-银联,4-余额',
17 pay_time DATETIME COMMENT '支付时间',
18 consignee_name VARCHAR(50) NOT NULL COMMENT '收货人',
19 consignee_mobile VARCHAR(20) NOT NULL COMMENT '收货手机',
20 consignee_province VARCHAR(50) COMMENT '收货省份',
21 consignee_city VARCHAR(50) COMMENT '收货城市',
22 consignee_district VARCHAR(50) COMMENT '收货区县',
23 consignee_address VARCHAR(200) NOT NULL COMMENT '收货详细地址',
24 buyer_message VARCHAR(500) COMMENT '买家留言',
25 seller_remark VARCHAR(500) COMMENT '卖家备注',
26 is_timeout TINYINT DEFAULT 0 COMMENT '是否超时:0-否,1-是',
27 cancel_reason VARCHAR(200) COMMENT '取消原因',
28 cancel_time DATETIME COMMENT '取消时间',
29 ship_time DATETIME COMMENT '发货时间',
30 finish_time DATETIME COMMENT '完成时间',
31 create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
32 update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
33 INDEX idx_order_no (order_no),
34 INDEX idx_user (user_id, create_time),
35 INDEX idx_status (order_status),
36 INDEX idx_create_time (create_time)
37) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单主表';

2. 订单明细表 (order_detail)

sql
1CREATE TABLE order_detail_{month} (
2 id BIGINT PRIMARY KEY AUTO_INCREMENT,
3 order_id BIGINT NOT NULL COMMENT '订单ID',
4 order_no VARCHAR(50) NOT NULL COMMENT '订单号',
5 product_id BIGINT NOT NULL COMMENT '商品ID',
6 sku_id BIGINT NOT NULL COMMENT 'SKU ID',
7 product_code VARCHAR(50) COMMENT '商品编码',
8 product_name VARCHAR(200) NOT NULL COMMENT '商品名称',
9 sku_name VARCHAR(200) COMMENT 'SKU名称',
10 product_image VARCHAR(500) COMMENT '商品图片',
11 category_id BIGINT COMMENT '分类ID',
12 brand_id BIGINT COMMENT '品牌ID',
13 unit_price DECIMAL(10,2) NOT NULL COMMENT '单价',
14 quantity INT NOT NULL COMMENT '数量',
15 discount_amount DECIMAL(10,2) DEFAULT 0 COMMENT '优惠金额',
16 total_amount DECIMAL(15,2) NOT NULL COMMENT '总金额',
17 is_gift TINYINT DEFAULT 0 COMMENT '是否赠品:0-否,1-是',
18 promotion_id BIGINT COMMENT '促销活动ID',
19 warehouse_id BIGINT COMMENT '发货仓库ID',
20 refund_status TINYINT DEFAULT 0 COMMENT '退款状态:0-无,1-退款中,2-已退款',
21 refund_quantity INT DEFAULT 0 COMMENT '退款数量',
22 create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
23 INDEX idx_order (order_id),
24 INDEX idx_order_no (order_no),
25 INDEX idx_sku (sku_id)
26) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单明细表';

3. 订单履约表 (order_fulfillment)

sql
1CREATE TABLE order_fulfillment (
2 id BIGINT PRIMARY KEY AUTO_INCREMENT,
3 order_id BIGINT NOT NULL COMMENT '订单ID',
4 order_no VARCHAR(50) NOT NULL COMMENT '订单号',
5 warehouse_id BIGINT NOT NULL COMMENT '仓库ID',
6 warehouse_name VARCHAR(100) COMMENT '仓库名称',
7 allocation_time DATETIME COMMENT '分配时间',
8 lock_status TINYINT DEFAULT 0 COMMENT '锁库状态:0-未锁定,1-已锁定,2-已释放',
9 lock_time DATETIME COMMENT '锁定时间',
10 pick_status TINYINT DEFAULT 0 COMMENT '拣货状态:0-待拣货,1-拣货中,2-已拣货',
11 pick_time DATETIME COMMENT '拣货时间',
12 pack_status TINYINT DEFAULT 0 COMMENT '打包状态:0-待打包,1-打包中,2-已打包',
13 pack_time DATETIME COMMENT '打包时间',
14 ship_status TINYINT DEFAULT 0 COMMENT '发货状态:0-待发货,1-已发货',
15 ship_time DATETIME COMMENT '发货时间',
16 logistics_company VARCHAR(50) COMMENT '物流公司',
17 logistics_no VARCHAR(50) COMMENT '物流单号',
18 create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
19 update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
20 UNIQUE KEY uk_order (order_id),
21 INDEX idx_order_no (order_no),
22 INDEX idx_warehouse (warehouse_id),
23 INDEX idx_ship_status (ship_status)
24) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单履约表';

4. 订单状态流转表 (order_status_log)

sql
1CREATE TABLE order_status_log (
2 id BIGINT PRIMARY KEY AUTO_INCREMENT,
3 order_id BIGINT NOT NULL COMMENT '订单ID',
4 order_no VARCHAR(50) NOT NULL COMMENT '订单号',
5 before_status TINYINT COMMENT '变更前状态',
6 after_status TINYINT NOT NULL COMMENT '变更后状态',
7 operator_type TINYINT COMMENT '操作类型:1-用户,2-系统,3-管理员',
8 operator_id BIGINT COMMENT '操作人ID',
9 operator_name VARCHAR(50) COMMENT '操作人',
10 remark VARCHAR(500) COMMENT '备注',
11 create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
12 INDEX idx_order (order_id),
13 INDEX idx_order_no (order_no),
14 INDEX idx_create_time (create_time)
15) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单状态流转表';

5. 售后单表 (aftersale_order)

sql
1CREATE TABLE aftersale_order (
2 id BIGINT PRIMARY KEY AUTO_INCREMENT,
3 aftersale_no VARCHAR(50) NOT NULL UNIQUE COMMENT '售后单号',
4 order_id BIGINT NOT NULL COMMENT '订单ID',
5 order_no VARCHAR(50) NOT NULL COMMENT '订单号',
6 order_detail_id BIGINT NOT NULL COMMENT '订单明细ID',
7 user_id BIGINT NOT NULL COMMENT '用户ID',
8 aftersale_type TINYINT NOT NULL COMMENT '售后类型:1-仅退款,2-退货退款,3-换货',
9 reason_type TINYINT COMMENT '原因类型:1-质量问题,2-发错货,3-不想要了,4-其他',
10 reason_desc VARCHAR(500) COMMENT '原因描述',
11 refund_amount DECIMAL(10,2) NOT NULL COMMENT '退款金额',
12 quantity INT NOT NULL COMMENT '数量',
13 status TINYINT DEFAULT 1 COMMENT '状态:1-待审核,2-审核通过,3-待退货,4-退货中,5-已完成,6-已拒绝,7-已取消',
14 approve_time DATETIME COMMENT '审核时间',
15 approver_id BIGINT COMMENT '审核人ID',
16 reject_reason VARCHAR(200) COMMENT '拒绝原因',
17 return_logistics_company VARCHAR(50) COMMENT '退货物流公司',
18 return_logistics_no VARCHAR(50) COMMENT '退货物流单号',
19 receive_time DATETIME COMMENT '收货时间',
20 refund_time DATETIME COMMENT '退款时间',
21 finish_time DATETIME COMMENT '完成时间',
22 create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
23 update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
24 INDEX idx_aftersale_no (aftersale_no),
25 INDEX idx_order (order_id),
26 INDEX idx_user (user_id),
27 INDEX idx_status (status)
28) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='售后单表';

6. 支付单表 (payment_order)

sql
1CREATE TABLE payment_order (
2 id BIGINT PRIMARY KEY AUTO_INCREMENT,
3 payment_no VARCHAR(50) NOT NULL UNIQUE COMMENT '支付单号',
4 order_no VARCHAR(50) NOT NULL COMMENT '订单号',
5 user_id BIGINT NOT NULL COMMENT '用户ID',
6 pay_type TINYINT NOT NULL COMMENT '支付方式:1-微信,2-支付宝,3-银联,4-余额',
7 pay_amount DECIMAL(15,2) NOT NULL COMMENT '支付金额',
8 pay_status TINYINT DEFAULT 0 COMMENT '支付状态:0-待支付,1-支付中,2-支付成功,3-支付失败,4-已取消',
9 third_party_no VARCHAR(100) COMMENT '第三方交易号',
10 pay_time DATETIME COMMENT '支付时间',
11 notify_time DATETIME COMMENT '回调时间',
12 notify_data TEXT COMMENT '回调数据',
13 timeout_time DATETIME COMMENT '超时时间',
14 remark VARCHAR(500) COMMENT '备注',
15 create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
16 update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
17 UNIQUE KEY uk_order (order_no),
18 INDEX idx_payment_no (payment_no),
19 INDEX idx_user (user_id),
20 INDEX idx_status (pay_status)
21) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='支付单表';

核心业务流程

1. 订单创建流程

1提交订单 → 订单验证 → 计算价格 → 创建订单 → 创建支付单 → 支付
2 ↓ ↓ ↓ ↓ ↓ ↓
3 (参数校验) (库存校验) (优惠计算) (订单入库) (支付单) (跳转支付)

关键步骤

  1. 接收订单数据(商品、数量、地址等)
  2. 订单参数验证(必填项、格式等)
  3. 库存可用性校验
  4. 优惠券/促销计算
  5. 计算订单金额(商品金额、运费、优惠)
  6. 创建订单(主表+明细表)
  7. 创建支付单
  8. 返回支付信息
  9. 用户支付

2. 订单支付成功流程

1支付回调 → 验证签名 → 更新支付单 → 更新订单状态 → 锁定库存 → 订单分配 → 发送MQ
2 ↓ ↓ ↓ ↓ ↓ ↓
3 (异步回调) (安全校验) (支付成功) (待发货) (锁库存) (分配仓库)

关键步骤

  1. 接收支付回调(微信/支付宝)
  2. 验证签名和金额
  3. 更新支付单状态
  4. 更新订单状态为"待发货"
  5. 锁定库存
  6. 订单分配(就近仓库分配)
  7. 发送订单已支付消息(MQ)
  8. 通知用户支付成功

3. 订单履约流程

1订单分配 → 锁定库存 → 生成拣货单 → 拣货 → 打包 → 发货 → 物流跟踪 → 签收
2 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
3 (分配仓库) (锁库存) (拣货任务) (拣货) (打包) (发货) (配送中) (已完成)

关键步骤

  1. 订单分配(选择最优仓库)
  2. 锁定库存(扣减可用库存)
  3. 生成拣货单
  4. 拣货(扫码拣货)
  5. 打包(称重、打印面单)
  6. 发货(交付物流)
  7. 推送物流信息
  8. 用户确认收货

4. 售后流程

1提交售后 → 售后审核 → 退货 → 收货检验 → 退款 → 完成
2 ↓ ↓ ↓ ↓ ↓ ↓
3 (申请) (审核) (寄回) (质检) (退款) (完成)

关键步骤

  1. 用户提交售后申请
  2. 客服审核(同意/拒绝)
  3. 用户寄回商品(退货退款)
  4. 仓库收货检验
  5. 处理退款
  6. 释放库存
  7. 完成售后

技术实现方案

1. 订单号生成(雪花算法)

java
1@Component
2public class OrderNoGenerator {
3
4 @Autowired
5 private SnowflakeIdWorker snowflakeIdWorker;
6
7 /**
8 * 生成订单号
9 * 格式:业务前缀(2位) + 时间戳(10位) + 雪花ID后6位
10 */
11 public String generateOrderNo() {
12 long id = snowflakeIdWorker.nextId();
13 String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
14 String suffix = String.valueOf(id % 1000000);
15
16 return "OD" + timestamp + String.format("%06d", Long.parseLong(suffix));
17 }
18}

2. 订单分库分表(ShardingSphere)

yaml
1spring:
2 shardingsphere:
3 datasource:
4 names: ds0,ds1,ds2,ds3,ds4,ds5,ds6,ds7
5 ds0:
6 type: com.zaxxer.hikari.HikariDataSource
7 driver-class-name: com.mysql.cj.jdbc.Driver
8 jdbc-url: jdbc:mysql://localhost:3306/oms_0
9 username: root
10 password: password
11 rules:
12 sharding:
13 tables:
14 order_main:
15 actual-data-nodes: ds$->{0..7}.order_main_$->{0..11}
16 database-strategy:
17 standard:
18 sharding-column: user_id
19 sharding-algorithm-name: db-mod
20 table-strategy:
21 standard:
22 sharding-column: create_time
23 sharding-algorithm-name: table-month
24 key-generate-strategy:
25 column: id
26 key-generator-name: snowflake
27 sharding-algorithms:
28 db-mod:
29 type: MOD
30 props:
31 sharding-count: 8
32 table-month:
33 type: INLINE
34 props:
35 algorithm-expression: order_main_$->{month(create_time) % 12}
36 key-generators:
37 snowflake:
38 type: SNOWFLAKE

3. 订单创建(分布式事务 - Seata)

java
1@Service
2public class OrderService {
3
4 @Autowired
5 private OrderMapper orderMapper;
6
7 @Autowired
8 private InventoryServiceClient inventoryServiceClient;
9
10 @Autowired
11 private PaymentServiceClient paymentServiceClient;
12
13 /**
14 * 创建订单(分布式事务)
15 */
16 @GlobalTransactional(name = "create-order", rollbackFor = Exception.class)
17 public OrderCreateResult createOrder(OrderCreateDTO dto) {
18 // 1. 验证订单
19 validateOrder(dto);
20
21 // 2. 检查库存(远程调用)
22 for (OrderItemDTO item : dto.getItems()) {
23 boolean available = inventoryServiceClient.checkStock(
24 item.getSkuId(),
25 item.getQuantity()
26 );
27 if (!available) {
28 throw new BusinessException("商品库存不足");
29 }
30 }
31
32 // 3. 计算价格
33 OrderPriceResult priceResult = calculatePrice(dto);
34
35 // 4. 创建订单
36 OrderMain order = new OrderMain();
37 order.setOrderNo(orderNoGenerator.generateOrderNo());
38 order.setUserId(dto.getUserId());
39 order.setTotalAmount(priceResult.getTotalAmount());
40 order.setPayAmount(priceResult.getPayAmount());
41 order.setOrderStatus(OrderStatus.WAIT_PAY);
42 // ... 其他字段
43 orderMapper.insert(order);
44
45 // 5. 创建订单明细
46 for (OrderItemDTO item : dto.getItems()) {
47 OrderDetail detail = new OrderDetail();
48 detail.setOrderId(order.getId());
49 detail.setOrderNo(order.getOrderNo());
50 detail.setSkuId(item.getSkuId());
51 detail.setQuantity(item.getQuantity());
52 // ... 其他字段
53 orderDetailMapper.insert(detail);
54 }
55
56 // 6. 创建支付单(远程调用)
57 PaymentCreateResult paymentResult = paymentServiceClient.createPayment(
58 order.getOrderNo(),
59 order.getUserId(),
60 order.getPayAmount(),
61 dto.getPayType()
62 );
63
64 // 7. 发送订单创建消息
65 sendOrderCreatedMessage(order.getOrderNo());
66
67 // 8. 返回结果
68 OrderCreateResult result = new OrderCreateResult();
69 result.setOrderId(order.getId());
70 result.setOrderNo(order.getOrderNo());
71 result.setPaymentNo(paymentResult.getPaymentNo());
72 result.setPayUrl(paymentResult.getPayUrl());
73
74 return result;
75 }
76
77 /**
78 * 计算订单价格
79 */
80 private OrderPriceResult calculatePrice(OrderCreateDTO dto) {
81 BigDecimal totalAmount = BigDecimal.ZERO;
82 BigDecimal discountAmount = BigDecimal.ZERO;
83
84 // 1. 计算商品总额
85 for (OrderItemDTO item : dto.getItems()) {
86 BigDecimal itemAmount = item.getPrice()
87 .multiply(new BigDecimal(item.getQuantity()));
88 totalAmount = totalAmount.add(itemAmount);
89 }
90
91 // 2. 计算优惠金额(优惠券、促销等)
92 if (dto.getCouponId() != null) {
93 discountAmount = calculateCouponDiscount(dto.getCouponId(), totalAmount);
94 }
95
96 // 3. 计算运费
97 BigDecimal freightAmount = calculateFreight(dto);
98
99 // 4. 计算实付金额
100 BigDecimal payAmount = totalAmount
101 .subtract(discountAmount)
102 .add(freightAmount);
103
104 OrderPriceResult result = new OrderPriceResult();
105 result.setTotalAmount(totalAmount);
106 result.setDiscountAmount(discountAmount);
107 result.setFreightAmount(freightAmount);
108 result.setPayAmount(payAmount);
109
110 return result;
111 }
112}

4. 支付回调处理(幂等性)

java
1@Service
2public class PaymentCallbackService {
3
4 @Autowired
5 private RedisTemplate<String, Object> redisTemplate;
6
7 @Autowired
8 private PaymentOrderMapper paymentOrderMapper;
9
10 @Autowired
11 private OrderService orderService;
12
13 /**
14 * 处理支付回调(幂等性)
15 */
16 public void handlePaymentCallback(PaymentCallbackDTO dto) {
17 String paymentNo = dto.getPaymentNo();
18
19 // 1. 幂等性校验(基于 Redis 分布式锁)
20 String lockKey = "payment:callback:" + paymentNo;
21 Boolean acquired = redisTemplate.opsForValue()
22 .setIfAbsent(lockKey, "1", 5, TimeUnit.MINUTES);
23
24 if (!acquired) {
25 log.warn("支付回调处理中,忽略重复回调: {}", paymentNo);
26 return;
27 }
28
29 try {
30 // 2. 查询支付单
31 PaymentOrder payment = paymentOrderMapper.selectByPaymentNo(paymentNo);
32
33 // 3. 验证支付单状态
34 if (payment.getPayStatus() == PayStatus.SUCCESS) {
35 log.warn("支付单已处理: {}", paymentNo);
36 return;
37 }
38
39 // 4. 验证签名和金额
40 validateCallback(dto, payment);
41
42 // 5. 更新支付单状态
43 payment.setPayStatus(PayStatus.SUCCESS);
44 payment.setThirdPartyNo(dto.getThirdPartyNo());
45 payment.setPayTime(new Date());
46 payment.setNotifyTime(new Date());
47 paymentOrderMapper.updateById(payment);
48
49 // 6. 处理订单(更新状态、锁库存、分配等)
50 orderService.handleOrderPaid(payment.getOrderNo());
51
52 // 7. 发送支付成功消息
53 sendPaymentSuccessMessage(payment);
54
55 } finally {
56 // 释放锁
57 redisTemplate.delete(lockKey);
58 }
59 }
60}

5. 订单自动分配(就近原则)

java
1@Service
2public class OrderAllocationService {
3
4 @Autowired
5 private WarehouseService warehouseService;
6
7 @Autowired
8 private InventoryService inventoryService;
9
10 /**
11 * 订单自动分配仓库
12 */
13 public Long allocateWarehouse(Long orderId) {
14 OrderMain order = orderMapper.selectById(orderId);
15 List<OrderDetail> details = orderDetailMapper.selectByOrderId(orderId);
16
17 // 1. 获取所有仓库
18 List<Warehouse> warehouses = warehouseService.listActiveWarehouses();
19
20 // 2. 解析收货地址
21 String province = order.getConsigneeProvince();
22 String city = order.getConsigneeCity();
23
24 // 3. 按优先级筛选仓库
25 List<Warehouse> candidates = new ArrayList<>();
26
27 // 优先级1:同城仓库
28 candidates = warehouses.stream()
29 .filter(w -> w.getCity().equals(city))
30 .collect(Collectors.toList());
31
32 // 优先级2:同省仓库
33 if (candidates.isEmpty()) {
34 candidates = warehouses.stream()
35 .filter(w -> w.getProvince().equals(province))
36 .collect(Collectors.toList());
37 }
38
39 // 优先级3:全国仓库
40 if (candidates.isEmpty()) {
41 candidates = warehouses;
42 }
43
44 // 4. 检查库存并选择仓库
45 for (Warehouse warehouse : candidates) {
46 boolean allAvailable = true;
47
48 for (OrderDetail detail : details) {
49 int available = inventoryService.getAvailableStock(
50 warehouse.getId(),
51 detail.getSkuId()
52 );
53
54 if (available < detail.getQuantity()) {
55 allAvailable = false;
56 break;
57 }
58 }
59
60 if (allAvailable) {
61 return warehouse.getId();
62 }
63 }
64
65 throw new BusinessException("无可用仓库");
66 }
67}

6. 订单超时自动取消(延时消息)

使用 RocketMQ 延时消息

java
1@Service
2public class OrderTimeoutService {
3
4 @Autowired
5 private RocketMQTemplate rocketMQTemplate;
6
7 /**
8 * 发送订单超时检查消息
9 */
10 public void sendOrderTimeoutCheckMessage(String orderNo, int delayMinutes) {
11 OrderTimeoutMessage message = new OrderTimeoutMessage();
12 message.setOrderNo(orderNo);
13 message.setSendTime(System.currentTimeMillis());
14
15 // 延时级别:1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
16 // 30分钟对应延时级别 16
17 rocketMQTemplate.syncSend(
18 "order-timeout-topic",
19 MessageBuilder.withPayload(message).build(),
20 3000,
21 16 // 延时级别
22 );
23 }
24}
25
26@Component
27@RocketMQMessageListener(
28 topic = "order-timeout-topic",
29 consumerGroup = "order-timeout-consumer"
30)
31public class OrderTimeoutConsumer implements RocketMQListener<OrderTimeoutMessage> {
32
33 @Autowired
34 private OrderService orderService;
35
36 @Override
37 public void onMessage(OrderTimeoutMessage message) {
38 String orderNo = message.getOrderNo();
39
40 // 查询订单
41 OrderMain order = orderMapper.selectByOrderNo(orderNo);
42
43 // 如果订单还是待支付状态,自动取消
44 if (order.getOrderStatus() == OrderStatus.WAIT_PAY) {
45 orderService.autoCancelOrder(orderNo, "超时未支付");
46 }
47 }
48}

7. 订单状态机

java
1@Component
2public class OrderStateMachine {
3
4 /**
5 * 订单状态流转校验
6 */
7 public boolean canTransition(OrderStatus from, OrderStatus to) {
8 // 定义状态流转规则
9 Map<OrderStatus, Set<OrderStatus>> transitions = new HashMap<>();
10
11 // 待付款 -> 待发货、已取消
12 transitions.put(OrderStatus.WAIT_PAY,
13 Set.of(OrderStatus.WAIT_SHIP, OrderStatus.CANCELED));
14
15 // 待发货 -> 待收货、已取消
16 transitions.put(OrderStatus.WAIT_SHIP,
17 Set.of(OrderStatus.WAIT_RECEIVE, OrderStatus.CANCELED));
18
19 // 待收货 -> 已完成、售后中
20 transitions.put(OrderStatus.WAIT_RECEIVE,
21 Set.of(OrderStatus.FINISHED, OrderStatus.AFTER_SALE));
22
23 // 售后中 -> 已完成
24 transitions.put(OrderStatus.AFTER_SALE,
25 Set.of(OrderStatus.FINISHED));
26
27 Set<OrderStatus> allowedTransitions = transitions.get(from);
28 return allowedTransitions != null && allowedTransitions.contains(to);
29 }
30
31 /**
32 * 更新订单状态
33 */
34 @Transactional
35 public void updateOrderStatus(String orderNo, OrderStatus newStatus, String remark) {
36 OrderMain order = orderMapper.selectByOrderNo(orderNo);
37 OrderStatus oldStatus = order.getOrderStatus();
38
39 // 校验状态流转
40 if (!canTransition(oldStatus, newStatus)) {
41 throw new BusinessException(
42 String.format("订单状态不允许从 %s 变更为 %s", oldStatus, newStatus)
43 );
44 }
45
46 // 更新订单状态
47 order.setOrderStatus(newStatus);
48 orderMapper.updateById(order);
49
50 // 记录状态流转日志
51 OrderStatusLog log = new OrderStatusLog();
52 log.setOrderId(order.getId());
53 log.setOrderNo(orderNo);
54 log.setBeforeStatus(oldStatus);
55 log.setAfterStatus(newStatus);
56 log.setRemark(remark);
57 orderStatusLogMapper.insert(log);
58
59 // 发送状态变更消息
60 sendOrderStatusChangeMessage(orderNo, oldStatus, newStatus);
61 }
62}

API 接口设计

1. 订单接口

java
1@RestController
2@RequestMapping("/api/oms/order")
3public class OrderController {
4
5 @Autowired
6 private OrderService orderService;
7
8 /**
9 * 创建订单
10 */
11 @PostMapping("")
12 public Result<OrderCreateResult> createOrder(@RequestBody @Valid OrderCreateDTO dto) {
13 OrderCreateResult result = orderService.createOrder(dto);
14 return Result.success(result);
15 }
16
17 /**
18 * 订单详情
19 */
20 @GetMapping("/{orderNo}")
21 public Result<OrderDetailVO> getOrderDetail(@PathVariable String orderNo) {
22 OrderDetailVO vo = orderService.getOrderDetail(orderNo);
23 return Result.success(vo);
24 }
25
26 /**
27 * 订单列表
28 */
29 @GetMapping("/list")
30 public Result<PageResult<OrderVO>> listOrders(
31 @RequestParam(required = false) Integer status,
32 @RequestParam(defaultValue = "1") Integer pageNum,
33 @RequestParam(defaultValue = "20") Integer pageSize) {
34
35 Long userId = SecurityUtils.getCurrentUserId();
36 PageResult<OrderVO> result = orderService.listUserOrders(
37 userId, status, pageNum, pageSize
38 );
39 return Result.success(result);
40 }
41
42 /**
43 * 取消订单
44 */
45 @PostMapping("/{orderNo}/cancel")
46 public Result<Void> cancelOrder(
47 @PathVariable String orderNo,
48 @RequestParam String reason) {
49
50 orderService.cancelOrder(orderNo, reason);
51 return Result.success();
52 }
53
54 /**
55 * 确认收货
56 */
57 @PostMapping("/{orderNo}/confirm")
58 public Result<Void> confirmReceive(@PathVariable String orderNo) {
59 orderService.confirmReceive(orderNo);
60 return Result.success();
61 }
62}

2. 售后接口

java
1@RestController
2@RequestMapping("/api/oms/aftersale")
3public class AftersaleController {
4
5 @Autowired
6 private AftersaleService aftersaleService;
7
8 /**
9 * 创建售后单
10 */
11 @PostMapping("")
12 public Result<String> createAftersale(@RequestBody @Valid AftersaleCreateDTO dto) {
13 String aftersaleNo = aftersaleService.createAftersale(dto);
14 return Result.success(aftersaleNo);
15 }
16
17 /**
18 * 售后详情
19 */
20 @GetMapping("/{aftersaleNo}")
21 public Result<AftersaleDetailVO> getAftersaleDetail(@PathVariable String aftersaleNo) {
22 AftersaleDetailVO vo = aftersaleService.getAftersaleDetail(aftersaleNo);
23 return Result.success(vo);
24 }
25
26 /**
27 * 审核售后单
28 */
29 @PostMapping("/{aftersaleNo}/approve")
30 public Result<Void> approveAftersale(
31 @PathVariable String aftersaleNo,
32 @RequestParam Boolean approved,
33 @RequestParam(required = false) String rejectReason) {
34
35 aftersaleService.approveAftersale(aftersaleNo, approved, rejectReason);
36 return Result.success();
37 }
38
39 /**
40 * 退货物流
41 */
42 @PostMapping("/{aftersaleNo}/logistics")
43 public Result<Void> submitReturnLogistics(
44 @PathVariable String aftersaleNo,
45 @RequestBody @Valid ReturnLogisticsDTO dto) {
46
47 aftersaleService.submitReturnLogistics(aftersaleNo, dto);
48 return Result.success();
49 }
50}

性能优化方案

1. 数据库优化

  • 分库分表:按用户ID分库,按月份分表
  • 读写分离:主库写,从库读
  • 索引优化:合理建立索引
  • 定期归档:归档历史订单数据

2. 缓存优化

  • 订单缓存:热点订单缓存到 Redis
  • 商品信息缓存:商品信息、库存缓存
  • 用户信息缓存:用户基本信息缓存

3. 异步化

  • 消息队列:订单创建、支付成功等事件异步处理
  • 延时消息:订单超时自动取消
  • 异步通知:短信、推送通知异步发送

4. 限流降级

  • 接口限流:使用 Sentinel 限流
  • 服务降级:非核心功能降级
  • 熔断保护:依赖服务熔断

5. 秒杀优化

  • Redis 预扣库存:减少数据库压力
  • 队列削峰:消息队列削峰
  • 限流:接口限流
  • 缓存预热:提前加载数据到缓存

总结

OMS 订单管理系统的核心在于:

  1. 高并发处理:支持大促、秒杀等高并发场景
  2. 数据一致性:确保订单数据的强一致性
  3. 可靠性:保证订单不丢失、不重复
  4. 实时性:订单状态实时更新
  5. 可扩展性:支持多渠道、多仓库扩展

本设计方案基于 Spring Cloud 微服务架构,采用分库分表、缓存、消息队列等技术,可满足电商平台的订单管理需求。

评论