SpringBoot之集成activiti5.22.0

一、Activiti简介

Activiti项目是一项新的基于Apache许可的开源BPM平台,从基础开始构建,旨在提供支持新的BPMN 2.0标准,包括支持对象管理组(OMG),面对新技术的机遇,诸如互操作性和云架构,提供技术实现。
其实Activiti就是一个工作流框架,用于实现我们自己业务系统的各种工作流程。

二、Maven配置文件的引入

我们先看Maven的配置
这里排除了org.mybatis、spring-security、slf4j-log4j12等包,org.mybatis是因为我们项目一般都会单独引入,这个包下面的就可以排除掉,spring-security是spring的授权、认证框架,跟shiro功能差不多,这里由于我们项目使用的是shiro,所以把这个排除掉,不然会出现Please sign in的画面,slf4j-log4j12是log4j的日志实现,我们本项目用的是logback-spring实现日志系统,所以这个也排除了,不然springboot启动会报错。

<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-spring-boot-starter-basic</artifactId>
    <version>${activiti.version}</version>
    <exclusions>
        <exclusion>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
        </exclusion>
        <!-- 排除juel-spi,会和tomcat下的servlet、jsp等jar有冲突 -->
        <exclusion>
            <groupId>de.odysseus.juel</groupId>
            <artifactId>juel-spi</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-modeler</artifactId>
    <version>${activiti.version}</version>
    <exclusions>
        <exclusion>
            <artifactId>slf4j-log4j12</artifactId>
            <groupId>org.slf4j</groupId>
        </exclusion>
        <exclusion>
            <artifactId>spring-security-config</artifactId>
            <groupId>org.springframework.security</groupId>
        </exclusion>
        <exclusion>
            <artifactId>spring-security-crypto</artifactId>
            <groupId>org.springframework.security</groupId>
        </exclusion>
        <exclusion>
            <artifactId>spring-security-web</artifactId>
            <groupId>org.springframework.security</groupId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-diagram-rest</artifactId>
    <version>${activiti.version}</version>
    <exclusions>
        <exclusion>
            <artifactId>spring-security-config</artifactId>
            <groupId>org.springframework.security</groupId>
        </exclusion>
        <exclusion>
            <artifactId>spring-security-core</artifactId>
            <groupId>org.springframework.security</groupId>
        </exclusion>
        <exclusion>
            <artifactId>spring-security-web</artifactId>
            <groupId>org.springframework.security</groupId>
        </exclusion>
        <exclusion>
            <artifactId>spring-security-crypto</artifactId>
            <groupId>org.springframework.security</groupId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-process-validation</artifactId>
    <version>${activiti.version}</version>
</dependency>
<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-rest</artifactId>
    <version>${activiti.version}</version>
    <exclusions>
        <exclusion>
            <artifactId>spring-security-core</artifactId>
            <groupId>org.springframework.security</groupId>
        </exclusion>
        <exclusion>
            <artifactId>spring-security-config</artifactId>
            <groupId>org.springframework.security</groupId>
        </exclusion>
        <exclusion>
            <artifactId>spring-security-crypto</artifactId>
            <groupId>org.springframework.security</groupId>
        </exclusion>
        <exclusion>
            <artifactId>spring-security-web</artifactId>
            <groupId>org.springframework.security</groupId>
        </exclusion>
        <exclusion>
            <artifactId>slf4j-log4j12</artifactId>
            <groupId>org.slf4j</groupId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-json-converter</artifactId>
    <version>${activiti.version}</version>
</dependency>
<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-image-generator</artifactId>
    <version>${activiti.version}</version>
</dependency>
<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-common-rest</artifactId>
    <version>${activiti.version}</version>
</dependency>
<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-bpmn-model</artifactId>
    <version>${activiti.version}</version>
</dependency>
<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-bpmn-converter</artifactId>
    <version>${activiti.version}</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.uuid</groupId>
    <artifactId>java-uuid-generator</artifactId>
    <version>3.1.4</version>
</dependency>

三、添加yml配置

activiti及数据库连接的配置

Activiti提供了history-level属性对其进行配置。history-level属性有点像log4j的日志输出级别,该属性有以下四个值:
none:不保存任何的历史数据,因此,在流程执行过程中,这是最高效的。
activity:级别高于none,保存流程实例与流程行为,其他数据不保存。
audit:除activity级别会保存的数据外,还会保存全部的流程任务及其属性。audit为history的默认值。
full:保存历史数据的最高级别,除了会保存audit级别的数据外,还会保存其他全部流程相关的细节数据,包括一些流程参数等。

spring:
  activiti:
    database-schema-update: true   #是否更新数据库
    check-process-definitions: false # 自动部署验证设置:true-开启(默认)、false-关闭
    history-level: full #history-level属性
    db-history-used: true #是否使用activti历史表
    db-identity-used: true #是否使用activti身份信息表
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    #druid连接池配置
    druid:
     driverClassName: com.mysql.jdbc.Driver
     url: jdbc:mysql://192.168.1.254:3306/zxhycloudnew2?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false
     username: root
     password: 111111
     initial-size: 10
     # 最大连接数
     max-active: 50
     #最小连接数
     min-idle: 10
     #获取连接等待超时时间
     max-wait: 5000
     pool-prepared-statements: true #是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
     max-pool-prepared-statement-per-connection-size: 20
     validation-query: SELECT 1 FROM DUAL
     validation-query-timeout: 20000
     test-on-borrow: false #申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
     test-on-return: false #归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
     test-while-idle: true #建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
     time-between-eviction-runs-millis: 60000 #配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
     min-evictable-idle-time-millis: 300000  #一个连接在池中最小生存的时间,单位是毫秒
     #StatViewServlet配置。(因为暴露的监控信息比较敏感,支持密码加密和访问ip限定)
     stat-view-servlet:
      enabled: true
      url-pattern: /druid/*
      #可以增加访问账号密码【去掉注释就可以】
      #login-username: admin
      #login-password: admin
     filter:
      stat:
        log-slow-sql: true
        slow-sql-millis: 1000
        merge-sql: true
      wall:
        config:
          multi-statement-allow: true

本datasource是DruidDataSource的,大家也可以按照自己的需要进行调整。

四、配置文件

添加java配置文件进行常用的几个service的配置
扫描包"org.activiti.rest.editor", "org.activiti.rest.diagram"下的controller组件,这个是web编辑器需要的。

package com.zxhy.config;

import org.activiti.engine.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;


@Configuration
@ComponentScan(basePackages = {"org.activiti.rest.editor", "org.activiti.rest.diagram"}, includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, value = org.springframework.web.bind.annotation.RestController.class))
public class ActivitiConfiguration {

    @Autowired
    private ProcessEngine processEngine;

    @Bean
    public ProcessEngineConfiguration processEngineConfiguration(ProcessEngine processEngine) {
        return processEngine.getProcessEngineConfiguration();
    }

    /**
     * RuntimeService
     *
     * @param processEngine
     * @return
     */
    @Bean
    public RuntimeService runtimeService(ProcessEngine processEngine) {
        return processEngine.getRuntimeService();
    }

    /**
     * TaskService
     *
     * @param processEngine
     * @return
     */
    @Bean
    public TaskService taskService(ProcessEngine processEngine) {
        return processEngine.getTaskService();
    }

    /**
     * RepositoryService
     *
     * @param processEngine
     * @return
     */
    @Bean
    public RepositoryService repositoryService(ProcessEngine processEngine) {
        return processEngine.getRepositoryService();
    }

    /**
     * HistoryService
     *
     * @param processEngine
     * @return
     */
    @Bean
    public HistoryService historyService(ProcessEngine processEngine) {
        return processEngine.getHistoryService();
    }

    /**
     * IdentityService
     *
     * @param processEngine
     * @return
     */
    @Bean
    public IdentityService identityService(ProcessEngine processEngine) {
        return processEngine.getIdentityService();
    }

    /**
     * FormService
     *
     * @param processEngine
     * @return
     */
    @Bean
    public FormService formService(ProcessEngine processEngine) {
        return processEngine.getFormService();
    }

    /**
     * ManagementService
     *
     * @param processEngine
     * @return
     */
    @Bean
    public ManagementService managementService(ProcessEngine processEngine) {
        return processEngine.getManagementService();
    }
}

