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