Spring statemachine-实际项目应用

1. 公司里一个项目真实的应用,需求如下:

image

创建的一个user或group,需要不同人员去approve,user 或group状态就会发生变化。这个很适合使用spring-statemachine来做。以建立group来说明如下

  1. 建立一个group,状态是pending Approved

  2. approve这个group,状态变成partial Approved

  3. approve这个group,如果建立的group级别是高级,状态变成Pending Admin Approve,需要公司人员(Admin级别)去approve. 如果group级别不是高级,状态直接变成Approved

  4. 公司人员(Admin级别) approve这个group,状态变成Pending Admin Approve Confirm

  5. 公司人员(Admin级别) approve这个group,状态变成Approved

  6. 第4步,如果公司人员(Admin级别) reject这个group,状态变成Pending Admin Reject Confirm

  7. 公司人员(Admin级别) approve这个group,状态变成Rejected

  8. 第7步,如果公司人员(Admin级别) reject这个group,状态变成Pending Admin Approve,重新走流程

以下为代码参考了官方sample-persist并做了更改

2.sql
CREATE TABLE `pagroup` (
  `groupId` int(11) DEFAULT NULL,
  `groupName` varchar(256) DEFAULT NULL,
  `status` int(11) DEFAULT NULL,
  `isAdvance` tinyint(1) NOT NULL DEFAULT 0
) ENGINE=InnoDB DEFAULT CHARSET=utf8

对应的Pojo

public class Group {
    private int groupId;
    private String groupName;
    private int state;
    private boolean isAdvance;
   //getter and setter...
}
3.statemahine config

spring statemachine的Configuration,java代码如下,实际上就是用java代码描述1的那个图。刚开始写有点懵,实际写下就很简单了,就是source到target的状态变化需要用什么event(事件)去trigger。需要说明的是withChoice(),这个就是说当状态从pending approved改到choice时,statemahine引擎需要知道如何去选下个状态是Approved还是pending Admin approve,这里逻辑如果group 是isadvance就是pending Admin approve否则就是approved

 
@Configuration
@EnableStateMachine
public class StateMachineConfig extends StateMachineConfigurerAdapter<Status,ActionType>{


    @Override
    public void configure(StateMachineStateConfigurer<Status,ActionType> states) throws Exception {
        states.withStates()
                .initial(Status.PENDING_APPROVAL)
                .choice(Status.CHOICE)
                .states(EnumSet.allOf(Status.class));
    }

    @Override
    public void configure(StateMachineTransitionConfigurer<Status,ActionType> transitions) throws Exception {
        transitions
                .withExternal()
                .source(Status.PENDING_APPROVAL).target(Status.PARTIALLY_APPROVED).event(ActionType.APPROVE)
                .and()
                .withExternal()
                .source(Status.PARTIALLY_APPROVED).target(Status.CHOICE).event(ActionType.APPROVE)
                .and()
                .withChoice()
                .source(Status.CHOICE)
                .first(Status.PENGDING_DOCUMENT_CHECK,(context)->{
                    Group group = context.getMessage().getHeaders().get("group",Group.class);
                    return group.isAdvance();
                })
                .last(Status.APPROVED)
                .and()
                .withExternal()
                .source(Status.PENGDING_DOCUMENT_CHECK).target(Status.PENDING_APPROVAL_CONFIRMATION).event(ActionType.APPROVE)
                .and()
                .withExternal()
                .source(Status.PENDING_APPROVAL_CONFIRMATION).target(Status.APPROVED).event(ActionType.APPROVE)
                .and()
                .withExternal()
                .source(Status.PENDING_APPROVAL_CONFIRMATION).target(Status.PENGDING_DOCUMENT_CHECK).event(ActionType.REJECT)
                .and()
                .withExternal()
                .source(Status.PENGDING_DOCUMENT_CHECK).target(Status.PENDING_REJECT_CONFIRMATION).event(ActionType.REJECT)
                .and()
                .withExternal()
                .source(Status.PENDING_REJECT_CONFIRMATION).target(Status.PENGDING_DOCUMENT_CHECK).event(ActionType.REJECT)
                .and()
                .withExternal()
                .source(Status.PENDING_REJECT_CONFIRMATION).target(Status.REJECTED).event(ActionType.APPROVE)
                .and()
                .withExternal()
                .source(Status.PENDING_APPROVAL).target(Status.REJECTED).event(ActionType.REJECT)
                .and()
                .withExternal()
                .source(Status.PARTIALLY_APPROVED).target(Status.REJECTED).event(ActionType.REJECT);
    }

}
4.事件(ActionType)和状态(Status)定义
public enum ActionType {
 APPROVE(1), REJECT(2);
}
public enum Status {
    PENDING_APPROVAL("status.pending_approval", 1),
    PARTIALLY_APPROVED("status.partially_approval", 2),
    APPROVED("status.approved", 3),
    REJECTED("status.rejected", 4),
    PENGDING_DOCUMENT_CHECK("status.pending_document_check", 5),
    PENDING_APPROVAL_CONFIRMATION("status.pending_approval_confirmation", 10),
    PENDING_REJECT_CONFIRMATION("status.pending_reject_confirmation", 11),
    CHOICE("spring.state.machine.choice",501);
    private String msgKey;
    private int statusCode;
    Status(String desc, int statusCode) {
        this.msgKey = desc;
        this.statusCode = statusCode;
    }
    public static Status valueOf(int statusCode) {
       Iterator<Status> iterator = EnumSet.allOf(Status.class).iterator();
       while (iterator.hasNext()){
           Status st =iterator.next();
           if(st.statusCode==statusCode)
               return st;
       }
        throw new IllegalArgumentException("invalid status code");
    }
    public String getMsgKey() {
        return msgKey;
    }
    public int getStatusCode() {
        return statusCode;
    }
}