五、添加工具类

package com.zxhy.activiti.utils;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.zxhy.base.common.Constant;
import com.zxhy.base.exception.MyException;
import com.zxhy.base.utils.SpringContextUtils;
import com.zxhy.base.utils.StringUtils;
import org.activiti.bpmn.model.*;
import org.activiti.bpmn.model.Process;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.impl.RepositoryServiceImpl;
import org.activiti.engine.impl.javax.el.ExpressionFactory;
import org.activiti.engine.impl.javax.el.ValueExpression;
import org.activiti.engine.impl.juel.ExpressionFactoryImpl;
import org.activiti.engine.impl.juel.SimpleContext;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.activiti.engine.impl.pvm.PvmActivity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.impl.pvm.process.ProcessDefinitionImpl;
import org.activiti.engine.impl.pvm.process.TransitionImpl;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.image.ProcessDiagramGenerator;
import org.activiti.engine.task.Task;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;

/**
 * 类的功能描述.
 * 流程工具类
 * @Auther zxhy
 * @Date 2017/7/28
 */
public class ActUtils {
    private static RepositoryService repositoryService = (RepositoryService) SpringContextUtils.getBean("repositoryService");
    private static HistoryService historyService = (HistoryService) SpringContextUtils.getBean("historyService");
    private static RuntimeService runtimeService = (RuntimeService) SpringContextUtils.getBean("runtimeService");
    private static TaskService taskService = (TaskService) SpringContextUtils.getBean("taskService");
    private static ObjectMapper objectMapper = (ObjectMapper) SpringContextUtils.getBean("objectMapper");
    private static ProcessEngineConfiguration processEngineConfiguration = (ProcessEngineConfiguration) SpringContextUtils.getBean("processEngineConfiguration");

    /**
     * 设置会签节点属性
     * 会签相关变量注释:nrOfInstances:实例总数
     *               nrOfActiveInstances:当前活动的,比如,还没完成的,实例数量。 对于顺序执行的多实例,值一直为1
     *               nrOfCompletedInstances:已经完成实例的数目
     *               可以通过execution.getVariable(x)方法获得这些变量
     * @param modelId 模型id
     * @param nodelId 流程对象id
     */
    public static void setMultiInstance(String modelId,String nodelId) throws Exception{
        //获取模型
        byte[] mes = repositoryService.getModelEditorSource(modelId);
        //转换成JsonNode
        JsonNode jsonNode = objectMapper.readTree(mes);
        //转换成BpmnModel
        BpmnJsonConverter bpmnJsonConverter = new BpmnJsonConverter();
        BpmnModel bpmnModel = bpmnJsonConverter.convertToBpmnModel(jsonNode);
        //获取物理形态的流程
        Process process = bpmnModel.getProcesses().get(0);
        //获取节点信息
        FlowElement flowElement = process.getFlowElement(nodelId);
        //只有人工任务才可以设置会签节点
        UserTask userTask = (UserTask)flowElement;
        //设置受理人,这里应该和ElementVariable的值是相同的
        userTask.setAssignee("${"+Constant.ACT_MUIT_VAR_NAME+"}");
        //userTask.setOwner("${user}");

        //获取多实例配置
        MultiInstanceLoopCharacteristics characteristics = new MultiInstanceLoopCharacteristics();
        //设置集合变量,统一设置成users
        characteristics.setInputDataItem(Constant.ACT_MUIT_LIST_NAME);
        //设置变量
        characteristics.setElementVariable(Constant.ACT_MUIT_VAR_NAME);
        //设置为同时接收(false 表示不按顺序执行)
        characteristics.setSequential(false);
        //设置条件(暂时处理成,全部会签完转下步)
        characteristics.setCompletionCondition("${nrOfCompletedInstances==nrOfInstances}");

        userTask.setLoopCharacteristics(characteristics);
        //保存
        ObjectNode objectNode = new BpmnJsonConverter().convertToJson(bpmnModel);
        repositoryService.addModelEditorSource(modelId, objectNode.toString().getBytes("utf-8"));
    }

    /**
     * 清空会签属性
     * @param modelId 模型id
     * @param nodelId 流程对象id
     * @throws Exception
     */
    public static void clearMultiInstance(String modelId,String nodelId) throws Exception{
        //获取模型
        byte[] mes = repositoryService.getModelEditorSource(modelId);
        //转换成JsonNode
        JsonNode jsonNode = new ObjectMapper().readTree(mes);
        //转换成BpmnModel
        BpmnJsonConverter bpmnJsonConverter = new BpmnJsonConverter();
        BpmnModel bpmnModel = bpmnJsonConverter.convertToBpmnModel(jsonNode);
        //获取物理形态的流程
        Process process = bpmnModel.getProcesses().get(0);
        //获取节点信息
        FlowElement flowElement = process.getFlowElement(nodelId);
        //只有人工任务才可以设置会签节点
        UserTask userTask = (UserTask)flowElement;
        //清空受理人
        userTask.setAssignee("");
        //获取多实例配置
        MultiInstanceLoopCharacteristics characteristics = userTask.getLoopCharacteristics();
        if(characteristics!=null){
            //清空集合
            characteristics.setInputDataItem("");
            //清空变量
            characteristics.setElementVariable("");
            //设置为顺序接收(true 表示不按顺序执行)
            characteristics.setSequential(true);
            //清空条件
            characteristics.setCompletionCondition("");
        }

        //保存
        ObjectNode objectNode = new BpmnJsonConverter().convertToJson(bpmnModel);
        repositoryService.addModelEditorSource(modelId, objectNode.toString().getBytes("utf-8"));
    }

