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