一、activiti依赖工具

1、springboot依赖

注意排除mybatisel表达式依赖

   <dependency>
        <groupId>org.activiti</groupId>
        &lt;artifactId&gt;activiti-spring-boot-starter</artifactId&gt;
        <version&gt;7.0.0.Beta2</version&gt;
        <exclusions&gt;
                <exclusion&gt;
                    <artifactId>mybatis</artifactId>
                    <groupId>org.mybatis</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>el-api</artifactId>
                    <groupId>javax.el</groupId>
                </exclusion>
            </exclusions>
   </dependency>

2、下载idea相关插件

流程图相关插件
在这里插入图片描述

二、配置activiti

1、参数相关介绍如下

spring:
  activiti:
    #自动更新数据库结构
    #1.flase默认值activiti在启动时,对比数据库表中保存版本,如果没有表或者版本不匹配,将抛出异常
    #2.trueactiviti会对数据库中所有表进行更新操作。如果表不存在,则自动创建
    #3.create_drop: 在activiti启动创建表,在关闭删除表(必须手动关闭引擎,才能删除表)
    #4.drop-create: 在activiti启动删除原来的旧表,然后创建新表(不需要手动关闭引擎
    database-schema-update: true
    #activiti7默认生成历史信息表,开启历史表
    db-history-used: true
    #记录历史等级配置的历史级别none, activity, audit, full
    #none:不保存任何的历史数据,因此,在流程执行过程中,这是最高效的。
    #activity级别高于none,保存流程实例与流程行为,其他数据不保存。
    #audit:除activity级别会保存的数据外,还会保存全部的流程任务及其属性audithistory默认值
    #full:保存历史数据的最高级别,除了会保存audit级别的数据外,还会保存其他全部流程相关的细节数据,包括一些流程参数等。
    history-level: full
    # =============================
    #自动检查部署流程定义文件 启动自动部署定义的流程
    check-process-definitions: true
    # asyncExecutorActivate是指activiti在流程引擎启动激活AsyncExecutor,异步:true-开启默认)、false-关闭
    async-executor-activate: true
  #流程定义文件存放目录,要具体到某个目录
  #      process-definition-location-prefix: classpath:/processes/holliday/
  #process-definition-location-suffixes: #流程文件格式
  #  - **.bpmn20.xml
  #  - **.bpmn

2、创建流程文件

创建新流程文件默认在此目录下,当然也可以yml文件中通过配置processdefinitionlocationprefix指定位置
在这里插入图片描述

3、创建新流程文件

在这里插入图片描述

4、springboot排除springSecurity自动装配

@SpringBootApplication(exclude = {org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class})
//@SpringBootApplication
@MapperScan(basePackages = "com.jt.dao")
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

5、增加config跳过springSecurity验证

config仅仅是为了跳过springSecurity验证

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;


/**
 * @author jiangtao
 * 该配置类仅仅为了跳过spring-security的验证
 */
@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
}

6、启动项目

启动springboot项目,会在mysql创建25张表
在这里插入图片描述

三、画流程图

  1. 简单的双节点流程图
    在这里插入图片描述

  2. 生成xml文件

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" 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" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/processdef">
  <process id="leaveDemo" name="leaveDemo" isExecutable="true">
    <userTask id="_3" name="secondUser" activiti:assignee="李四"/>
    <startEvent id="_1" name="startEvent"/>
    <endEvent id="_4" name="endEvent"/>
    <userTask id="_2" name="firstUser" activiti:assignee="张三"/>
    <sequenceFlow id="sid-f594c092-ac67-40ee-a41d-ce952d8844ad" sourceRef="_1" targetRef="_2"/>
    <sequenceFlow id="sid-c685f76f-b3a2-4edd-8a58-f8b8c055a070" sourceRef="_2" targetRef="_3"/>
    <sequenceFlow id="sid-145ed254-20fd-47d3-9c08-1f545b99f35b" sourceRef="_3" targetRef="_4"/>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_leave">
    <bpmndi:BPMNPlane bpmnElement="leaveDemo" id="BPMNPlane_leave">
      <bpmndi:BPMNShape id="shape-3c81f674-87f0-4fea-8040-7700dbb20a94" bpmnElement="_3">
        <omgdc:Bounds x="44.04801" y="80.552" width="100.0" height="80.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="shape-0a797082-f4d4-4378-a571-751e7a94fcda" bpmnElement="_1">
        <omgdc:Bounds x="60.385612" y="-149.3312" width="30.0" height="30.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="shape-938cb97e-5123-433f-9822-4cfb66208a80" bpmnElement="_4">
        <omgdc:Bounds x="69.5556" y="206.69162" width="30.0" height="30.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="shape-0eef7a46-93d8-4571-aba2-fa4524294cd1" bpmnElement="_2">
        <omgdc:Bounds x="34.555603" y="-69.0972" width="100.0" height="80.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-6965c761-2b2c-4a73-a3bd-5aa9fa568784" bpmnElement="sid-f594c092-ac67-40ee-a41d-ce952d8844ad">
        <omgdi:waypoint x="75.38561" y="-119.33121"/>
        <omgdi:waypoint x="84.5556" y="-69.0972"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="edge-2f655aef-dfd0-415d-9e2b-c4730cc74b73" bpmnElement="sid-c685f76f-b3a2-4edd-8a58-f8b8c055a070">
        <omgdi:waypoint x="84.5556" y="10.9028015"/>
        <omgdi:waypoint x="84.5556" y="80.552"/>
        <omgdi:waypoint x="94.04801" y="80.552"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="edge-424dfd94-8841-44ca-bfbf-2542b172bfc2" bpmnElement="sid-145ed254-20fd-47d3-9c08-1f545b99f35b">
        <omgdi:waypoint x="94.04801" y="160.552"/>
        <omgdi:waypoint x="92.0556" y="206.69162"/>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

