为什么要用状态机呢?说到底就是限制我们在程序中的硬编码,避免随便的去update某一个状态。
例如一个审核系统,状态的流转只能是A->B-C,为了避免我们在某一个地方人为疏忽将A->C,我们需要借助状态机来操作状态变化。
package com.clubfactory.tms.bill.core.service.fsm;import com.clubfactory.tms.bill.core.enums.OrderAuditState;import lombok.AllArgsConstructor;import lombok.Builder;import lombok.Data;import lombok.NoArgsConstructor;import org.springframework.beans.factory.BeanInitializationException;import org.springframework.beans.factory.InitializingBean;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import java.util.Arrays;import java.util.List;import java.util.Map;import java.util.Optional;/*** 参考:https://zhuanlan.zhihu.com/p/97442825* 当(主动)执行了某个动作,某条数据的状态要流转到下一个状态。通过状态机的方式来update状态*/@Servicepublic class OrderAuditFSM implements InitializingBean {@Autowiredprivate UpdateAuditStateEvent updateAuditStateEvent;private static List<MachineTransaction> fsmList = null;@Overridepublic void afterPropertiesSet() throws Exception {fsmList = Arrays.asList(//待比对->待确认MachineTransaction.builder().currentState(OrderAuditState.TO_BE_COMPARED).action(Action.SYSTEM_CHECK_EXIST_DIFF_CONFIRMED).nextState(OrderAuditState.TO_BE_CHECKED).event(updateAuditStateEvent).build(),//待比对->待复核MachineTransaction.builder().currentState(OrderAuditState.TO_BE_COMPARED).action(Action.SYSTEM_CHECK_NO_DIFF_CONFIRMED).nextState(OrderAuditState.TO_BE_REVIEWED).event(updateAuditStateEvent).build(),//待确认->待复核MachineTransaction.builder().currentState(OrderAuditState.TO_BE_CHECKED).action(Action.CLICK_CHECK_CONFIRMED).nextState(OrderAuditState.TO_BE_REVIEWED).event(updateAuditStateEvent).build(),//待复核->待支付MachineTransaction.builder().currentState(OrderAuditState.TO_BE_REVIEWED).action(Action.CLICK_REVIEW_CONFIRMED).nextState(OrderAuditState.TO_BE_PAID).event(updateAuditStateEvent).build(),//待复核->待确认MachineTransaction.builder().currentState(OrderAuditState.TO_BE_REVIEWED).action(Action.CLICK_REVIEW_REJECTED).nextState(OrderAuditState.TO_BE_CHECKED).event(updateAuditStateEvent).build(),//待支付->支付完成MachineTransaction.builder().currentState(OrderAuditState.TO_BE_PAID).action(Action.CLICK_PAY_CONFIRMED).nextState(OrderAuditState.PAID).event(updateAuditStateEvent).build(),//(批量)待审核->待支付MachineTransaction.builder().currentState(OrderAuditState.TO_BE_REVIEWED).action(Action.BATCH_REVIEW_CONFIRMED).nextState(OrderAuditState.TO_BE_PAID).event(updateAuditStateEvent).build(),//(批量)待支付->支付完成MachineTransaction.builder().currentState(OrderAuditState.TO_BE_PAID).action(Action.BATCH_PAY_CONFIRMED).nextState(OrderAuditState.PAID).event(updateAuditStateEvent).build());}public void execute(String action, OrderAuditState currentState, Long carrierOrderId) {Optional<MachineTransaction> transactionOptional =fsmList.stream().filter(transaction -> transaction.getAction().equals(action) && transaction.getCurrentState().equals(currentState)).findFirst();if (!transactionOptional.isPresent()) {throw new InvalidActionException();}MachineTransaction transaction = transactionOptional.get();transaction.getEvent().doUpdate(transaction.getNextState(), carrierOrderId);}//批量流转public void batchExecute(String action, Map<OrderAuditState, List<Long>> map){for (Map.Entry<OrderAuditState, List<Long>> orderAuditStateEntry : map.entrySet()) {OrderAuditState currentState = orderAuditStateEntry.getKey();Optional<MachineTransaction> transactionOptional =fsmList.stream().filter(transaction -> transaction.getAction().equals(action) && transaction.getCurrentState().equals(currentState)).findFirst();if (transactionOptional.isPresent()) {MachineTransaction transaction = transactionOptional.get();transaction.getEvent().doBatchUpdate(transaction.getNextState(), orderAuditStateEntry.getValue());}}}}@Data@Builder@NoArgsConstructor@AllArgsConstructorclass MachineTransaction{private OrderAuditState currentState;private String action;private OrderAuditState nextState;private Event event;}class InvalidActionException extends RuntimeException{}
package com.clubfactory.tms.bill.core.service.fsm;import com.clubfactory.tms.bill.core.enums.OrderAuditState;import java.util.List;public interface Event {public void doUpdate(OrderAuditState nextState, Long id);public void doBatchUpdate(OrderAuditState nextState, List<Long> ids);}
package com.clubfactory.tms.bill.core.service.fsm;import com.clubfactory.tms.bill.core.dao.CarrierOrderMapper;import com.clubfactory.tms.bill.core.dataobject.CarrierOrderDO;import com.clubfactory.tms.bill.core.enums.OrderAuditState;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import java.util.List;@Servicepublic class UpdateAuditStateEvent implements Event{@Autowiredprivate CarrierOrderMapper carrierOrderMapper;@Overridepublic void doUpdate(OrderAuditState nextState, Long id) {CarrierOrderDO updateDO = CarrierOrderDO.builder().id(id).auditState(nextState.getCode()).build();carrierOrderMapper.update(updateDO);}@Overridepublic void doBatchUpdate(OrderAuditState nextState, List<Long> ids) {if(!ids.isEmpty()){carrierOrderMapper.updateStateByIds(nextState.getCode(), ids);}}}
package com.clubfactory.tms.bill.core.service.fsm;public class Action {public final static String SYSTEM_CHECK_NO_DIFF_CONFIRMED="SYSTEM_CHECK_NO_DIFF_CONFIRMED";public final static String SYSTEM_CHECK_EXIST_DIFF_CONFIRMED="SYSTEM_CHECK_EXIST_DIFF_CONFIRMED";public final static String CLICK_CHECK_CONFIRMED="CLICK_CHECK_CONFIRMED";public final static String CLICK_REVIEW_CONFIRMED="CLICK_REVIEW_CONFIRMED";public final static String CLICK_REVIEW_REJECTED="CLICK_REVIEW_REJECTED";public final static String CLICK_PAY_CONFIRMED="CLICK_PAY_CONFIRMED";public final static String BATCH_REVIEW_CONFIRMED="BATCH_REVIEW_CONFIRMED";public final static String BATCH_PAY_CONFIRMED="BATCH_PAY_CONFIRMED";}
