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 (参数校验) (库存校验) (优惠计算) (订单入库) (支付单) (跳转支付)关键步骤:
- 接收订单数据(商品、数量、地址等)
- 订单参数验证(必填项、格式等)
- 库存可用性校验
- 优惠券/促销计算
- 计算订单金额(商品金额、运费、优惠)
- 创建订单(主表+明细表)
- 创建支付单
- 返回支付信息
- 用户支付
2. 订单支付成功流程
1支付回调 → 验证签名 → 更新支付单 → 更新订单状态 → 锁定库存 → 订单分配 → 发送MQ2 ↓ ↓ ↓ ↓ ↓ ↓3 (异步回调) (安全校验) (支付成功) (待发货) (锁库存) (分配仓库)关键步骤:
- 接收支付回调(微信/支付宝)
- 验证签名和金额
- 更新支付单状态
- 更新订单状态为"待发货"
- 锁定库存
- 订单分配(就近仓库分配)
- 发送订单已支付消息(MQ)
- 通知用户支付成功
3. 订单履约流程
1订单分配 → 锁定库存 → 生成拣货单 → 拣货 → 打包 → 发货 → 物流跟踪 → 签收2 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓3 (分配仓库) (锁库存) (拣货任务) (拣货) (打包) (发货) (配送中) (已完成)关键步骤:
- 订单分配(选择最优仓库)
- 锁定库存(扣减可用库存)
- 生成拣货单
- 拣货(扫码拣货)
- 打包(称重、打印面单)
- 发货(交付物流)
- 推送物流信息
- 用户确认收货
4. 售后流程
1提交售后 → 售后审核 → 退货 → 收货检验 → 退款 → 完成2 ↓ ↓ ↓ ↓ ↓ ↓3 (申请) (审核) (寄回) (质检) (退款) (完成)关键步骤:
- 用户提交售后申请
- 客服审核(同意/拒绝)
- 用户寄回商品(退货退款)
- 仓库收货检验
- 处理退款
- 释放库存
- 完成售后
技术实现方案
1. 订单号生成(雪花算法)
java
1@Component2public class OrderNoGenerator {3 4 @Autowired5 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,ds75 ds0:6 type: com.zaxxer.hikari.HikariDataSource7 driver-class-name: com.mysql.cj.jdbc.Driver8 jdbc-url: jdbc:mysql://localhost:3306/oms_09 username: root10 password: password11 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_id19 sharding-algorithm-name: db-mod20 table-strategy:21 standard:22 sharding-column: create_time23 sharding-algorithm-name: table-month24 key-generate-strategy:25 column: id26 key-generator-name: snowflake27 sharding-algorithms:28 db-mod:29 type: MOD30 props:31 sharding-count: 832 table-month:33 type: INLINE34 props:35 algorithm-expression: order_main_$->{month(create_time) % 12}36 key-generators:37 snowflake:38 type: SNOWFLAKE3. 订单创建(分布式事务 - Seata)
java
1@Service2public class OrderService {3 4 @Autowired5 private OrderMapper orderMapper;6 7 @Autowired8 private InventoryServiceClient inventoryServiceClient;9 10 @Autowired11 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 = totalAmount101 .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@Service2public class PaymentCallbackService {3 4 @Autowired5 private RedisTemplate<String, Object> redisTemplate;6 7 @Autowired8 private PaymentOrderMapper paymentOrderMapper;9 10 @Autowired11 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@Service2public class OrderAllocationService {3 4 @Autowired5 private WarehouseService warehouseService;6 7 @Autowired8 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@Service2public class OrderTimeoutService {3 4 @Autowired5 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 2h16 // 30分钟对应延时级别 1617 rocketMQTemplate.syncSend(18 "order-timeout-topic",19 MessageBuilder.withPayload(message).build(),20 3000,21 16 // 延时级别22 );23 }24}2526@Component27@RocketMQMessageListener(28 topic = "order-timeout-topic",29 consumerGroup = "order-timeout-consumer"30)31public class OrderTimeoutConsumer implements RocketMQListener<OrderTimeoutMessage> {32 33 @Autowired34 private OrderService orderService;35 36 @Override37 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@Component2public 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 @Transactional35 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@RestController2@RequestMapping("/api/oms/order")3public class OrderController {4 5 @Autowired6 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, pageSize38 );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@RestController2@RequestMapping("/api/oms/aftersale")3public class AftersaleController {4 5 @Autowired6 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 订单管理系统的核心在于:
- 高并发处理:支持大促、秒杀等高并发场景
- 数据一致性:确保订单数据的强一致性
- 可靠性:保证订单不丢失、不重复
- 实时性:订单状态实时更新
- 可扩展性:支持多渠道、多仓库扩展
本设计方案基于 Spring Cloud 微服务架构,采用分库分表、缓存、消息队列等技术,可满足电商平台的订单管理需求。
参与讨论