Java 实战项目:手把手教你写一个电商订单系统

🌸你好呀!我是 lbb小魔仙
🌟 感谢陪伴~ 小白博主在线求友
🌿 跟着小白学Linux/Java/Python
📖 专栏汇总:
《Linux》专栏 | 《Java》专栏 | 《Python》专栏

- [Java 实战项目:手把手教你写一个电商订单系统](#Java 实战项目:手把手教你写一个电商订单系统)
-
- 一、系统核心模块与技术栈说明
-
- [1.1 核心业务模块](#1.1 核心业务模块)
- [1.2 技术栈详情](#1.2 技术栈详情)
- 二、环境搭建与项目初始化
-
- [2.1 初始化 Spring Boot 项目](#2.1 初始化 Spring Boot 项目)
- [2.2 数据库表设计](#2.2 数据库表设计)
- 三、核心模块开发实现
-
- [3.1 实体类设计](#3.1 实体类设计)
- [3.2 Mapper 层开发](#3.2 Mapper 层开发)
- [3.3 Service 层开发(核心业务逻辑)](#3.3 Service 层开发(核心业务逻辑))
- [3.4 Controller 层开发(接口暴露)](#3.4 Controller 层开发(接口暴露))
- [3.5 订单状态机设计(优化版)](#3.5 订单状态机设计(优化版))
- 四、系统核心流程图
- 五、项目结构与开源仓库说明
-
- [5.1 项目结构(GitHub风格)](#5.1 项目结构(GitHub风格))
- [5.2 开源仓库链接(虚构)](#5.2 开源仓库链接(虚构))
- 六、扩展与优化建议
本文将带领大家基于 Spring Boot + MyBatis + MySQL 开发一个简易电商订单系统,覆盖下单核心流程,适合具备 Java 基础和 Spring Boot 入门经验的开发者。系统聚焦核心业务模块,不冗余复杂功能,助力快速掌握订单系统设计与开发思路。
一、系统核心模块与技术栈说明
1.1 核心业务模块
电商订单系统的核心是围绕"用户下单到支付完成"的全流程管控,主要包含以下模块:
-
用户下单模块:接收用户下单请求,校验用户状态、收货地址等基础信息;
-
订单创建模块:生成唯一订单号,保存订单主表与订单明细表数据,关联商品、用户、地址信息;
-
库存扣减模块:下单时校验并扣减商品库存,处理库存不足场景,保证数据一致性;
-
订单状态管理模块:通过状态机管控订单生命周期(待支付、已支付、待发货、已完成等);
-
支付回调模块 :接收支付平台回调通知,更新订单状态,触发后续流程(如通知发货)。

1.2 技术栈详情
-
后端框架:Spring Boot 2.7.x(稳定版,适配性强);
-
ORM 框架:MyBatis 3.x(入门简单,SQL 可控性强);
-
数据库:MySQL 8.0(关系型数据库,适配订单数据存储);
-
工具依赖:Lombok(简化实体类代码)、MyBatis Generator(自动生成 Mapper 代码)、Spring Boot Starter Web(Web 服务);
-
流程图工具:Mermaid(可视化业务流程)。
二、环境搭建与项目初始化
2.1 初始化 Spring Boot 项目
通过 Spring Initializr(https://start.spring.io/)创建项目,选择以下依赖:
-
Spring Web
-
MyBatis Framework
-
MySQL Driver
-
Lombok
生成项目后,导入 IDEA,配置 application.yml 文件,配置数据库连接与 MyBatis 相关参数:
yaml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/ecommerce_order?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
username: root
password: 123456
mybatis:
mapper-locations: classpath:mapper/**/*.xml # Mapper.xml 文件路径
type-aliases-package: com.example.ecommerceorder.entity # 实体类包路径
configuration:
map-underscore-to-camel-case: true # 下划线转驼峰命名
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印 SQL 日志
server:
port: 8080
2.2 数据库表设计
创建核心数据表:订单主表(t_order)、订单明细表(t_order_item)、商品表(t_product)、用户表(t_user),SQL 脚本如下:
sql
-- 用户表(简化版)
CREATE TABLE t_user (
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '用户ID',
username VARCHAR(50) NOT NULL COMMENT '用户名',
status TINYINT DEFAULT 1 COMMENT '状态:1-正常,0-禁用',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间'
) COMMENT '用户表';
-- 商品表(简化版)
CREATE TABLE t_product (
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '商品ID',
product_name VARCHAR(100) NOT NULL COMMENT '商品名称',
stock INT NOT NULL DEFAULT 0 COMMENT '库存数量',
price DECIMAL(10,2) NOT NULL COMMENT '商品单价',
status TINYINT DEFAULT 1 COMMENT '状态:1-上架,0-下架',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间'
) COMMENT '商品表';
-- 订单主表
CREATE TABLE t_order (
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '订单ID',
order_no VARCHAR(32) NOT NULL UNIQUE COMMENT '订单编号',
user_id BIGINT NOT NULL COMMENT '用户ID',
total_amount DECIMAL(10,2) NOT NULL COMMENT '订单总金额',
status TINYINT NOT NULL COMMENT '订单状态:0-待支付,1-已支付,2-待发货,3-已完成,4-已取消',
receive_address VARCHAR(200) NOT NULL COMMENT '收货地址',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
pay_time DATETIME COMMENT '支付时间',
FOREIGN KEY (user_id) REFERENCES t_user(id)
) COMMENT '订单主表';
-- 订单明细表
CREATE TABLE t_order_item (
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '明细ID',
order_id BIGINT NOT NULL COMMENT '订单ID',
product_id BIGINT NOT NULL COMMENT '商品ID',
product_name VARCHAR(100) NOT NULL COMMENT '商品名称',
product_price DECIMAL(10,2) NOT NULL COMMENT '购买单价',
buy_num INT NOT NULL COMMENT '购买数量',
FOREIGN KEY (order_id) REFERENCES t_order(id),
FOREIGN KEY (product_id) REFERENCES t_product(id)
) COMMENT '订单明细表';
三、核心模块开发实现
3.1 实体类设计
基于数据表创建对应实体类,使用 Lombok 简化 Getter/Setter/构造方法:
java
// 订单主表实体
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Order {
private Long id;
private String orderNo;
private Long userId;
private BigDecimal totalAmount;
private Integer status;
private String receiveAddress;
private LocalDateTime createTime;
private LocalDateTime payTime;
}
// 订单明细表实体
@Data
@NoArgsConstructor
@AllArgsConstructor
public class OrderItem {
private Long id;
private Long orderId;
private Long productId;
private String productName;
private BigDecimal productPrice;
private Integer buyNum;
}
// 商品实体
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Product {
private Long id;
private String productName;
private Integer stock;
private BigDecimal price;
private Integer status;
private LocalDateTime createTime;
}
3.2 Mapper 层开发
创建 Mapper 接口与对应的 XML 文件,实现数据CRUD操作。以订单和商品 Mapper 为例:
java
// OrderMapper.java
@Mapper
public interface OrderMapper {
// 创建订单
int insertOrder(Order order);
// 根据订单号查询订单
Order selectOrderByNo(String orderNo);
// 更新订单状态
int updateOrderStatus(@Param("orderNo") String orderNo, @Param("status") Integer status, @Param("payTime") LocalDateTime payTime);
}
// ProductMapper.java
@Mapper
public interface ProductMapper {
// 根据商品ID查询商品(加锁,防止并发问题)
Product selectProductByIdForUpdate(Long productId);
// 扣减库存
int deductStock(@Param("productId") Long productId, @Param("num") Integer num);
}
OrderMapper.xml 核心内容:
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.example.ecommerceorder.mapper.OrderMapper">
<insert id="insertOrder" parameterType="com.example.ecommerceorder.entity.Order" useGeneratedKeys="true" keyProperty="id">
INSERT INTO t_order (order_no, user_id, total_amount, status, receive_address, create_time)
VALUES (#{orderNo}, #{userId}, #{totalAmount}, #{status}, #{receiveAddress}, #{createTime})
</insert>
<select id="selectOrderByNo" parameterType="String" resultType="com.example.ecommerceorder.entity.Order">
SELECT * FROM t_order WHERE order_no = #{orderNo}
</select>
<update id="updateOrderStatus">
UPDATE t_order
SET status = #{status}, pay_time = #{payTime}
WHERE order_no = #{orderNo}
</update>
</mapper>
3.3 Service 层开发(核心业务逻辑)
Service 层是业务核心,需处理订单创建、库存扣减、事务控制等逻辑。首先定义订单状态枚举,明确订单生命周期:
java
// 订单状态枚举
public enum OrderStatusEnum {
PENDING_PAY(0, "待支付"),
PAID(1, "已支付"),
PENDING_SHIP(2, "待发货"),
COMPLETED(3, "已完成"),
CANCELLED(4, "已取消");
private Integer code;
private String desc;
OrderStatusEnum(Integer code, String desc) {
this.code = code;
this.desc = desc;
}
// Getter 方法
public Integer getCode() { return code; }
public String getDesc() { return desc; }
}
创建 OrderService,实现订单创建核心方法,通过 @Transactional 控制事务,保证库存扣减与订单创建的原子性:
java
@Service
@Slf4j
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private OrderItemMapper orderItemMapper;
@Autowired
private ProductMapper productMapper;
@Autowired
private UserMapper userMapper;
/**
* 创建订单
* @param userId 用户ID
* @param productId 商品ID
* @param buyNum 购买数量
* @param receiveAddress 收货地址
* @return 订单编号
*/
@Transactional(rollbackFor = Exception.class)
public String createOrder(Long userId, Long productId, Integer buyNum, String receiveAddress) {
// 1. 校验基础信息
User user = userMapper.selectById(userId);
if (user == null || user.getStatus() == 0) {
throw new RuntimeException("用户状态异常,无法下单");
}
if (buyNum <= 0) {
throw new RuntimeException("购买数量不能小于等于0");
}
// 2. 校验商品库存(加行锁,防止并发下单超卖)
Product product = productMapper.selectProductByIdForUpdate(productId);
if (product == null || product.getStatus() == 0) {
throw new RuntimeException("商品已下架或不存在");
}
if (product.getStock() < buyNum) {
throw new RuntimeException("商品库存不足");
}
// 3. 扣减库存
int deductResult = productMapper.deductStock(productId, buyNum);
if (deductResult < 1) {
throw new RuntimeException("库存扣减失败");
}
// 4. 生成订单编号(时间戳+随机数,保证唯一性)
String orderNo = generateOrderNo();
// 5. 计算订单总金额
BigDecimal totalAmount = product.getPrice().multiply(new BigDecimal(buyNum));
// 6. 保存订单主表
Order order = new Order();
order.setOrderNo(orderNo);
order.setUserId(userId);
order.setTotalAmount(totalAmount);
order.setStatus(OrderStatusEnum.PENDING_PAY.getCode());
order.setReceiveAddress(receiveAddress);
order.setCreateTime(LocalDateTime.now());
orderMapper.insertOrder(order);
// 7. 保存订单明细表
OrderItem orderItem = new OrderItem();
orderItem.setOrderId(order.getId());
orderItem.setProductId(productId);
orderItem.setProductName(product.getProductName());
orderItem.setProductPrice(product.getPrice());
orderItem.setBuyNum(buyNum);
orderItemMapper.insertOrderItem(orderItem);
log.info("订单创建成功,订单编号:{}", orderNo);
return orderNo;
}
/**
* 生成唯一订单编号
* 格式:yyyyMMddHHmmss + 3位随机数
*/
private String generateOrderNo() {
String dateStr = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
String randomStr = String.valueOf(new Random().nextInt(900) + 100); // 3位随机数
return dateStr + randomStr;
}
/**
* 支付回调更新订单状态
*/
@Transactional(rollbackFor = Exception.class)
public boolean updateOrderAfterPay(String orderNo) {
Order order = orderMapper.selectOrderByNo(orderNo);
if (order == null) {
throw new RuntimeException("订单不存在");
}
// 只有待支付状态可以更新为已支付
if (!OrderStatusEnum.PENDING_PAY.getCode().equals(order.getStatus())) {
log.warn("订单{}状态异常,当前状态:{},无法更新为已支付", orderNo, order.getStatus());
return false;
}
int updateResult = orderMapper.updateOrderStatus(orderNo, OrderStatusEnum.PAID.getCode(), LocalDateTime.now());
return updateResult > 0;
}
}
关键说明:
-
事务控制:
@Transactional(rollbackFor = Exception.class)表示所有异常都回滚,确保库存扣减与订单创建要么同时成功,要么同时失败; -
并发防超卖:通过
selectProductByIdForUpdate方法加行锁(MySQL 悲观锁),避免多用户同时下单导致库存超卖; -
订单编号生成:采用时间戳+随机数策略,保证唯一性,实际生产可优化为分布式ID(如雪花算法)。
3.4 Controller 层开发(接口暴露)
创建 OrderController,暴露下单、支付回调接口,供前端调用:
java
@RestController
@RequestMapping("/api/order")
@Slf4j
public class OrderController {
@Autowired
private OrderService orderService;
/**
* 下单接口
*/
@PostMapping("/create")
public ResultVO createOrder(@RequestParam Long userId,
@RequestParam Long productId,
@RequestParam Integer buyNum,
@RequestParam String receiveAddress) {
try {
String orderNo = orderService.createOrder(userId, productId, buyNum, receiveAddress);
return ResultVO.success("下单成功", orderNo);
} catch (RuntimeException e) {
log.error("下单失败", e);
return ResultVO.error(e.getMessage());
}
}
/**
* 支付回调接口(模拟支付平台回调)
*/
@PostMapping("/pay/callback")
public ResultVO payCallback(@RequestParam String orderNo) {
try {
boolean result = orderService.updateOrderAfterPay(orderNo);
if (result) {
return ResultVO.success("订单状态更新成功");
} else {
return ResultVO.error("订单状态更新失败");
}
} catch (RuntimeException e) {
log.error("支付回调处理失败", e);
return ResultVO.error(e.getMessage());
}
}
/**
* 查询订单状态
*/
@GetMapping("/status")
public ResultVO getOrderStatus(@RequestParam String orderNo) {
Order order = orderService.getOrderByNo(orderNo);
if (order == null) {
return ResultVO.error("订单不存在");
}
Map<String, Object> data = new HashMap<>();
data.put("orderNo", orderNo);
data.put("status", order.getStatus());
data.put("statusDesc", OrderStatusEnum.values()[order.getStatus()].getDesc());
return ResultVO.success("查询成功", data);
}
}
其中 ResultVO 是统一返回结果封装类,简化接口响应格式:
java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ResultVO {
private Integer code; // 0-成功,1-失败
private String msg;
private Object data;
// 成功响应
public static ResultVO success(String msg, Object data) {
return new ResultVO(0, msg, data);
}
// 失败响应
public static ResultVO error(String msg) {
return new ResultVO(1, msg, null);
}
}
3.5 订单状态机设计(优化版)
对于复杂订单状态流转,可引入 Spring StateMachine 简化状态管理。首先添加依赖:
xml
<dependency>
<groupId>org.springframework.statemachine</groupId>
<artifactId>spring-statemachine-core</artifactId>
<version>3.0.1.RELEASE</version>
</dependency>
配置状态机,定义状态与事件流转规则:
java
@Configuration
@EnableStateMachineFactory
public class OrderStateMachineConfig extends StateMachineConfigurerAdapter<OrderStatusEnum, OrderEventEnum> {
// 状态流转配置
@Override
public void configure(StateMachineStateConfigurer<OrderStatusEnum, OrderEventEnum> states) throws Exception {
states
.withStates()
.initial(OrderStatusEnum.PENDING_PAY) // 初始状态:待支付
.state(OrderStatusEnum.PAID)
.state(OrderStatusEnum.PENDING_SHIP)
.state(OrderStatusEnum.COMPLETED)
.end(OrderStatusEnum.CANCELLED) // 结束状态:已取消
.end(OrderStatusEnum.COMPLETED); // 结束状态:已完成
}
// 状态流转规则
@Override
public void configure(StateMachineTransitionConfigurer<OrderStatusEnum, OrderEventEnum> transitions) throws Exception {
// 待支付 -> 已支付(支付事件)
transitions.withExternal()
.source(OrderStatusEnum.PENDING_PAY).target(OrderStatusEnum.PAID)
.event(OrderEventEnum.PAY)
.guard(payGuard()) // 守卫:校验状态合法性
.action(payAction()); // 动作:执行支付后的业务逻辑
// 已支付 -> 待发货(发货通知事件)
transitions.withExternal()
.source(OrderStatusEnum.PAID).target(OrderStatusEnum.PENDING_SHIP)
.event(OrderEventEnum.SHIP_NOTIFY);
// 待发货 -> 已完成(确认收货事件)
transitions.withExternal()
.source(OrderStatusEnum.PENDING_SHIP).target(OrderStatusEnum.COMPLETED)
.event(OrderEventEnum.CONFIRM_RECEIVE);
// 待支付 -> 已取消(取消订单事件)
transitions.withExternal()
.source(OrderStatusEnum.PENDING_PAY).target(OrderStatusEnum.CANCELLED)
.event(OrderEventEnum.CANCEL);
}
// 支付守卫:校验订单是否可支付
@Bean
public Guard<OrderStatusEnum, OrderEventEnum> payGuard() {
return context -> {
Order order = context.getExtendedState().get("order", Order.class);
return order != null && OrderStatusEnum.PENDING_PAY.equals(order.getStatus());
};
}
// 支付动作:更新订单支付时间等信息
@Bean
public Action<OrderStatusEnum, OrderEventEnum> payAction() {
return context -> {
Order order = context.getExtendedState().get("order", Order.class);
order.setPayTime(LocalDateTime.now());
log.info("订单{}支付成功,更新支付时间:{}", order.getOrderNo(), order.getPayTime());
};
}
}
// 订单事件枚举
enum OrderEventEnum {
PAY, // 支付
SHIP_NOTIFY, // 发货通知
CONFIRM_RECEIVE, // 确认收货
CANCEL // 取消订单
}
在 Service 中使用状态机处理订单状态流转,替代手动状态判断,使代码更清晰、可维护。
四、系统核心流程图
以下是"用户下单到支付完成"的完整业务流程图,使用 Mermaid 语法绘制,涵盖参数校验、库存扣减、订单创建、支付回调等全流程:
携带参数:用户ID、商品ID、购买数量、收货地址
校验失败(用户异常/数量非法)
校验成功
商品不存在/已下架
商品存在
库存不足
库存充足
扣减失败
扣减成功
支付失败
支付成功
取消订单
用户发起下单请求
Controller层接收请求
校验基础信息
返回错误信息
Service层开启事务
查询商品信息(加行锁)
事务回滚,返回错误
校验库存是否充足
扣减商品库存
生成唯一订单编号
计算订单总金额
保存订单主表数据
保存订单明细表数据
事务提交,返回订单编号
用户发起支付
支付平台处理支付
用户重新支付/取消订单
支付平台回调接口
Service层更新订单状态为已支付
返回回调成功响应
后续流程(通知发货、物流跟踪等)
恢复商品库存,更新订单状态为已取消
五、项目结构与开源仓库说明
5.1 项目结构(GitHub风格)
Java
ecommerce-order-system/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── example/
│ │ │ └── ecommerceorder/
│ │ │ ├── EcommerceOrderApplication.java // 项目启动类
│ │ │ ├── controller/ // 控制层
│ │ │ │ └── OrderController.java
│ │ │ ├── entity/ // 实体类
│ │ │ │ ├── Order.java
│ │ │ │ ├── OrderItem.java
│ │ │ │ ├── Product.java
│ │ │ │ └── User.java
│ │ │ ├── enums/ // 枚举类
│ │ │ │ ├── OrderStatusEnum.java
│ │ │ │ └── OrderEventEnum.java
│ │ │ ├── mapper/ // Mapper接口
│ │ │ │ ├── OrderMapper.java
│ │ │ │ ├── OrderItemMapper.java
│ │ │ │ ├── ProductMapper.java
│ │ │ │ └── UserMapper.java
│ │ │ ├── service/ // 服务层
│ │ │ │ ├── impl/
│ │ │ │ │ └── OrderServiceImpl.java
│ │ │ │ └── OrderService.java
│ │ │ ├── config/ // 配置类
│ │ │ │ ├── OrderStateMachineConfig.java
│ │ │ │ └── MyBatisConfig.java
│ │ │ └── vo/ // 响应VO
│ │ │ └── ResultVO.java
│ │ └── resources/
│ │ ├── application.yml // 全局配置文件
│ │ ├── mapper/ // Mapper XML文件
│ │ │ ├── OrderMapper.xml
│ │ │ ├── OrderItemMapper.xml
│ │ │ ├── ProductMapper.xml
│ │ │ └── UserMapper.xml
│ │ └── db/ // SQL脚本
│ │ └── init.sql
│ └── test/ // 测试类
│ └── java/
│ └── com/
│ └── example/
│ └── ecommerceorder/
│ └── OrderServiceTest.java
├── pom.xml // 依赖配置
├── README.md // 项目说明文档
└── .gitignore // Git忽略文件
5.2 开源仓库链接(虚构)
完整项目代码已开源至 GitHub,包含详细注释、SQL脚本、测试用例,可直接克隆运行:
GitHub 地址:https://github.com/example/ecommerce-order-system
运行说明:
-
克隆项目至本地,导入 IDEA;
-
创建 MySQL 数据库
ecommerce_order,执行resources/db/init.sql脚本初始化表结构与测试数据; -
修改
application.yml中的数据库连接信息,适配本地环境; -
启动项目,通过 Postman 调用接口测试(接口文档可参考 README.md)。
六、扩展与优化建议
本项目为简易版订单系统,实际生产环境需进行以下优化:
-
分布式场景优化:使用分布式ID生成订单号(雪花算法),分布式锁(Redisson)替代数据库行锁,解决集群环境下并发问题;
-
性能优化:添加Redis缓存热点商品信息,减少数据库查询;订单创建可引入消息队列(RabbitMQ/Kafka),异步处理非核心流程(如通知、日志);
-
异常处理优化:自定义业务异常类,全局异常处理器统一捕获异常,避免重复代码;
-
安全性优化:接口添加签名验证,防止非法调用;用户ID通过Token解析,避免直接传参;
-
功能扩展:增加购物车、优惠券、物流跟踪、订单退款等模块,完善电商生态。
📕个人领域 :Linux/C++/java/AI
🚀 个人主页 :有点流鼻涕 · CSDN
💬 座右铭 : "向光而行,沐光而生。"