PersistStateMachineHandler

该代码就是往statemachine里添加一个拦截器PersistingStateChangeInterceptor
拦截到所有的preStateChange事件,就通知CompositePersistStateChangeListener里面注册的listener去处理


@Component
public class PersistStateMachineHandler extends LifecycleObjectSupport {

    private final StateMachine<Status,ActionType> stateMachine;
    private final PersistingStateChangeInterceptor interceptor = new PersistingStateChangeInterceptor();
    private final CompositePersistStateChangeListener listeners = new CompositePersistStateChangeListener();
    /**
     * Instantiates a new persist state machine handler.
     *
     * @param stateMachine the state machine
     */
    @Autowired
    public PersistStateMachineHandler(StateMachine<Status,ActionType> stateMachine) {
        Assert.notNull(stateMachine, "State machine must be set");
        this.stateMachine = stateMachine;
    }

    //会被LifecycleObjectSupport父类的InitializingBean.afterPropertiesSet()里调用
    protected void onInit() throws Exception {
    
       //往stateMachine加入拦截器PersistingStateChangeInterceptor
        stateMachine.getStateMachineAccessor().doWithAllRegions(new StateMachineFunction<StateMachineAccess<Status,ActionType>>() {
            public void apply(StateMachineAccess<Status,ActionType> function) {
                function.addStateMachineInterceptor(interceptor);
            }
        });

        //获取所有 PersistStateChangeListener的bean注册到CompositePersistStateChangeListener
        Map<String, PersistStateChangeListener> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors((ListableBeanFactory) this.getBeanFactory(), PersistStateChangeListener.class, true, false);
        if (!matchingBeans.isEmpty()) {
            listeners.setListeners(new ArrayList(matchingBeans.values()));
        }
    }

    /**
     * Handle event with entity.
     *
     * @param event the event
     * @param state the state
     * @return true if event was accepted
     */
    public boolean handleEventWithState(Message<ActionType> event, Status state) {
        stateMachine.stop();
        List<StateMachineAccess<Status,ActionType>> withAllRegions = stateMachine.getStateMachineAccessor().withAllRegions();
        for (StateMachineAccess<Status,ActionType> a : withAllRegions) {
            a.resetStateMachine(new DefaultStateMachineContext<Status,ActionType>(state, null, null, null));
        }
        stateMachine.start();
        return stateMachine.sendEvent(event);
    }

    /**
     * Adds the persist state change listener.
     *
     * @param listener the listener
     */
    public void addPersistStateChangeListener(PersistStateChangeListener listener) {
        listeners.register(listener);
    }

    /**
     * The listener interface for receiving persistStateChange events.
     * The class that is interested in processing a persistStateChange
     * event implements this interface, and the object created
     * with that class is registered with a component using the
     * component's <code>addPersistStateChangeListener</code> method. When
     * the persistStateChange event occurs, that object's appropriate
     * method is invoked.
     */
    public interface PersistStateChangeListener {
        /**
         * Called when state needs to be persisted.
         *
         * @param state        the state
         * @param message      the message
         * @param transition   the transition
         * @param stateMachine the state machine
         */
        void onPersist(State<Status,ActionType> state, Message<ActionType> message, Transition<Status,ActionType> transition,
                       StateMachine<Status,ActionType> stateMachine);
    }

