Spring?Boot?3.3?實現(xiàn)職責鏈模式輕松應對電商訂單流程分析
在電商系統(tǒng)中,訂單的處理流程通常涉及多個步驟,每個步驟都可能有不同的業(yè)務邏輯。例如,當用戶提交訂單時,系統(tǒng)需要校驗庫存、驗證優(yōu)惠券、計算運費、處理支付、分配物流等。這些操作看似獨立,但實際上具有一定的順序依賴性。為了更好地管理這些業(yè)務邏輯,我們需要將這些流程模塊化,并按需執(zhí)行。
通常的做法是將所有邏輯寫在一起,但這會導致代碼冗長且難以維護。如果未來需要對某個步驟進行修改或者添加新的處理環(huán)節(jié),代碼變動的范圍將會很大。為了避免這種情況,職責鏈模式提供了一個靈活、可擴展的解決方案。
什么是職責鏈模式?
職責鏈模式(Chain of Responsibility)是一種行為設計模式,它允許多個對象都有機會處理請求,直到其中一個對象處理成功為止。職責鏈模式使多個處理對象通過鏈式關系鏈接在一起,每個處理對象知道它的下一個處理對象,并且在完成自身處理后,將請求傳遞給下一個對象。
職責鏈模式的優(yōu)點:
- 解耦請求發(fā)送者和接收者:請求發(fā)送者不需要知道是誰在處理請求,避免了系統(tǒng)中各個模塊的強耦合。
- 靈活擴展:通過調(diào)整鏈條中的處理器順序,或者增加新的處理器,可以靈活地擴展業(yè)務邏輯。
- 動態(tài)組合:職責鏈的處理器可以動態(tài)組合,可以根據(jù)不同的需求創(chuàng)建不同的鏈條。
適用場景:
- 需要對請求進行一系列處理,并且請求處理者不明確或不固定時。
- 多個對象可以處理同一個請求,具體由運行時動態(tài)決定哪個對象來處理。
職責鏈模式在電商訂單流程中的應用
在電商系統(tǒng)中,職責鏈模式可以將訂單處理過程中的各個環(huán)節(jié)(如庫存校驗、優(yōu)惠券核驗、支付處理等)封裝為獨立的處理器,并通過職責鏈將這些處理器串聯(lián)起來。每個處理器獨立處理其對應的任務,處理完成后將請求傳遞給下一個處理器,直到所有處理環(huán)節(jié)完成或者中斷。
運行效果:
本文將深入探討如何通過職責鏈模式來處理電商訂單流程,并結合 Spring Boot 3.3 和前后端代碼示例,展示如何實現(xiàn)這一模式。同時,前端使用 jQuery 調(diào)用后端 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"
訂單處理職責鏈實現(xiàn)
為了優(yōu)化 Handler
方法,可以結合訂單的處理流程,對不同的 Handler
進行職責分工,比如驗證訂單信息、處理優(yōu)惠券、計算運費和最終結算等步驟。我們可以使用職責鏈模式(Chain of Responsibility)將這些不同的處理邏輯獨立封裝在各自的 Handler
類中,并且每個 Handler
負責處理其自身的邏輯,處理完后將處理流程交給下一個 Handler
。
我們將實現(xiàn)以下幾個 Handler
:
- 訂單驗證處理器(
OrderValidationHandler
):負責驗證訂單的基本信息,比如商品是否存在、庫存是否充足等。 - 優(yōu)惠券處理器(
CouponHandler
):負責處理優(yōu)惠券的校驗和折扣計算。 - 運費處理器(
ShippingFeeHandler
):負責計算訂單的運費。 - 總金額處理器(
TotalAmountHandler
):負責計算訂單的總金額。 - 支付處理器( PaymentHandler`):負責訂單支付功能。
每個 Handler
類都遵循職責鏈的接口,將邏輯封裝在 Handler
中,最后調(diào)用下一個 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; // 用戶使用的優(yōu)惠券 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; } // 計算訂單總金額(含運費和扣除優(yōu)惠) public BigDecimal calculateTotalAmount() { BigDecimal productTotal = productList.stream() .map(product -> product.getPrice().multiply(BigDecimal.valueOf(product.getQuantity()))) .reduce(BigDecimal.ZERO, BigDecimal::add); // 簡單模擬優(yōu)惠金額 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); }
具體處理器實現(xiàn)
庫存校驗處理器 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("訂單驗證通過"); // 調(diào)用下一個處理器 if (nextHandler != null) { nextHandler.handle(orderRequest); } } }
優(yōu)惠券核驗處理器 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(); // 簡單模擬優(yōu)惠券驗證邏輯 if (couponCode != null && !couponCode.isEmpty()) { // 假設優(yōu)惠券折扣金額為 10 BigDecimal discount = new BigDecimal("10.00"); System.out.println("使用優(yōu)惠券: " + couponCode + ",折扣金額: " + discount); } else { System.out.println("未使用優(yōu)惠券"); } // 調(diào)用下一個處理器 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); // 調(diào)用下一個處理器 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); // 調(diào)用下一個處理器(如果有) 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("處理鏈步驟不能為空"); } // 動態(tài)創(chuàng)建處理鏈 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
列表動態(tài)注入。這允許我們在配置中使用字符串名稱引用處理器實例。 - 動態(tài)創(chuàng)建處理鏈:在
orderChain
方法中,根據(jù)steps
配置的順序動態(tài)創(chuàng)建處理鏈。每個處理器根據(jù)配置連接到下一個處理器。 - 配置檢查:確保
steps
配置有效,不為空,并且在處理鏈創(chuàng)建時校驗處理器是否存在。 - 處理器實例:在
OrderChainConfig
的構造函數(shù)中初始化處理器映射。確保OrderValidationHandler
、VerifyCouponHandler
、ShippingFeeHandler
、TotalAmountHandler
和ProcessPaymentHandler
被正確注入并配置。
控制器接口優(yōu)化
在優(yōu)化后的控制器中,前端調(diào)用時返回 JSON 數(shù)據(jù),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(); // 創(chuàng)建商品對象 Product product = new Product("10001", "手機", 2, new BigDecimal("20000.00")); // 創(chuàng)建商品列表并添加商品 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 調(diào)用 JSON 接口
在前端,我們使用 jQuery 發(fā)起 AJAX 請求,并通過 Bootstrap 提示處理結果。
在 src/main/resources/templates
目錄下創(chuàng)建 index.html
文件:
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <title>訂單處理</title> <link 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">訂單處理狀態(tài)</h5> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div> <div class="modal-body"> <!-- 動態(tài)顯示訂單處理結果 --> </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) { // 根據(jù)返回狀態(tài)顯示提示 $(".modal-body").text(response.message); $('#orderModal').modal('show'); } }); }); }); </script> </body> </html>
總結
通過職責鏈模式,我們可以將復雜的訂單處理流程解耦成多個獨立的步驟,提升了代碼的可維護性和擴展性。每個處理環(huán)節(jié)可以靈活配置和擴展,便于后續(xù)的功能迭代。同時,結合 Spring Boot 和前后端交互技術,進一步增強了系統(tǒng)的可用性與用戶體驗。
今天就講到這里,如果有問題需要咨詢,大家可以直接留言,我們會盡力為你解答。
到此這篇關于Spring Boot 3.3 實現(xiàn)職責鏈模式,輕松應對電商訂單流程的文章就介紹到這了,更多相關Spring Boot 職責鏈模式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
spring?boot配置dubbo方式(properties)
這篇文章主要介紹了spring?boot配置dubbo方式(properties),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-01-01MybatisPlus調(diào)用原生SQL的三種方法實例詳解
這篇文章主要介紹了MybatisPlus調(diào)用原生SQL的三種方法,在有些情況下需要用到MybatisPlus查詢原生SQL,MybatisPlus其實帶有運行原生SQL的方法,我這里列舉三種,需要的朋友可以參考下2022-09-09spring中通過ApplicationContext getBean獲取注入對象的方法實例
今天小編就為大家分享一篇關于spring中通過ApplicationContext getBean獲取注入對象的方法實例,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-03-03Spring Boot 3.4.0 結合 Mybatis-plus 實
本文詳細介紹了在 Spring Boot 3.4.0 項目中結合 Mybatis-plus 實現(xiàn)動態(tài)數(shù)據(jù)源切換的完整方案,通過自定義注解和AOP切面,我們可以優(yōu)雅地實現(xiàn)方法級別的數(shù)據(jù)源切換,滿足多數(shù)據(jù)源場景下的各種需求,感興趣的朋友一起看看吧2025-04-04SpringBoot4.5.2 整合HikariCP 數(shù)據(jù)庫連接池操作
這篇文章主要介紹了SpringBoot4.5.2 整合HikariCP 數(shù)據(jù)庫連接池操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09idea數(shù)據(jù)庫驅(qū)動下載失敗的問題及解決
這篇文章主要介紹了idea數(shù)據(jù)庫驅(qū)動下載失敗的問題及解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-01-01