寫在前面
"紙上得來終覺淺,絕知此事要躬行".
讀完23種設計模式后,感覺肚子還是空空的,不知道無從下手,今天翻了一下之前的博客,曾經記錄過關于Apache Commons Chain的使用,它其實基于責任鏈,命令模式來寫的,它很適合做流程化的邏輯代碼,比如訂單的下單,支付流程以及退款流程,讓代碼寫的很nice,下面把我應用在訂單系統的實戰記錄下,看下對其他人有沒有幫助.
概念
20180701173353203.png
)
Command接口:
1. 命令模式,實現它來執行責任鏈中某個節點的業務.
2. 主要方法 boolean execute(Context context); return false 會繼續執行下一個節點,否則終止.
Filter接口:
1. extend command.
2. 比Command接口多了一個方法 boolean postprocess(Context var1, Exception var2);只要Filter的execute方法被調用,不論鏈的執行過程中是否拋出錯誤,Commons Chain都將保證Filter的postprocess方法被調用。
ChainBase基類:
它表示“命令鏈”,要在其中執行的命令,需要先添加到Chain中
Context接口:
它表示命令執行的上下文,在命令間實現共享信息的傳遞
訂單系統支付流程
直接上代碼了,代碼里面有注釋,代碼可以運行測試
1. pom文件引入
<!-- 引入commons-chain即可 -->
<dependency>
<groupId>commons-chain</groupId>
<artifactId>commons-chain</artifactId>
<version>1.2</version>
</dependency>
<!--Lombok 程序員的高效開發利器 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
<!-- 引入 logback日志 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.0-alpha1</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.3.0-alpha5</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.3.0-alpha5</version>
</dependency>
2. 訂單支付主流程
業務邏輯: 訂單下單后,應付金額 = 訂單總價, 如果使用了優惠券,我們需要把優惠券的金額從支付金額減去,如果還有商旅卡之類營銷類卡余額得扣去,最后才是客戶應該支付的現金,計算好應該支付的現金后,需要同步訂單的信息到公共訂單系統,供app查詢,app刷到訂單系統給的該筆訂單的支付開關是打開的,則跳轉到收銀臺,比如微信或者支付寶開始支付,下面我們看下怎么實現的.
- OrderPayChain 定義支付流程的步驟
- OrderContext 定義訂單的信息上下文:訂單總價,已支付的價格,使用的商旅卡價格
- PromotionPayCommand 優惠券支付邏輯
- BusinessPayCommand 商旅卡支付邏輯
- CashPayCommand 現金支付邏輯
- SysncOrderInfoCommand 同步訂單信息,打開支付開關
3. 實現過程
import org.apache.commons.chain.impl.ChainBase;
import java.math.BigDecimal;
/**
* 這是變異后的責任鏈和命令模式
*
* 它表示“命令鏈”,要在其中執行的命令,需要先添加到Chain中
*/
public class OrderPayChain extends ChainBase {
public void init() {
//第一步: 扣優惠券
this.addCommand(new PromotionPayCommand());
//第二步:優先商旅卡
this.addCommand(new BusinessPayCommand());
//第三步: 扣現金
this.addCommand(new CashPayCommand());
// 同步訂單信息
this.addCommand(new SysncOrderInfoCommand());
}
public static void main(String[] args) throws Exception {
var refundTicketChain = new OrderPayChain();
refundTicketChain.init();
var context = new OrderContext();
context.setOrderId(1621940242);
context.setTotalPrice(BigDecimal.valueOf(100));
refundTicketChain.execute(context);
}
}
import lombok.Data;
import org.apache.commons.chain.impl.ContextBase;
import java.math.BigDecimal;
/**
* Context接口。它表示命令執行的上下文,在命令間實現共享信息的傳遞
*/
@Data
public class OrderContext extends ContextBase {
/**
* 訂單號
*/
private Integer orderId;
/**
* 訂單總價
*/
private BigDecimal totalPrice = BigDecimal.ZERO;
/**
* 該筆訂單已經支付的總價
*/
private BigDecimal payTotalPrice = BigDecimal.ZERO;
/**
* 使用商旅卡支付的金額
*/
private BigDecimal businessCardPayPrice = BigDecimal.ZERO;
}
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.chain.Context;
import org.apache.commons.chain.Filter;
import java.math.BigDecimal;
@Slf4j
public class BusinessPayCommand implements Filter {
@Override
public boolean execute(Context context) throws Exception {
OrderContext orderContext = (OrderContext) context;
//查詢客戶賬戶下的商旅卡余額,比如查詢結果是12.5元
// var bPrice = new BigDecimal(12.5); 禁止這種寫法
var bPrice = BigDecimal.valueOf(12.5);
//算出還需要支付的價格
var remainPrice = orderContext.getTotalPrice().subtract(orderContext.getPayTotalPrice());
//該筆訂單,優先商旅卡支付全額
if (bPrice.compareTo(remainPrice) >= 0) {
orderContext.setPayTotalPrice(orderContext.getPayTotalPrice().add(remainPrice));
orderContext.setBusinessCardPayPrice(remainPrice);
if (log.isDebugEnabled()) {
log.debug("訂單orderId:{} ,訂單總價:{},使用商旅卡支付了:{},還需要支付:{}",orderContext.getOrderId(),orderContext.getTotalPrice(),orderContext.getBusinessCardPayPrice(),orderContext.getTotalPrice().subtract(orderContext.getPayTotalPrice()));
}
return true;
}else {
// 使用商旅卡支付部分
orderContext.setPayTotalPrice(orderContext.getPayTotalPrice().add(bPrice));
orderContext.setBusinessCardPayPrice(bPrice);
if (log.isDebugEnabled()) {
log.debug("訂單orderId:{} ,訂單總價:{},使用商旅卡支付了:{},還需要支付:{}",orderContext.getOrderId(),orderContext.getTotalPrice(),orderContext.getBusinessCardPayPrice(),orderContext.getTotalPrice().subtract(orderContext.getPayTotalPrice()));
}
return false;
}
}
@Override
public boolean postprocess(Context context, Exception e) {
OrderContext orderContext = (OrderContext) context;
var businessCardPayPrice = orderContext.getBusinessCardPayPrice();
// 請求扣除賬戶下商旅卡
//httpClient.post("/api/businessCard/reduce",businessCardPayPrice);
return false;
}
}
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;
import java.math.BigDecimal;
@Slf4j
public class PromotionPayCommand implements Command {
@Override
public boolean execute(Context context) throws Exception {
OrderContext orderContext = (OrderContext) context;
//查詢客戶賬戶下的優惠券余額,比如查詢結果是10元
var bPrice = BigDecimal.valueOf(10);
//算出還需要支付的價格
var remainPrice = orderContext.getTotalPrice().subtract(orderContext.getPayTotalPrice());
//該筆訂單,優先商旅卡支付全額
if (bPrice.compareTo(remainPrice) >= 0) {
orderContext.setPayTotalPrice(orderContext.getPayTotalPrice().add(remainPrice));
orderContext.setBusinessCardPayPrice(remainPrice);
if (log.isDebugEnabled()) {
log.debug("訂單orderId:{} ,訂單總價:{},使用優惠券支付了:{},還需要支付:{}",orderContext.getOrderId(),orderContext.getTotalPrice(),orderContext.getBusinessCardPayPrice(),orderContext.getTotalPrice().subtract(orderContext.getPayTotalPrice()));
}
return true;
}else {
// 使用商旅卡支付部分
orderContext.setPayTotalPrice(orderContext.getPayTotalPrice().add(bPrice));
orderContext.setBusinessCardPayPrice(bPrice);
if (log.isDebugEnabled()) {
log.debug("訂單orderId:{} ,訂單總價:{},使用優惠券支付了:{},還需要支付:{}",orderContext.getOrderId(),orderContext.getTotalPrice(),orderContext.getBusinessCardPayPrice(),orderContext.getTotalPrice().subtract(orderContext.getPayTotalPrice()));
}
return false;
}
}
}
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;
import org.apache.commons.chain.Filter;
import java.math.BigDecimal;
@Slf4j
public class CashPayCommand implements Filter {
@Override
public boolean execute(Context context) throws Exception {
OrderContext orderContext = (OrderContext) context;
//算出還需要支付的現金
var remainPrice = orderContext.getTotalPrice().subtract(orderContext.getPayTotalPrice());
if (log.isDebugEnabled()) {
log.debug("訂單orderId:{} ,訂單總價:{},還需要支付現金:{}",orderContext.getOrderId(),orderContext.getTotalPrice(),remainPrice);
}
return false;
}
@Override
public boolean postprocess(Context context, Exception e) {
log.info("打開支付開關,客戶端引導跳轉收銀臺去支付");
return false;
}
}
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;
@Slf4j
public class SysncOrderInfoCommand implements Command {
@Override
public boolean execute(Context context) throws Exception {
if (log.isDebugEnabled()) {
log.debug("同步子系統訂單信息到公共訂單系統");
}
return false;
}
}
測試
[PromotionPayCommand.java:33]2020-07-29 16:26:45.982 [DEBUG] {main} 訂單orderId:1621940242 ,訂單總價:100,使用優惠券支付了:10,還需要支付:90
[BusinessPayCommand.java:36]2020-07-29 16:26:45.992 [DEBUG] {main} 訂單orderId:1621940242 ,訂單總價:100,使用商旅卡支付了:12.5,還需要支付:77.5
[CashPayCommand.java:19]2020-07-29 16:26:45.993 [DEBUG] {main} 訂單orderId:1621940242 ,訂單總價:100,還需要支付現金:77.5
[SysncOrderInfoCommand.java:12]2020-07-29 16:26:45.994 [DEBUG] {main} 同步子系統訂單信息到公共訂單系統
[CashPayCommand.java:26]2020-07-29 16:26:45.994 [INFO] {main} 打開支付開關,客戶端引導跳轉收銀臺去支付