    private class PersistingStateChangeInterceptor extends StateMachineInterceptorAdapter<Status,ActionType> {
        @Override
        public void preStateChange(State<Status, ActionType> state, Message<ActionType> message, Transition<Status, ActionType> transition, StateMachine<Status, ActionType> stateMachine) {
            listeners.onPersist(state,message,transition,stateMachine);
        }
    }

    private class CompositePersistStateChangeListener extends AbstractCompositeListener<PersistStateChangeListener> implements
            PersistStateChangeListener {
        public void onPersist(State<Status,ActionType> state, Message<ActionType> message,
                              Transition<Status,ActionType> transition, StateMachine<Status,ActionType> stateMachine) {
            for (Iterator<PersistStateChangeListener> iterator = getListeners().reverse(); iterator.hasNext(); ) {
                PersistStateChangeListener listener = iterator.next();
                listener.onPersist(state, message, transition, stateMachine);
            }
        }
    }
}

GroupController


@RestController
public class GroupController {

    @Autowired
    GroupService groupService;

    @RequestMapping("/group/list")
    public List<Group> list(){
        return groupService.listAll();
    }

    @PostMapping("/group/create")
    public boolean  create(@RequestBody Group group){
        groupService.create(group);
        return true;
    }

   //web入口,处理某个group{id}的某个事件,例如group/2/APPROVE就是对group id 为2  做approve
    @RequestMapping("/group/{id}/{event}")
    public boolean handle(@PathVariable("id")Integer id,@PathVariable("event") String event){
        return groupService.handleAction(id,event);
    }

}

GroupService

@org.springframework.stereotype.Service
public class GroupService {

    @Autowired
    private PersistStateMachineHandler handler;
    @Autowired
    private GroupRepository repository;

    public boolean handleAction(int groupId, String event) {
        Group group = repository.findGroupById(groupId);
        //发送事件去触发状态机
        return handler.handleEventWithState(MessageBuilder.withPayload(ActionType.valueOf(event))
                .setHeader("group", group).build(), Status.valueOf(group.getState()));
    }

    public void create(Group group) {
        repository.create(group);
    }
    public List listAll() {
        return repository.listAll();
    }
}

GroupRepoository 相当于Dao

@Repository
public class GroupRepository {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void create(Group group){
        jdbcTemplate.update("insert into pagroup(groupId,groupName,status) values (?,?,?)",
                group.getGroupId(),
                group.getGroupName(),
                Status.PENDING_APPROVAL.getStatusCode());
    }

    public List listAll() {
        List list  = jdbcTemplate.query("select groupId,groupName,status,isAdvance from pagroup",rowMapper());
        return list;
    }

    public Group findGroupById(int groupId) {
        Group group = jdbcTemplate.queryForObject("select groupId, groupName,status,isAdvance from pagroup where groupId = ?", new Object[]{groupId},rowMapper());
        return group;
    }
    private RowMapper<Group> rowMapper(){
        return  new RowMapper<Group>() {
            public Group mapRow(ResultSet rs, int rowNum) throws SQLException {
                Group group = new Group(rs.getInt("groupId"), rs.getString("groupName"));
                group.setState(rs.getInt("status"));
                group.setAdvance(rs.getBoolean("isAdvance"));
                return group;
            }
        };
    }
}

GroupPersistStateChangeListener 监听到状态机状态更改,就更新数据库里的对应字段

@Component
public class GroupPersistStateChangeListener  implements PersistStateMachineHandler.PersistStateChangeListener {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public GroupPersistStateChangeListener(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public void onPersist(State<Status, ActionType> state, Message<ActionType> message, Transition<Status, ActionType> transition, StateMachine<Status, ActionType> stateMachine) {
        if (message != null && message.getHeaders().containsKey("group")) {
            Group group = message.getHeaders().get("group", Group.class);
            jdbcTemplate.update("update pagroup set status = ? where groupId = ?", state.getId().getStatusCode(), group.getGroupId());
        }
    }
}

启动Spring工程

@SpringBootApplication
public class BootStrap {

    public static void main(String[] args) {
        SpringApplicationBuilder builder = new SpringApplicationBuilder(BootStrap.class);
        builder.run(args);
    }
}

代码在github

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值