四、开启流程并运行

1、查看所有已经发布的流程定义

List<ProcessDefinition> list = repositoryService
                .createProcessDefinitionQuery()
                .orderByProcessDefinitionVersion()
                .desc()
                .list();
        for (ProcessDefinition item :
                list) {
            System.out.println("========");
            System.out.println(item.getDeploymentId());
            System.out.println(item.getId());
            System.out.println(item.getKey());
            System.out.println(item.getName());
        }

2、开启流程

        String processDefinitionKey = "candidateUser";
        Map<String, Object> map = new HashMap<>();
        map.put("num", 2);
        ProcessInstance processInstance = runtimeService
                .startProcessInstanceByKey(processDefinitionKey);
        if (processInstance != null) {
            System.out.println("=============");
            System.out.println(processInstance.getName());
            System.out.println(processInstance.getProcessDefinitionKey());
            System.out.println(processInstance.getId());
        }

3、查询已经启动的流程实例

List<ProcessInstance> list = runtimeService
                .createProcessInstanceQuery()
                .processInstanceId("e7f7f241-317e-11ed-8ab0-005056c00008")
                .processDefinitionKey("leave")
                .list();
        
        list.forEach(item -> {
            System.out.println("============");
            System.out.println(item.getBusinessKey());
            System.out.println(item.getDeploymentId());
            System.out.println(item.getStartTime());
            System.out.println(item.getProcessDefinitionKey());
            System.out.println(item.getName());
        });

4、查询正在执行任务

List<Task> list = taskService.createTaskQuery()
                .processDefinitionKey("varAndParaGateway")
//                .taskAssignee("张三")
//                .taskId("d9cc9362-3124-11ed-8715-005056c00008")
//                .processInstanceId("cca885a4-31a3-11ed-a478-005056c00008")
                .list();
        for (int i = 0; i < list.size(); i++) {
            Task task = list.get(i);
            System.out.println("===========");
            System.out.println(task.getProcessDefinitionId());
            System.out.println(task.getProcessInstanceId());
            System.out.println(task.getAssignee());
            System.out.println(task.getName());
            System.out.println(task.getId());
        }

5、完成个人任务

String assignee = "张三";
        String processInstanceId = "3dc5fbc4-3249-11ed-bf5a-005056c00008";
        String processDefinitionKey = "gateway1";
        List<Task> list = taskService.createTaskQuery()
//                .processDefinitionKey(processDefinitionKey)
//                .taskAssignee(assignee)
                .processInstanceId(processInstanceId)
                .list();
//        taskService.setVariableLocal("862e5029-31e4-11ed-ab1a-005056c00008", "num", "4");
        // executionId 就当是processInstanceId
//        runtimeService.setVariableLocal("862b1bd5-31e4-11ed-ab1a-005056c00008", "num", 3);
        for (int i = 0; i < list.size(); i++) {
            Task task = list.get(i);
//            taskService.complete(task.getId());
            System.out.println("==========");
            System.out.println(task.getId());
            System.out.println(task.getName());
            System.out.println(task.getAssignee());
            System.out.println(task.getProcessDefinitionId());
            System.out.println(task.getProcessInstanceId());
            System.out.println(task.getTaskDefinitionKey());
            System.out.println(task.getExecutionId());
        }

6、拒绝个人任务

 ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
