責任鏈和命令模式在訂單系統的實戰記錄

寫在前面

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

讀完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} 打開支付開關,客戶端引導跳轉收銀臺去支付
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,316評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,481評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,241評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,939評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,697評論 6 409
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,182評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,247評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,406評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,933評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,772評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,973評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,516評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,638評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,866評論 1 285
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,644評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,953評論 2 373