Activit7流程退回到指定节点

在这里插入图片描述

package org.bingo.activiti;

import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.junit.Test;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;

public class Test01 {

    /**
     * 生成activiti相关表结构
     */
    @Test
    public void test01(){
        //使用classpath下的activiti.cfg.xml中的配置来创建ProcessEngine对象
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        System.out.println(engine);
    }

    /**
     * 实现单个文件部署
     */
    @Test
    public void test03(){
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        RepositoryService service = engine.getRepositoryService();
        Deployment deploy = service.createDeployment()
                .addClasspathResource("bpmn/execution.bpmn")
                .addClasspathResource("bpmn/execution.png")
                .name("请假流程部署")
                .key("execution-key")
                .deploy();
        System.out.println("流程部署id: "+deploy.getId());
        System.out.println("流程部署name: "+deploy.getName());
        System.out.println("流程部署key: "+deploy.getKey());
    }

    /**
     * 启动流程实例
     */
    @Test
    public void test05(){
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        RuntimeService service = engine.getRuntimeService();
        String id = "execution:1:4";
        ProcessInstance processInstance = service.startProcessInstanceById(id);
        System.out.println("流程实例的businessKey: "+processInstance.getBusinessKey());
        System.out.println("流程实例的Name: "+processInstance.getName());
        System.out.println("流程实例的流程部署id: "+processInstance.getDeploymentId());
        System.out.println("流程实例的流程部署key: "+processInstance.getProcessDefinitionKey());
        System.out.println("流程实例的startUserId: "+processInstance.getStartUserId());
    }

    /**
     * 任务查询
     */
    @Test
    public void test06(){
        String assignee = "lisi";
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = engine.getTaskService();
        List<Task> list = taskService.createTaskQuery()
                .processDefinitionKey("execution")
                .taskAssignee(assignee)
                .list();
        for (Task task : list) {
            System.out.println("流程实例id: " + task.getProcessInstanceId());
            System.out.println("任务id: " + task.getId());
            System.out.println("任务负责人: " + task.getAssignee());
            System.out.println("任务名称呢个: " + task.getName());
        }
    }

    /**
     * 流程任务处理
     */
    @Test
    public void test07(){
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = engine.getTaskService();
        Task task = taskService.createTaskQuery()
                .processDefinitionKey("execution")
                .taskAssignee("lisi")
                .singleResult();

        taskService.complete(task.getId());
    }

    public static void main(String[] args) throws Exception{
        backProcess("32502", "N0001");
    }

    /**
     * 退回到指定节点
     * @param taskId 当前任务ID
     * @param preNode 上一个节点key
     */
    public static void backProcess(String taskId, String preNode) throws Exception {
        if(StringUtils.isEmpty(taskId)){
            throw new RuntimeException("任务taskId为空");
        }
        if(StringUtils.isEmpty(preNode)){
            throw new RuntimeException("退回节点preNode为空");
        }
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        HistoryService historyService = engine.getHistoryService();
        RuntimeService runtimeService = engine.getRuntimeService();
        TaskService taskService = engine.getTaskService();
        RepositoryService repositoryService = engine.getRepositoryService();
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        String processInstanceId = task.getProcessInstanceId();
        // 取得所有历史任务按时间降序排序
        List<HistoricTaskInstance> htiList = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId)
                .orderByTaskCreateTime()
                .desc()
                .list();
        if (CollectionUtils.isEmpty(htiList) || htiList.size() < 2) {
            return;
        }
        // list里面节点等于退回到的指定节点
        HistoricTaskInstance lastTask = null;
        for (HistoricTaskInstance instance : htiList) {
            String key = instance.getTaskDefinitionKey();
            if(preNode.equals(key)){
                lastTask = instance;
            }
        }
        if(lastTask == null){
            throw new RuntimeException("没有找到指定的退回节点");
        }