//                .processInstanceId(processInstanceId)
                .processInstanceBusinessKey(businessKey)
                .singleResult();
        if (processInstance != null) {
            runtimeService.deleteProcessInstance(processInstance.getId(), endReason);
            log.info("流程删除成功!");
        } else {
            throw new ProcessNotFoundException("流程不存在!");
        }

7、判断流程是否结束

 HistoricProcessInstance historicProcessInstance = historyService
                .createHistoricProcessInstanceQuery()
                .processInstanceId("d8c1137a-31bf-11ed-aee8-005056c00008")
                .singleResult();
        if (historicProcessInstance != null) {
            if (historicProcessInstance.getEndTime() != null) {
                System.out.println("流程已结束!");
            } else {
                System.out.println("流程未结束!");
            }
        } else {
            System.out.println("流程不存在!");
        }

8、查询审批过程

// 历史节点
        List<HistoricActivityInstance> list = historyService
                .createHistoricActivityInstanceQuery()
//                .processInstanceId("5e787d34-31a6-11ed-95b6-005056c00008")
                .processInstanceId("70ebc1e0-3312-11ed-b4b0-005056c00008")
//                .orderByActivityId()
//                .desc()
                .orderByHistoricActivityInstanceEndTime()
                .asc()
                .finished()
//                .unfinished()
                .list()
                .stream()
                .filter(item -> !StringUtils.containsAny(item.getActivityType(), "inclusiveGateway", "parallelGateway"))
                .collect(Collectors.toList());
        // 历史变量
        for (int i = 0; i < list.size(); i++) {
            System.out.println("========");
            HistoricActivityInstance historicActivityInstance = list.get(i);
//            System.out.println(historicActivityInstance.getActivityId());  // _2
            System.out.println(historicActivityInstance.getActivityName());
            String taskId = historicActivityInstance.getTaskId();
//            System.out.println(taskId);
            String result = "";
            if (StringUtils.isNotEmpty(taskId)) {
                HistoricVariableInstance historicVariableInstance = historyService.createHistoricVariableInstanceQuery().taskId(taskId).variableName("result").singleResult();
                result = (String) historicVariableInstance.getValue();
                // 已经经过的节点不适用该方法查询变量
//                result = (String) taskService.getVariableLocal(taskId, "result");
            }
            System.out.println(historicActivityInstance.getAssignee() + "----" + result);
            System.out.println(historicActivityInstance.getStartTime());
            System.out.println(historicActivityInstance.getEndTime());
//            System.out.println("ExecutionId:" + historicActivityInstance.getExecutionId());
//            System.out.println("ProcessInstanceId:" + historicActivityInstance.getProcessInstanceId());
//            System.out.println(historicActivityInstance.getActivityType()); // userTask
        }

9、申领任务

// 获取当前用户
        String candidateUser = ActivitiUtils.getActivitiUser();
        if (StringUtils.isBlank(candidateUser)) {
            throw new RuntimeException("candidateUser不能为空!");
        }
        if (StringUtils.isBlank(businessKey)) {
            throw new RuntimeException("businessKey不能为空!");
        }
        Task task = taskService.createTaskQuery()
                .taskCandidateUser(candidateUser)
//                .taskId(taskId)
                .processInstanceBusinessKey(businessKey)
                .singleResult();
        if (task != null) {
            taskService.claim(task.getId(), candidateUser);
            log.info(candidateUser + "申领到了任务!");
            return true;
        }
        return false;

10、放弃申领到的任务

// 获取当前用户
        String assignee = ActivitiUtils.getActivitiUser();
        if (StringUtils.isBlank(businessKey)) {
            throw new RuntimeException("businessKey不能为空!");
        }
        if (StringUtils.isBlank(assignee)) {
            throw new RuntimeException("assignee不能为空!");

        }
        Task task = taskService.createTaskQuery()
                .taskAssignee(assignee)
//                .taskId(taskId)
                .processInstanceBusinessKey(businessKey)
                .singleResult();
        if (task != null) {
            taskService.setAssignee(task.getId(), null);
            log.info(assignee + "放弃了任务!");
        }

五、动态指定审批人

1、EL表达式动态指定审批人

在bpmn文件中借助EL表达式,并通过变量赋值,达到动态指定审批人的效果
以下是流程图,审批人处并没有直接指定相应的审批人,而是通过EL表达式站位。
在这里插入图片描述
对应xml文件:

