Spring state machine 状态机

一、活动状态机配置

package com.zhibi.xiuba.configurer; 

import com.zhibi.xiuba.statemachine.EventsOnTransition; 
import com.zhibi.xiuba.statemachine.TaskEvent; 
import com.zhibi.xiuba.task.spi.domain.Task; 
import org.apache.commons.lang3.builder.ToStringBuilder; 
import org.apache.commons.lang3.builder.ToStringStyle; 
import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 
import org.springframework.aop.support.AopUtils; 
import org.springframework.beans.BeansException; 
import org.springframework.context.ApplicationContext; 
import org.springframework.context.ApplicationContextAware; 
import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.Configuration; 
import org.springframework.core.annotation.AnnotationUtils; 
import org.springframework.statemachine.StateContext; 
import org.springframework.statemachine.StateMachine; 
import org.springframework.statemachine.action.Action; 
import org.springframework.statemachine.annotation.WithStateMachine; 
import org.springframework.statemachine.config.EnableStateMachineFactory; 
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter; 
import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer; 
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer; 
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer; 
import org.springframework.statemachine.listener.StateMachineListenerAdapter; 
import org.springframework.statemachine.processor.StateMachineMethodInvokerHelper; 
import org.springframework.statemachine.processor.StateMachineRuntime; 
import org.springframework.statemachine.transition.Transition; 
import org.springframework.util.ReflectionUtils; 

import java.lang.reflect.Method; 
import java.util.EnumSet; 
import java.util.HashMap; 
import java.util.Map; 

import static com.zhibi.xiuba.task.spi.domain.Task.TaskStatusEnum.*; 

/** 
* Created by QINHE on 2017/8/14. 
*/ 
@Configuration 
@EnableStateMachineFactory(name = "taskStateMachine", contextEvents = false) 
public class TaskStateMachineConfig extends EnumStateMachineConfigurerAdapter<Task.TaskStatusEnum, TaskEvent> implements ApplicationContextAware { 

private Logger logger = LoggerFactory.getLogger(this.getClass()); 

private Map<TaskEvent, StateMachineMethodInvokerHelper> actionMap = new HashMap<>(); 

@Override 
public void configure(StateMachineConfigurationConfigurer<Task.TaskStatusEnum, TaskEvent> config) throws Exception { 
super.configure(config); 
config.withConfiguration().autoStartup(false).listener(new StateMachineListenerAdapter<Task.TaskStatusEnum, TaskEvent>() { 

@Override 
public void transition(Transition transition) { 
logger.info(ToStringBuilder.reflectionToString(transition, ToStringStyle.MULTI_LINE_STYLE)); 
} 

@Override 
public void stateMachineError(StateMachine stateMachine, Exception exception) { 
logger.error(exception.getLocalizedMessage(), exception); 
} 
}); 
} 

@Override 
public void configure(StateMachineStateConfigurer<Task.TaskStatusEnum, TaskEvent> states) throws Exception { 
states.withStates().initial(WAITING_PAY).end(FINISHED) 
.end(CLOSED).end(DELETED).states(EnumSet.allOf(Task.TaskStatusEnum.class)); 
} 

@Override 
public void configure(StateMachineTransitionConfigurer<Task.TaskStatusEnum, TaskEvent> transitions) throws Exception { 
transitions 
.withExternal().source(WAITING_PAY).target(DELETED).event(TaskEvent.PAY_WITH_DELETE).action(buildAction()).and() 
.withExternal().source(WAITING_PAY).target(WAITING_PAY).event(TaskEvent.NEED_PAY_MORE).action(buildAction()).and() 
.withExternal().source(WAITING_PAY).target(WAITING_AUDIT).event(TaskEvent.SUBMIT_AUDIT).action(buildAction()).and() 
.withExternal().source(WAITING_MODIFY).target(WAITING_PAY).event(TaskEvent.MODIFY_WAITING_PAY).action(buildAction()).and() 
.withExternal().source(WAITING_MODIFY).target(WAITING_AUDIT).event(TaskEvent.MODIFY_SUBMIT_AUDIT).action(buildAction()).and() 
.withExternal().source(WAITING_MODIFY).target(DELETED).event(TaskEvent.MODIFY_WITH_DELETE).action(buildAction()).and() 
.withExternal().source(WAITING_AUDIT).target(WAITING_MODIFY).event(TaskEvent.AUDIT_NOT_PASS).action(buildAction()).and() 
.withExternal().source(WAITING_AUDIT).target(UNDER_WAY).event(TaskEvent.AUDIT_PASS).action(buildAction()).and() 
.withExternal().source(UNDER_WAY).target(FINISHED).event(TaskEvent.TASK_FINISH).action(buildAction()); 

//TODO:任务关闭状态这里没配置 
} 

@Bean 
public Action<Task.TaskStatusEnum, TaskEvent> buildAction() { 
return new Action<Task.TaskStatusEnum, TaskEvent>() { 
@Override 
public void execute(StateContext<Task.TaskStatusEnum, TaskEvent> context) { 
TaskEvent event = context.getEvent(); 
StateMachineMethodInvokerHelper helper = actionMap.get(event); 
if (helper == null) { 
throw new RuntimeException("not found invoke execute!!!!event is" + event.name()); 
} 
StateMachineRuntime runtime = new StateMachineRuntime() { 
@Override 
public StateContext getStateContext() { 
return context; 
} 
}; 
try { 
helper.process(runtime); 
} catch (Exception e) { 
throw new RuntimeException(e); 
} 
} 

}; 
} 

@Override 
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 
Map<String, Object> map = applicationContext.getBeansWithAnnotation(WithStateMachine.class); 
for (Map.Entry<String, Object> entry : map.entrySet()) { 
Object bean = entry.getValue(); 
Class<?> beanClass = getBeanClass(bean); 
ReflectionUtils.doWithMethods(beanClass, new ReflectionUtils.MethodCallback() { 
@Override 
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException { 
EventsOnTransition a = AnnotationUtils.findAnnotation(method, EventsOnTransition.class); 
if (a == null) { 
return; 
} 
TaskEvent[] taskEvent = a.events(); 
StateMachineMethodInvokerHelper helper = new StateMachineMethodInvokerHelper(bean, method); 
for (TaskEvent event : taskEvent) { 
actionMap.put(event, helper); 
} 
} 
}); 
} 
} 

/** 
* Gets the bean class. Will check if bean is a proxy and 
* find a class from there as target class, otherwise 
* we just get bean class. 
* 
* @param bean the bean 
* @return the bean class 
*/ 
private Class<?> getBeanClass(Object bean) { 
Class<?> targetClass = AopUtils.getTargetClass(bean); 
return (targetClass != null) ? targetClass : bean.getClass(); 
} 
} 

状态转变触发(trigger)->状态机启动并加载配置->guard->action->onTransition->变成新状态

1、项目中一般都需要打印日志,所有实体的toString()方法都是用简单的”+”,因为每”+” 一个就会 new 一个 String 对象,这样如果系统内存小的话会暴内存。使用ToStringBuilder就可以避免暴内存这种问题。

ToStringBuilder的reflectionToString方法:

logger.info(“请求数据:”+ToStringBuilder.reflectionToString(req));

2、本项目没有使用原生的statemachine的listen注解@WithStateMachine 和@OnTransition(target = “UNPAID”)开发,而是自己做了状态迁移listener,该注解不使用OnTransition避免业务异常无法正常被状态机接收,状态还是会继续改变

3、configure(StateMachineConfigurationConfigurer

package com.zhibi.xiuba.statemachine; 

import com.zhibi.xiuba.exception.StateEventException; 
import com.zhibi.xiuba.model.ControllerResult; 
import com.zhibi.xiuba.task.spi.domain.ShowkerTask; 
import com.zhibi.xiuba.task.spi.domain.Task; 
import com.zhibi.xiuba.task.spi.enums.OperatorEnum; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.messaging.support.MessageBuilder; 
import org.springframework.statemachine.StateMachine; 
import org.springframework.statemachine.access.StateMachineAccess; 
import org.springframework.statemachine.config.StateMachineFactory; 
import org.springframework.statemachine.support.DefaultStateMachineContext; 
import org.springframework.stereotype.Service; 

import java.util.HashMap; 
import java.util.List; 
import java.util.Map; 

/** 
* 状态机事件发送服务 
* <p> 
* 请使用本服务发送事件已保证逻辑正确性 
* Created by RUAN on 2017/8/16. 
*/ 
@Service 
public class StateMachineInvokeService implements IStateMachineInvokeService { 

@Autowired 
private StateMachineFactory<Task.TaskStatusEnum, TaskEvent> taskStateMachine; 


@Autowired 
private StateMachineFactory<ShowkerTask.StatusEnum, ShowkerTaskEvent> showKerTaskStateMachine; 


/** 
* 发送活动事件 
* 
* @param event活动事件 
* @param originalTask 原始活动对象,未变更前数据 
* @param params 扩展参数 
*/ 
@Override 
public void sendTaskEvent(TaskEvent event, Task originalTask, OperatorEnum operator, Map<String, Object> params) { 
if (params == null) { 
params = new HashMap<>(); 
} 
params.put(IStateMachineConstants.OPERATOR, operator.getCode()); 
sendTaskEvent(event, originalTask, params); 
} 

/** 
* 发送活动事件 
* 
* @param event活动事件 
* @param originalTask 原始活动对象,未变更前数据 
* @param params 扩展参数 
*/ 
@Override 
public void sendTaskEvent(TaskEvent event, Task originalTask, Map<String, Object> params) { 
StateMachine<Task.TaskStatusEnum, TaskEvent> stateMachine = taskStateMachine.getStateMachine("TaskStateMachine:" + originalTask.getId()); 
String taskStatus = originalTask.getTaskStatus(); 
Task.TaskStatusEnum statusEnum = Task.TaskStatusEnum.getEnumByCode(taskStatus); 
if (statusEnum == null) { 
throw new RuntimeException("not found task status:" + taskStatus); 
} 
stateMachine.stop(); 
//启动之前恢复状态机的状态 
List<StateMachineAccess<Task.TaskStatusEnum, TaskEvent>> withAllRegions = stateMachine.getStateMachineAccessor().withAllRegions(); 
for (StateMachineAccess<Task.TaskStatusEnum, TaskEvent> a : withAllRegions) { 
a.resetStateMachine(new DefaultStateMachineContext<>(statusEnum, null, null, null, null, stateMachine.getId())); 
} 
stateMachine.start(); 
stateMachine.sendEvent(MessageBuilder.withPayload(event).setHeader(IStateMachineConstants.TASK_OBJECT, originalTask).copyHeaders(params).build()); 
} 

/** 
* 发送拿手任务事件 
* 
* @param event拿手任务事件 
* @param originalTask 原始拿手任务对象,未变更前数据 
* @param params 扩展参数 
*/ 
@Override 
public void sendShowKerTaskEvent(ShowkerTaskEvent event, ShowkerTask originalTask, OperatorEnum operatorEnum, Map<String, Object> params) { 
if (params == null) { 
params = new HashMap<>(); 
} 
params.put(IStateMachineConstants.OPERATOR, operatorEnum.getCode()); 
sendShowKerTaskEvent(event, originalTask, params); 
} 

/** 
* 发送拿手任务事件 
* 
* @param event拿手任务事件 
* @param originalTask 原始拿手任务对象,未变更前数据 
* @param params 扩展参数 
*/ 
@Override 
public void sendShowKerTaskEvent(ShowkerTaskEvent event, ShowkerTask originalTask, Map<String, Object> params) { 
StateMachine<ShowkerTask.StatusEnum, ShowkerTaskEvent> stateMachine = showKerTaskStateMachine.getStateMachine("ShowkerTaskStateMachine:" + originalTask.getId()); 
String taskStatus = originalTask.getStatus(); 
ShowkerTask.StatusEnum statusEnum = ShowkerTask.StatusEnum.getEnumByCode(taskStatus); 
if (statusEnum == null) { 
throw new RuntimeException("not found showker task status:" + taskStatus); 
} 
stateMachine.stop(); 
//启动之前恢复状态机的状态 
List<StateMachineAccess<ShowkerTask.StatusEnum, ShowkerTaskEvent>> withAllRegions = stateMachine.getStateMachineAccessor().withAllRegions(); 
for (StateMachineAccess<ShowkerTask.StatusEnum, ShowkerTaskEvent> a : withAllRegions) { 
a.resetStateMachine(new DefaultStateMachineContext<>(statusEnum, null, null, null, null, stateMachine.getId())); 
} 
stateMachine.start(); 
if (!stateMachine.sendEvent(MessageBuilder.withPayload(event).setHeader(IStateMachineConstants.TASK_OBJECT, originalTask).copyHeaders(params).build())) { 
throw new StateEventException(ControllerResult.build(false, "stateMachine_not_executed", "状态机未调用")); 
} 
} 
} 

三、状态迁移处理器

package com.zhibi.xiuba.statemachine; 

import com.zhibi.xiuba.job.IJobType; 
import com.zhibi.xiuba.service.IJobService; 
import com.zhibi.xiuba.service.ITaskService; 
import com.zhibi.xiuba.task.spi.domain.Task; 
import com.zhibi.xiuba.task.spi.domain.TaskOpLog; 
import com.zhibi.xiuba.task.spi.enums.OperatorEnum; 
import com.zhibi.xiuba.utils.StringUtils; 
import org.springframework.statemachine.StateContext; 
import org.springframework.statemachine.annotation.EventHeaders; 
import org.springframework.statemachine.annotation.WithStateMachine; 

import java.util.Calendar; 
import java.util.Map; 

/** 

* 活动状态迁移处理器 

* Created by RUAN on 2017/8/15. 

* 

* @see com.zhibi.xiuba.configurer.TaskStateMachineConfig 

*/ 
@WithStateMachine(name = "taskStateMachine") 
public class TaskTransitionHandler { 

private final ITaskService taskService; 

private final IJobService jobService; 

public TaskTransitionHandler(ITaskService taskService, 
IJobService jobService) { 
this.taskService = taskService; 
this.jobService = jobService; 
} 

/** 

* 提交审核 

*/ 
@SuppressWarnings("Duplicates") 
@EventsOnTransition(events = {TaskEvent.SUBMIT_AUDIT}) 
public void submitAudit(@EventHeaders Map<String, Object> headers, 
StateContext<Task.TaskStatusEnum, TaskEvent> context) { 
try { 
OperatorEnum operator = null; 
if (StringUtils.isNotBlank((String) context.getMessageHeaders().get(IStateMachineConstants.OPERATOR))) { 
//传参中带有操作人的 

operator = OperatorEnum.getEnumByCode((String) context.getMessageHeaders().get(IStateMachineConstants.OPERATOR)); 
} 

Task task = (Task) headers.get(IStateMachineConstants.TASK_OBJECT); 
Long fee = (Long) headers.get("fee"); 
if (task.getMarginPaid() == null) { 
task.setMarginPaid(0L); 
} 
TaskOpLog.TypeEnum typeEnum; 
if (task.getMarginPaid() <= 0) { 
typeEnum = TaskOpLog.TypeEnum.FEE_PAY; 
} else { 
typeEnum = TaskOpLog.TypeEnum.FEE_REPAY; 
} 
if (task.getPromotionExpensesPaid() == null) { 
task.setPromotionExpensesPaid(0L); 
} 
if (task.getMarginSurplus() == null) { 
task.setMarginSurplus(0L); 
} 

task.setMarginPaid(fee - (task.getPromotionExpensesNeed() - task.getPromotionExpensesPaid()) + task.getMarginPaid()); 
task.setMarginSurplus(task.getMarginPaid() - task.getMarginSurplus()); 
task.setPromotionExpensesPaid(task.getPromotionExpensesNeed()); 
task.setTaskStatus(context.getTarget().getId().getCode()); 
taskService.save(task); 
taskService.createLog(task.getId(), typeEnum, operator); 
} catch (Exception e) { 
e.printStackTrace(); 
throw new RuntimeException(e.getMessage()); 
} 
} 

/** 

* 待补充保证金 

*/ 
@SuppressWarnings("Duplicates") 
@EventsOnTransition(events = {TaskEvent.NEED_PAY_MORE}) 
public void needPayMore(@EventHeaders Map<String, Object> headers, 
StateContext<Task.TaskStatusEnum, TaskEvent> context) { 
try { 
OperatorEnum operator = null; 
if (StringUtils.isNotBlank((String) context.getMessageHeaders().get(IStateMachineConstants.OPERATOR))) { 
//传参中带有操作人的 

operator = OperatorEnum.getEnumByCode((String) context.getMessageHeaders().get(IStateMachineConstants.OPERATOR)); 
} 

Task task = (Task) headers.get(IStateMachineConstants.TASK_OBJECT); 
Long fee = (Long) headers.get("fee"); 
if (task.getMarginPaid() == null) { 
task.setMarginPaid(0L); 
} 
TaskOpLog.TypeEnum typeEnum; 
if (task.getMarginPaid() <= 0) { 
typeEnum = TaskOpLog.TypeEnum.FEE_PAY; 
} else { 
typeEnum = TaskOpLog.TypeEnum.FEE_REPAY; 
} 
if (task.getPromotionExpensesPaid() == null) { 
task.setPromotionExpensesPaid(0L); 
} 
if (task.getMarginSurplus() == null) { 
task.setMarginSurplus(0L); 
} 
if (fee <= (task.getPromotionExpensesNeed() - task.getPromotionExpensesPaid())) { 
task.setPromotionExpensesPaid(fee + task.getPromotionExpensesPaid()); 
} else { 
task.setMarginPaid(fee - (task.getPromotionExpensesNeed() - task.getPromotionExpensesPaid()) + task.getMarginPaid()); 
task.setMarginSurplus(task.getMarginPaid() - task.getMarginSurplus()); 
task.setPromotionExpensesPaid(task.getPromotionExpensesNeed()); 
} 
task.setTaskStatus(context.getTarget().getId().getCode()); 
taskService.save(task); 
taskService.createLog(task.getId(), typeEnum, operator); 
} catch (Exception e) { 
e.printStackTrace(); 
throw new RuntimeException(e.getMessage()); 
} 
} 

/** 

* 任务完成 

*/ 
@EventsOnTransition(events = TaskEvent.TASK_FINISH) 
public void taskFinish(@EventHeaders Map<String, Object> headers, 
StateContext<Task.TaskStatusEnum, TaskEvent> context) { 
try { 
OperatorEnum operator = null; 
if (StringUtils.isNotBlank((String) context.getMessageHeaders().get(IStateMachineConstants.OPERATOR))) { 
//传参中带有操作人的 

operator = OperatorEnum.getEnumByCode((String) context.getMessageHeaders().get(IStateMachineConstants.OPERATOR)); 
} 

Task task = (Task) headers.get(IStateMachineConstants.TASK_OBJECT); 
task.setTaskStatus(context.getTarget().getId().getCode()); 
task.setSettlementStatus(Task.SettlementStatusEnum.WAITING_SETTLEMENT.getCode()); 
Calendar calendar = Calendar.getInstance(); 
task.setFinishTime(calendar.getTime()); 
taskService.save(task); 
taskService.createLog(task.getId(), TaskOpLog.TypeEnum.FINISH, operator); 
taskService.moveToEsAndIndexCacheHistory(task); 
jobService.submitTaskJob(task.getId(), IJobType.TASK_AUTO_AUDIT_SHOWKER, 48); 
} catch (Exception e) { 
e.printStackTrace(); 
throw new RuntimeException(e.getMessage()); 
} 
} 
} 

猜你喜欢

转载自blog.csdn.net/u014229652/article/details/81748859