        // 当前节点的executionId
        String curExecutionId = task.getExecutionId();
        // 上个节点的taskId
        String lastTaskId = lastTask.getId();
        // 上个节点的executionId
        String lastExecutionId = lastTask.getExecutionId();
        if (null == lastTaskId) {
            throw new Exception("LAST TASK IS NULL");
        }
        String processDefinitionId = lastTask.getProcessDefinitionId();
        //通过processDefinitionId从act_re_procdef查出一条记录, 通过act_re_procdef.DEPLOYMENT_ID_关联到act_ge_bytearray表查出存在数据库的bpmn文件信息
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        String lastActivityId = null;
        List<HistoricActivityInstance> haiFinishedList = historyService.createHistoricActivityInstanceQuery()
                .executionId(lastExecutionId).finished().list();
        for (HistoricActivityInstance hai : haiFinishedList) {
            if (lastTaskId.equals(hai.getTaskId())) {
                // 得到ActivityId,只有HistoricActivityInstance对象里才有此方法
                lastActivityId = hai.getActivityId();
                break;
            }
        }
        // 得到上个节点的信息
        FlowNode lastFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(lastActivityId);
        // 取得当前节点的信息
        Execution execution = runtimeService.createExecutionQuery().executionId(curExecutionId).singleResult();
        String curActivityId = execution.getActivityId();
        FlowNode curFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(curActivityId);
        //记录当前节点的原活动方向
        List<SequenceFlow> oriSequenceFlows = new ArrayList<SequenceFlow>();
        oriSequenceFlows.addAll(curFlowNode.getOutgoingFlows());
        //清理活动方向
        curFlowNode.getOutgoingFlows().clear();
        //建立新方向
        List<SequenceFlow> newSequenceFlowList = new ArrayList<SequenceFlow>();
        SequenceFlow newSequenceFlow = new SequenceFlow();
        newSequenceFlow.setId("newSequenceFlowId");
        newSequenceFlow.setSourceFlowElement(curFlowNode);
        newSequenceFlow.setTargetFlowElement(lastFlowNode);
        newSequenceFlowList.add(newSequenceFlow);
        curFlowNode.setOutgoingFlows(newSequenceFlowList);
        // 完成任务
        taskService.complete(task.getId());
        //恢复原方向
        curFlowNode.setOutgoingFlows(oriSequenceFlows);
        Task nextTask = taskService
                .createTaskQuery().processInstanceId(processInstanceId).singleResult();
        // 设置执行人
        if(nextTask!=null) {
            taskService.setAssignee(nextTask.getId(), lastTask.getAssignee());
        }
    }

}

下面是execution.xml文件, 生成png图片后改后缀为.bpmn, 2个文件放到maven项目的resources/bpmn目录下
在这里插入图片描述

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" xmlns:tns="http://www.activiti.org/test" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" expressionLanguage="http://www.w3.org/1999/XPath" id="m1639913348109" name="" targetNamespace="http://www.activiti.org/test" typeLanguage="http://www.w3.org/2001/XMLSchema">
  <process id="execution" isClosed="false" isExecutable="true" name="外出请假流程" processType="None">
    <startEvent id="N0000" name="StartEvent"/>
    <userTask activiti:assignee="zhangsan" activiti:exclusive="true" id="N0001" name="创建请假单"/>
    <userTask activiti:assignee="lisi" activiti:exclusive="true" id="N0002" name="经理审批"/>
    <userTask activiti:assignee="wangwu" activiti:exclusive="true" id="N0003" name="总经理审批"/>
    <userTask activiti:assignee="zhaoliu" activiti:exclusive="true" id="N0004" name="财务审批"/>
    <endEvent id="N0005" name="EndEvent"/>
    <sequenceFlow id="_9" sourceRef="N0000" targetRef="N0001"/>
    <sequenceFlow id="_10" sourceRef="N0001" targetRef="N0002"/>
    <sequenceFlow id="_11" sourceRef="N0002" targetRef="N0003"/>
    <sequenceFlow id="_12" sourceRef="N0003" targetRef="N0004"/>
    <sequenceFlow id="_13" sourceRef="N0004" targetRef="N0005"/>
  </process>
  <bpmndi:BPMNDiagram documentation="background=#3C3F41;count=1;horizontalcount=1;orientation=0;width=842.4;height=1195.2;imageableWidth=832.4;imageableHeight=1185.2;imageableX=5.0;imageableY=5.0" id="Diagram-_1" name="New Diagram">
    <bpmndi:BPMNPlane bpmnElement="execution">
      <bpmndi:BPMNShape bpmnElement="N0000" id="Shape-N0000">
        <omgdc:Bounds height="32.0" width="32.0" x="170.0" y="10.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="N0001" id="Shape-N0001">
        <omgdc:Bounds height="55.0" width="85.0" x="145.0" y="90.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="N0002" id="Shape-N0002">
        <omgdc:Bounds height="55.0" width="85.0" x="145.0" y="200.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="N0003" id="Shape-N0003">
        <omgdc:Bounds height="55.0" width="85.0" x="145.0" y="300.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="N0004" id="Shape-N0004">
        <omgdc:Bounds height="55.0" width="85.0" x="145.0" y="395.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="N0005" id="Shape-N0005">
        <omgdc:Bounds height="32.0" width="32.0" x="170.0" y="490.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="_13" id="BPMNEdge__13" sourceElement="N0004" targetElement="N0005">
        <omgdi:waypoint x="186.0" y="450.0"/>
        <omgdi:waypoint x="186.0" y="490.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_12" id="BPMNEdge__12" sourceElement="N0003" targetElement="N0004">
        <omgdi:waypoint x="187.5" y="355.0"/>
        <omgdi:waypoint x="187.5" y="395.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_9" id="BPMNEdge__9" sourceElement="N0000" targetElement="N0001">
        <omgdi:waypoint x="186.0" y="42.0"/>
        <omgdi:waypoint x="186.0" y="90.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_11" id="BPMNEdge__11" sourceElement="N0002" targetElement="N0003">
        <omgdi:waypoint x="187.5" y="255.0"/>
        <omgdi:waypoint x="187.5" y="300.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_10" id="BPMNEdge__10" sourceElement="N0001" targetElement="N0002">
        <omgdi:waypoint x="187.5" y="145.0"/>
        <omgdi:waypoint x="187.5" y="200.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