    /**
     * 增加流程连线条件
     * @param modelId 模型id
     * @param nodelId 流程对象id
     * @param condition el 条件表达式
     */
    public static void setSequenceFlowCondition(String modelId,String nodelId ,String condition) throws IOException {
        //获取模型--设置连线条件 到 流程中
        byte[] bytes = repositoryService.getModelEditorSource(modelId);
        JsonNode jsonNode = objectMapper.readTree(bytes);
        BpmnModel bpmnModel = new BpmnJsonConverter().convertToBpmnModel(jsonNode);
        FlowElement flowElement = bpmnModel.getFlowElement(nodelId);
        if(!(flowElement instanceof SequenceFlow)){
            throw new MyException("不是连线,不能设置条件");
        }
        SequenceFlow sequenceFlow = (SequenceFlow)flowElement;
        sequenceFlow.setConditionExpression(condition);
        ObjectNode objectNode = new BpmnJsonConverter().convertToJson(bpmnModel);
        repositoryService.addModelEditorSource(modelId, objectNode.toString().getBytes("utf-8"));
    }

    /**
     * 根据流程实例Id,获取实时流程图片
     * @param processInstanceId
     * @return
     */
    public static InputStream getFlowImgByInstantId(String processInstanceId){
        if(StringUtils.isEmpty(processInstanceId)){
            return null;
        }
        //获取流程图输入流
        InputStream inputStream = null;
        // 查询历史
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        if (historicProcessInstance.getEndTime() != null) { // 该流程已经结束
            ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(historicProcessInstance.getProcessDefinitionId()).singleResult();
            inputStream = repositoryService.getResourceAsStream( processDefinition.getDeploymentId(), processDefinition.getDiagramResourceName());
        } else {
            // 查询当前的流程实例
            ProcessInstance processInstance = runtimeService .createProcessInstanceQuery() .processInstanceId(processInstanceId).singleResult();
            BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
            ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity) repositoryService.createProcessDefinitionQuery().processDefinitionId(processInstance.getProcessDefinitionId()).singleResult();
            List<String> highLightedFlows = new ArrayList<String>();
            List<HistoricActivityInstance> historicActivityInstances = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).orderByHistoricActivityInstanceStartTime().asc().list();
            List<String> historicActivityInstanceList = new ArrayList<String>();
            for (HistoricActivityInstance hai : historicActivityInstances) {
                historicActivityInstanceList.add(hai.getActivityId());
            }
            List<String> highLightedActivities = runtimeService.getActiveActivityIds(processInstanceId);
            historicActivityInstanceList.addAll(highLightedActivities);
            for (ActivityImpl activity : processDefinitionEntity.getActivities()) {
                int index = historicActivityInstanceList.indexOf(activity.getId());
                if (index >= 0 && index + 1 < historicActivityInstanceList.size()) {
                    List<PvmTransition> pvmTransitionList = activity.getOutgoingTransitions();
                    for (PvmTransition pvmTransition : pvmTransitionList) {
                        String destinationFlowId = pvmTransition.getDestination().getId();
                        if (destinationFlowId.equals(historicActivityInstanceList.get(index + 1))) {
                            highLightedFlows.add(pvmTransition.getId());
                        }
                    }
                }
            }
            ProcessDiagramGenerator diagramGenerator = processEngineConfiguration.getProcessDiagramGenerator();
            List<String> activeActivityIds = new ArrayList<String>();
            List<org.activiti.engine.task.Task> tasks = taskService.createTaskQuery().processInstanceId(processInstanceId).list();
            for (org.activiti.engine.task.Task task : tasks) {
                activeActivityIds.add(task.getTaskDefinitionKey());
            }
            inputStream = diagramGenerator.generateDiagram(bpmnModel, "png", activeActivityIds, highLightedFlows, "宋体", "宋体", null,null, 1.0);
        }
        return inputStream;
    }

    /**
     * 根据节点Id取得当前节点的下一流向流程节点,如果Id为空则默认为首节点(条件路由则只返回符合条件的流向)
     * @param defid 流程定义Id
     * @param nodelId 流程节点
     * @param elMap 流程变量el表达式集合
     * @return
     */
    public static List<PvmActivity> getNextActNodes(String defid, String nodelId, Map<String,Object> elMap){
        List<PvmActivity> pvmActivities=new ArrayList<PvmActivity>();
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(defid).singleResult();
        ProcessDefinitionEntity def = (ProcessDefinitionEntity) ((RepositoryServiceImpl)repositoryService).getDeployedProcessDefinition(processDefinition.getId());
        //获取所有节点
        List<ActivityImpl> activitiList = def.getActivities();
        //然后循环activitiList 并判断出当前流程所处节点,然后得到当前节点实例,根据节点实例获取所有从当前节点出发的路径,然后根据路径获得下一个节点实例:
        for(ActivityImpl activityImpl:activitiList){
            String id = activityImpl.getId();
            if("".equals(nodelId)){
                Object o = activityImpl.getProperties().get("type");
                if(o.equals("startEvent")){
                    //startEvent节点
                    PvmTransition startEvent = activityImpl.getOutgoingTransitions().get(0);
                    List<PvmTransition> pvmTransitions = startEvent.getDestination().getOutgoingTransitions();
                    pvmActivities=getNextActivities(pvmTransitions, elMap);
                    return pvmActivities;
                }
            }else{
                if(nodelId.equals(id)){
                    List<PvmTransition> pvmTransitions=activityImpl.getOutgoingTransitions();
                    pvmActivities=getNextActivities(pvmTransitions, elMap);
                    return pvmActivities;
                }
            }

        }
        return pvmActivities;
    }

    /**
     * 各种情况的下级节点
     * @param pvmTransitions 流向 列表
     * @param elMap 分支节点变量
     * @return
     */
    public static List<PvmActivity> getNextActivities(List<PvmTransition> pvmTransitions,Map<String,Object> elMap){
        List<PvmActivity> pvmActivities=new ArrayList<PvmActivity>();
        for(PvmTransition p:pvmTransitions){
            PvmActivity pvmActivity = p.getDestination();
            String type=(String)pvmActivity.getProperty("type");
            if("userTask".equals(type)){
                //当前接点下一流向为userTask,则加入下一流向
                pvmActivities.add(pvmActivity);
            }else if ("exclusiveGateway".equals(type)){
                List<PvmTransition> outgoingTransitions = pvmActivity.getOutgoingTransitions();
                for(PvmTransition tr1:outgoingTransitions){
                    //获取分支节点流向中的判断el表达式
                    String conditionText=(String)tr1.getProperty("conditionText");
                    //进行解析el表达式
                    ExpressionFactory factory = new ExpressionFactoryImpl();
                    SimpleContext context = new SimpleContext();
                    for(String key:elMap.keySet()){
                        if(conditionText.indexOf(key) != -1){
                            context.setVariable(key, factory.createValueExpression(elMap.get(key), String.class));
                        }
                    }
                    ValueExpression e = factory.createValueExpression(context, conditionText, boolean.class);
                    //判断该流向是否符合传入的参数条件
                    if((Boolean)e.getValue(context)){
                        pvmActivities.add(tr1.getDestination());
                        break;
                    }
                }

            } else if ("parallelGateWay".equals(type)) {
                //并行路由
                List<PvmTransition> outgoingTransitions = pvmActivity.getOutgoingTransitions();
                for(PvmTransition tr2:outgoingTransitions){
                    pvmActivities.add(tr2.getDestination());
                }
            }else if ("endEvent".equals(type)){
                //结束
                pvmActivities.add(pvmActivity);
            }
        }
        return pvmActivities;
    }

    /**
     * 根据流向,取下级所有流向
     * @param pvmTransitions 流向 列表
     * @return
     */
    public static List<PvmTransition> getNextActivities(List<PvmTransition> pvmTransitions){
        List<PvmTransition> pvmActivities = new ArrayList<>();
        for(PvmTransition p:pvmTransitions){
            PvmActivity pvmActivity = p.getDestination();
            String type=(String)pvmActivity.getProperty("type");
            if("userTask".equals(type)){
                pvmActivities.add(p);
            }else{
                //条件分支
                if("exclusiveGateway".equals(type)){
                    List<PvmTransition> outgoingTransitions = pvmActivity.getOutgoingTransitions();
                    pvmActivities.addAll(outgoingTransitions);
                }else{
                    //并行路由
                    List<PvmTransition> outgoingTransitions = pvmActivity.getOutgoingTransitions();
                    pvmActivities.addAll(outgoingTransitions);
                }
            }
        }
        return pvmActivities;
    }

    /***
     * 根据节点id获取下一流向(也就是线)
     * @param defId 流程定义id
     * @param nodelId 节点id
     */
    public static List<PvmTransition> getNextPvmTransitions(String defId,String nodelId){
        List<PvmTransition> pvmTransitions = new ArrayList<>();
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(defId).singleResult();
        ProcessDefinitionEntity def = (ProcessDefinitionEntity) ((RepositoryServiceImpl)repositoryService).getDeployedProcessDefinition(processDefinition.getId());
        //获取所有节点
        List<ActivityImpl> activitiList = def.getActivities();
        //然后循环activitiList 并判断出当前流程所处节点,然后得到当前节点实例,根据节点实例获取所有从当前节点出发的路径,然后根据路径获得下一个节点实例:
        for(ActivityImpl activityImpl:activitiList){
            String id = activityImpl.getId();
            if(nodelId.equals(id)){
                pvmTransitions=activityImpl.getOutgoingTransitions();
                pvmTransitions = getNextActivities(pvmTransitions);
            }
        }
        return pvmTransitions;
    }

    /*************************回退开始***************************/

    /**
     * 根据任务ID获取对应的流程实例
     *
     * @param taskId 任务ID
     * @return
     * @throws Exception
     */
    public static ProcessInstance findProcessInstanceByTaskId(String taskId)
            throws Exception {
        // 找到流程实例
        ProcessInstance processInstance = runtimeService
                .createProcessInstanceQuery().processInstanceId(
                        findTaskById(taskId).getProcessInstanceId())
                .singleResult();
        if (processInstance == null) {
            throw new MyException("流程实例未找到!");
        }
        return processInstance;
    }

    /**
     * 根据任务ID获得任务实例
     * @param taskId 任务ID
     * @return TaskEntity
     * @throws Exception
     */
    private static TaskEntity findTaskById(String taskId) throws Exception {
        TaskEntity task = (TaskEntity) taskService.createTaskQuery().taskId(
                taskId).singleResult();
        if (task == null) {
            throw new MyException("任务实例未找到!");
        }
        return task;
    }

    /**
     * 设置任务处理人,根据任务ID
     * @param taskId
     * @param userCode
     */
    public static void setTaskDealerByTaskId(String taskId,String userCode){
        taskService.setAssignee(taskId, userCode);
    }

    /**
     * 根据流程对象Id,查询当前节点Id
     * @param executionId
     * @return
     */
    public static String getActiviIdByExecutionId(String executionId){
        //根据任务获取当前流程执行ID,执行实例以及当前流程节点的ID:
        ExecutionEntity execution = (ExecutionEntity) runtimeService.createExecutionQuery().executionId(executionId).singleResult();
        String activitiId = execution.getActivityId();
        return activitiId;
    }

    /**
     * 根据流程实例ID和任务key值查询所有同级任务集合
     *
     * @param processInstanceId
     * @param key
     * @return
     */
    public static List<Task> findTaskListByKey(String processInstanceId, String key) {
        List<Task> list = taskService.createTaskQuery().processInstanceId(processInstanceId).taskDefinitionKey(key).list();
        return list;
    }

    /**
     * 根据任务ID和节点ID获取活动节点 <br>
     *
     * @param taskId     任务ID
     * @param activityId 活动节点ID <br>
     *                   如果为null或"",则默认查询当前活动节点 <br>
     *                   如果为"end",则查询结束节点 <br>
     * @return
     * @throws Exception
     */
    public static ActivityImpl findActivitiImpl(String taskId, String activityId)
            throws Exception {
        // 取得流程定义
        ProcessDefinitionEntity processDefinition = findProcessDefinitionEntityByTaskId(taskId);
        // 获取当前活动节点ID
        if (StringUtils.isEmpty(activityId)) {
            activityId = findTaskById(taskId).getTaskDefinitionKey();
        }
        // 根据流程定义,获取该流程实例的结束节点
        if (activityId.toUpperCase().equals("END")) {
            for (ActivityImpl activityImpl : processDefinition.getActivities()) {
                String type =(String) activityImpl.getProperty("type");
                if(type.equals("endEvent")){
                    return activityImpl;
                }
            }
        }

        // 根据节点ID,获取对应的活动节点
        ActivityImpl activityImpl = ((ProcessDefinitionImpl) processDefinition)
                .findActivity(activityId);

        return activityImpl;
    }
    /**
     * 根据任务ID获取流程定义
     *
     * @param taskId 任务ID
     * @return
     * @throws Exception
     */
    public static ProcessDefinitionEntity findProcessDefinitionEntityByTaskId(
            String taskId) throws Exception {
        // 取得流程定义
        ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService)
                .getDeployedProcessDefinition(findTaskById(taskId)
                        .getProcessDefinitionId());

        if (processDefinition == null) {
            throw new Exception("流程定义未找到!");
        }

        return processDefinition;
    }



    /**
     * 根据任务Id,查找当前任务
     * @param taskId 任务Id
     * @return
     */
    public static Task getTaskById(String taskId){
        //当前处理人的任务
        Task currenTask = taskService.createTaskQuery().taskId(taskId).singleResult();
        return currenTask;
    }

    /**
     * 根据流程实例查询正在执行的任务
     * @param processInst
     * @return
     */
    public static List<Task> getTaskByProcessInst(String processInst){
        List<Task> list = taskService.createTaskQuery().processInstanceId(processInst).list();
        return list;
    }

    /**
     * 根据activitiId,流程key获取当前节点
     * @param processDefinitionKey
     * @return
     */
    public static ActivityImpl getCurrenActivitiy(String processDefinitionKey,String activitiId){
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey(processDefinitionKey).latestVersion().singleResult();
        ProcessDefinitionEntity def = (ProcessDefinitionEntity) ((RepositoryServiceImpl)repositoryService).getDeployedProcessDefinition(processDefinition.getId());
        List<ActivityImpl> activitiList = def.getActivities();
        for (ActivityImpl activity:activitiList){
            if(activitiId.equals(activity.getId())){
                return activity;
            }
        }
        return null;
    }

    /**
     * 驳回流程
     * @param taskId     当前任务ID
     * @param activityId 驳回节点ID
     * @param variables  流程存储参数
     * @throws Exception
     */
    public static void backProcess(String taskId, String activityId,
                                   Map<String, Object> variables,String comment) throws Exception {
        Map<String,String> map =new HashMap<>();
        if (StringUtils.isEmpty(activityId)) {
            throw new MyException("驳回目标节点ID为空!");
        }
        if (StringUtils.isEmpty(activityId)) {
            throw new MyException("任务iD为空!");
        }
        //根据任务ID获取对应的流程实例
        ProcessInstance processInstance = findProcessInstanceByTaskId(taskId);
        //根据任务ID获得任务实例
        TaskEntity taskById = findTaskById(taskId);
        String taskDefinitionKey = taskById.getTaskDefinitionKey();
        map.put(taskDefinitionKey,taskDefinitionKey);
        // 查找所有并行任务节点,同时驳回
        Map<String, Object> parallelGateways = getParallelGateways(taskId);
        //并行开始所有结点
        List<PvmTransition> parallelStart=(List<PvmTransition>)parallelGateways.get("pStart");
        //并行结束所有结点
        List<PvmTransition> parallelEnd=(List<PvmTransition>)parallelGateways.get("pEnd");
        //并行节点开始
        if(parallelStart !=null && parallelStart.size()>0){
            List<PvmActivity> pvmActivities=new ArrayList<PvmActivity>();
            for (PvmTransition pvmTransition:parallelStart){
                PvmActivity destination = pvmTransition.getDestination();
                map.put(destination.getId(),destination.getId());
                pvmActivities.add(destination);
            }
            startParallelGateways(map,processInstance,variables,activityId,comment);
        }else if (parallelEnd !=null && parallelEnd.size()>0){
            //并行节点结束
            List<String> list = new ArrayList<String>();
            for (PvmTransition pvmTransition:parallelEnd){
                PvmActivity destination = pvmTransition.getSource();
                list.add(destination.getId());
            }
            endParallelGateways(map,processInstance,variables,list,comment);
        }else{
            //一般处理
            comment(map,processInstance,variables,activityId,comment);
        }
    }

    /**
     * 根据当前任务ID,查询并行路由的分支
     * @param taskId
     * @return
     */
    public static Map<String,Object> getParallelGateways(String taskId){
        Map<String,Object> map =new HashMap<String, Object>();
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        //然后根据当前任务获取当前流程的流程定义,然后根据流程定义获得所有的节点:
        ProcessDefinitionEntity def = (ProcessDefinitionEntity) ((RepositoryServiceImpl)repositoryService).getDeployedProcessDefinition(task.getProcessDefinitionId());
        List<ActivityImpl> activitiList = def.getActivities();
        //根据任务获取当前流程执行ID,执行实例以及当前流程节点的ID:
        String excId = task.getExecutionId();
        String activitiId=getActiviIdByExecutionId(excId);
        //然后循环activitiList 并判断出当前流程所处节点,然后得到当前节点实例,根据节点实例获取所有从当前节点出发的路径,然后根据路径获得下一个节点实例:
        for(ActivityImpl activityImpl:activitiList){
            String id = activityImpl.getId();
            if(activitiId.equals(id)){
                System.out.println("当前任务:"+activityImpl.getProperty("name")); //输出某个节点的某种属性
                List<PvmTransition> outTransitions = activityImpl.getIncomingTransitions();//获取从某个节点出来的所有线路
                List<PvmTransition> inTransitions = activityImpl.getIncomingTransitions();//获取从某个节点流进的所有线路
                //并行开始
                for(PvmTransition tr:outTransitions){
                    PvmActivity ac = tr.getSource(); //获取线路的终点节点
                    Object o = ac.getProperty("type");
                    if("parallelGateway".equals(o)){
                        String gatewayId = ac.getId();
                        String gatewayType = gatewayId.substring(gatewayId
                                .lastIndexOf("_") + 1);
                        if ("START".equals(gatewayType.toUpperCase())) {
                            List<PvmTransition> outgoingTransitions = ac.getOutgoingTransitions();
                            List<PvmTransition> pvmTransitions=new ArrayList<PvmTransition>();
                            outgoingTransitions = forCheckQueryPvm(outgoingTransitions, pvmTransitions);
                            map.put("pStart",outgoingTransitions);
                        }
                    }
                }
                //并行结束
                for(PvmTransition tr:inTransitions){
                    PvmActivity ac = tr.getSource(); //获取线路的终点节点
                    Object o = ac.getProperty("type");
                    if("parallelGateway".equals(o)){
                        String gatewayId = ac.getId();
                        String gatewayType = gatewayId.substring(gatewayId
                                .lastIndexOf("_") + 1);
                        if ("END".equals(gatewayType.toUpperCase())) {
                            List<PvmTransition> ingoingTransitions = ac.getIncomingTransitions();
                            map.put("pStart",ingoingTransitions);
                        }
                    }
                }
                break;
            }
        }
        return map;
    }

    /**
     * 并行路由开始,查询所有正在执行并行任务的节点
     * @param pvmTransitions
     * @param pvmTransitionList
     * @return
     */
    private static List<PvmTransition> forCheckQueryPvm(List<PvmTransition> pvmTransitions,List<PvmTransition> pvmTransitionList){
        for (PvmTransition pvmTransition:pvmTransitions){
            PvmActivity destination = pvmTransition.getDestination();
            String activitid = destination.getId();
            String processDef = destination.getProcessDefinition().getId();
            HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery().taskDefinitionKey(activitid).processDefinitionId(processDef)
                    .orderByTaskCreateTime().desc().list().get(0);
            if("completed".equals(historicTaskInstance.getDeleteReason())){
                List<PvmTransition> outgoingTransitions = pvmTransition.getDestination().getOutgoingTransitions();
                for(PvmTransition p:outgoingTransitions){
                    Object type = p.getDestination().getProperty("type");
                    if("parallelGateway".equals(type)){
                        continue;
                    }else {
                        forCheckQueryPvm(outgoingTransitions,pvmTransitionList);
                    }
                }
            }else {
                pvmTransitionList.add(pvmTransition);
                continue;
            }
        }
        return pvmTransitionList;
    }

    /**
     * 并行开始回退处理
     * @param map 要回退的节点Id集合
     * @param processInstance 流程实例
     * @param variables
     * @param activityId
     * @throws Exception
     */
    private static void startParallelGateways(Map map,ProcessInstance processInstance,Map<String, Object> variables,String activityId,String comment) throws Exception {
        List<Task> taskList;
        Iterator it=map.keySet().iterator();
        int i=0;
        while (it.hasNext()){
            String id = (String) it.next();
            taskList = findTaskListByKey(processInstance.getId(), id);
            //第一次回退,正常回退
            if(i==0){
                for (Task task : taskList) {
                    commitProcess(task.getId(), variables, activityId,comment);
                }
            }
            //多次回退,就只完成任务,不更改流向,不设置回退任务处理人
            if(i>0){
                for (Task task : taskList) {
                    parallelgateway_end(task.getId(),variables);
                }
            }
            i++;
        }
    }

    /**
     * 并行开始返回,只执行任务,不做流向转向处理
     *
     * @param taskId     当前任务ID
     * @param variables  流程变量
     * @throws Exception
     */
    private static void parallelgateway_end(String taskId,  Map<String, Object> variables ) throws Exception {
        // 当前节点
        ActivityImpl currActivity = findActivitiImpl(taskId, null);
        // 清空当前流向
        List<PvmTransition> oriPvmTransitionList = clearTransition(currActivity);
        //当前任务
        Task task = getTaskById(taskId);
        task.setDescription("callback");
        taskService.saveTask(task);
        taskService.complete(taskId, variables);
        // 还原以前流向
        restoreTransition(currActivity, oriPvmTransitionList);
    }

    /**
     *并行结束后退处理
     * @param map 要回退的节点Id
     * @param processInstance
     * @param variables
     * @param list
     * @throws Exception
     */
    private static void endParallelGateways(Map map,ProcessInstance processInstance,Map<String, Object> variables,List<String> list,String comment) throws Exception {
        List<Task> taskList = new ArrayList<Task>();
        Iterator it=map.keySet().iterator();
        while (it.hasNext()){
            String id = (String) it.next();
            taskList = findTaskListByKey(processInstance.getId(), id);
            for (Task task : taskList) {
                commitProcess(task.getId(), variables, list,comment);
            }
        }
    }

    /**
     * 一般式的流程回退处理
     * @param map
     * @param processInstance
     * @param variables
     * @param activityId
     * @throws Exception
     */
    private static void comment(Map map,ProcessInstance processInstance,Map<String, Object> variables,String activityId,String comment) throws Exception {
        List<Task> taskList = new ArrayList<Task>();
        Iterator it=map.keySet().iterator();
        while (it.hasNext()){
            String id = (String) it.next();
            taskList = findTaskListByKey(processInstance.getId(), id);
            for (Task task : taskList) {
                commitProcess(task.getId(), variables, activityId,comment);
            }
        }
    }

    /**
     * @param taskId     当前任务ID
     * @param variables  流程变量
     * @param activityId 流程转向执行任务节点ID<br>
     *                   此参数为空,默认为提交操作
     * @throws Exception
     */
    public static void commitProcess(String taskId, Map<String, Object> variables,
                                     String activityId,String comment) throws Exception {
        if (variables == null) {
            variables = new HashMap<String, Object>();
        }
        // 跳转节点为空,默认提交操作
        if (StringUtils.isEmpty(activityId)) {
            taskService.complete(taskId, variables);
        } else {// 流程转向操作
            turnTransition(taskId, activityId, variables,comment);
        }
    }
    /**
     * @param taskId     当前任务ID
     * @param variables  流程变量
     * @param activityIds 流程转向执行任务节点ID<br>
     *                   此参数为空,默认为提交操作
     * @throws Exception
     */
    private static void commitProcess(String taskId, Map<String, Object> variables,
                                      List<String> activityIds,String comment) throws Exception {
        if (variables == null) {
            variables = new HashMap<String, Object>();
        }
        // 跳转节点为空,默认提交操作
        if (StringUtils.isEmpty(activityIds)) {
            taskService.complete(taskId, variables);
        } else {// 流程转向操作
            turnTransition(taskId, activityIds, variables,comment);
        }
    }

    /**
     * 流程转向操作
     *
     * @param taskId     当前任务ID
     * @param activityIds 目标节点任务ID
     * @param variables  流程变量
     * @throws Exception
     */
    private static void turnTransition(String taskId, List<String> activityIds, Map<String, Object> variables ,String comment) throws Exception {
        // 当前节点
        ActivityImpl currActivity = findActivitiImpl(taskId, null);
        // 清空当前流向
        List<PvmTransition> oriPvmTransitionList = clearTransition(currActivity);

        List<TransitionImpl> transitions=new ArrayList<TransitionImpl>();
        List<ActivityImpl> pointActivitys=new ArrayList<ActivityImpl>();
        for(String activityId:activityIds){
            // 创建新流向
            TransitionImpl newTransition = currActivity.createOutgoingTransition();
            // 目标节点
            ActivityImpl pointActivity = findActivitiImpl(taskId, activityId);
            // 设置新流向的目标节点
            newTransition.setDestination(pointActivity);
            transitions.add(newTransition);
            pointActivitys.add(pointActivity);
        }
        // 执行转向任务
        Task currenTask = taskService.createTaskQuery().taskId(taskId).singleResult();
        taskService.addComment(taskId, currenTask.getProcessInstanceId(), comment);
        taskService.complete(taskId, variables);
        // 删除目标节点新流入
        for (int i =0;i<transitions.size();i++){
            pointActivitys.get(i).getIncomingTransitions().remove(transitions.get(i));
        }

        // 还原以前流向
        restoreTransition(currActivity, oriPvmTransitionList);
    }

    /**
     * 流程转向操作
     *
     * @param taskId     当前任务ID
     * @param activityId 目标节点任务ID
     * @param variables  流程变量
     * @throws Exception
     */
    public static void turnTransition(String taskId, String activityId, Map<String, Object> variables ,String comment) throws Exception {
        // 当前节点
        ActivityImpl currActivity = findActivitiImpl(taskId, null);
        // 清空当前流向
        List<PvmTransition> oriPvmTransitionList = clearTransition(currActivity);

        // 创建新流向
        TransitionImpl newTransition = currActivity.createOutgoingTransition();
        // 目标节点
        ActivityImpl pointActivity = findActivitiImpl(taskId, activityId);
        // 设置新流向的目标节点
        newTransition.setDestination(pointActivity);

        //查询当前任务
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if(comment==null){
            comment="";
        }
        taskService.addComment(taskId, task.getProcessInstanceId(), comment);
        // 执行转向任务
        task.setDescription("callback");
        taskService.saveTask(task);
        taskService.complete(taskId, variables);

        //顺序会签处理
        dealMultiSequential(task.getProcessInstanceId(), comment, task.getTaskDefinitionKey());
        //设置回退的任务处理人
        setBackTaskDealer(task,activityId);
        // 删除目标节点新流入
        pointActivity.getIncomingTransitions().remove(newTransition);
        // 还原以前流向
        restoreTransition(currActivity, oriPvmTransitionList);
    }

    /**
     * 清空指定活动节点流向
     *
     * @param activityImpl 活动节点
     * @return 节点流向集合
     */
    private static List<PvmTransition> clearTransition(ActivityImpl activityImpl) {
        // 存储当前节点所有流向临时变量
        List<PvmTransition> oriPvmTransitionList = new ArrayList<PvmTransition>();
        // 获取当前节点所有流向,存储到临时变量,然后清空
        List<PvmTransition> pvmTransitionList = activityImpl
                .getOutgoingTransitions();
        for (PvmTransition pvmTransition : pvmTransitionList) {
            oriPvmTransitionList.add(pvmTransition);
        }
        pvmTransitionList.clear();

        return oriPvmTransitionList;
    }

    /**
     * 还原指定活动节点流向
     *
     * @param activityImpl         活动节点
     * @param oriPvmTransitionList 原有节点流向集合
     */
    private static void restoreTransition(ActivityImpl activityImpl,
                                          List<PvmTransition> oriPvmTransitionList) {
        // 清空现有流向
        List<PvmTransition> pvmTransitionList = activityImpl
                .getOutgoingTransitions();
        pvmTransitionList.clear();
        // 还原以前流向
        for (PvmTransition pvmTransition : oriPvmTransitionList) {
            pvmTransitionList.add(pvmTransition);
        }
    }

    /**
     * 顺序会签后退时处理
     * @param instanceId 流程实例
     * @param comment 意见
     * @param preActivityId 上个已经完成任务ID
     */
    public static void dealMultiSequential(String instanceId,String comment,String preActivityId){
        //该流程实例正在运行的任务
        List<Task> runTask = getTaskByProcessInst(instanceId);
        for(Task t:runTask){
            String runActivityId=t.getTaskDefinitionKey();
            //正在运行的任务节点id和上个已经完成的任务节点id相等,则判定为顺序会签
            if(runActivityId.equals(preActivityId)){
                if(comment==null){
                    comment="";
                }
                taskService.addComment(t.getId(), t.getProcessInstanceId(), comment);
                // 执行转向任务
                t.setDescription("callback");
                taskService.saveTask(t);
                taskService.complete(t.getId());
                //递归顺序会签,直到正在运行的任务还是顺序会签
                dealMultiSequential(t.getProcessInstanceId(), comment, t.getTaskDefinitionKey());
            }
        }
    }

    /**
     * 设置回退的任务处理人
     * @param task 当前任务
     * @param activityId 回退节点ID
     */
    public static void setBackTaskDealer(Task task,String activityId){
        List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery().processInstanceId(task.getProcessInstanceId())
                .taskDefinitionKey(activityId).taskDeleteReason("completed").orderByTaskCreateTime().desc().list();
        HistoricTaskInstance historicTask =null;
        if(list != null && list.size()>0){
            historicTask=list.get(0);
            //查询回退后的节点正在运行的任务
            List<Task> taskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId())
                    .taskDefinitionKey(activityId).active().list();
            //同一节点下有多个任务,则认定为会签任务
            if(taskList != null && taskList.size()>1){
                for(int i=0;i<taskList.size();i++){
                    //设置会签任务处理人(处理人顺序不管)
                    taskService.setAssignee(taskList.get(i).getId(),list.get(i).getAssignee());
                }
            }else{
                Task taskNew = taskList.get(0);
                //顺序会签流程变量处理人
                String variable =(String) runtimeService.getVariable(taskNew.getExecutionId(), "countersign");
                if(!StringUtils.isEmpty(variable)){
                    //设置下个顺序会签处理人
                    setTaskDealerByTaskId(taskNew.getId(), variable);
                }else{
                    //设置一般回退任务处理人
                    taskService.setAssignee(taskNew.getId(), historicTask.getAssignee());
                }
            }
        }
    }

    /*************************回退结束***************************/

    /**
     * (取上一个任务节点)
     * 根据节点Id取得当前节点的上一流向,分支路由结束点,需要查询历史任务出现过的分支结点,并行路由则随便取一个
     * @param processDefinitionKey 流程定义Key
     * @param processInstanceId 流程实例ID
     * @param preNodeId 上个节点id
     * @return
     */
    public static String getProActivityId(String processDefinitionKey,String activitiId,String processInstanceId,String preNodeId){
        List<PvmActivity> pvmActivities=new ArrayList<PvmActivity>();
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey(processDefinitionKey).latestVersion().singleResult();
        ProcessDefinitionEntity def = (ProcessDefinitionEntity) ((RepositoryServiceImpl)repositoryService).getDeployedProcessDefinition(processDefinition.getId());
        List<ActivityImpl> activitiList = def.getActivities();
        //然后循环activitiList 并判断出当前流程所处节点,然后得到当前节点实例,根据节点实例获取所有从当前节点出发的路径,然后根据路径获得下一个节点实例:
        for(ActivityImpl activityImpl:activitiList){
            String id = activityImpl.getId();
            if(!StringUtils.isEmpty(activitiId)){
                if(activitiId.equals(id)){
                    List<PvmTransition> pvmTransitions = activityImpl.getIncomingTransitions();
                    for(PvmTransition tr:pvmTransitions){
                        PvmActivity ac = tr.getSource();
                        Object type = ac.getProperty("type");
                        if("userTask".equals(type)){
                            preNodeId=ac.getId();
                            return preNodeId;
                        }else if("exclusiveGateway".equals(type)){
                            PvmActivity exclusiveActiviti = getExclusiveActiviti(ac,processDefinitionKey,processInstanceId);
                            if(exclusiveActiviti !=null){
                                preNodeId=exclusiveActiviti.getId();
                                return preNodeId;
                            }
                        }else{
                            //其它情况则进行递归 并行路由
                            preNodeId = getProActivityId(processDefinitionKey,ac.getId(),processInstanceId,preNodeId);
                        }
                    }
                }
            }
        }
        return preNodeId;
    }

    /**
     * 分支路由结束点,需要查询历史任务出现过的分支结点(取上一个任务节点)
     * @param ac exclusiveGateway节点
     * @param processDefinitionKey
     * @param processInstanceId
     * @return
     */
    public static PvmActivity getExclusiveActiviti(PvmActivity ac,String processDefinitionKey,String processInstanceId){
        //上一个节点为条件路由,则查询任务历史,查询历史最近走过的分支节点
        List<PvmTransition> exclusiveEnds = ac.getIncomingTransitions();
        //保存分支路由所有节点的最新记录
        List<HistoricTaskInstance> taskList=new ArrayList<HistoricTaskInstance>();
        //条件路由结束
        for(PvmTransition p:exclusiveEnds){
            PvmActivity pvmActivity = p.getSource();
            //根据分支路由的节点ID,查询历史任务中最新记录的节点ID
            HistoricTaskInstance taskInstance = historyService.createHistoricTaskInstanceQuery().processDefinitionKey(processDefinitionKey)
                    .processInstanceId(processInstanceId).taskDefinitionKey(pvmActivity.getId()).orderByTaskCreateTime().desc().list().get(0);
            //有记录
            if(taskInstance != null && !StringUtils.isEmpty(taskInstance.getId())){
                taskList.add(taskInstance);
            }
        }

        if(taskList !=null && taskList.size()>1){
            //循环判断那个节点是最新操作的节点  按时间从大
            for(int i=0;i<taskList.size();i++){
                for(int j=i;j<taskList.size();j++){
                    Date d1 = taskList.get(i).getStartTime();
                    Date d2 = taskList.get(j).getStartTime();
                    if(d1.before(d2)){
                        HistoricTaskInstance temp;
                        temp=taskList.get(i);
                        taskList.set(i, taskList.get(j));
                        taskList.set(j, temp);
                    }
                }
            }
        }
        //最新分支节点
        String activityId=taskList.get(0).getTaskDefinitionKey();
        return getCurrenActivitiy(processDefinitionKey, activityId);
    }

    /**
     * 转办流程
     * @param taskId  当前任务节点ID
     * @param userId 被转办人id
     */
    public static boolean transferAssignee(String taskId, String userId) {
        try{
            taskService.setAssignee(taskId, userId);
            return true;
        }catch(Exception e){
            return false;
        }
    }
}

