为什么要用状态机呢?说到底就是限制我们在程序中的硬编码,避免随便的去update某一个状态。
    例如一个审核系统,状态的流转只能是A->B-C,为了避免我们在某一个地方人为疏忽将A->C,我们需要借助状态机来操作状态变化。
    image.png

    1. package com.clubfactory.tms.bill.core.service.fsm;
    2. import com.clubfactory.tms.bill.core.enums.OrderAuditState;
    3. import lombok.AllArgsConstructor;
    4. import lombok.Builder;
    5. import lombok.Data;
    6. import lombok.NoArgsConstructor;
    7. import org.springframework.beans.factory.BeanInitializationException;
    8. import org.springframework.beans.factory.InitializingBean;
    9. import org.springframework.beans.factory.annotation.Autowired;
    10. import org.springframework.stereotype.Service;
    11. import java.util.Arrays;
    12. import java.util.List;
    13. import java.util.Map;
    14. import java.util.Optional;
    15. /**
    16. * 参考:https://zhuanlan.zhihu.com/p/97442825
    17. * 当(主动)执行了某个动作,某条数据的状态要流转到下一个状态。通过状态机的方式来update状态
    18. */
    19. @Service
    20. public class OrderAuditFSM implements InitializingBean {
    21. @Autowired
    22. private UpdateAuditStateEvent updateAuditStateEvent;
    23. private static List<MachineTransaction> fsmList = null;
    24. @Override
    25. public void afterPropertiesSet() throws Exception {
    26. fsmList = Arrays.asList(
    27. //待比对->待确认
    28. MachineTransaction.builder()
    29. .currentState(OrderAuditState.TO_BE_COMPARED)
    30. .action(Action.SYSTEM_CHECK_EXIST_DIFF_CONFIRMED)
    31. .nextState(OrderAuditState.TO_BE_CHECKED)
    32. .event(updateAuditStateEvent).build(),
    33. //待比对->待复核
    34. MachineTransaction.builder()
    35. .currentState(OrderAuditState.TO_BE_COMPARED)
    36. .action(Action.SYSTEM_CHECK_NO_DIFF_CONFIRMED)
    37. .nextState(OrderAuditState.TO_BE_REVIEWED)
    38. .event(updateAuditStateEvent).build(),
    39. //待确认->待复核
    40. MachineTransaction.builder()
    41. .currentState(OrderAuditState.TO_BE_CHECKED)
    42. .action(Action.CLICK_CHECK_CONFIRMED)
    43. .nextState(OrderAuditState.TO_BE_REVIEWED)
    44. .event(updateAuditStateEvent).build(),
    45. //待复核->待支付
    46. MachineTransaction.builder()
    47. .currentState(OrderAuditState.TO_BE_REVIEWED)
    48. .action(Action.CLICK_REVIEW_CONFIRMED)
    49. .nextState(OrderAuditState.TO_BE_PAID)
    50. .event(updateAuditStateEvent).build(),
    51. //待复核->待确认
    52. MachineTransaction.builder()
    53. .currentState(OrderAuditState.TO_BE_REVIEWED)
    54. .action(Action.CLICK_REVIEW_REJECTED)
    55. .nextState(OrderAuditState.TO_BE_CHECKED)
    56. .event(updateAuditStateEvent).build(),
    57. //待支付->支付完成
    58. MachineTransaction.builder()
    59. .currentState(OrderAuditState.TO_BE_PAID)
    60. .action(Action.CLICK_PAY_CONFIRMED)
    61. .nextState(OrderAuditState.PAID)
    62. .event(updateAuditStateEvent).build(),
    63. //(批量)待审核->待支付
    64. MachineTransaction.builder()
    65. .currentState(OrderAuditState.TO_BE_REVIEWED)
    66. .action(Action.BATCH_REVIEW_CONFIRMED)
    67. .nextState(OrderAuditState.TO_BE_PAID)
    68. .event(updateAuditStateEvent).build(),
    69. //(批量)待支付->支付完成
    70. MachineTransaction.builder()
    71. .currentState(OrderAuditState.TO_BE_PAID)
    72. .action(Action.BATCH_PAY_CONFIRMED)
    73. .nextState(OrderAuditState.PAID)
    74. .event(updateAuditStateEvent).build()
    75. );
    76. }
    77. public void execute(String action, OrderAuditState currentState, Long carrierOrderId) {
    78. Optional<MachineTransaction> transactionOptional =
    79. fsmList.stream().filter(transaction -> transaction.getAction().equals(action) && transaction.getCurrentState().equals(currentState)).findFirst();
    80. if (!transactionOptional.isPresent()) {
    81. throw new InvalidActionException();
    82. }
    83. MachineTransaction transaction = transactionOptional.get();
    84. transaction.getEvent().doUpdate(transaction.getNextState(), carrierOrderId);
    85. }
    86. //批量流转
    87. public void batchExecute(String action, Map<OrderAuditState, List<Long>> map){
    88. for (Map.Entry<OrderAuditState, List<Long>> orderAuditStateEntry : map.entrySet()) {
    89. OrderAuditState currentState = orderAuditStateEntry.getKey();
    90. Optional<MachineTransaction> transactionOptional =
    91. fsmList.stream().filter(transaction -> transaction.getAction().equals(action) && transaction.getCurrentState().equals(currentState)).findFirst();
    92. if (transactionOptional.isPresent()) {
    93. MachineTransaction transaction = transactionOptional.get();
    94. transaction.getEvent().doBatchUpdate(transaction.getNextState(), orderAuditStateEntry.getValue());
    95. }
    96. }
    97. }
    98. }
    99. @Data
    100. @Builder
    101. @NoArgsConstructor
    102. @AllArgsConstructor
    103. class MachineTransaction{
    104. private OrderAuditState currentState;
    105. private String action;
    106. private OrderAuditState nextState;
    107. private Event event;
    108. }
    109. class InvalidActionException extends RuntimeException{}
    1. package com.clubfactory.tms.bill.core.service.fsm;
    2. import com.clubfactory.tms.bill.core.enums.OrderAuditState;
    3. import java.util.List;
    4. public interface Event {
    5. public void doUpdate(OrderAuditState nextState, Long id);
    6. public void doBatchUpdate(OrderAuditState nextState, List<Long> ids);
    7. }
    1. package com.clubfactory.tms.bill.core.service.fsm;
    2. import com.clubfactory.tms.bill.core.dao.CarrierOrderMapper;
    3. import com.clubfactory.tms.bill.core.dataobject.CarrierOrderDO;
    4. import com.clubfactory.tms.bill.core.enums.OrderAuditState;
    5. import org.springframework.beans.factory.annotation.Autowired;
    6. import org.springframework.stereotype.Service;
    7. import java.util.List;
    8. @Service
    9. public class UpdateAuditStateEvent implements Event{
    10. @Autowired
    11. private CarrierOrderMapper carrierOrderMapper;
    12. @Override
    13. public void doUpdate(OrderAuditState nextState, Long id) {
    14. CarrierOrderDO updateDO = CarrierOrderDO.builder().id(id).auditState(nextState.getCode()).build();
    15. carrierOrderMapper.update(updateDO);
    16. }
    17. @Override
    18. public void doBatchUpdate(OrderAuditState nextState, List<Long> ids) {
    19. if(!ids.isEmpty()){
    20. carrierOrderMapper.updateStateByIds(nextState.getCode(), ids);
    21. }
    22. }
    23. }
    1. package com.clubfactory.tms.bill.core.service.fsm;
    2. public class Action {
    3. public final static String SYSTEM_CHECK_NO_DIFF_CONFIRMED="SYSTEM_CHECK_NO_DIFF_CONFIRMED";
    4. public final static String SYSTEM_CHECK_EXIST_DIFF_CONFIRMED="SYSTEM_CHECK_EXIST_DIFF_CONFIRMED";
    5. public final static String CLICK_CHECK_CONFIRMED="CLICK_CHECK_CONFIRMED";
    6. public final static String CLICK_REVIEW_CONFIRMED="CLICK_REVIEW_CONFIRMED";
    7. public final static String CLICK_REVIEW_REJECTED="CLICK_REVIEW_REJECTED";
    8. public final static String CLICK_PAY_CONFIRMED="CLICK_PAY_CONFIRMED";
    9. public final static String BATCH_REVIEW_CONFIRMED="BATCH_REVIEW_CONFIRMED";
    10. public final static String BATCH_PAY_CONFIRMED="BATCH_PAY_CONFIRMED";
    11. }