spring狀態機與自建狀態機

狀態機是有限狀態自動機的簡稱,是現實事物運行規則抽象而成的一個數學模型。目前網上已經有很多實現方案,可以根據自己需要採用。

spring狀態機框架:Spring StateMachine

在網上看了下關於spring狀態機的文章,很多都很相似,好像都來自“程序員DD”的《使用Spring StateMachine框架實現狀態機》 一文。
但是文中只是簡單舉了正常的例子,對於異常邏輯沒有詳細寫出。狗尾續貂一下,補充下異常流程的demo。

package online.javaadu.statemachinedemo.model;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.statemachine.annotation.OnEventNotAccepted;
import org.springframework.statemachine.annotation.OnTransition;
import org.springframework.statemachine.annotation.WithStateMachine;

@WithStateMachine
public class EventConfig {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @OnTransition(target = "UNPAID")
    public void create() {
        logger.info("訂單創建,待支付");
    }

    @OnTransition(source = "UNPAID", target = "WAITING_FOR_RECEIVE")
    public void pay() {
        logger.info("用戶完成支付,待收貨");
    }

    /**
     * 狀態不支持支付
     */
    @OnEventNotAccepted(event = "PAY")
    public void unpay() {
        logger.info("當前狀態不支持-完成支付");
    }

    @OnTransition(source = "WAITING_FOR_RECEIVE", target = "DONE")
    public void receive() {
        logger.info("用戶已收貨,訂單完成");
    }

    /**
     * 狀態不支持收貨
     */
    @OnEventNotAccepted(event = "RECEIVE")
    public void unreceive() {
        logger.info("當前狀態不支持-收貨");
    }

}

自建狀態機

package machine.base;

import lombok.Getter;

/**
 * 有限狀態機的動作枚舉 1-暫存 2-提交 3-審覈 4-撤回 5-駁回 6-反審覈 7-作廢  8-刪除
 */
@Getter
public enum EnumFsmEvent {

    TEMP_STORAGE(1, "暫存"),
    SUBMIT(2, "提交"),
    AUDITED(3, "審覈"),
    WITHDRAW(4, "撤回"),
    REJECT(5, "駁回"),
    UNAUDITED(6, "反審覈"),
    CANCEL(7, "作廢"),
    DELETE(8, "刪除")
    ;

    private Integer code;
    private String desc;

    EnumFsmEvent(Integer code, String desc) {
        this.code = code;
        this.desc = desc;
    }

    public static EnumFsmEvent findEnumFsmEvent(Integer code) {
        for (EnumFsmEvent event : EnumFsmEvent.values()) {
            if (event.getCode().equals(code)) {
                return event;
            }
        }
        return null;
    }
}
package machine.base;

import lombok.Getter;

/**
 * @ClassName: EnumFsmStatus
 * @Description: 1:暫存,2,審覈中,3,已審覈,4,已駁回,5,已反審,6,已作廢;
 * @Author: Yu RuiXin
 * @Date: 2020/5/24 18:46
 */
@Getter
public enum EnumFsmStatus {

    HAVE_BEAN_TEMP_STORAGE(1, "暫存"),
    IS_AUDITED(2, "審覈中"),
    HAVE_BEAN_AUDITED(3, "已審覈"),
    HAVA_BEAN_REJECT(4, "已駁回"),
    HAVE_BEAN_UNAUDITED(5, "已反審"),
    HAVE_BEAN_CANCEL(6, "已作廢")
    ;

    private Integer code;
    private String info;

    EnumFsmStatus(Integer code, String info) {
        this.code = code;
        this.info = info;
    }

    public static EnumFsmStatus findEnumStatus(Integer code) {
        for (EnumFsmStatus status : EnumFsmStatus.values()) {
            if (status.getCode().equals(code)) {
                return status;
            }
        }
        return null;
    }

}


package machine.base;


/**
 * @ClassName: FsmMachine
 * @Description: 狀態機
 * @Author: Yu RuiXin
 * @Date: 2020/5/24 18:46
 */
public interface FsmMachine {

    /**
     * 初始化狀態機
     * 需要將狀態與狀態實體對應
     */
    void initMachine() ;

    /**
     * 根據實際狀態,獲取狀態實體
     * @param status 單據狀態
     * @return
     */
    FsmState getState(String status) ;

    /**
     * 執行狀態扭轉方法 實現類中可以
     * @param event 操作事件
     * @param status 單據狀態
     * @return
     */
    FsmRetBody doEvent(String event, String status) ;

}

package machine.base;

/**
 * @ClassName: FsmRetBody
 * @Description: 狀態機返回格式
 * @Author: Yu RuiXin
 * @Date: 2020/5/24 18:46
 */
public class FsmRetBody<T> {
    private int code;
    private String msg;
    private T data;

