news 2026/4/30 17:48:31

电商订单系统设计(简单版)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
电商订单系统设计(简单版)

下单 + 支付 + 主动取消订单 + 超时自动关单配套:完整建表、实体、Mapper、XML、Service、Controller、事务、定时任务、异步、防超卖、状态流转,基于SpringBoot2.5 + MyBatis原生XML + MySQL8.0

一、完整数据库表结构

sql

CREATE DATABASE IF NOT EXISTS shop_full DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE shop_full; -- 商品表 CREATE TABLE product ( id BIGINT PRIMARY KEY AUTO_INCREMENT, product_name VARCHAR(100) NOT NULL COMMENT '商品名称', price DECIMAL(10,2) NOT NULL COMMENT '单价', stock INT NOT NULL DEFAULT 0 COMMENT '库存', version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号', create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 订单主表 CREATE TABLE order_info ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL COMMENT '订单号', user_id BIGINT NOT NULL, total_amount DECIMAL(10,2) NOT NULL, pay_amount DECIMAL(10,2) DEFAULT 0, -- 订单状态:0待支付 1已支付 2已取消 3已退款 order_status TINYINT NOT NULL DEFAULT 0, pay_time DATETIME NULL COMMENT '支付时间', cancel_time DATETIME NULL COMMENT '取消时间', expire_time DATETIME NULL COMMENT '超时过期时间', create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_order_no (order_no), KEY idx_user_id (user_id), KEY idx_order_status (order_status) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 订单明细表 CREATE TABLE order_item ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL, product_id BIGINT NOT NULL, product_name VARCHAR(100) NOT NULL, product_price DECIMAL(10,2) NOT NULL, quantity INT NOT NULL COMMENT '购买数量', create_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_order_no (order_no) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 支付记录表 CREATE TABLE pay_log ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL, pay_no VARCHAR(64) COMMENT '第三方支付流水号', pay_amount DECIMAL(10,2) NOT NULL, pay_status TINYINT NOT NULL DEFAULT 0 COMMENT '0待支付 1支付成功 2支付失败', pay_type TINYINT DEFAULT 1 COMMENT '1微信 2支付宝', create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, KEY idx_order_no (order_no) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 操作日志表 CREATE TABLE operation_log ( id BIGINT PRIMARY KEY AUTO_INCREMENT, content VARCHAR(255) NOT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

二、核心依赖 pom.xml

xml

<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.15</version> <relativePath/> </parent> <groupId>com.ecommerce</groupId> <artifactId>order-full-system</artifactId> <version>0.0.1-SNAPSHOT</version> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.2</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-task</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project>

三、配置文件 application.yml

yaml

server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/shop_full?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowMultiQueries=true username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver # 开启定时任务 task: scheduling: pool: core-size: 5 mybatis: mapper-locations: classpath:mapper/*.xml configuration: map-underscore-to-camel-case: true # 订单超时时间 30分钟 order: expire-minute: 30

四、统一常量 & 实体类

1. 订单状态常量

java

运行

public class OrderConstant { // 订单状态 public static final int ORDER_WAIT_PAY = 0; public static final int ORDER_PAY_SUCCESS = 1; public static final int ORDER_CANCEL = 2; public static final int ORDER_REFUND = 3; // 支付状态 public static final int PAY_WAIT = 0; public static final int PAY_SUCCESS = 1; public static final int PAY_FAIL = 2; }

2. 核心实体(简化展示,全量 lombok)

java

运行

import lombok.Data; import java.math.BigDecimal; import java.time.LocalDateTime; @Data public class Product { private Long id; private String productName; private BigDecimal price; private Integer stock; private Integer version; private LocalDateTime createTime; private LocalDateTime updateTime; } @Data public class OrderInfo { private Long id; private String orderNo; private Long userId; private BigDecimal totalAmount; private BigDecimal payAmount; private Integer orderStatus; private LocalDateTime payTime; private LocalDateTime cancelTime; private LocalDateTime expireTime; private LocalDateTime createTime; } @Data public class OrderItem { private Long id; private String orderNo; private Long productId; private String productName; private BigDecimal productPrice; private Integer quantity; } @Data public class PayLog { private Long id; private String orderNo; private String payNo; private BigDecimal payAmount; private Integer payStatus; private Integer payType; }

五、Mapper 接口 + XML

ProductMapper

java

运行

@Mapper public interface ProductMapper { Product selectById(Long id); // 乐观锁扣库存 int deductStock(@Param("id") Long id, @Param("num") Integer num, @Param("version") Integer version); // 取消订单回补库存 int addStock(@Param("id") Long id, @Param("num") Integer num); }

xml

<!-- ProductMapper.xml --> <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.ecommerce.mapper.ProductMapper"> <update id="deductStock"> UPDATE product SET stock = stock - #{num}, version = version + 1 WHERE id = #{id} AND version = #{version} AND stock >= #{num} </update> <update id="addStock"> UPDATE product SET stock = stock + #{num} WHERE id = #{id} </update> </mapper>

OrderMapper、OrderItemMapper、PayLogMapper、LogMapper 省略,只贴核心 SQL

xml

<!-- 订单更新状态 --> <update id="updateOrderStatus"> UPDATE order_info SET order_status = #{status}, pay_time = #{payTime}, update_time = NOW() WHERE order_no = #{orderNo} AND order_status = #{oldStatus} </update> <!-- 查询超时未支付订单 --> <select id="listExpireWaitPayOrder"> SELECT order_no FROM order_info WHERE order_status = 0 AND expire_time < NOW() LIMIT 100 </select>

六、核心 Service 层

1. 日志服务(独立事务 REQUIRES_NEW)

java

运行

@Service public class OperationLogService { @Resource private OperationLogMapper operationLogMapper; @Transactional(propagation = Propagation.REQUIRES_NEW) public void recordLog(String content) { OperationLog log = new OperationLog(); log.setContent(content); operationLogMapper.insert(log); } }

2. 订单核心服务(下单 + 支付 + 取消)

java

运行

@Service @Slf4j public class OrderService { @Resource private ProductMapper productMapper; @Resource private OrderMapper orderMapper; @Resource private OrderItemMapper orderItemMapper; @Resource private PayLogMapper payLogMapper; @Resource private OperationLogService operationLogService; @Value("${order.expire-minute}") private Integer expireMinute; //===================== 1. 创建订单 ===================== @Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED) public String createOrder(Long userId, Long productId, Integer quantity) { // 1. 查询商品+校验 Product product = productMapper.selectById(productId); if (product == null) throw new RuntimeException("商品不存在"); if (product.getStock() < quantity) throw new RuntimeException("库存不足"); // 2. 乐观锁扣库存 int deduct = productMapper.deductStock(productId, quantity, product.getVersion()); if (deduct <= 0) throw new RuntimeException("下单失败,库存不足或并发冲突"); // 3. 生成订单号 + 过期时间 String orderNo = "ORD" + System.currentTimeMillis() + UUID.randomUUID().toString().substring(0,6); LocalDateTime expireTime = LocalDateTime.now().plusMinutes(expireMinute); // 4. 保存订单 OrderInfo order = new OrderInfo(); order.setOrderNo(orderNo); order.setUserId(userId); order.setTotalAmount(product.getPrice().multiply(new BigDecimal(quantity))); order.setOrderStatus(OrderConstant.ORDER_WAIT_PAY); order.setExpireTime(expireTime); orderMapper.insert(order); // 5. 订单明细 OrderItem item = new OrderItem(); item.setOrderNo(orderNo); item.setProductId(productId); item.setProductName(product.getProductName()); item.setProductPrice(product.getPrice()); item.setQuantity(quantity); orderItemMapper.insert(item); // 6. 初始化支付记录 PayLog payLog = new PayLog(); payLog.setOrderNo(orderNo); payLog.setPayAmount(order.getTotalAmount()); payLog.setPayStatus(OrderConstant.PAY_WAIT); payLogMapper.insert(payLog); // 异步日志 operationLogService.recordLog("用户["+userId+"]创建订单:"+orderNo); return orderNo; } //===================== 2. 支付订单 ===================== @Transactional(rollbackFor = Exception.class) public void payOrder(String orderNo, String payNo, Integer payType) { // 1. 查询订单 OrderInfo order = orderMapper.selectByOrderNo(orderNo); if (order == null) throw new RuntimeException("订单不存在"); if (!order.getOrderStatus().equals(OrderConstant.ORDER_WAIT_PAY)) { throw new RuntimeException("订单状态异常,无法支付"); } // 2. 更新订单状态为已支付 orderMapper.updateOrderStatus(orderNo, OrderConstant.ORDER_PAY_SUCCESS, OrderConstant.ORDER_WAIT_PAY, LocalDateTime.now()); // 3. 更新支付记录 payLogMapper.updatePaySuccess(orderNo, payNo, payType, order.getPayAmount()); operationLogService.recordLog("订单["+orderNo+"]支付成功"); } //===================== 3. 主动取消订单 ===================== @Transactional(rollbackFor = Exception.class) public void cancelOrder(String orderNo) { // 1. 查询订单 OrderInfo order = orderMapper.selectByOrderNo(orderNo); if (order == null) throw new RuntimeException("订单不存在"); if (!order.getOrderStatus().equals(OrderConstant.ORDER_WAIT_PAY)) { throw new RuntimeException("只有待支付订单可取消"); } // 2. 查询订单商品 OrderItem item = orderItemMapper.selectByOrderNo(orderNo); // 3. 回补库存 productMapper.addStock(item.getProductId(), item.getQuantity()); // 4. 修改订单为已取消 orderMapper.updateOrderCancel(orderNo, LocalDateTime.now()); operationLogService.recordLog("订单["+orderNo+"]手动取消,库存已回补"); } //===================== 4. 超时自动关闭订单 ===================== @Transactional(rollbackFor = Exception.class) public void autoCloseOrder(String orderNo) { OrderInfo order = orderMapper.selectByOrderNo(orderNo); if (order == null || !order.getOrderStatus().equals(OrderConstant.ORDER_WAIT_PAY)) { return; } // 回补库存 OrderItem item = orderItemMapper.selectByOrderNo(orderNo); productMapper.addStock(item.getProductId(), item.getQuantity()); // 关闭订单 orderMapper.updateOrderCancel(orderNo, LocalDateTime.now()); operationLogService.recordLog("订单["+orderNo+"]超时自动关闭"); } }

3. 定时任务:定时扫描超时订单

java

运行

@Component @EnableScheduling @Slf4j public class OrderExpireTask { @Resource private OrderMapper orderMapper; @Resource private OrderService orderService; // 每1分钟执行一次 @Scheduled(cron = "0 */1 * * * ?") public void closeExpireOrder() { // 查询所有超时待支付订单 List<String> expireOrderNos = orderMapper.listExpireWaitPayOrder(); if (CollectionUtils.isEmpty(expireOrderNos)) { return; } for (String orderNo : expireOrderNos) { try { orderService.autoCloseOrder(orderNo); } catch (Exception e) { log.error("超时关闭订单{}失败:{}",orderNo,e.getMessage()); } } } }

七、Controller 完整接口

java

运行

@RestController @RequestMapping("/api/order") public class OrderController { @Resource private OrderService orderService; // 1. 创建订单 @PostMapping("/create") public Result create(Long userId, Long productId, Integer quantity){ try { String orderNo = orderService.createOrder(userId,productId,quantity); return Result.success(orderNo,"下单成功"); }catch (Exception e){ return Result.fail(e.getMessage()); } } // 2. 支付订单 @PostMapping("/pay") public Result pay(String orderNo,String payNo,Integer payType){ try { orderService.payOrder(orderNo,payNo,payType); return Result.success("支付成功"); }catch (Exception e){ return Result.fail(e.getMessage()); } } // 3. 手动取消订单 @PostMapping("/cancel") public Result cancel(String orderNo){ try { orderService.cancelOrder(orderNo); return Result.success("取消成功"); }catch (Exception e){ return Result.fail(e.getMessage()); } } } // 统一返回工具 class Result{ private Integer code; private String msg; private Object data; public static Result success(Object data,String msg){ Result r = new Result(); r.setCode(200); r.setMsg(msg); r.setData(data); return r; } public static Result fail(String msg){ Result r = new Result(); r.setCode(500); r.setMsg(msg); return r; } // getter/setter }

八、系统核心亮点 & 面试考点

  1. 完整状态流转待支付 → 已支付 / 已取消(手动 / 超时)
  2. 乐观锁防超卖库存扣减带 version 版本号,解决并发超卖
  3. 事务闭环
    • 下单 / 支付 / 取消 强事务保证
    • 日志独立事务REQUIRES_NEW,业务失败日志不丢失
  4. 订单超时自动关闭定时任务 cron 每分钟扫描,自动回补库存、关闭订单
  5. 库存双向操作下单扣库存、取消 / 超时 自动回补库存,数据一致
  6. 短事务设计只在 DB 写操作上加事务,减少锁持有时间,高性能

九、接口测试地址

http

# 创建订单 POST http://localhost:8080/api/order/create?userId=1&productId=1&quantity=2 # 模拟支付 POST http://localhost:8080/api/order/pay?orderNo=xxx&payNo=PAY123456&payType=2 # 手动取消 POST http://localhost:8080/api/order/cancel?orderNo=xxx
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/30 17:47:34

如何应对 AI 时代,和大家聊聊飞哥的思考!

大家好&#xff0c;我是飞哥&#xff01;我在2026年4月25日周六晚&#xff0c;在距离上一次公众号发文已经过去了 3 个月零 26 天之后&#xff0c;又重新投入到了公众号文章的写作中来了。最近这么长的时间里我都一直没有发文&#xff0c;原因是 AI 能力的进一步提升打破了我原…

作者头像 李华
网站建设 2026/4/30 17:46:24

扩散模型剪枝技术:挑战、创新与实战指南

1. 扩散模型剪枝的技术挑战与创新突破在AIGC技术爆发的当下&#xff0c;文本到图像生成模型如Stable Diffusion系列已成为创意产业的基础设施。然而这些模型的庞大规模&#xff08;如SD 3.5-Large的80亿参数&#xff09;带来了严峻的部署挑战&#xff1a;单次推理需要11.26 TFL…

作者头像 李华
网站建设 2026/4/30 17:42:33

深度探索MIKE IO:水文数据处理的Python革命

深度探索MIKE IO&#xff1a;水文数据处理的Python革命 【免费下载链接】mikeio Read, write and manipulate dfs0, dfs1, dfs2, dfs3, dfsu and mesh files. 项目地址: https://gitcode.com/gh_mirrors/mi/mikeio 在海洋工程、水文模拟和环境科学领域&#xff0c;数据格…

作者头像 李华
网站建设 2026/4/30 17:40:49

如何高效管理微信聊天记录:WeChatMsg完整数据导出指南

如何高效管理微信聊天记录&#xff1a;WeChatMsg完整数据导出指南 【免费下载链接】WeChatMsg 提取微信聊天记录&#xff0c;将其导出成HTML、Word、CSV文档永久保存&#xff0c;对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trending/we/WeCha…

作者头像 李华
网站建设 2026/4/30 17:40:37

终极指南:如何简单快速地完成Windows和Office永久激活

终极指南&#xff1a;如何简单快速地完成Windows和Office永久激活 【免费下载链接】KMS_VL_ALL_AIO Smart Activation Script 项目地址: https://gitcode.com/gh_mirrors/km/KMS_VL_ALL_AIO 还在为Windows系统频繁弹出激活提示而烦恼吗&#xff1f;Office文档突然变成只…

作者头像 李华