Java生鮮電商平台-訂單模塊狀態機架構設計


Java生鮮電商平台-訂單模塊狀態機架構設計

 

說明:在Java生鮮電商平台中訂單的狀態流轉業務

       我們知道 一個訂單會有很多種狀態:臨時單、已下單、待支付、待收貨、待評價、已完成,退貨中等等。每一種狀態都和其扭轉前的狀態、在扭轉前狀態所執行的操作有關。

一 實例說明

舉例一個過程:用戶將商品加入購物車,在后台生成了一個所謂的“臨時單”,這個訂單實際上還沒有正式生成,因為用戶仍然沒有點擊下單。只有當用戶下單后,這個“臨時單”才可以轉化為一個“待支付的訂單”。那么這個過程中:只有將一個處於“臨時單”狀態的訂單執行下單操作,才能得到一個狀態為“待支付”的訂單。 即--一個前置狀態+一個恰當的操作,才能扭轉訂單的狀態。在這個過程中,如果是硬編碼,那么我們需要一系列的 if...else 語句來檢查訂單的當前狀態、可執行操作以及這兩個的組合得到的下一個應該被流轉的狀態值。如果訂單的狀態流轉很復雜的話,寫出來的邏輯就會很復雜,並且可讀性很低。后期的維護就是一個坑。

二 狀態設計模式與訂單狀態流轉

處理這個問題,我們可以使用 狀態機設計模式 來處理。對應到實踐,就是狀態機。

關於狀態機設計模式的具體內容,可以自行百度。這里用簡單的一句話來概括的話:對象的內部狀態隨外部執行條件的變化而變化。再映射到訂單狀態的流轉上:訂單的狀態,隨訂單當前狀態和目前執行操作的組合而變化。

三 編碼前的抽象與設計

 
 

圖示模擬一個訂單狀態的流轉流程。從一個臨時訂單開始,每當訂單處於某一個已知的狀態的時候,要想讓這個訂單改變狀態,就需要我們去執行對應的操作。

從狀態機角度來說,我們先將各種信息進行抽象和處理

3.1 代碼抽象

編寫對應訂單狀態枚舉類

public enum OrderStatusEnum {

    CREATE_EVENT(1, "創建訂單"),
    FORMAL_EVENT(2, "正式訂單"),
    NEED_PAY(3, "待支付"),
    PAY_DONE(4, "已支付"),
    ORDER_FINISHED(5, "訂單已完成"),

    ORDER_CANCEL(6, "訂單已取消");

    OrderStatusEnum(int status, String desc) {
        this.status = status;
        this.desc = desc;
    }

    public int status;

    public String desc;
}

 

枚舉類中先准備好需要用的狀態信息。

先用一張圖來描述整個工作機制:

 

然后是需要的核心代碼部分:一個管理訂單狀態的中轉站manager類,一組用於扭轉訂單狀態的operator類,一組扭轉完訂單狀態后執行后續邏輯操作的processor類。

manager類需要根據對應傳入的當前訂單狀態、要對該訂單執行操作來得到這個訂單的結果狀態(依靠對應的opertor類),然后執行一系列需要的業務邏輯操作(編寫對應的processor類)。這樣的好處就是將訂單狀態流轉和對應的業務處理解耦。並且也不會再有一堆繁雜的 if...else 操作。每當需要新的訂單狀態流轉操作的時候,可以去編寫對應的一套operator和processor組件來完成,和已有業務的分離度很高。

接下來貼代碼舉例

 
          
/**
 * 訂單狀態流轉管理器--狀態機核心組件
 * @author Java生鮮電商平台
 *
 **/
@Component
public class OrderStateManager {

    Map<Integer, AbstractOrderOperator> orderOperatorMaps = new HashMap<Integer, AbstractOrderOperator>();

    Map<Integer, AbstractOrderProcessor> orderProcessorMaps = new HashMap<Integer, AbstractOrderProcessor>();

    public OrderStateManager() { }

    /**
     * 狀態流轉方法
     * @param orderId 訂單id
     * @param event 流轉的訂單操作事件
     * @param status 當前訂單狀態
     * @return 扭轉后的訂單狀態
     */
    public int handleEvent(final String orderId, OrderStatusEnum event, final int status) {
        if (this.isFinalStatus(status)) {
            throw new IllegalArgumentException("handle event can't process final state order.");
        }
        // 獲取對應處理器,根據入參狀態和時間獲取訂單流轉的結果狀態
        AbstractOrderOperator abstractOrderOperator = this.getStateOperator(event);
        int resState = abstractOrderOperator.handleEvent(status, event);
        // 得到結果狀態,在對應的processor中處理訂單數據及其相關信息
        AbstractOrderProcessor orderProcessor = this.getOrderProcessor(event);
        if (!orderProcessor.process(orderId, resState)) {
            throw new IllegalStateException(String.format("訂單狀態流轉失敗,訂單id:%s", orderId));
        }
        return resState;
    }

