优雅!Spring Boot 3.3 实现职责链模式,轻松应对电商订单流程
在电商系统中,订单的处理流程通常涉及多个步骤,每个步骤都可能有不同的业务逻辑。例如,当用户提交订单时,系统需要校验库存、验证优惠券、计算运费、处理支付、分配物流等。这些操作看似独立,但实际上具有一定的顺序依赖性。为了更好地管理这些业务逻辑,我们需要将这些流程模块化,并按需执行。
通常的做法是将所有逻辑写在一起,但这会导致代码冗长且难以维护。如果未来需要对某个步骤进行修改或者添加新的处理环节,代码变动的范围将会很大。为了避免这种情况,职责链模式提供了一个灵活、可扩展的解决方案。
什么是职责链模式?
职责链模式(Chain of Responsibility)是一种行为设计模式,它允许多个对象都有机会处理请求,直到其中一个对象处理成功为止。职责链模式使多个处理对象通过链式关系链接在一起,每个处理对象知道它的下一个处理对象,并且在完成自身处理后,将请求传递给下一个对象。
职责链模式的优点:
- 解耦请求发送者和接收者:请求发送者不需要知道是谁在处理请求,避免了系统中各个模块的强耦合。
- 灵活扩展:通过调整链条中的处理器顺序,或者增加新的处理器,可以灵活地扩展业务逻辑。
- 动态组合:职责链的处理器可以动态组合,可以根据不同的需求创建不同的链条。
适用场景:
- 需要对请求进行一系列处理,并且请求处理者不明确或不固定时。
- 多个对象可以处理同一个请求,具体由运行时动态决定哪个对象来处理。
职责链模式在电商订单流程中的应用
在电商系统中,职责链模式可以将订单处理过程中的各个环节(如库存校验、优惠券核验、支付处理等)封装为独立的处理器,并通过职责链将这些处理器串联起来。每个处理器独立处理其对应的任务,处理完成后将请求传递给下一个处理器,直到所有处理环节完成或者中断。
运行效果:
若想获取项目完整代码以及其他文章的项目源码,且在代码编写时遇到问题需要咨询交流,欢迎加入下方的知识星球。
本文将深入探讨如何通过职责链模式来处理电商订单流程,并结合 Spring Boot 3.3 和前后端代码示例,展示如何实现这一模式。同时,前端使用 jQuery 调用后端 JSON 接口,并通过 Bootstrap 提示用户订单处理的结果。
POM 文件配置
项目中我们需要使用 Spring Boot 和 Thymeleaf 模板引擎,具体依赖配置如下:
<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
):负责计算订单的总金额。 - 支付处理器( PaymentHandler`):负责订单支付功能。
每个 Handler
类都遵循职责链的接口,将逻辑封装在 Handler
中,最后调用下一个 Handler
。
订单请求类 OrderRequest.java
package com.icoderoad.orderchain.entity; import java.math.BigDecimal; import java.util.List; import lombok.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); } // 商品类 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; public class OrderValidationHandler extends OrderHandler { 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; public class VerifyCouponHandler extends OrderHandler { 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; public class ShippingFeeHandler extends OrderHandler { 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; public class TotalAmountHandler extends OrderHandler { 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; public class ProcessPaymentHandler extends OrderHandler { 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; 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)); } 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; public class OrderController { private final OrderHandler orderHandler; public OrderController( ("orderChain") OrderHandler orderHandler) { this.orderHandler = orderHandler; } "/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
文件:
<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 和前后端交互技术,进一步增强了系统的可用性与用户体验。
今天就讲到这里,如果有问题需要咨询,大家可以直接留言,我们会尽力为你解答。