状态机的技术选型看这篇就够了,最后一个直叫好! - 掘金
实现一个状态机引擎,教你看清DSL的本质_cola状态机-CSDN博客
一、引入jar包
<!--阿里状态机jar-->
<dependency><groupId>com.alibaba.cola</groupId><artifactId>cola-component-statemachine</artifactId><version>4.3.2</version>
</dependency>
二、引入流转类型枚举
package com.lx.designPattern.statepattern.statemachine.enums;/*** 流转类型枚举* @author lwp* @date 2023-07-06 14:56:27* @version 1.0*/
public enum TransitionTypeEnum {/*** 内部流转*/INTERNAL,/*** 外部流转*/EXTERNAL,/*** 多状态外部流转*/EXTERNALS;
}
三、引入事件枚举
package com.lx.designPattern.statepattern.statemachine.enums;/*** 履约状态事件枚举** @author lwp* @version 1.0* @date 2023-07-06 13:40:11*/
public enum FulfillmentStatusEventEnum {/*** 开始租赁非标商品履约*/BEGIN_RENT_NON_STANDARD_GOODS_FULFILLMENT,/*** 完成资源匹配*/MATCH_RESOURCE,/*** 完成租赁交付*/COMPLETE_RENT_DELIVER,/*** 重置履约计划*/RESET_FULFILLMENT,/*** 取消履约单*/CANCEL_FULFILLMENT,/*** 创建售车交付履约单*/CREATE_SALE_DELIVER_FULFILLMENT,/*** 完成服务执行中*/COMPLETE_SERVICE_FULFILLMENT,/*** 非灰度履约单交付*/NON_GRAY_DELIVER,/*** 欢乐租备车*/HAPPY_RENT_PLAN_CAR,/*** 开始替换车履约*/START_REPLACE_DELIVER_FULFILLMENT,/*** 开始常规租赁退车履约*/START_RENT_BACK_CAR_FULFILLMENT,/*** 完成替换车车退车*/COMPLETE_REPLACE_BACK_CAR_FULFILLMENT,/*** 完成租赁退车*/COMPLETE_RENT_BACK_CAR_FULFILLMENT,/*** 取消履约 (新的)*/CANCEL_FULFILLMENT_2ND,/*** 完成服务履约*/COMPLETE_VEHICLE_SERVICE,/*** 渠道商履约*/CHANNEL_FULFILLMENT,;
}
四、引入流转状态枚举
package com.lx.designPattern.statepattern.statemachine.enums;import com.lx.exception.BusinessException;import java.util.Objects;/*** 履约单状态枚举* @author lwp* @date 2023/1/5 9:16* @version 1.0*/
public enum FulfillmentStatusEnum {/*** 初始状态*/INITIAL(0, "初始状态"),/*** 待匹配*/WAIT_MATCH(2, "待匹配"),/*** 进行中*/IN_PROGRESS(3, "进行中"),/*** 已完成*/COMPLETED(4, "已完成"),/*** 已取消*/CANCELED(5, "已取消"),;private Integer status;private String desc;FulfillmentStatusEnum(Integer status, String desc) {this.status = status;this.desc = desc;}public Integer getStatus() {return status;}public void setStatus(Integer status) {this.status = status;}public String getDesc() {return desc;}public void setDesc(String desc) {this.desc = desc;}public static FulfillmentStatusEnum getByStatus(Integer status) {for (FulfillmentStatusEnum fulfillmentStatusEnum : FulfillmentStatusEnum.values()) {if (Objects.equals(status, fulfillmentStatusEnum.getStatus())) {return fulfillmentStatusEnum;}}throw new BusinessException("未找到履约状态枚举");}
}
1.需要处理的业务参数类
package com.lx.designPattern.statepattern.statemachine.dto;import lombok.Data;/*** 履约单dto* @author lwp* @date 2023-07-25 16:22:04* @version 1.0*/
@Data
public class FulfillmentBillDTO {/*** 交易订单*/private A a;/*** 履约主单*/private B b;/*** 车辆履约子单*/private C c;
}
五、准备状态机
package com.lx.designPattern.statepattern.statemachine;import com.alibaba.cola.statemachine.StateMachine;
import com.alibaba.cola.statemachine.builder.StateMachineBuilder;
import com.alibaba.cola.statemachine.builder.StateMachineBuilderFactory;
import com.lx.designPattern.statepattern.statemachine.statustransition.FulfillmentStatusTransition;
import com.lx.designPattern.statepattern.statemachine.dto.FulfillmentBillDTO;
import com.lx.designPattern.statepattern.statemachine.enums.FulfillmentStatusEnum;
import com.lx.designPattern.statepattern.statemachine.enums.FulfillmentStatusEventEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;
import java.util.List;/*** 履约状态机** @author lwp* @version 1.0* @date 2023-07-27 10:31:27*/
@Slf4j
@Lazy
@Component
public class FulfillmentStatusStateMachine {/*** 状态机ID*/private static final String MACHINE_ID = "FulfillmentStatusStateMachine";/*** 状态机*/private StateMachine<FulfillmentStatusEnum, FulfillmentStatusEventEnum, FulfillmentBillDTO> stateMachine;@Autowiredprivate List<FulfillmentStatusTransition> fulfillmentStatusTransitions;/* ******************************* 履约单状态流转 *****************************//*** 初始化*/@PostConstructpublic void init() {// 创建状态机StateMachineBuilder<FulfillmentStatusEnum, FulfillmentStatusEventEnum, FulfillmentBillDTO> stateMachineBuilder= StateMachineBuilderFactory.create();// 添加没有命中配置规则时的回调stateMachineBuilder.setFailCallback(this::stateMachineFailCallback);// 初始化transitioninitTransition(stateMachineBuilder);stateMachine = stateMachineBuilder.build(MACHINE_ID);}/*** 触发流转*/public FulfillmentStatusEnum fireEvent(FulfillmentStatusEnum status,FulfillmentStatusEventEnum event,FulfillmentBillDTO fulfillmentBill) {FulfillmentStatusEnum resultStatus = stateMachine.fireEvent(status, event, fulfillmentBill);// 如果结果状态和源状态相同,代表流转失败if (resultStatus == status) {log.warn("履约单{}状态流转失败, from:{}, to:{}, event:{}",fulfillmentBill.getMainFulfillmentBill().getFulfillmentCode(),status.getStatus(),resultStatus.getDesc(),event);} else {log.info("履约单{}状态流转, from:{}, to:{}, event:{}",fulfillmentBill.getMainFulfillmentBill().getFulfillmentCode(),status.getStatus(),resultStatus.getDesc(),event);}return resultStatus;}/*** 没有命中配置规则时的回调*/private void stateMachineFailCallback(FulfillmentStatusEnum status,FulfillmentStatusEventEnum event,FulfillmentBillDTO fulfillmentBill) {log.error("履约单{}状态流转失败: 未找到匹配的流转规则, from:{}, event:{}",fulfillmentBill.getMainFulfillmentBill().getId(),status.getDesc(),event);}/*** 初始化transition*/private void initTransition(StateMachineBuilder<FulfillmentStatusEnum, FulfillmentStatusEventEnum,FulfillmentBillDTO> builder) {fulfillmentStatusTransitions.forEach(transition -> {switch (transition.getTransitionType()) {case INTERNAL:if (transition.getWithin().isPresent()) {builder.internalTransition().within(transition.getWithin().get()).on(transition.getEvent()).when(transition.getCondition()).perform(transition.getAction());} else {log.error("初始化履约单内部状态流转错误: 缺少within参数");throw new IllegalArgumentException("履约状态状态机初始化失败");}break;case EXTERNAL:if (transition.getFrom().isPresent() && transition.getTo().isPresent()) {builder.externalTransition().from(transition.getFrom().get()).to(transition.getTo().get()).on(transition.getEvent()).when(transition.getCondition()).perform(transition.getAction());} else {log.error("初始化履约单外部状态流转错误: 缺少from或to参数");throw new IllegalArgumentException("履约状态状态机初始化失败");}break;case EXTERNALS:if (transition.getFromAmong().isPresent() && transition.getTo().isPresent()) {builder.externalTransitions().fromAmong(transition.getFromAmong().get()).to(transition.getTo().get()).on(transition.getEvent()).when(transition.getCondition()).perform(transition.getAction());} else {log.error("初始化履约单多状态外部状态流转错误: 缺少fromAmong或to参数");throw new IllegalArgumentException("履约状态状态机初始化失败");}break;default:log.error("不支持的流转类型:{}", transition.getTransitionType());}});}
}
六、准备流转接口
package com.lx.designPattern.statepattern.statemachine.statustransition;import com.alibaba.cola.statemachine.Action;
import com.alibaba.cola.statemachine.Condition;
import com.lx.designPattern.statepattern.statemachine.dto.FulfillmentBillDTO;
import com.lx.designPattern.statepattern.statemachine.enums.FulfillmentStatusEnum;
import com.lx.designPattern.statepattern.statemachine.enums.FulfillmentStatusEventEnum;
import com.lx.designPattern.statepattern.statemachine.enums.TransitionTypeEnum;import java.util.Optional;/*** 履约单状态流转* @author lwp* @date 2023-07-06 15:08:18* @version 1.0*/
public interface FulfillmentStatusTransition {/*** 获取类转类型* @return 流转类型*/TransitionTypeEnum getTransitionType();/*** 获取within参数(内部流转必需)* @return within*/default Optional<FulfillmentStatusEnum> getWithin() {return Optional.empty();}/*** 获取from参数(外部流转必需)* @return from*/default Optional<FulfillmentStatusEnum> getFrom() {return Optional.empty();}/*** 获取fromAmong参数(多状态外部流转必需)* @return fromAmong*/default Optional<FulfillmentStatusEnum[]> getFromAmong() {return Optional.empty();}/*** 获取to参数(外部流转和多状态外部流转必需)* @return to*/default Optional<FulfillmentStatusEnum> getTo() {return Optional.empty();};/*** 获取event* @return on参数*/FulfillmentStatusEventEnum getEvent();/*** 获取condition* @return when*/Condition<FulfillmentBillDTO> getCondition();/*** 获取action* @return preform*/Action<FulfillmentStatusEnum, FulfillmentStatusEventEnum, FulfillmentBillDTO> getAction();
}
七、实现流转接口和定义流转规则
package com.lx.designPattern.statepattern.statemachine.statustransition;import com.alibaba.cola.statemachine.Action;
import com.alibaba.cola.statemachine.Condition;
import com.lx.designPattern.statepattern.statemachine.dto.FulfillmentBillDTO;
import com.lx.designPattern.statepattern.statemachine.dto.MainFulfillmentBill;
import com.lx.designPattern.statepattern.statemachine.enums.FulfillmentStatusEnum;
import com.lx.designPattern.statepattern.statemachine.enums.FulfillmentStatusEventEnum;
import com.lx.designPattern.statepattern.statemachine.enums.TransitionTypeEnum;
import org.springframework.stereotype.Component;import java.util.Optional;/*** 完成履约状态流转* @author lwp* @date 2023-07-12 10:30:32* @version 1.0*/
@Component
public class CompleteFulfillmentTransition implements FulfillmentStatusTransition {//@Autowired//private FulfillmentBillDaoService fulfillmentBillDaoService;@Overridepublic TransitionTypeEnum getTransitionType() {return TransitionTypeEnum.EXTERNAL;}@Overridepublic Optional<FulfillmentStatusEnum> getFrom() {return Optional.of(FulfillmentStatusEnum.IN_PROGRESS);}@Overridepublic Optional<FulfillmentStatusEnum> getTo() {return Optional.of(FulfillmentStatusEnum.COMPLETED);}@Overridepublic FulfillmentStatusEventEnum getEvent() {return FulfillmentStatusEventEnum.COMPLETE_RENT_DELIVER;}@Overridepublic Condition<FulfillmentBillDTO> getCondition() {return (fulfillmentBillDTO -> true);}@Overridepublic Action<FulfillmentStatusEnum, FulfillmentStatusEventEnum, FulfillmentBillDTO> getAction() {return (srcState, tarState, event, fulfillmentBillDTO) -> {// 修改履约单状态MainFulfillmentBill updateMainBill = new MainFulfillmentBill();updateMainBill.setId(fulfillmentBillDTO.getMainFulfillmentBill().getId());updateMainBill.setStatus(tarState.getStatus());//fulfillmentBillDaoService.updateMainBillById(updateMainBill);System.out.println("状态改为完成了");};}
}
八、准备测试类测试
package com.lx.designPattern;import com.lx.designPattern.statepattern.statemachine.FulfillmentStatusStateMachine;
import com.lx.designPattern.statepattern.statemachine.business.InsuranceStatusChangeServiceImpl;
import com.lx.designPattern.statepattern.statemachine.dto.FulfillmentBillDTO;
import com.lx.designPattern.statepattern.statemachine.dto.MainFulfillmentBill;
import com.lx.designPattern.statepattern.statemachine.enums.FulfillmentStatusEnum;
import com.lx.designPattern.statepattern.statemachine.enums.FulfillmentStatusEventEnum;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;/*** @Desc 阿里状态机测试类* @author lwp* @version 1.0* @date 2024-01-13 22:16:22*/
@RunWith(SpringRunner.class)
@SpringBootTest(properties = "dev")
public class StateMachineTest {@Autowiredprivate FulfillmentStatusStateMachine fulfillmentStatusStateMachine;@Testpublic void handStatusChangeComplete() {FulfillmentBillDTO billDTO = new FulfillmentBillDTO();MainFulfillmentBill mainFulfillmentBill = new MainFulfillmentBill();mainFulfillmentBill.setFulfillmentCode("LY001");mainFulfillmentBill.setStatus(1);billDTO.setMainFulfillmentBill(mainFulfillmentBill);fulfillmentStatusStateMachine.fireEvent(FulfillmentStatusEnum.getByStatus(3),FulfillmentStatusEventEnum.COMPLETE_RENT_DELIVER,billDTO);}}
九、测试效果如下图