状态机是有限状态自动机的简称,是现实事物运行规则抽象而成的一个数学模型。目前网上已经有很多实现方案,可以根据自己需要采用。

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);

}