六、添加编辑器

将编辑器的html文件添加至templates里面,然后通过get方式传入模板id就可以编辑流程了,modeler.html?modelId=" + modelId
在这里插入图片描述
如果保存报错,可以更改ModelSaveRestResource.java,这个springboot传参方式和springmvc有区别,参数无法传进来,activiti5.22.0版本比较旧,所以有这个问题。

/* Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.activiti.rest.editor.model;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;

import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.engine.ActivitiException;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Model;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.image.PNGTranscoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;

/**
 * @author Tijs Rademakers
 */
@RestController
public class ModelSaveRestResource implements ModelDataJsonConstants {

    protected static final Logger LOGGER = LoggerFactory.getLogger(ModelSaveRestResource.class);

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private ObjectMapper objectMapper;

    @RequestMapping(value = "/model/{modelId}/save", method = RequestMethod.PUT)
    @ResponseStatus(value = HttpStatus.OK)
    public void saveModel(@PathVariable String modelId, String name, String description, String json_xml,
                          String svg_xml) {
        try {
            Model model = repositoryService.getModel(modelId);

            ObjectNode modelJson = (ObjectNode) objectMapper.readTree(model.getMetaInfo());

            modelJson.put(MODEL_NAME, name);
            modelJson.put(MODEL_DESCRIPTION, description);
            model.setMetaInfo(modelJson.toString());
            model.setName(name);

            repositoryService.saveModel(model);

            repositoryService.addModelEditorSource(model.getId(), json_xml.getBytes("utf-8"));

            InputStream svgStream = new ByteArrayInputStream(svg_xml.getBytes("utf-8"));
            TranscoderInput input = new TranscoderInput(svgStream);

            PNGTranscoder transcoder = new PNGTranscoder();
            // Setup output
            ByteArrayOutputStream outStream = new ByteArrayOutputStream();
            TranscoderOutput output = new TranscoderOutput(outStream);

            // Do the transformation
            transcoder.transcode(input, output);
            final byte[] result = outStream.toByteArray();
            repositoryService.addModelEditorSourceExtra(model.getId(), result);
            outStream.close();

        } catch (Exception e) {
            LOGGER.error("Error saving model", e);
            throw new ActivitiException("Error saving model", e);
        }
    }
}