<process id="varDemo" name="varDemo" isExecutable="true">
    <startEvent id="_1"/>
    <userTask id="_2" name="一级审批" activiti:assignee="${user1}"/>
    <userTask id="_3" name="二级审批" activiti:assignee="${user2}"/>
    <endEvent id="_4"/>
    <sequenceFlow id="sid-fba31a25-c2aa-487d-9ae5-89c0e2e04524" sourceRef="_1" targetRef="_2"/>
    <sequenceFlow id="sid-8576ba0f-6a70-4ee2-b940-9a336015b744" sourceRef="_2" targetRef="_3"/>
    <sequenceFlow id="sid-e4ff88d6-62e7-42bc-8ef9-a981f31d0f3e" sourceRef="_3" targetRef="_4"/>
  </process>

对应的一二级审批人为变量user1和user2。通过下面api完成当前任务节点并填充变量

HashMap<String, Object> map = new HashMap<>();
map.put("user2", "王五");
taskService.complete(task.getId(), map, true);

2、EL+javaBean动态指定候选人

候选人概念:候选人和审批人的区别在于候选人只有领取到了任务才能审批任务,否则不能审批任务。常用场景一个节点多人审批,其中一个人审批通过则该节点通过
在这里插入图片描述
对应xml文件:

<process id="candidateUser" name="candidateUser" isExecutable="true">
    <startEvent id="_1" name="开始"/>
    <userTask id="_2" name="一级审批" activiti:candidateUsers="${authService.getCandidateUsers()}"/>
    <endEvent id="_3" name="结束"/>
    <sequenceFlow id="sid-f1c43cee-5b56-4a07-94fe-25d9a271a855" sourceRef="_1" targetRef="_2"/>
    <sequenceFlow id="sid-bcd5f698-98b4-4153-bd8e-fcc10b141ec5" sourceRef="_2" targetRef="_3"/>
  </process>

创建java类,定义方法方法逻辑为候选人选取逻辑方法返回list。

@Service
public class AuthService implements Serializable {

    public List<String> getCandidateUsers() {
        List<String> users = new ArrayList<>();
        users.add("小二");
        users.add("小三");
        users.add("小四");
        users.add("小五");
        return users;
    }

候选人领取任务:

// candidateUser为String类型
taskService.claim(task.getId(), candidateUser);

六、网关

1、排他网关

排他网关的特点就是流程只能根据条件一条流程线,例如:
该审批流程只能经选择二级审批一或者二级审批二其中一条,排他网关需要新的网关汇总分支
在这里插入图片描述

2、并行网关

并行网关顾名思义,经过该网关时所有流程都会被触发,例如:
该网关需要一个并行网关进行汇总,否则就就会在一次网关分流之后各自进行流程,也就是并行网关之后的节点会被执行两次
在这里插入图片描述

3、包含网关

该网关是排他网关和并行网关的结合,该网关与排他网关不同的是,排他网关只能执行满足条件一条线的流程,而包含网关可以执行所有满足条件的流程;包含网关与并行网关不同的是,包含网关支持设置条件进行限制可以经过哪些流程,并不是所有线的流程都可以被执行。同样的,为了避免经过网关之后产生不同的分支,也是需要相同类型的网关进行汇总
在这里插入图片描述

七、避坑点

1. 如果排他网关设置条件会怎么样?
不设置条件,并不影响程序执行,不过在经过网关时,会选择xml中最先出现sequenceFlow那条线,所以在使用排他网关时,最好设置上条件
2. processInstance和execution的联系是什么?
如下两张图片最直观表现他们的关系,一个流程实例对应一个父级execution,然后真正走实例流程的execution是另外生成的,是父级execution的子executiion。
在这里插入图片描述
在这里插入图片描述

3. taskService.setVariableLocal 和 runtimeService.setVariableLocal的区别
前者的作用域当前execution的当前节点上,且数据库当前数据行的task_id有数据,且经过测试,变量仅用于节点,不能用于节点后面的流程线;后者的作用域是当前execution的所有节点上。
在这里插入图片描述
4. taskService.setVariable 和 runtimeService.setVariable的区别
前者是如果当前流程变量中没有没有此变量,则默认是最外层execution,即全局变量,如果流程中有此变量,测覆盖此变量,作用域范围不变;后者是最外层execution,也是全局变量
5. 没有到达任务节点,会不会对后面的节点进行赋值赋值之后改变流程变量会不会改变已经赋值的节点的值?
在这里插入图片描述

到达二级审批之前,尽管变量中有user2的信息,是不会给user2赋值的,只有到达二级审批节点的时候才会给user2赋值;并且已经给user1赋值之后改变流程变量并不会改变user1的值。

原文地址:https://blog.csdn.net/java_eiji/article/details/126815986

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_14549.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注