責(zé)任鏈和命令模式在訂單系統(tǒng)的實戰(zhàn)記錄

寫在前面

"紙上得來終覺淺,絕知此事要躬行".

讀完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)收銀臺去支付
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。