activiti.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
        <!-- 连接数据的配置 -->
        <!--<property name="jdbcDriver" value="com.mysql.cj.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/activiti?useUnicode=true&amp;characterEncoding=utf8"></property>
        <property name="jdbcUsername" value="root"></property>
        <property name="jdbcPassword" value="root"></property>-->
        <!-- 没有表创建表 -->
        <property name="databaseSchemaUpdate" value="true"></property>
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/activiti?useUnicode=true&amp;characterEncoding=utf8"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
        <property name="maxActive" value="3"></property>
        <property name="maxIdle" value="2"></property>
    </bean>

</beans>

log4j.properties

### 设置###
log4j.rootLogger = debug,stdout,D,E

### 输出信息到控制抬 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n

### 输出DEBUG 级别以上的日志到=E://logs/error.log ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = E://logs/log.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG 
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

### 输出ERROR 级别以上的日志到=E://logs/error.log ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File =E://log/act/activiti.log 
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR 
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.bingo</groupId>
    <artifactId>activiti</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <slf4j-version>1.6.6</slf4j-version>
        <log4j-version>1.2.12</log4j-version>
        <activiti-version>7.0.0.Beta1</activiti-version>
        <mysql-version>8.0.25</mysql-version>
        <mybatis-version>3.4.5</mybatis-version>
    </properties>

    <dependencies>

        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-engine</artifactId>
            <version>${activiti-version}</version>
        </dependency>
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-spring</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>org.activiti</groupId>
            <artifactId>activiti-json-converter</artifactId>
            <version>${activiti-version}</version>
        </dependency>
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-bpmn-layout</artifactId>
            <version>${activiti-version}</version>
            <exclusions>
                <exclusion>
                    <groupId>com.github.jgraph</groupId>
                    <artifactId>jgraph</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.activiti.cloud</groupId>
            <artifactId>activiti-cloud-services-api</artifactId>
            <version>${activiti-version}</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql-version}</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>${mybatis-version}</version>
        </dependency>
        <dependency>
            <groupId>commons-dbcp</groupId>
            <artifactId>commons-dbcp</artifactId>
            <version>1.4</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>${log4j-version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j-version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>${slf4j-version}</version>
        </dependency>

    </dependencies>

</project>

版权声明:本文为weixin_42490383原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。