SpringBoot 电商系统完整流程实现方案
一、系统架构设计
1.1 微服务架构
online-shopping-system/
├── user-service # 用户服务
├── product-service # 商品服务
├── cart-service # 购物车服务
├── order-service # 订单服务
├── payment-service # 支付服务
├── gateway-service # API网关
├── eureka-server # 服务注册发现
└── config-server # 配置中心
1.2 技术栈选择
- 框架: Spring Boot 2.7.x, Spring Cloud 2021.x
- 数据库: MySQL 8.0, Redis 6.x
- 消息队列: RabbitMQ
- 搜索引擎: Elasticsearch
- 认证: Spring Security + JWT
- 支付: 支付宝、微信支付SDK
二、完整流程设计
2.1 核心流程图
用户选购商品 → 添加到购物车 → 结算购物车 → 创建订单 → 选择支付方式 → 调用支付接口 → 支付回调 → 更新订单状态 → 完成购买
三、详细实现代码
3.1 商品服务 - 选购功能
package com.example.product.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
@Data
@TableName("product")
public class Product {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private String description;
private BigDecimal price;
private Integer stock;
private String image;
private Integer status; // 0下架 1上架
private Long categoryId;
private Date createTime;
private Date updateTime;
}
package com.example.product.service;
import com.example.product.entity.Product;
import java.util.List;
public interface ProductService {
// 获取商品详情
Product getProductById(Long id);
// 分页查询商品
List<Product> getProductsByPage(Integer page, Integer size);
// 根据分类查询商品
List<Product> getProductsByCategory(Long categoryId);
// 搜索商品
List<Product> searchProducts(String keyword);
// 检查商品库存
boolean checkStock(Long productId, Integer quantity);
// 扣减库存
boolean deductStock(Long productId, Integer quantity);
// 增加库存
boolean addStock(Long productId, Integer quantity);
}
3.2 购物车服务
package com.example.cart.service;
import com.example.cart.vo.CartItemVO;
import java.util.List;
public interface CartService {
// 添加商品到购物车
void addToCart(Long userId, Long productId, Integer quantity);
// 获取购物车列表
List<CartItemVO> getCartList(Long userId);
// 更新购物车商品数量
void updateCartItemQuantity(Long userId, Long productId, Integer quantity);
// 删除购物车商品
void removeCartItem(Long userId, Long productId);
// 选择/取消选择商品
void checkCartItem(Long userId, Long productId, Boolean checked);
// 获取选中的商品
List<CartItemVO> getCheckedCartItems(Long userId);
// 清空购物车
void clearCart(Long userId);
// 合并购物车(登录时)
void mergeCart(Long tempUserId, Long userId);
}
3.3 订单服务
package com.example.order.enums;
import lombok.Getter;
@Getter
public enum OrderStatusEnum {
CREATED(0, "待付款"),
PAID(1, "待发货"),
SHIPPED(2, "待收货"),
COMPLETED(3, "已完成"),
CANCELLED(4, "已取消"),
REFUNDED(5, "已退款");
private Integer code;
private String message;
OrderStatusEnum(Integer code, String message) {
this.code = code;
this.message = message;
}
public static OrderStatusEnum getByCode(Integer code) {
for (OrderStatusEnum status : values()) {
if (status.code.equals(code)) {
return status;
}
}
return null;
}
}
package com.example.order.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
@Data
@TableName("orders")
public class Order {
@TableId(type = IdType.AUTO)
private Long id;
private String orderSn; // 订单编号
private Long userId;
private String userName;
private String userPhone;
private String address;
private BigDecimal totalAmount;
private BigDecimal paymentAmount; // 实际支付金额
private Integer orderStatus; // 订单状态
private Integer paymentType; // 支付方式 1支付宝 2微信
private Date createTime;
private Date payTime;
private Date shipTime;
private Date completeTime;
private Date cancelTime;
private String transactionId; // 支付平台交易ID
private String remark;
}
package com.example.order.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.math.BigDecimal;
@Data
@TableName("order_item")
public class OrderItem {
@TableId(type = IdType.AUTO)
private Long id;
private Long orderId;
private Long productId;
private String productName;
private String productImage;
private BigDecimal productPrice;
private Integer quantity;
private BigDecimal totalPrice;
}
package com.example.order.service;
import com.example.order.entity.Order;
import com.example.order.vo.OrderCreateVO;
import java.util.List;
public interface OrderService {
// 创建订单
Order createOrder(OrderCreateVO orderCreateVO);
// 获取订单详情
Order getOrderById(Long id);
// 获取订单列表
List<Order> getUserOrders(Long userId, Integer status, Integer page, Integer size);
// 取消订单
boolean cancelOrder(Long orderId, Long userId);
// 支付订单
String payOrder(Long orderId, Long userId, Integer paymentType);
// 确认收货
boolean confirmReceive(Long orderId, Long userId);
// 处理支付结果回调
String handlePayCallback(String notifyData, Integer paymentType);
// 关闭超时未支付订单
void closeTimeoutOrder();
}
package com.example.order.service.impl;
import com.example.order.entity.Order;
import com.example.order.entity.OrderItem;
import com.example.order.enums.OrderStatusEnum;
import com.example.order.mapper.OrderMapper;
import com.example.order.mapper.OrderItemMapper;
import com.example.order.service.OrderService;
import com.example.order.vo.OrderCreateVO;
import com.example.product.feign.ProductFeignClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.Date;
import java.util.UUID;
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private OrderItemMapper orderItemMapper;
@Autowired
private ProductFeignClient productFeignClient;
@Override
@Transactional(rollbackFor = Exception.class)
public Order createOrder(OrderCreateVO orderCreateVO) {
// 1. 生成订单号
String orderSn = generateOrderSn();
// 2. 创建订单主表
Order order = new Order();
order.setOrderSn(orderSn);
order.setUserId(orderCreateVO.getUserId());
order.setUserName(orderCreateVO.getUserName());
order.setUserPhone(orderCreateVO.getUserPhone());
order.setAddress(orderCreateVO.getAddress());
order.setTotalAmount(orderCreateVO.getTotalAmount());
order.setPaymentAmount(orderCreateVO.getTotalAmount());
order.setOrderStatus(OrderStatusEnum.CREATED.getCode());
order.setCreateTime(new Date());
orderMapper.insert(order);
// 3. 创建订单商品明细
for (OrderItem item : orderCreateVO.getOrderItems()) {
item.setOrderId(order.getId());
orderItemMapper.insert(item);
// 4. 扣减库存(分布式事务,可使用Seata或可靠消息)
boolean deductResult = productFeignClient.deductStock(item.getProductId(), item.getQuantity());
if (!deductResult) {
throw new RuntimeException("库存扣减失败");
}
}
// 5. 清空购物车中已购买的商品
// TODO: 调用购物车服务清空已购买商品
return order;
}
private String generateOrderSn() {
return UUID.randomUUID().toString().replace("-", "");
}
@Override
public String payOrder(Long orderId, Long userId, Integer paymentType) {
// 1. 查询订单
Order order = orderMapper.selectById(orderId);
if (order == null) {
throw new RuntimeException("订单不存在");
}
if (!order.getUserId().equals(userId)) {
throw new RuntimeException("无权操作此订单");
}
if (!order.getOrderStatus().equals(OrderStatusEnum.CREATED.getCode())) {
throw new RuntimeException("订单状态不允许支付");
}
// 2. 更新订单支付方式
order.setPaymentType(paymentType);
orderMapper.updateById(order);
// 3. 调用支付服务生成支付链接
// TODO: 调用支付服务
return "支付链接或二维码";
}
@Override
public String handlePayCallback(String notifyData, Integer paymentType) {
// 1. 验证支付回调数据
// TODO: 验证签名和数据
// 2. 解析回调参数
String orderSn = "解析得到的订单号";
String transactionId = "解析得到的交易流水号";
// 3. 查询订单
Order order = orderMapper.selectByOrderSn(orderSn);
if (order == null) {
return "fail";
}
// 4. 更新订单状态
order.setOrderStatus(OrderStatusEnum.PAID.getCode());
order.setPayTime(new Date());
order.setTransactionId(transactionId);
orderMapper.updateById(order);
// 5. 发送消息通知库存发货等
// TODO: 发送消息
return "success";
}
// 其他方法实现...
}
3.4 支付服务
package com.example.payment.enums;
import lombok.Getter;
@Getter
public enum PaymentTypeEnum {
ALIPAY(1, "支付宝"),
WECHAT_PAY(2, "微信支付");
private Integer code;
private String name;
PaymentTypeEnum(Integer code, String name) {
this.code = code;
this.name = name;
}
public static PaymentTypeEnum getByCode(Integer code) {
for (PaymentTypeEnum type : values()) {
if (type.code.equals(code)) {
return type;
}
}
return null;
}
}
package com.example.payment.service;
import com.example.payment.vo.PaymentRequestVO;
import com.example.payment.vo.PaymentResponseVO;
public interface PaymentService {
// 创建支付
PaymentResponseVO createPayment(PaymentRequestVO request);
// 查询支付状态
String queryPaymentStatus(String orderSn);
// 退款
boolean refund(String orderSn, String refundAmount);
// 处理支付回调
String handleCallback(String notifyData, Integer paymentType);
}
package com.example.payment.strategy;
import com.example.payment.vo.PaymentRequestVO;
import com.example.payment.vo.PaymentResponseVO;
public interface PaymentStrategy {
// 创建支付
PaymentResponseVO createPayment(PaymentRequestVO request);
// 查询支付状态
String queryPaymentStatus(String orderSn);
// 退款
boolean refund(String orderSn, String refundAmount);
// 处理回调
String handleCallback(String notifyData);
// 获取支付类型
Integer getPaymentType();
}
package com.example.payment.strategy.impl;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.example.payment.config.AlipayConfig;
import com.example.payment.strategy.PaymentStrategy;
import com.example.payment.vo.PaymentRequestVO;
import com.example.payment.vo.PaymentResponseVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class AlipayStrategy implements PaymentStrategy {
@Autowired
private AlipayConfig alipayConfig;
@Override
public PaymentResponseVO createPayment(PaymentRequestVO request) {
try {
// 创建AlipayClient实例
AlipayClient alipayClient = new DefaultAlipayClient(
alipayConfig.getGatewayUrl(),
alipayConfig.getAppId(),
alipayConfig.getPrivateKey(),
"json",
"UTF-8",
alipayConfig.getPublicKey(),
alipayConfig.getSignType());
// 创建API对应的request
AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
alipayRequest.setReturnUrl(alipayConfig.getReturnUrl());
alipayRequest.setNotifyUrl(alipayConfig.getNotifyUrl());
// 构造业务参数
String bizContent = "{\"out_trade_no\":\"" + request.getOrderSn() + "\","
+ "\"total_amount\":\"" + request.getAmount() + "\","
+ "\"subject\":\"" + request.getSubject() + "\","
+ "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}";
alipayRequest.setBizContent(bizContent);
// 调用SDK生成表单
String form = alipayClient.pageExecute(alipayRequest).getBody();
PaymentResponseVO response = new PaymentResponseVO();
response.setPaymentUrl(form);
response.setOrderSn(request.getOrderSn());
return response;
} catch (AlipayApiException e) {
e.printStackTrace();
throw new RuntimeException("支付宝支付创建失败");
}
}
@Override
public String queryPaymentStatus(String orderSn) {
// TODO: 实现支付宝查询接口
return "SUCCESS";
}
@Override
public boolean refund(String orderSn, String refundAmount) {
// TODO: 实现支付宝退款接口
return true;
}
@Override
public String handleCallback(String notifyData) {
// TODO: 实现支付宝回调处理
return "success";
}
@Override
public Integer getPaymentType() {
return 1; // 支付宝
}
}
package com.example.payment.factory;
import com.example.payment.strategy.PaymentStrategy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Component
public class PaymentStrategyFactory {
private final Map<Integer, PaymentStrategy> paymentStrategyMap = new ConcurrentHashMap<>();
@Autowired
public PaymentStrategyFactory(Map<String, PaymentStrategy> strategyMap) {
// 自动注入所有的支付策略实现
strategyMap.forEach((key, strategy) -> {
paymentStrategyMap.put(strategy.getPaymentType(), strategy);
});
}
// 根据支付类型获取对应的支付策略
public PaymentStrategy getPaymentStrategy(Integer paymentType) {
PaymentStrategy strategy = paymentStrategyMap.get(paymentType);
if (strategy == null) {
throw new RuntimeException("不支持的支付方式");
}
return strategy;
}
}
package com.example.payment.service.impl;
import com.example.payment.factory.PaymentStrategyFactory;
import com.example.payment.service.PaymentService;
import com.example.payment.strategy.PaymentStrategy;
import com.example.payment.vo.PaymentRequestVO;
import com.example.payment.vo.PaymentResponseVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class PaymentServiceImpl implements PaymentService {
@Autowired
private PaymentStrategyFactory paymentStrategyFactory;
@Override
public PaymentResponseVO createPayment(PaymentRequestVO request) {
// 根据支付类型获取对应的支付策略
PaymentStrategy strategy = paymentStrategyFactory.getPaymentStrategy(request.getPaymentType());
// 调用对应的支付策略创建支付
return strategy.createPayment(request);
}
@Override
public String queryPaymentStatus(String orderSn) {
// 需要知道订单的支付方式,可以从订单服务获取
// TODO: 实现查询逻辑
return "SUCCESS";
}
@Override
public boolean refund(String orderSn, String refundAmount) {
// 需要知道订单的支付方式,可以从订单服务获取
// TODO: 实现退款逻辑
return true;
}
@Override
public String handleCallback(String notifyData, Integer paymentType) {
// 根据支付类型获取对应的支付策略
PaymentStrategy strategy = paymentStrategyFactory.getPaymentStrategy(paymentType);
// 调用对应的支付策略处理回调
return strategy.handleCallback(notifyData);
}
}
3.5 控制器层实现
package com.example.order.controller;
import com.example.order.entity.Order;
import com.example.order.service.OrderService;
import com.example.order.vo.OrderCreateVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private OrderService orderService;
// 创建订单
@PostMapping("/create")
public Order createOrder(@RequestBody OrderCreateVO orderCreateVO) {
return orderService.createOrder(orderCreateVO);
}
// 支付订单
@PostMapping("/pay/{orderId}")
public String payOrder(@PathVariable Long orderId,
@RequestParam Integer paymentType,
HttpServletRequest request) {
// 从token或session中获取用户ID
Long userId = (Long) request.getAttribute("userId");
return orderService.payOrder(orderId, userId, paymentType);
}
// 支付回调
@PostMapping("/pay/callback/{paymentType}")
public String payCallback(@PathVariable Integer paymentType,
HttpServletRequest request) {
// 获取回调数据
String notifyData = "获取请求体数据";
return orderService.handlePayCallback(notifyData, paymentType);
}
// 取消订单
@PostMapping("/cancel/{orderId}")
public boolean cancelOrder(@PathVariable Long orderId, HttpServletRequest request) {
Long userId = (Long) request.getAttribute("userId");
return orderService.cancelOrder(orderId, userId);
}
// 确认收货
@PostMapping("/confirm/{orderId}")
public boolean confirmReceive(@PathVariable Long orderId, HttpServletRequest request) {
Long userId = (Long) request.getAttribute("userId");
return orderService.confirmReceive(orderId, userId);
}
// 获取订单列表
@GetMapping("/list")
public Map<String, Object> getOrderList(@RequestParam(required = false) Integer status,
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer size,
HttpServletRequest request) {
Long userId = (Long) request.getAttribute("userId");
// TODO: 实现分页查询
return null;
}
}
3.6 消息队列配置(处理异步任务)
package com.example.order.config;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitMQConfig {
// 订单创建交换机
public static final String ORDER_CREATE_EXCHANGE = "order.create.exchange";
// 订单支付交换机
public static final String ORDER_PAY_EXCHANGE = "order.pay.exchange";
// 订单取消交换机
public static final String ORDER_CANCEL_EXCHANGE = "order.cancel.exchange";
// 库存处理队列
public static final String INVENTORY_QUEUE = "inventory.queue";
// 消息通知队列
public static final String NOTIFY_QUEUE = "notify.queue";
// 订单超时队列
public static final String ORDER_TIMEOUT_QUEUE = "order.timeout.queue";
// 创建交换机
@Bean
public Exchange orderCreateExchange() {
return new DirectExchange(ORDER_CREATE_EXCHANGE, true, false);
}
@Bean
public Exchange orderPayExchange() {
return new DirectExchange(ORDER_PAY_EXCHANGE, true, false);
}
@Bean
public Exchange orderCancelExchange() {
return new DirectExchange(ORDER_CANCEL_EXCHANGE, true, false);
}
// 创建队列
@Bean
public Queue inventoryQueue() {
return new Queue(INVENTORY_QUEUE, true, false, false);
}
@Bean
public Queue notifyQueue() {
return new Queue(NOTIFY_QUEUE, true, false, false);
}
@Bean
public Queue orderTimeoutQueue() {
return QueueBuilder.durable(ORDER_TIMEOUT_QUEUE)
.withArgument("x-dead-letter-exchange", ORDER_CANCEL_EXCHANGE)
.withArgument("x-dead-letter-routing-key", "order.cancel.key")
.build();
}
// 绑定队列和交换机
@Bean
public Binding inventoryBinding(Queue inventoryQueue, Exchange orderCreateExchange) {
return BindingBuilder.bind(inventoryQueue)
.to(orderCreateExchange)
.with("inventory.key")
.noargs();
}
// 其他绑定...
}
四、完整流程演示
4.1 用户选购商品
- 前端调用商品服务查询商品列表/详情
- 用户浏览商品,选择商品规格和数量
4.2 添加到购物车
// 前端调用添加购物车接口
async function addToCart(productId, quantity) {
const response = await fetch('/api/cart/add', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
productId: productId,
quantity: quantity
})
});
return response.json();
}
4.3 结算购物车
- 前端展示购物车列表,用户选择要结算的商品
- 调用购物车服务获取选中的商品信息
- 前端组装订单创建数据
4.4 创建订单
// 前端调用创建订单接口
async function createOrder(orderData) {
const response = await fetch('/api/order/create', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token
},
body: JSON.stringify(orderData)
});
return response.json();
}
4.5 选择支付方式并支付
// 前端调用支付接口
async function payOrder(orderId, paymentType) {
const response = await fetch(`/api/order/pay/${orderId}?paymentType=${paymentType}`, {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + token
}
});
const data = await response.text();
// 根据返回的支付链接或表单进行支付
if (paymentType === 1) { // 支付宝
document.write(data); // 输出支付宝表单
} else if (paymentType === 2) { // 微信支付
// 处理微信支付二维码
}
}
4.6 支付回调处理
- 支付平台调用支付回调接口
- 验证回调数据,更新订单状态
- 发送消息通知相关服务
4.7 订单状态流转
创建订单 → 待付款 → 用户支付 → 支付回调 → 待发货 → 商家发货 → 待收货 → 用户确认收货 → 交易完成
↓ ↑
└──────── 订单取消 ────────────┘
五、分布式事务处理
对于分布式系统中的事务处理,推荐以下几种方案:
5.1 可靠消息最终一致性
- 订单服务创建订单并发送半消息
- 消息队列确认收到半消息
- 订单服务执行本地事务
- 根据本地事务结果提交或回滚半消息
- 支付服务/库存服务消费消息并执行相应操作
5.2 Seata分布式事务
引入Seata框架,使用@GlobalTransactional注解标记全局事务:
@GlobalTransactional
public Order createOrder(OrderCreateVO orderCreateVO) {
// 创建订单
// 扣减库存
// 记录日志
return order;
}
六、系统优化建议
6.1 性能优化
- 使用Redis缓存热点商品数据
- 使用Elasticsearch进行商品搜索
- 订单号生成使用分布式ID生成器
- 异步处理非核心流程(如消息通知、日志记录)
6.2 高可用设计
- 服务集群部署
- 数据库主从复制,读写分离
- Redis集群(哨兵模式或Redis Cluster)
- 使用API网关进行流量控制和熔断
6.3 安全措施
- 接口签名验证
- 敏感数据加密存储
- 接口访问频率限制
- 防SQL注入、XSS等攻击
- 支付安全(严格的签名验证、防重复支付)
七、测试要点
- 完整流程测试(选购→购物车→下单→支付→完成)
- 异常场景测试(库存不足、支付失败、超时取消等)
- 并发测试(多用户同时下单)
- 性能测试(响应时间、吞吐量)
- 安全测试(数据安全、接口安全)
通过以上设计和实现,可以构建一个完整、稳定、高性能的电商系统支付流程,满足实际业务需求。