    public FsmRetBody() {}

    public FsmRetBody(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public FsmRetBody(FsmRetCode retCode) {
        this.code = retCode.getCode();
        this.msg = retCode.getDesc();
    }

    public static FsmRetBody success() {
        return new FsmRetBody(FsmRetCode.SUCCESS);
    }

    public static FsmRetBody error() {
        return new FsmRetBody(FsmRetCode.ERROR);
    }

    public static FsmRetBody createRetByCode(FsmRetCode retCode) {
        return new FsmRetBody(retCode);
    }

    public static FsmRetBody noEvent() {
        return new FsmRetBody(FsmRetCode.NO_EVENT);
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}
package machine.base;

import lombok.Getter;

/**
 * @ClassName: FsmRetCode
 * @Description: 狀態碼
 * @Author: Yu RuiXin
 * @Date: 2020/5/24 18:45
 */
@Getter
public enum FsmRetCode {

    SUCCESS(200, "執行成功"),
    ERROR(500, "系統錯誤"),
    NO_EVENT(401, "沒有此事件"),

    NON_EXECUTABLE_TEMP_STORAGE(60000, "當前狀態,不可暫存"),
    NON_EXECUTABLE_SUBMIT(60001, "當前狀態,不可提交"),
    NON_EXECUTABLE_AUDITED(60002, "當前狀態,不可審覈"),
    NON_EXECUTABLE_WITHDRAW(60003, "當前狀態,不可撤回"),
    NON_EXECUTABLE_REJECT(60004, "當前狀態,不可駁回"),
    NON_EXECUTABLE_UNAUDITED(60005, "當前狀態,不可反審覈"),
    NON_EXECUTABLE_CANCEL(60006, "當前狀態,不可作廢"),
    NON_EXECUTABLE_DELETE(60007, "當前狀態,不可刪除"),

    UNAUDITED_ERROR_HAS_RETURN_NOTE(60011,"存在退貨單,不可反關單")
    ;

    FsmRetCode(int code, String desc) {
        this.code = code;
        this.desc = desc;
    }

    private int code;
    private String desc;

}

package machine.base;

import org.springframework.core.NamedThreadLocal;

/**
 * @ClassName: FsmState
 * @Description: 初始狀態父類
 * @Author: Yu RuiXin
 * @Date: 2020/5/24 18:46
 */
public class FsmState<T> {

    /**
     * 作爲實體屬性,可根據實際業務需要實例化,比如訂單實體等,方便操作事件的方法使用
     */
    private ThreadLocal<T> tl = new NamedThreadLocal("SmpFsmThreadLocal");;

    /**
     * 暫存
     */
    public FsmRetBody doTempStorage() {
        return FsmRetBody.createRetByCode(FsmRetCode.NON_EXECUTABLE_TEMP_STORAGE);
    }

    /**
     * 提交
     */
    public FsmRetBody doSubmit() {
        return FsmRetBody.createRetByCode(FsmRetCode.NON_EXECUTABLE_SUBMIT);
    }

    /**
     * 審覈
     */
    public FsmRetBody doAudited() {
        return FsmRetBody.createRetByCode(FsmRetCode.NON_EXECUTABLE_AUDITED);
    }

    /**
     * 撤回
     */
    public FsmRetBody doWithdraw() {
        return FsmRetBody.createRetByCode(FsmRetCode.NON_EXECUTABLE_WITHDRAW);
    }

    /**
     * 駁回
     */
    public FsmRetBody doReject() {
        return FsmRetBody.createRetByCode(FsmRetCode.NON_EXECUTABLE_REJECT);
    }

    /**
     * 反審覈
     */
    public FsmRetBody doUnaudited() {
        return FsmRetBody.createRetByCode(FsmRetCode.NON_EXECUTABLE_UNAUDITED);
    }

    /**
     * 作廢
     */
    public FsmRetBody doCancel() {
        return FsmRetBody.createRetByCode(FsmRetCode.NON_EXECUTABLE_CANCEL);
    }

    /**
     * 刪除
     */
    public FsmRetBody doDelete() {
        return FsmRetBody.createRetByCode(FsmRetCode.NON_EXECUTABLE_DELETE);
    }

    /**
     * 操作事件
     * 此方法是唯一需要判斷執行的邏輯,根據操作事件決定執行具體的方法,只在父類中風狀態,實現類中不需要處理
     * @param event 需要執行的事件
     */
    public FsmRetBody doEvent(EnumFsmEvent event) {
        switch (event) {
            case TEMP_STORAGE:
                return this.doTempStorage();
            case SUBMIT:
                return this.doSubmit();
            case AUDITED:
                return this.doAudited();
            case WITHDRAW:
                return this.doWithdraw();
            case REJECT:
                return this.doReject();
            case UNAUDITED:
                return this.doUnaudited();
            case CANCEL:
                return this.doCancel();
            case DELETE:
                return this.doDelete();
            default:
                return FsmRetBody.noEvent();
        }
    }

    public T getT() {
        T t = this.tl.get();
        tl.remove();
        return t;
    }

    public void setT(T t) {
        this.tl.set(t);
    }
}

package machine.state;

import machine.FsmOperService;
import machine.base.FsmRetBody;
import machine.base.FsmState;

/**
 * @ClassName: FsmStateAudited
 * @Description: 已審覈狀態體-支持操作:反審覈
 * @Author: RuiXin Yu
 * @Date: 2018/12/23 17:49
 */
public class FsmStateAudited extends FsmState {

    FsmOperService fsmOperService;

    public FsmStateAudited(FsmOperService fsmOperService) {
        this.fsmOperService = fsmOperService;
    }

    @Override
    public FsmRetBody doUnaudited() {
        fsmOperService.doUnaudited(this.getT());
        return FsmRetBody.success();
    }

}

package machine.state;


import machine.base.FsmState;

/**
 * @ClassName: FsmStateCancel
 * @Description: 已作廢狀態體-支持動作:無
 * @Author: RuiXin Yu
 * @Date: 2018/12/23 17:50
 */
public class FsmStateCancel extends FsmState {

    public FsmStateCancel() {}

}

package machine.state;


import machine.FsmOperService;
import machine.base.FsmRetBody;
import machine.base.FsmState;

/**
 * @ClassName: FsmStateIsAudited
 * @Description: 審覈中狀態體-支持操作:撤回、審覈、駁回
 * @Author: RuiXin Yu
 * @Date: 2018/12/23 17:50
 */
public class FsmStateIsAudited extends FsmState {

    FsmOperService fsmOperService;

    public FsmStateIsAudited(FsmOperService fsmOperService) {
        this.fsmOperService = fsmOperService;
    }

    @Override
    public FsmRetBody doWithdraw() {
        fsmOperService.doWithdraw(this.getT());
        return FsmRetBody.success();
    }

    @Override
    public FsmRetBody doAudited() {
        fsmOperService.doAudited(this.getT());
        return FsmRetBody.success();
    }

    @Override
    public FsmRetBody doReject() {
        fsmOperService.doReject(this.getT());
        return FsmRetBody.success();
    }

}

package machine.state;


import machine.FsmOperService;
import machine.base.FsmRetBody;
import machine.base.FsmState;

/**
 * @ClassName: FsmStateReject
 * @Description: 已駁回狀態體-支持時間:提交、審覈、作廢
 * @Author: RuiXin Yu
 * @Date: 2018/12/23 17:50
 */
public class FsmStateReject extends FsmState {

    FsmOperService fsmOperService;

    public FsmStateReject(FsmOperService fsmOperService) {
        this.fsmOperService = fsmOperService;
    }

    @Override
    public FsmRetBody doSubmit() {
        fsmOperService.doSubmit(this.getT());
        return FsmRetBody.success();
    }

    @Override
    public FsmRetBody doAudited() {
        fsmOperService.doAudited(this.getT());
        return FsmRetBody.success();
    }

    @Override
    public FsmRetBody doCancel() {
        fsmOperService.doCancel(this.getT());
        return FsmRetBody.success();
    }

}

package machine.state;


import machine.FsmOperService;
import machine.base.FsmRetBody;
import machine.base.FsmState;

/**
 * @ClassName: FsmStateTempStorage
 * @Description: 暫存狀態體-支持動作:暫存、提交、審覈、刪除
 * @Author: RuiXin Yu
 * @Date: 2018/12/23 17:50
 */
public class FsmStateTempStorage extends FsmState {

    FsmOperService fsmOperService;

    public FsmStateTempStorage(FsmOperService fsmOperService) {
        this.fsmOperService = fsmOperService;
    }

    @Override
    public FsmRetBody doTempStorage() {
        fsmOperService.doTempStorage(this.getT());
        return FsmRetBody.success();
    }

    @Override
    public FsmRetBody doSubmit() {
        fsmOperService.doSubmit(this.getT());
        return FsmRetBody.success();
    }

    @Override
    public FsmRetBody doAudited() {
        fsmOperService.doAudited(this.getT());
        return FsmRetBody.success();
    }

    @Override
    public FsmRetBody doDelete() {
        fsmOperService.doDelete(this.getT());
        return FsmRetBody.success();
    }

}

package machine.state;


import machine.FsmOperService;
import machine.base.FsmRetBody;
import machine.base.FsmState;

/**
 * @ClassName: FsmStateUnaudited
 * @Description: 已反審狀態體-支持動作:提交、審覈、作廢
 * @Author: RuiXin Yu
 * @Date: 2018/12/23 17:50
 */
public class FsmStateUnaudited extends FsmState {

    FsmOperService fsmOperService;

    public FsmStateUnaudited(FsmOperService fsmOperService) {
        this.fsmOperService = fsmOperService;
    }

    @Override
    public FsmRetBody doSubmit() {
        fsmOperService.doSubmit(this.getT());
        return FsmRetBody.success();
    }

    @Override
    public FsmRetBody doAudited() {
        fsmOperService.doAudited(this.getT());
        return FsmRetBody.success();
    }

    @Override
    public FsmRetBody doCancel() {
        fsmOperService.doCancel(this.getT());
        return FsmRetBody.success();
    }

}

package machine;

import machine.base.*;
import machine.state.*;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;

/**
 * @ClassName: FsmMachineService
 * @Description: 狀態機實現業務類
 * @Author: RuiXin Yu
 * @Date: 2018/12/23 17:49
 */
public class FsmMachineService<T> implements FsmMachine {

    private static final Map<EnumFsmStatus, FsmState> stateMap = new HashMap();

    private FsmStateTempStorage fsmStateTempStorage;
    private FsmStateIsAudited fsmStateIsAudited;
    private FsmStateAudited fsmStateAudited;
    private FsmStateReject fsmStateReject;
    private FsmStateUnaudited fsmStateUnaudited;
    private FsmStateCancel fsmStateCancel;

    public FsmMachineService(FsmStateTempStorage fsmStateTempStorage, FsmStateIsAudited fsmStateIsAudited,
                      FsmStateAudited fsmStateAudited, FsmStateReject fsmStateReject,
                      FsmStateUnaudited fsmStateUnaudited, FsmStateCancel fsmStateCancel) {
        this.fsmStateTempStorage = fsmStateTempStorage;
        this.fsmStateIsAudited = fsmStateIsAudited;
        this.fsmStateAudited = fsmStateAudited;
        this.fsmStateReject = fsmStateReject;
        this.fsmStateUnaudited = fsmStateUnaudited;
        this.fsmStateCancel = fsmStateCancel;
    }

    @Override
    @PostConstruct
    public void initMachine() {
        stateMap.put(EnumFsmStatus.HAVE_BEAN_TEMP_STORAGE, fsmStateTempStorage);
        stateMap.put(EnumFsmStatus.IS_AUDITED, fsmStateIsAudited);
        stateMap.put(EnumFsmStatus.HAVE_BEAN_AUDITED, fsmStateAudited);
        stateMap.put(EnumFsmStatus.HAVA_BEAN_REJECT, fsmStateReject);
        stateMap.put(EnumFsmStatus.HAVE_BEAN_UNAUDITED, fsmStateUnaudited);
        stateMap.put(EnumFsmStatus.HAVE_BEAN_CANCEL, fsmStateCancel);
    }

    @Override
    public FsmState getState(String statusCode) {
        EnumFsmStatus status = EnumFsmStatus.findEnumStatus(Integer.valueOf(statusCode));
        return stateMap.get(status);
    }
    /**
     * 執行狀態扭轉方法
     * @param eventCode 操作事件
     * @param status 單據狀態
     * @return
     */
    @Override
    public FsmRetBody doEvent(String eventCode, String status) {
        EnumFsmEvent event = EnumFsmEvent.findEnumFsmEvent(Integer.valueOf(eventCode));
        FsmState state = this.getState(status);
        return state.doEvent(event);
    }

    /**
     * 執行狀態扭轉方法
     * 由於退貨業務有需要依賴退貨單數據實體的地方,在上面的方法doEvent的基礎上私有化定製,所以增加了設置實體T
     * @param event
     * @param status
     * @param t 實體,可以根據業務需要設置類型
     * @return
     */
    public FsmRetBody doEvent(EnumFsmEvent event, EnumFsmStatus status, T t) {
        FsmState state = this.getState(status.getCode().toString());
        state.setT(t);
        return state.doEvent(event);
    }

}

package machine;

/**
 * @ClassName: FsmOperService
 * @Description:
 * @Author: RuiXin Yu
 * @Date: 2018/12/29 13:33
 */
public interface FsmOperService<T> {

    /**
     * 暫存
     * @param t
     */
    void doTempStorage(T t);

    /**
     * 提交
     * @param t
     */
    void doSubmit(T t);

    /**
     * 審覈
     * @param t
     */
    void doAudited(T t);

    /**
     * 撤回
     * @param t
     */
    void doWithdraw(T t);

    /**
     * 駁回
     * @param t
     */
    void doReject(T t);

    /**
     * 反審覈
     * @param t
     */
    void doUnaudited(T t);

    /**
     * 作廢
     * @param t
     */
    void doCancel(T t);

    /**
     * 刪除
     * @param t
     */
    void doDelete(T t);

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章