七、添加业务类

添加业务类这块自己按照自己的需求进行添加就好,由于内容过多,这里不做描述了。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
在Spring Boot中集成Activiti,可以通过以下步骤实现: 1. 在pom.xml中添加Activiti依赖: ```xml <dependency> <groupId>org.activiti</groupId> <artifactId>activiti-spring-boot-starter-basic</artifactId> <version>6.0.0</version> </dependency> ``` 2. 在application.properties中添加Activiti配置: ```properties # Activiti spring.activiti.check-process-definitions=false spring.activiti.database-schema-update=true ``` 3. 创建Activiti配置类: ```java @Configuration public class ActivitiConfig { @Autowired private DataSource dataSource; @Bean public ProcessEngine processEngine() throws Exception { SpringProcessEngineConfiguration configuration = new SpringProcessEngineConfiguration(); configuration.setDataSource(dataSource); configuration.setDatabaseSchemaUpdate("true"); configuration.setAsyncExecutorActivate(false); return configuration.buildProcessEngine(); } @Bean public RepositoryService repositoryService(ProcessEngine processEngine) { return processEngine.getRepositoryService(); } @Bean public RuntimeService runtimeService(ProcessEngine processEngine) { return processEngine.getRuntimeService(); } @Bean public TaskService taskService(ProcessEngine processEngine) { return processEngine.getTaskService(); } @Bean public HistoryService historyService(ProcessEngine processEngine) { return processEngine.getHistoryService(); } @Bean public ManagementService managementService(ProcessEngine processEngine) { return processEngine.getManagementService(); } @Bean public IdentityService identityService(ProcessEngine processEngine) { return processEngine.getIdentityService(); } } ``` 4. 在业务逻辑中使用Activiti服务: ```java @Service public class MyService { @Autowired private RuntimeService runtimeService; public void startProcess(String processDefinitionKey, Map<String, Object> variables) { runtimeService.startProcessInstanceByKey(processDefinitionKey, variables); } } ``` 这些步骤将会使你的Spring Boot应用程序集成Activiti,从而可以方便地使用Activiti的流程引擎服务。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值