Spring StateMachine狀態機引擎在項目中的應用(三)-狀態及事件設計

背景

這期說下狀態以及對應事件的相關設計,這部份內容是後續狀態機相關的配置的基礎,其中有些設計在實現的時候來回修改了幾版,仍是挺考驗細節設計的。java

狀態變遷

狀態機是爲了解決訂單的狀態變遷問題,爲了方便理解,就須要有一個具體的狀態變化圖,下面是以前處理過的一個案例,先上其對應的狀態變遷圖:apache

複雜訂單狀態機.png

說明:markdown

  1. 這個狀態變化比較複雜,也比較奇葩,將部分不該該由訂單來維護的狀態都統一放到了訂單中,好比待實名認證、待業務審覈、待用戶資料不全、待上傳影像這種,其實都是開戶過程當中應該作的,可是業務方抽象能力比較弱,又比較難溝通,且若是但願能夠儘快落地,只能由訂單系統吞掉這部分邏輯,因此整個流程就變成了上圖所示,一個很複雜的狀態節點變動流程。
  2. 用戶在建立訂單時,建立只是一個瞬時狀態,只會有待實名及待借款兩種狀況,因此在這裏,create狀態設計成了一個choice;統一的初始入口比較容易處理、理解。
  3. 其實還有一些終結狀態,好比各類審覈失敗、風控失敗等等,這裏爲了簡化處理,統一設置爲close狀態。
狀態枚舉

根據上圖所示的狀態,設計其對應的狀態枚舉以下:app

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.StringUtils;

import java.util.Arrays;
import java.util.Optional;

@AllArgsConstructor
@NoArgsConstructor
@Getter
public enum BizOrderStatusEnum {

    CREATE("1", "建立"),

    WYD_INITIAL_JUMP("2", "初始建立時可能爲待實名校驗,也可能爲待發起借款,須要用guard判斷"),

    WAIT_REAL_NAME_AUTH("3", "待實名認證"),

    WAIT_BORROW("4", "待發起借款"),

    CANCEL("999", "用戶取消"),

    CLOSE("996", "訂單關閉"),

    SUCCESS("888", "成功,指已銷帳,訂單徹底終結"),

    /**
     * 審覈相關狀態定義-start
     */
    AUDITING("100", "審覈中"), 

    WAIT_BIZ_AUDIT("101", "待業務審覈"),

    BIZ_APPROVED("102", "業務審覈經過"),

    WAIT_COMPLEMENT("103", "待補全審覈資料"), 

    CHECK_COMPLEMENT("104", "補全資料檢查"),// --流程內部使用

    WAIT_UPLOAD_IMG("105", "待上傳影像資料"), 

    CHECK_UPLOAD("106", "上傳影像檢查"),//  --- 流程內部使用

    WAIT_BEF_DEAL_RISK_AUDIT("111", "待貸前額度評估"),

    IN_DEAL_RISK_AUDITING("121", "貸中風控審覈"),

    WAIT_AF_DEAL_RISK_AUDIT("131", "待貸後審覈"),  

    AF_DEAL_RISK_APPROVED("132", "貸後審覈經過"), 

    APPROVED("198", "審覈經過"),

    /**
     * 審覈相關狀態定義--end
     */
    WAIT_SIGN("200", "待簽約"),

    SIGNED("288", "簽約完成"),

    LOANING("300", "放款中"),

    LOANED("388", "放款完成"),

    REFUNDING("401", "退款中"), // 對於消費貸,存在退款狀況

    REFUNDED("488", "退款完成"), // 對於消費貸,存在退款狀況

    BILL_GEN("501", "生成帳單"),

    REPAYING("600", "還款中"),  // 內部使用的中間瞬時狀態,外部傳入時不要使用,部分還款採用PART_REPAID類型

    PART_REPAID("601", "部分還款"),

    REPAID("688", "已還清"),

    OVERDUE("700", "已逾期"),;


    private String status;

    private String desc;

    /**
     * status是否合法
     *
     * @param status
     * @return
     */
    public static boolean isIn(String status) {
        return Arrays.asList(BizOrderStatusEnum.values()).parallelStream().
                anyMatch(value -> StringUtils.equals(value.getStatus(), status));

    }

    /**
     * 判斷status是否相等
     *
     * @param status
     * @param statusEnum
     * @return
     */
    public static boolean equals(String status, BizOrderStatusEnum statusEnum) {
        return StringUtils.equalsIgnoreCase(status, statusEnum.getStatus());

    }