    /**
     * 根據入參狀態枚舉實例獲取對應的狀態處理器
     * @param event event
     * @return
     */
    private AbstractOrderOperator getStateOperator(OrderStatusEnum event) {
        AbstractOrderOperator operator = null;
        for (Map.Entry<Integer, AbstractOrderOperator> entry: orderOperatorMaps.entrySet()) {
            if (event.status == entry.getKey()) {
                operator = entry.getValue();
            }
        }
        if (null == operator) {
            throw new IllegalArgumentException(String.format("can't find proper operator. The parameter state :%s", event.toString()));
        }
        return operator;
    }

    /**
     * 根據入參狀態枚舉實例獲取對應的狀態后處理器
     * @param event event
     * @return
     */
    private AbstractOrderProcessor getOrderProcessor(OrderStatusEnum event) {
        AbstractOrderProcessor processor = null;
        for (Map.Entry<Integer, AbstractOrderProcessor> entry : orderProcessorMaps.entrySet()) {
            if (event.status == entry.getKey()) {
                processor = entry.getValue();
            }
        }
        if (null == processor) {
            throw new IllegalArgumentException(String.format("can't find proper processor. The parameter state :%s", event.toString()));
        }
        return processor;
    }

    /**
     * 判斷是不是已完成訂單
     * @param status 訂單狀態碼
     * @return
     */
    private boolean isFinalStatus(int status) {
        return OrderStatusEnum.ORDER_FINISHED.status == status;
    }

}

 

核心的代碼就是類中的 handleEvent 方法。

對應的獲取到的組件處理類示例:

/**
 * @author Java生鮮電商平台-訂單模塊狀態機架構設計
 **/
@Data
public abstract class AbstractOrderOperator {

    int status;

    public abstract int handleEvent(int orderStatus, OrderStatusEnum orderStatusEnum);
}
===================================
/**
 * 創建訂單操作狀態流轉
 * Java生鮮電商平台-訂單模塊狀態機架構設計
 **/
@Component
@OrderOperator
public class CreateOrderOperator extends AbstractOrderOperator {

    public CreateOrderOperator() {
        super.setStatus(1);
    }

    @Override
    public int handleEvent(int orderStatus, OrderStatusEnum orderStatusEnum) {
        if (orderStatus != OrderStatusEnum.CREATE_EVENT.status && orderStatus != OrderStatusEnum.ORDER_CANCEL.status) {
            throw new IllegalArgumentException(String.format("create operation can't handle the status: %s", orderStatus));
        }
        System.out.println("進入創建訂單狀態扭轉處理器...");
        switch (orderStatusEnum) {
            case CREATE_EVENT:
                return OrderStatusEnum.FORMAL_EVENT.status;
            case ORDER_CANCEL:
                return OrderStatusEnum.ORDER_CANCEL.status;
            default:
                return getStatus();
        }
    }
}

 

后處理器

/**
 * 訂單處理器
 **/
@Data
public abstract class AbstractOrderProcessor {

    int status;

    public abstract boolean process(String orderId, Object... params);
}
@Component @OrderProcessor public class CreateOrderProcessor extends AbstractOrderProcessor{ public CreateOrderProcessor() { super.setStatus(1); } @Override public boolean process(String orderId, Object... params) { // TODO 創建/取消訂單對應的數據庫修改,mq發送等操作,可以在此處process方法中完成 System.out.println("進入創建訂單后處理器..."); return true; } }

 

這些組件類都是依賴於spring的組件掃描注入。如果要定制化地處理自己的組件類。可以用一些其他的技巧來處理。比如此處使用到了自定義注解,通過自定義注解+自定義狀態機初始化類來完成對應組件類的篩選與初始化。將這個初始化類加載完畢,狀態機就可以正常使用了。

/**
 * 狀態機前置激活類,在spring中掃描配置此類 <br/>
 * 使用自定義注解標記對應的狀態處理器和后置處理器並在初始化操作中完成對應處理器的初始化。
 **/
@Component
public class Initialization implements BeanPostProcessor {

    @Resource
    OrderStateManager manager;

    @Nullable
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof AbstractOrderOperator && bean.getClass().isAnnotationPresent(OrderOperator.class) ) {
            AbstractOrderOperator orderState = (AbstractOrderOperator) bean;
            manager.orderOperatorMaps.put(orderState.getStatus(), orderState);
        }
        if (bean instanceof AbstractOrderProcessor && bean.getClass().isAnnotationPresent(OrderProcessor.class) ) {
            AbstractOrderProcessor orderProcessor = (AbstractOrderProcessor) bean;
            manager.orderProcessorMaps.put(orderProcessor.getStatus(), orderProcessor);
        }
        return bean;
    }
}

這里有一個問題就是在正式開發環境中,依賴於項目的spring環境,需要在狀態機正式運行前將對應的狀態扭轉組件類(operator和processor)注入到環境中。

 

聯系QQ:137071249

QQ群:793305035


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM