项目使用springboot + spring cloud + activiti 6.0.0 实现动态工作流引擎,遇到了节点超时时长可以通过js脚本方式设置的需求。
实现思路方案
1. 沿用activiti的asyncexecutor任务调度,不采用quartz等其他方案。使用Activiti bpmn的timer boundry event。
2. activiti流程定义bpmn中task上定义timer boundry event,此处event的id为${timerBoundryId}
3. 在任务usertask的documentation中设置{"timerBoundryId":"${timerBoundryId}","script":"if(true){return 60;}"}
4. activiti执行到task后,将task上的timer全部删掉,然后再通过程序读取task上documentation设置,执行js脚本获取到duration时长
5. 因为activiti没有开放创建timers的api,所以通过实现org.activiti.engine.impl.interceptor.Command接口类CreateTimerCmd在act_ru_timers表中创建timer记录。
--------------
代码部分
删除task上全部timers的代码
private void clearTimerJobs(String processId, List newTasks) {
newTasks.forEach(item->{
List executions = runtimeService.createExecutionQuery()
.processInstanceId(processId).parentId(item.getExecutionId()).list();
executions.forEach(exec -> {
List timeJobs = managementService.createTimerJobQuery().processInstanceId(processId)
.executionId(exec.getId()).list();
if(!timeJobs.isEmpty()){
// delete execution
timeJobs.forEach(job->{
managementService.deleteTimerJob(job.getId());
});
CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor();
commandExecutor.execute(new DeleteExecutionCmd(exec.getId()));
}
});
});
}
DeleteExecutionCmd.java
import org.activiti.engine.impl.interceptor.Command;
import org.activiti.engine.impl.interceptor.CommandContext;
import org.activiti.engine.task.Comment;
public class DeleteExecutionCmd implements Command {
protected String executorId;
public DeleteExecutionCmd(String executorId) {
this.executorId = executorId;
}
@Override
public Comment execute(CommandContext commandContext) {
commandContext.getExecutionEntityManager().delete(executorId);
return null;
}
}
CreateTimerCmd.java
import org.activiti.bpmn.model.BoundaryEvent;
import org.activiti.bpmn.model.TimerEventDefinition;
import org.activiti.engine.impl.asyncexecutor.JobManager;
import org.activiti.engine.impl.context.Context;
import org.activiti.engine.impl.interceptor.Command;
import org.activiti.engine.impl.interceptor.CommandContext;
import org.activiti.engine.impl.jobexecutor.TimerEventHandler;
import org.activiti.engine.impl.jobexecutor.TriggerTimerEventJobHandler;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.activiti.engine.impl.persistence.entity.TimerJobEntity;
import org.activiti.engine.task.Comment;
public class CreateTimerCmd implements Command {
protected String duration;
protected String taskId;
protected BoundaryEvent timerBoundaryEvent;
public CreateTimerCmd(BoundaryEvent timerBoundaryEvent, String taskId , String duration) {
this.taskId = taskId;
this.duration = duration;
this.timerBoundaryEvent = timerBoundaryEvent;
}
/**
* 创建timer job
* 需要为当前task的execution上创建child execution,并且创建timer job,并且将execution绑定到timerboudryevent上
*/
public Comment execute(CommandContext commandContext) {
TaskEntity task = commandContext.getTaskEntityManager().findById(taskId);
ExecutionEntity execution = commandContext.getExecutionEntityManager().findById(task.getExecutionId());
ExecutionEntity childExecutionEntity = commandContext.getExecutionEntityManager().createChildExecution((ExecutionEntity) execution);
childExecutionEntity.setParentId(execution.getId());
childExecutionEntity.setCurrentFlowElement(timerBoundaryEvent);
childExecutionEntity.setScope(false);
JobManager jobManager = Context.getCommandContext().getJobManager();
TimerEventDefinition timerEventDefinition = new TimerEventDefinition();
timerEventDefinition.setTimeDuration(duration);
TimerJobEntity timerJob = jobManager.createTimerJob(timerEventDefinition, false, childExecutionEntity, TriggerTimerEventJobHandler.TYPE,
TimerEventHandler.createConfiguration(childExecutionEntity.getCurrentActivityId(), timerEventDefinition.getEndDate(), timerEventDefinition.getCalendarName()));
if (timerJob != null) {
jobManager.scheduleTimerJob(timerJob);
}
return null;
}
}
执行CreateTimerCommand的方法
@Transactional
public void addTimerToTask(String taskId, String timerBoudryElementId,float duration) {
TaskEntity task = (TaskEntity) taskService.createTaskQuery().taskId(taskId).singleResult();
BpmnModel model = repositoryService.getBpmnModel(task.getProcessDefinitionId());
if (model.getProcesses() != null && model.getProcesses().size() > 0) {
// 从模型上获取到当前的task节点
BoundaryEvent timerBoudry = (BoundaryEvent)model.getProcesses().get(0).getFlowElement(timerBoudryElementId, true);
CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor();
commandExecutor.execute(new CreateTimerCmd(timerBoudry, taskId, "PT"+(int)(duration*60)+"M"));
logger.info("创建timer成功,任务Id:"+taskId +" timerBoudryElementId:"+timerBoudryElementId+" duration:"+"PT"+(int)(duration*60)+"M");
}
}
其中taskId是任务id,timerBoundryElementId是task documentation中的${timerBoundryId}, duration则通过js解析获取。
------
网上Activiti 6.0.0的此类实现比较少,故分享出来,如果有更好实现,望各位老师分享。