    /**
     * status-->statusEnum
     *
     * @param status
     * @return
     */
    public static BizOrderStatusEnum getByStatus(String status) {
        Optional<BizOrderStatusEnum> statusEnumOptional = Arrays.asList(BizOrderStatusEnum.values()).parallelStream()
                .filter(statusEnum -> StringUtils.equalsIgnoreCase(status, statusEnum.getStatus())).findAny();

        if (statusEnumOptional.isPresent()) {
            return statusEnumOptional.get();
        }

        return null;

    }

    /**
     * 判斷status是否合法
     *
     * @param status
     * @param statusEnums
     * @return
     */
    public static boolean isIn(String status, BizOrderStatusEnum... statusEnums) {
        return Arrays.asList(statusEnums).parallelStream().
                anyMatch(value -> StringUtils.equals(value.getStatus(), status));

    }

    /**
     * 判斷是否訂單已終結,取消、關閉、成功、拒絕都屬於終結狀態
     *
     * @param status
     * @return
     */
    public static boolean isFinish(String status) {
        return isIn(status, SUCCESS, CANCEL, CLOSE);
    }

    /**
     * 判斷訂單是不是初始建立狀態
     * 對於: WAIT_REAL_NAME_AUTH, WAIT_BORROW 均可能是初始狀態
     * 對於其餘:暫時爲CREATE狀態
     *
     * @param status
     * @return
     */
    public static boolean isInitialStatus(String status) {
        return isIn(status, CREATE, WAIT_REAL_NAME_AUTH, WAIT_BORROW);
    }

}複製代碼

須要注意的是,這裏面有些狀態在一些業務場景是用不到的,好比退款,在當前業務場景並無用,這裏的狀態是全集。spa

事件枚舉

事件會致使訂單的狀態發生變化(固然,不是絕對,有些事件是內部事件,並不會致使狀態變化,這時就須要用withInternal來串聯,詳見下節配置),下面是本次的事件枚舉設計

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.StringUtils;

import java.util.Arrays;
import java.util.Optional;

@AllArgsConstructor
@NoArgsConstructor
@Getter
public enum BizOrderStatusChangeEventEnum {

    EVT_CREATE("evt_create"),

    EVT_CANCEL("evt_cancel"), // 取消

    EVT_NAME_AUTH("evt_name_auth"), // 實名

    EVT_SYS_CLOSE("evt_sys_close"), // 系統關單,如超時或風控關單等

    EVT_AUDIT("evt_audit"), // 審覈

    EVT_COMPLEMENT("evt_complement"), // 補全材料

    EVT_UPLOAD_IMG("evt_upload_img"), // 上傳影像

    EVT_APPROVED("evt_approved"), // 批准

    EVT_REFUSE("evt_refuse"), // 拒絕

    EVT_SIGN("evt_sign"), // 簽約

    EVT_LOAN("evt_loan"), // 放款

    EVT_LOAN_FAILED("evt_loan_failed"),

    EVT_REFUND("evt_refund"), // 退款

    EVT_REPAY("evt_repay"), // 還款

    EVT_GEN_BILL("evt_gen_bill"), // 生成帳單

    EVT_TOSUCCESS("evt_tosuccess"), // 銷帳

    EVT_RETRY("evt_retry"), // 重試

    EVT_OVERDUE("evt_overdue")  // 逾期,用戶無動做,由系統定時任務發起
    ,
    EVT_LOAN_SUCC("evt_loan_succ"),

    EVT_NEED_NAME_AUTH("evt_need_name_auth");

    String event;

    /**
     * 判斷
     * @param eventName
     * @return
     */
    public static BizOrderStatusChangeEventEnum getEvent(String eventName) {
        if (StringUtils.isBlank(eventName)) {
            return null;
        }

        Optional<BizOrderStatusChangeEventEnum> resultOptional = Arrays.asList(BizOrderStatusChangeEventEnum.values()).parallelStream().filter(eventEnum ->
                StringUtils.equals(eventName, eventEnum.getEvent())).findAny();

        if (resultOptional.isPresent()) {
            return resultOptional.get();
        }
        return null;
    }
}複製代碼

這裏的狀態就是StateMachine中的S,事件就是對應的E。code

相關文章
相關標籤/搜索