Spring狀态機StateMachine架構—企業開發實戰(含代碼)
最近,在工作中上級派了一個任務給我,叫我寫下狀态機。一開始我聽了是直接懵逼的,我都不知道什麼是狀态機。後來聽了業務需求後才慢慢弄懂,這個狀态機的作用是修改訂單狀态,為了讓業務代碼能夠高效複用。這又引出了第二問題了,要怎樣實作狀态機,是寫一堆if-else判斷嗎?一開始我的想法就是這樣,後來上網查了狀态機,發現有個StateMachine架構,然後就去看了官方文檔https://docs.spring.io/spring-statemachine/docs/2.0.2.RELEASE/reference/htmlsingle/#with-enablestatemachinefactory。當然期間在使用過程中也踩了不少的坑,就把自己一些心得寫下來,當然自己沒閱讀過什麼源碼,隻能是自己一些粗淺的見解而已。僅供參考。
一、狀态流程圖
這個流程圖我是感覺非常重要的,要清楚每個狀态的流轉,和哪種事件會觸發什麼事件流轉這都是很重要的。下面是我自己工作的一個狀态流程圖
[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-Olhxfhjh-1631519370357)(D:\MyData\ligy112\AppData\Roaming\Typora\typora-user-images\image-20210913135419684.png)]
二、了解下幾個基本常用的元件
.withExternal()
.source(TradeOrderStateMachineEnum.WAIT_FOR_PAY)
.target(TradeOrderStateMachineEnum.CLOSED)
.event(TradeOrderEvent.CLOSE).and()
這是配置規則,表示從WAIT_FOR_PAY->CLOSED,需要CLOSE事件來觸發。這是比較簡單的一種,沒有guard判斷,直接流轉到CLOSED狀态。
.withExternal()
.source(TradeOrderStateMachineEnum.WAIT_FOR_AUDIT)
.target(TradeOrderStateMachineEnum.WAIT_FOR_DELIVER)
.event(TradeOrderEvent.AUDIT).guard(tradeOrderGuardFactory.new TradeOrderGuard()).and()
下面一種,則是多了一個.guard(),這是判斷,相當于java裡面if的判斷條件,這個自定義判斷的類必須實作Guard接口,重寫裡面evaluate方法,這個方法就是傳回boolean。值得一提的是,每個規則都可以配置action,可以直接在後面加上.action(),也可以用@WithStateMachine和@OnTransition兩個注解配合用寫下自己業務代碼。這個action表示,滿足整個鍊路規則後才要做的是。
.withChoice()
.source(TradeOrderStateMachineEnum.AUDIT_CHOICE)
.first(TradeOrderStateMachineEnum.COMPLETED, tradeOrderGuardFactory.new TradeOrderAuditChoiceGuard(),new TradeOrderChoiceAction())
.then(TradeOrderStateMachineEnum.WAIT_FOR_EVALUATE, tradeOrderGuardFactory.new TradeOrderAuditChoiceGuard2(),new TradeOrderChoiceAction())
.then(TradeOrderStateMachineEnum.WAIT_FOR_SIGN, tradeOrderGuardFactory.new TradeOrderAuditChoiceGuard3(),new TradeOrderChoiceAction())
.then(TradeOrderStateMachineEnum.WAIT_FOR_DELIVER, tradeOrderGuardFactory.new TradeOrderAuditChoiceGuard4(),new TradeOrderChoiceAction())
.last(TradeOrderStateMachineEnum.WAIT_FOR_AUDIT).and()
前面兩種是比較簡單的,一個事件隻會流轉到一個狀态,上面這個就是比較複雜點,也是業務上經常會用,一個event會有幾種狀态,first-then-last,就相當于if-else if,隻要滿足一個guard判斷就不會往下流轉,注意這裡有幾個坑,後面說下的。
三、代碼
3.1 引入依賴包
<dependency>
<groupId>org.springframework.statemachine</groupId>
<artifactId>spring-statemachine-starter</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>
3.2 定義狀态枚舉和事件枚舉
TradeOrderStateMachineEnum
public enum TradeOrderStateMachineEnum {
WAIT_FOR_PAY(10, "待付款"),
WAIT_FOR_AUDIT(20, "待評審"),
WAIT_FOR_DELIVER(30, "待發貨"),
WAIT_FOR_SIGN(40, "待簽收"),
WAIT_FOR_EVALUATE(45, "待評價"),
COMPLETED(98, "完成"),
CLOSED(99, "關閉"),
AUDIT_CHOICE(1000, "評審選擇态"),
SIGN_CHOICE(1001, "簽收選擇态");
private final Integer value;
private final String desc;
private static final Map<Integer, TradeOrderStateMachineEnum> valueMap = (Map) Arrays.stream(values()).collect(Collectors.toMap(TradeOrderStateMachineEnum::getValue, Function.identity()));
private TradeOrderStateMachineEnum(Integer value, String desc) {
this.value = value;
this.desc = desc;
}
public Integer getValue() {
return this.value;
}
public String getDesc() {
return this.desc;
}
public static TradeOrderStateMachineEnum fromValue(Integer value) {
return (TradeOrderStateMachineEnum) Optional.ofNullable(valueMap.get(value)).orElseThrow(() -> {
return new RuntimeException("can not find the enum for this value: " + value);
});
}
}
TradeOrderEvent
public enum TradeOrderEvent {
PAY,//付款
CLOSE,//關閉訂單
CANCEL,//取消數量
AUDIT,//評審
DELIVER,//發貨
SIGN,//簽收
EVALUATE;//評價
}
3.2 定義狀态機規則和配置狀态機
TradeOrderStateMachineBuilder
@Component
@EnableStateMachine(name= TradeOrderStateMachineBuilder.MACHINEID_TO)
public class TradeOrderStateMachineBuilder {
private static final TradeOrderGuardFactory tradeOrderGuardFactory= new TradeOrderGuardFactory();
@Autowired
private BeanFactory beanFactory;
private Logger logger = LoggerFactory.getLogger(getClass());
public final static String MACHINEID_TO = "MACHINEID_TO";//TO狀态機
public StateMachine<TradeOrderStateMachineEnum, TradeOrderEvent> build() throws Exception {
StateMachine<TradeOrderStateMachineEnum, TradeOrderEvent> stateMachine = build(beanFactory);
logger.info("狀态機ID:"+stateMachine.getId());
stateMachine.start();
return stateMachine;
}
/**
* 建構狀态機
* -建構TO單狀态機
* @param beanFactory
* @return
* @throws Exception
*/
public StateMachine<TradeOrderStateMachineEnum, TradeOrderEvent> build(BeanFactory beanFactory) throws Exception {
StateMachineBuilder.Builder<TradeOrderStateMachineEnum, TradeOrderEvent> builder = StateMachineBuilder.builder();
builder.configureConfiguration()
.withConfiguration()
.machineId(MACHINEID_TO)
.beanFactory(beanFactory);
builder.configureStates()
.withStates()
.initial(TradeOrderStateMachineEnum.WAIT_FOR_PAY)
.choice(TradeOrderStateMachineEnum.AUDIT_CHOICE)
.choice(TradeOrderStateMachineEnum.SIGN_CHOICE)
.states(EnumSet.allOf(TradeOrderStateMachineEnum.class));
builder.configureTransitions()
//支付後,從待付款到待稽核
.withExternal()
.source(TradeOrderStateMachineEnum.WAIT_FOR_PAY)
.target(TradeOrderStateMachineEnum.WAIT_FOR_AUDIT)
.event(TradeOrderEvent.PAY).and()
//取消訂單,從待付款到關閉
.withExternal()
.source(TradeOrderStateMachineEnum.WAIT_FOR_PAY)
.target(TradeOrderStateMachineEnum.CLOSED)
.event(TradeOrderEvent.CLOSE).and()
//取消數量,從待稽核到稽核選擇态
.withExternal()
.source(TradeOrderStateMachineEnum.WAIT_FOR_AUDIT)
.target(TradeOrderStateMachineEnum.AUDIT_CHOICE)
.event(TradeOrderEvent.CANCEL).and()
//取消數量,從稽核選擇态->待發貨,待簽收,待評價,完成任意一種狀态
.withChoice()
.source(TradeOrderStateMachineEnum.AUDIT_CHOICE)
.first(TradeOrderStateMachineEnum.COMPLETED, tradeOrderGuardFactory.new TradeOrderAuditChoiceGuard(),new TradeOrderChoiceAction())
.then(TradeOrderStateMachineEnum.WAIT_FOR_EVALUATE, tradeOrderGuardFactory.new TradeOrderAuditChoiceGuard2(),new TradeOrderChoiceAction())
.then(TradeOrderStateMachineEnum.WAIT_FOR_SIGN, tradeOrderGuardFactory.new TradeOrderAuditChoiceGuard3(),new TradeOrderChoiceAction())
.then(TradeOrderStateMachineEnum.WAIT_FOR_DELIVER, tradeOrderGuardFactory.new TradeOrderAuditChoiceGuard4(),new TradeOrderChoiceAction())
.last(TradeOrderStateMachineEnum.WAIT_FOR_AUDIT).and()
//稽核後,從待稽核到待發貨
.withExternal()
.source(TradeOrderStateMachineEnum.WAIT_FOR_AUDIT)
.target(TradeOrderStateMachineEnum.WAIT_FOR_DELIVER)
.event(TradeOrderEvent.AUDIT).guard(tradeOrderGuardFactory.new TradeOrderGuard()).and()
//發貨後,從待發貨到待簽收
.withExternal()
.source(TradeOrderStateMachineEnum.WAIT_FOR_DELIVER)
.target(TradeOrderStateMachineEnum.WAIT_FOR_SIGN)
.event(TradeOrderEvent.DELIVER).guard(tradeOrderGuardFactory.new TradeOrderGuard()).and()
//簽收後,從待簽收到待簽收選擇态
.withExternal()
.source(TradeOrderStateMachineEnum.WAIT_FOR_SIGN)
.target(TradeOrderStateMachineEnum.SIGN_CHOICE)
.event(TradeOrderEvent.SIGN).and()
//簽收後,從待簽收選擇态到待評價或者到已完成
.withChoice()
.source(TradeOrderStateMachineEnum.SIGN_CHOICE)
.first(TradeOrderStateMachineEnum.WAIT_FOR_EVALUATE, tradeOrderGuardFactory.new TradeOrderSignChoiceGuard(),new TradeOrderChoiceAction())
.then(TradeOrderStateMachineEnum.COMPLETED, tradeOrderGuardFactory.new TradeOrderSignChoiceGuard2(),new TradeOrderChoiceAction())
.last(TradeOrderStateMachineEnum.WAIT_FOR_SIGN).and()
//評價後,從待評價到已完成
.withExternal()
.source(TradeOrderStateMachineEnum.WAIT_FOR_EVALUATE)
.target(TradeOrderStateMachineEnum.COMPLETED)
.event(TradeOrderEvent.EVALUATE);
return builder.build();
}
@Bean(name = "tradeOrderStateMachinePersister")
public StateMachinePersister<TradeOrderStateMachineEnum, TradeOrderEvent, TradeOrder> getOrderPersister() {
return new DefaultStateMachinePersister<>(new StateMachinePersist<TradeOrderStateMachineEnum, TradeOrderEvent, TradeOrder>() {
@Override
public void write(StateMachineContext<TradeOrderStateMachineEnum, TradeOrderEvent> context, TradeOrder contextObj) {
}
@Override
public StateMachineContext<TradeOrderStateMachineEnum, TradeOrderEvent> read(TradeOrder contextObj) {
StateMachineContext<TradeOrderStateMachineEnum, TradeOrderEvent> result = new DefaultStateMachineContext(TradeOrderStateMachineEnum.fromValue(contextObj.getOrderState()),
null, null, null, null, MACHINEID_TO);
return result;
}
;
});
}
}
3.3 配置guard判斷類
這部分是可以優化,因為我看官網的文檔,每個判斷都要建立guard類重寫evaluate方法。我不想建太多類,就內建一個類裡面了。我本意是想用工廠模式想建立Guard類,但是本人開發經驗不是很豐富。
TradeOrderGuardFactory
public class TradeOrderGuardFactory {
private static final String TRADE_ORDER = StateMachineHeaderNameConstants.TRADE_ORDER;
public class TradeOrderAuditChoiceGuard implements Guard<TradeOrderStateMachineEnum, TradeOrderEvent> {
@Override
public boolean evaluate(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context) {
TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
if(isAllSign(tradeOrder) && !StringUtils.isEmpty(tradeOrder.getBuyer().getBuyerTenantCode())){
return true;
}
return false;
}
}
public class TradeOrderAuditChoiceGuard2 implements Guard<TradeOrderStateMachineEnum, TradeOrderEvent> {
@Override
public boolean evaluate(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context) {
TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
if(isAllSign(tradeOrder) && StringUtils.isEmpty(tradeOrder.getBuyer().getBuyerTenantCode())){
return true;
}
return false;
}
}
public class TradeOrderAuditChoiceGuard3 implements Guard<TradeOrderStateMachineEnum, TradeOrderEvent> {
@Override
public boolean evaluate(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context) {
TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
if(isAllDeliver(tradeOrder)){
return true;
}
return false;
}
}
public class TradeOrderAuditChoiceGuard4 implements Guard<TradeOrderStateMachineEnum, TradeOrderEvent> {
@Override
public boolean evaluate(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context) {
TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
if(isAllAudit(tradeOrder) ){
return true;
}
return false;
}
}
public class TradeOrderSignChoiceGuard implements Guard<TradeOrderStateMachineEnum, TradeOrderEvent> {
@Override
public boolean evaluate(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context) {
TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
if(isAllSign(tradeOrder) && StringUtils.isEmpty(tradeOrder.getBuyer().getBuyerTenantCode())){
return true;
}
return false;
}
}
public class TradeOrderSignChoiceGuard2 implements Guard<TradeOrderStateMachineEnum, TradeOrderEvent> {
@Override
public boolean evaluate(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context) {
TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
if(isAllSign(tradeOrder) && !StringUtils.isEmpty(tradeOrder.getBuyer().getBuyerTenantCode())){
return true;
}
return false;
}
}
public class TradeOrderGuard implements Guard<TradeOrderStateMachineEnum, TradeOrderEvent> {
@Override
public boolean evaluate(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context) {
boolean result=false;
System.out.println(context.getSource().getId());
System.out.println(context.getTarget().getId());
switch (context.getTarget().getId()) {
case WAIT_FOR_DELIVER:
return WAIT_FOR_DELIVER(context);
case WAIT_FOR_SIGN:
return WAIT_FOR_SIGN(context);
case SIGN_CHOICE:
return SIGN_CHOICE(context);
case WAIT_FOR_EVALUATE:
return WAIT_FOR_EVALUATE(context);
case COMPLETED:
return COMPLETED(context);
default:
break;
}
return result;
}
private boolean WAIT_FOR_DELIVER(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context){
TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
if(isAllAudit(tradeOrder)){
return true;
}
return false;
}
private boolean WAIT_FOR_SIGN(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context){
TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
if(isAllDeliver(tradeOrder)){
return true;
}
return false;
}
private boolean SIGN_CHOICE(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context){
TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
if(isAllSign(tradeOrder)){
return true;
}
return false;
}
private boolean WAIT_FOR_EVALUATE(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context){
TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
if(isAllSign(tradeOrder)&& StringUtils.isEmpty(tradeOrder.getBuyer().getBuyerTenantCode())){
return true;
}
return false;
}
private boolean COMPLETED(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context){
TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
if(isAllSign(tradeOrder)&& !StringUtils.isEmpty(tradeOrder.getBuyer().getBuyerTenantCode())){
return true;
}
return false;
}
}
private boolean isAllAudit(TradeOrder tradeOrder) {
for (TradeOrderDetail tradeOrderDetail : tradeOrder.getTradeOrderDetailList()) {
if(ObjectUtils.isEmpty(tradeOrderDetail.getCancelQty())){
tradeOrderDetail.setCancelQty(0);
}
if(ObjectUtils.isEmpty(tradeOrderDetail.getAuditQty())){
tradeOrderDetail.setAuditQty(0);
}
//待評審的數量
if(tradeOrderDetail.getItemQty()-tradeOrderDetail.getCancelQty()-tradeOrderDetail.getAuditQty()!=0){
return false;
}
}
return true;
}
private boolean isAllDeliver(TradeOrder tradeOrder) {
for (TradeOrderDetail tradeOrderDetail : tradeOrder.getTradeOrderDetailList()) {
if(ObjectUtils.isEmpty(tradeOrderDetail.getCancelQty())){
tradeOrderDetail.setCancelQty(0);
}
if(ObjectUtils.isEmpty(tradeOrderDetail.getDeliverQty())){
tradeOrderDetail.setDeliverQty(0);
}
//待評審的數量
if(tradeOrderDetail.getItemQty()-tradeOrderDetail.getCancelQty()-tradeOrderDetail.getDeliverQty()!=0){
return false;
}
}
return true;
}
private boolean isAllSign(TradeOrder tradeOrder) {
for (TradeOrderDetail tradeOrderDetail : tradeOrder.getTradeOrderDetailList()) {
if(ObjectUtils.isEmpty(tradeOrderDetail.getCancelQty())){
tradeOrderDetail.setCancelQty(0);
}
if(ObjectUtils.isEmpty(tradeOrderDetail.getCustSignQty())){
tradeOrderDetail.setCustSignQty(0);
}
//代簽收的數量
if((tradeOrderDetail.getItemQty()-tradeOrderDetail.getCancelQty()-tradeOrderDetail.getCustSignQty()!=0)){
return false;
}
}
return true;
}
}
3.4 action類
這裡有用兩種方式,一種是注解,另外一種是實作Action類重寫execute方法,至于為什麼用兩種,後面會有說的,這也是其中一個坑。
TradeOrderChoiceAction
@Slf4j
public class TradeOrderChoiceAction implements Action<TradeOrderStateMachineEnum, TradeOrderEvent> {
private static final String TRADE_ORDER = StateMachineHeaderNameConstants.TRADE_ORDER;
@Override
public void execute(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context) {
System.out.println(context.getTarget().getId());
switch (context.getTarget().getId()) {
case AUDIT_CHOICE:
AUDIT_CHOICE(context);
break;
case SIGN_CHOICE:
SIGN_CHOICE(context);
break;
default:
break;
}
}
private void SIGN_CHOICE(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context) {
TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
log.info("簽收事件之前,訂單的狀态為:{}"+tradeOrder.getOrderState());
if(isAllSign(tradeOrder)){
//全部簽收,并且是2C,則為待評價狀态
if(StringUtils.isEmpty(tradeOrder.getBuyer().getBuyerTenantCode())){
tradeOrder.setOrderState(TradeOrderStateMachineEnum.WAIT_FOR_EVALUATE.getValue());
}else{
//全部簽收,并且是2B,則為完成狀态
tradeOrder.setOrderState(TradeOrderStateMachineEnum.COMPLETED.getValue());
}
}
log.info("簽收事件之後,訂單的狀态為:{}"+tradeOrder.getOrderState());
}
private void AUDIT_CHOICE(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context) {
TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
log.info("取消數量事件之前,訂單的狀态為:{}"+tradeOrder.getOrderState());
//如果全部簽收,則可能是待評價狀态或者是完成狀态
if(isAllSign(tradeOrder)){
//2C,則為待評價狀态
if(StringUtils.isEmpty(tradeOrder.getBuyer().getBuyerTenantCode())){
tradeOrder.setOrderState(TradeOrderStateMachineEnum.WAIT_FOR_EVALUATE.getValue());
}else{
//2B,則為完成狀态
tradeOrder.setOrderState(TradeOrderStateMachineEnum.COMPLETED.getValue());
}
}else if(isAllDeliver(tradeOrder)){
//如果全部發貨,則為代簽收狀态
tradeOrder.setOrderState(TradeOrderStateMachineEnum.WAIT_FOR_SIGN.getValue());
}else if(isAllAudit(tradeOrder)){
//如果全部稽核,則為待發貨狀态
tradeOrder.setOrderState(TradeOrderStateMachineEnum.WAIT_FOR_DELIVER.getValue());
}
log.info("取消數量事件之後,訂單的狀态為:{}"+tradeOrder.getOrderState());
}
private boolean isAllAudit(TradeOrder tradeOrder) {
for (TradeOrderDetail tradeOrderDetail : tradeOrder.getTradeOrderDetailList()) {
if(ObjectUtils.isEmpty(tradeOrderDetail.getCancelQty())){
tradeOrderDetail.setCancelQty(0);
}
if(ObjectUtils.isEmpty(tradeOrderDetail.getAuditQty())){
tradeOrderDetail.setAuditQty(0);
}
//待評審的數量
if(tradeOrderDetail.getItemQty()-tradeOrderDetail.getCancelQty()-tradeOrderDetail.getAuditQty()!=0){
return false;
}
}
return true;
}
private boolean isAllDeliver(TradeOrder tradeOrder) {
for (TradeOrderDetail tradeOrderDetail : tradeOrder.getTradeOrderDetailList()) {
if(ObjectUtils.isEmpty(tradeOrderDetail.getCancelQty())){
tradeOrderDetail.setCancelQty(0);
}
if(ObjectUtils.isEmpty(tradeOrderDetail.getDeliverQty())){
tradeOrderDetail.setDeliverQty(0);
}
//待評審的數量
if(tradeOrderDetail.getItemQty()-tradeOrderDetail.getCancelQty()-tradeOrderDetail.getDeliverQty()!=0){
return false;
}
}
return true;
}
private boolean isAllSign(TradeOrder tradeOrder) {
for (TradeOrderDetail tradeOrderDetail : tradeOrder.getTradeOrderDetailList()) {
if(ObjectUtils.isEmpty(tradeOrderDetail.getCancelQty())){
tradeOrderDetail.setCancelQty(0);
}
if(ObjectUtils.isEmpty(tradeOrderDetail.getCustSignQty())){
tradeOrderDetail.setCustSignQty(0);
}
//代簽收的數量
if((tradeOrderDetail.getItemQty()-tradeOrderDetail.getCancelQty()-tradeOrderDetail.getCustSignQty()!=0)){
return false;
}
}
return true;
}
}
TradeOrderAction
@WithStateMachine(id= TradeOrderStateMachineBuilder.MACHINEID_TO)
public class TradeOrderAction {
private static final String TRADE_ORDER = StateMachineHeaderNameConstants.TRADE_ORDER;
@OnTransition(source = "WAIT_FOR_PAY", target = "WAIT_FOR_AUDIT")
public void CUSTOMER_PAY(Message<TradeOrderEvent> message) {
TradeOrder tradeOrder = (TradeOrder) message.getHeaders().get(TRADE_ORDER);
tradeOrder.setOrderState(TradeOrderStateMachineEnum.WAIT_FOR_AUDIT.getValue());
}
@OnTransition(source = "WAIT_FOR_PAY", target = "CLOSED")
public void CUSTOMER_CLOSE(Message<TradeOrderEvent> message) {
TradeOrder tradeOrder = (TradeOrder) message.getHeaders().get(TRADE_ORDER);
tradeOrder.setOrderState(TradeOrderStateMachineEnum.CLOSED.getValue());
}
@OnTransition(source = "WAIT_FOR_AUDIT", target = "WAIT_FOR_DELIVER")
public void CUSTOMER_AUDIT(Message<TradeOrderEvent> message) {
TradeOrder tradeOrder = (TradeOrder) message.getHeaders().get(TRADE_ORDER);
tradeOrder.setOrderState(TradeOrderStateMachineEnum.WAIT_FOR_DELIVER.getValue());
}
@OnTransition(source = "WAIT_FOR_DELIVER", target = "WAIT_FOR_SIGN")
public void CUSTOMER_DELIVER(Message<TradeOrderEvent> message) {
TradeOrder tradeOrder = (TradeOrder) message.getHeaders().get(TRADE_ORDER);
tradeOrder.setOrderState(TradeOrderStateMachineEnum.WAIT_FOR_SIGN.getValue());
}
@OnTransition(source = "WAIT_FOR_EVALUATE", target = "COMPLETED")
public void CUSTOMER_EVALUATE(Message<TradeOrderEvent> message) {
TradeOrder tradeOrder = (TradeOrder) message.getHeaders().get(TRADE_ORDER);
tradeOrder.setOrderState(TradeOrderStateMachineEnum.COMPLETED.getValue());
}
}
3.5 其他類
這個類是為了發message保證header一緻,當然也可以寫死保證一緻。
StateMachineHeaderNameConstants
public class StateMachineHeaderNameConstants {
//交易訂單
public static final String TRADE_ORDER = "tradeOrder";
}
3.6 使用工具類
StateMachineUtils
@Slf4j
public class StateMachineUtils {
private static final String TRADE_ORDER = StateMachineHeaderNameConstants.TRADE_ORDER;
@Autowired
private TradeOrderStateMachineBuilder tradeOrderStateMachineBuilder;
@Resource(name = "tradeOrderStateMachinePersister")
private StateMachinePersister tradeOrderStateMachinePersister;
public void execute(TradeOrder tradeOrder, TradeOrderEvent event) throws Exception{
log.debug("調用狀态機前的訂單狀态為>>>>>>>>>>{}"+tradeOrder.getOrderState());
//擷取TO狀态機
StateMachine<TradeOrderStateMachineEnum, TradeOrderEvent> stateMachine = tradeOrderStateMachineBuilder.build();
Message message = MessageBuilder.withPayload(event).setHeader(TRADE_ORDER, tradeOrder).build();
//初始化狀态機
tradeOrderStateMachinePersister.restore(stateMachine,tradeOrder);
stateMachine.sendEvent(message);
log.debug("調用狀态機後的訂單狀态為>>>>>>>>>>{}"+tradeOrder.getOrderState());
}
}
這裡說一下,為什麼要用StateMachinePersister,這個主要作用是做持久化的,當然還有做redis持久化,我這邊沒用到就沒寫。而在這裡引用是為了初始化狀态機的狀态,能讓狀态機一進來就是自己想要的狀态的。不然觸發不了流轉規則。
四、出現的一些問題(自己踩過的坑)
4.1 使用withChoice,一定要在在初始化上加上choice,不然的話不生效。
[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-VJjSM7rM-1631519370359)(D:\MyData\ligy112\AppData\Roaming\Typora\typora-user-images\image-20210913151753492.png)]
這裡一定要記得配置對應的狀态。
4.2 withChoice的觸發
withChoice的生效,是又上一個withExternal流程後,如果直接設定withChoice裡面的狀态,是不能執行guard和action。是以我這邊才會多出一個中間态,就是為了觸發流轉判斷的。
[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-IN3DCLWI-1631519370361)(D:\MyData\ligy112\AppData\Roaming\Typora\typora-user-images\image-20210913152331982.png)]
4.3 guard裡面的target問題。
在一般情況下,我們都可以switch來判斷,不用建立一堆guard類,但是在withChoice的first,then,lat裡面的狀态是流轉後,但是不能用這個判斷,自己可以列印下日志試下。這也是我為什麼在GuardFactory裡現有許多内部類的原因。
[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-kDN8BvIp-1631519370362)(D:\MyData\ligy112\AppData\Roaming\Typora\typora-user-images\image-20210913152927415.png)]
4.4 withChoice的action事件,不能用注解觸發。
其實這個問題跟上一個問題有點像,用注解方式的話,一般都會有target,但是在withChoice裡的target是上一個withExternal的target,是以是不會生效的。下圖是一般情況的action配置注解。
@OnTransition(source = "WAIT_FOR_PAY", target = "WAIT_FOR_AUDIT")
是以這也是為什麼會有兩種方式的Action。
五、總結
StateMachine還是挺好的,能夠聲調很多ifelse,更重要是讓業務解耦。當然以上是我個人實際開發遇到的問題,就想記錄下來而已。有什麼錯誤,請指出,畢竟我也是個菜鳥,這就當是我個人的筆記而已。