优雅!Spring Boot 3.3 实现职责链模式,轻松应对电商订单流程
在电商系统中,订单的处理流程通常涉及多个步骤,每个步骤都可能有不同的业务逻辑。例如,当用户提交订单时,系统需要校验库存、验证优惠券、计算运费、处理支付、分配物流等。这些操作看似独立,但实际上具有一定的顺序依赖性。为了更好地管理这些业务逻辑,我们需要将这些流程模块化,并按需执行。
通常的做法是将所有逻辑写在一起,但这会导致代码冗长且难以维护。如果未来需要对某个步骤进行修改或者添加新的处理环节,代码变动的范围将会很大。为了避免这种情况,职责链模式提供了一个灵活、可扩展的解决方案。
什么是职责链模式?
职责链模式(Chain of Responsibility)是一种行为设计模式,它允许多个对象都有机会处理请求,直到其中一个对象处理成功为止。职责链模式使多个处理对象通过链式关系链接在一起,每个处理对象知道它的下一个处理对象,并且在完成自身处理后,将请求传递给下一个对象。
职责链模式的优点:
适用场景:
职责链模式在电商订单流程中的应用
在电商系统中,职责链模式可以将订单处理过程中的各个环节(如库存校验、优惠券核验、支付处理等)封装为独立的处理器,并通过职责链将这些处理器串联起来。每个处理器独立处理其对应的任务,处理完成后将请求传递给下一个处理器,直到所有处理环节完成或者中断。
运行效果:
本文将深入探讨如何通过职责链模式来处理电商订单流程,并结合 Spring Boot 3.3 和前后端代码示例,展示如何实现这一模式。同时,前端使用 jQuery 调用后端 JSON 接口,并通过 Bootstrap 提示用户订单处理的结果。
POM 文件配置
项目中我们需要使用 Spring Boot 和 Thymeleaf 模板引擎,具体依赖配置如下:
<?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>3.3.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.icoderoad</groupId>
<artifactId>order-chain</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>order-chain</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<!-- Spring Boot 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Thymeleaf 模板引擎 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- Lombok 插件 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
配置文件 application.yml
使用 @ConfigurationProperties
读取订单处理链的配置步骤:
order:
chain:
steps:
- "orderValidationHandler"
- "verifyCouponHandler"
- "shippingFeeHandler"
- "totalAmountHandler"
- "processPaymentHandler"
订单处理职责链实现
为了优化 Handler
方法,可以结合订单的处理流程,对不同的 Handler
进行职责分工,比如验证订单信息、处理优惠券、计算运费和最终结算等步骤。我们可以使用职责链模式(Chain of Responsibility)将这些不同的处理逻辑独立封装在各自的 Handler
类中,并且每个 Handler
负责处理其自身的逻辑,处理完后将处理流程交给下一个 Handler
。
我们将实现以下几个 Handler
:
OrderValidationHandler
):负责验证订单的基本信息,比如商品是否存在、库存是否充足等。CouponHandler
):负责处理优惠券的校验和折扣计算。ShippingFeeHandler
):负责计算订单的运费。TotalAmountHandler
):负责计算订单的总金额。每个 Handler
类都遵循职责链的接口,将逻辑封装在 Handler
中,最后调用下一个 Handler
。
订单请求类 OrderRequest.java
package com.icoderoad.orderchain.entity;
import java.math.BigDecimal;
import java.util.List;
import lombok.Data;
@Data
public class OrderRequest {
// 商品列表
private List<Product> productList;
// 用户使用的优惠券
private String couponCode;
// 运费
private BigDecimal shippingFee;
// 订单总金额
private BigDecimal totalAmount;
public OrderRequest() {}
// 构造方法
public OrderRequest(List<Product> productList, String couponCode, BigDecimal shippingFee, BigDecimal totalAmount) {
this.productList = productList;
this.couponCode = couponCode;
this.shippingFee = shippingFee;
this.totalAmount = totalAmount;
}
// 计算订单总金额(含运费和扣除优惠)
public BigDecimal calculateTotalAmount() {
BigDecimal productTotal = productList.stream()
.map(product -> product.getPrice().multiply(BigDecimal.valueOf(product.getQuantity())))
.reduce(BigDecimal.ZERO, BigDecimal::add);
// 简单模拟优惠金额
BigDecimal discount = (couponCode != null && !couponCode.isEmpty()) ? BigDecimal.valueOf(10) : BigDecimal.ZERO;
return productTotal.add(shippingFee).subtract(discount);
}
@Data
// 商品类
public static class Product {
private String productId;
private String name;
private int quantity;
private BigDecimal price;
// 构造方法
public Product(String productId, String name, int quantity, BigDecimal price) {
this.productId = productId;
this.name = name;
this.quantity = quantity;
this.price = price;
}
}
}
抽象处理器类 OrderHandler.java
package com.icoderoad.orderchain.handler;
import com.icoderoad.orderchain.entity.OrderRequest;
public abstract class OrderHandler {
protected OrderHandler nextHandler;
// 设置下一个处理器
public void setNextHandler(OrderHandler nextHandler) {
this.nextHandler = nextHandler;
}
// 抽象方法,处理订单
public abstract void handle(OrderRequest request);
}
具体处理器实现
库存校验处理器 OrderValidationHandler
package com.icoderoad.orderchain.handler;
import org.springframework.stereotype.Component;
import com.icoderoad.orderchain.entity.OrderRequest;
@Component
public class OrderValidationHandler extends OrderHandler {
@Override
public void handle(OrderRequest orderRequest) {
// 验证商品列表是否为空
if (orderRequest.getProductList() == null || orderRequest.getProductList().isEmpty()) {
throw new IllegalArgumentException("订单中没有商品");
}
// 验证每个商品的库存(此处为模拟逻辑)
for (OrderRequest.Product product : orderRequest.getProductList()) {
if (product.getQuantity() <= 0) {
throw new IllegalArgumentException("商品库存不足: " + product.getName());
}
}
System.out.println("订单验证通过");
// 调用下一个处理器
if (nextHandler != null) {
nextHandler.handle(orderRequest);
}
}
}
优惠券核验处理器 VerifyCouponHandler.java
package com.icoderoad.orderchain.handler;
import java.math.BigDecimal;
import org.springframework.stereotype.Component;
import com.icoderoad.orderchain.entity.OrderRequest;
@Component
public class VerifyCouponHandler extends OrderHandler {
@Override
public void handle(OrderRequest orderRequest) {
String couponCode = orderRequest.getCouponCode();
// 简单模拟优惠券验证逻辑
if (couponCode != null && !couponCode.isEmpty()) {
// 假设优惠券折扣金额为 10
BigDecimal discount = new BigDecimal("10.00");
System.out.println("使用优惠券: " + couponCode + ",折扣金额: " + discount);
} else {
System.out.println("未使用优惠券");
}
// 调用下一个处理器
if (nextHandler != null) {
nextHandler.handle(orderRequest);
}
}
}
运费处理器
package com.icoderoad.orderchain.handler;
import java.math.BigDecimal;
import org.springframework.stereotype.Component;
import com.icoderoad.orderchain.entity.OrderRequest;
@Component
public class ShippingFeeHandler extends OrderHandler {
@Override
public void handle(OrderRequest orderRequest) {
// 简单模拟运费计算逻辑
BigDecimal shippingFee = orderRequest.getShippingFee();
System.out.println("运费: " + shippingFee);
// 调用下一个处理器
if (nextHandler != null) {
nextHandler.handle(orderRequest);
}
}
}
总金额处理器
package com.icoderoad.orderchain.handler;
import java.math.BigDecimal;
import org.springframework.stereotype.Component;
import com.icoderoad.orderchain.entity.OrderRequest;
@Component
public class TotalAmountHandler extends OrderHandler {
@Override
public void handle(OrderRequest orderRequest) {
// 计算订单总金额
BigDecimal totalAmount = orderRequest.calculateTotalAmount();
orderRequest.setTotalAmount(totalAmount);
System.out.println("订单总金额: " + totalAmount);
// 调用下一个处理器(如果有)
if (nextHandler != null) {
nextHandler.handle(orderRequest);
}
}
}
支付处理器 ProcessPaymentHandler.java
package com.icoderoad.orderchain.handler;
import org.springframework.stereotype.Component;
import com.icoderoad.orderchain.entity.OrderRequest;
@Component
public class ProcessPaymentHandler extends OrderHandler {
@Override
public void handle(OrderRequest request) {
// 支付处理逻辑
System.out.println("正在处理支付...");
// 支付完成,职责链结束
}
}
初始化职责链 OrderChainConfig.java
package com.icoderoad.orderchain.config;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.icoderoad.orderchain.handler.OrderHandler;
import com.icoderoad.orderchain.handler.OrderValidationHandler;
import com.icoderoad.orderchain.handler.ProcessPaymentHandler;
import com.icoderoad.orderchain.handler.ShippingFeeHandler;
import com.icoderoad.orderchain.handler.TotalAmountHandler;
import com.icoderoad.orderchain.handler.VerifyCouponHandler;
@Configuration
@ConfigurationProperties(prefix = "order.chain")
public class OrderChainConfig {
private List<String> steps;
// 将处理器的映射存储在一个集合中
private final Map<String, OrderHandler> handlerMap = new HashMap<>();
public OrderChainConfig(List<OrderHandler> handlers) {
// 初始化处理器映射
handlerMap.put("orderValidationHandler", handlers.stream().filter(h -> h instanceof OrderValidationHandler).findFirst().orElse(null));
handlerMap.put("verifyCouponHandler", handlers.stream().filter(h -> h instanceof VerifyCouponHandler).findFirst().orElse(null));
handlerMap.put("shippingFeeHandler", handlers.stream().filter(h -> h instanceof ShippingFeeHandler).findFirst().orElse(null));
handlerMap.put("totalAmountHandler", handlers.stream().filter(h -> h instanceof TotalAmountHandler).findFirst().orElse(null));
handlerMap.put("processPaymentHandler", handlers.stream().filter(h -> h instanceof ProcessPaymentHandler).findFirst().orElse(null));
}
@Bean(name = "orderChain")
public OrderHandler orderChain() {
if (steps == null || steps.isEmpty()) {
throw new IllegalArgumentException("处理链步骤不能为空");
}
// 动态创建处理链
OrderHandler firstHandler = null;
OrderHandler lastHandler = null;
for (String step : steps) {
OrderHandler handler = handlerMap.get(step);
if (handler == null) {
throw new IllegalArgumentException("未找到处理器: " + step);
}
if (firstHandler == null) {
firstHandler = handler;
}
if (lastHandler != null) {
lastHandler.setNextHandler(handler);
}
lastHandler = handler;
}
if (lastHandler != null) {
lastHandler.setNextHandler(null); // 最后一个处理器的 nextHandler 设置为 null
}
return firstHandler;
}
public void setSteps(List<String> steps) {
this.steps = steps;
}
}
说明
Map<String, OrderHandler>
来存储处理器实例,通过 handlers
列表动态注入。这允许我们在配置中使用字符串名称引用处理器实例。orderChain
方法中,根据 steps
配置的顺序动态创建处理链。每个处理器根据配置连接到下一个处理器。steps
配置有效,不为空,并且在处理链创建时校验处理器是否存在。OrderChainConfig
的构造函数中初始化处理器映射。确保 OrderValidationHandler
、VerifyCouponHandler
、ShippingFeeHandler
、TotalAmountHandler
和 ProcessPaymentHandler
被正确注入并配置。控制器接口优化
在优化后的控制器中,前端调用时返回 JSON 数据,jQuery 解析响应后通过 Bootstrap 弹出提示。
package com.icoderoad.orderchain.controller;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import com.icoderoad.orderchain.entity.OrderRequest;
import com.icoderoad.orderchain.entity.OrderRequest.Product;
import com.icoderoad.orderchain.handler.OrderHandler;
@RestController
public class OrderController {
private final OrderHandler orderHandler;
@Autowired
public OrderController(@Qualifier("orderChain") OrderHandler orderHandler) {
this.orderHandler = orderHandler;
}
@PostMapping("/processOrder")
public Map<String, Object> processOrder() {
Map<String, Object> response = new HashMap<>();
try {
OrderRequest request = new OrderRequest();
// 创建商品对象
Product product = new Product("10001", "手机", 2, new BigDecimal("20000.00"));
// 创建商品列表并添加商品
List<Product> productList = new ArrayList<>();
productList.add(product);
request.setShippingFee(new BigDecimal("200.00"));
// 将商品列表设置到 OrderRequest 中
request.setProductList(productList);
orderHandler.handle(request);
response.put("status", "success");
response.put("message", "订单处理成功!");
} catch (Exception e) {
response.put("status", "error");
response.put("message", "订单处理失败:" + e.getMessage());
}
return response;
}
}
前端界面及 jQuery 调用 JSON 接口
在前端,我们使用 jQuery 发起 AJAX 请求,并通过 Bootstrap 提示处理结果。
在 src/main/resources/templates
目录下创建 index.html
文件:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>订单处理</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-5">
<h2>电商订单处理</h2>
<button class="btn btn-primary" id="processOrder">处理订单</button>
</div>
<!-- 弹出提示框 -->
<div class="modal fade" id="orderModal" tabindex="-1" aria-labelledby="orderModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="orderModalLabel">订单处理状态</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<!-- 动态显示订单处理结果 -->
</div>
</div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
$(document).ready(function() {
$("#processOrder").click(function() {
$.ajax({
url: '/processOrder',
type: 'POST',
success: function(response) {
// 根据返回状态显示提示
$(".modal-body").text(response.message);
$('#orderModal').modal('show');
}
});
});
});
</script>
</body>
</html>
总结
通过职责链模式,我们可以将复杂的订单处理流程解耦成多个独立的步骤,提升了代码的可维护性和扩展性。每个处理环节可以灵活配置和扩展,便于后续的功能迭代。同时,结合 Spring Boot 和前后端交互技术,进一步增强了系统的可用性与用户体验。
今天就讲到这里,如果有问题需要咨询,大家可以直接留言,我们会尽力为你解答。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。