• Activiti工作流引擎学习笔记


    Activiti7框架分享

    前言

    什么是工作流?

    工作流(Workflow),指业务过程的部分或整体在计算机应用环境下的自动化。是对工作流程及其各操作步骤之间业务规则的抽象。

    大白话就是比如说我要请假,整个流程谁提起,第一步谁审批,第二步又是谁审批,这样的一个过程。

    实现工作流为什么要用框架?

    在没有专门的工作流引擎之前,程序员为了实现流程控制,通常的做法就是采用状态字段的值来跟踪流程的变化情况。这样不同角色的用户,通过状态字段的取值来决定记录是否显示。

    针对有权限可以查看的记录,当前用户根据自己的角色来决定审批是否合格的操作。如果合格将状态字段设置一个值,来代表合格;当然如果不合格也需要设置一个值来代表不合格的情况。

    img

    但这样实现的话,缺点其实有很多:

    • 业务流程分散,比如说部门经理只看到节点2,总监只看到节点3
    • 很难从代码层面理清整个流程,需要其它文档的辅助
    • 当流程变更的时候,代码需要进行很大的改动

    所以这个时候就迫切的需要一个专门用来管理工作流的框架,它可以做到即使我们的流程发生变化,程序也不用改变。所以Activiti应运而生。

    什么是Activiti?

    Activiti是一个工作流引擎, Activiti可以将业务系统中复杂的业务流程抽取出来,使用专门的建模语言BPMN2.0进行定义,业务流程按照预先定义的流程进行执行.

    Activiti实现了系统的流程由其进行管理**,减少业务系统由于流程变更进行系统升级改造的工作量,从而提高系统的健壮性,同时也减少了系统开发维护成本。**

    什么是BPMN2.0

    BPMN(Business Process Model AndNotation): 业务流程模型和符号

    BPMI(BusinessProcess Management Initiative)开发的一套标准的业务流程建模符号,使用BPMN提供的符号可以创建业务流程。

    请添加图片描述

    规定由Start Event,End Event结束,期间由很多其它事件及网关,彼此关系由连线维系。

    在这里插入图片描述

    这些图标有着分类:

    • Event(事件):BPMN规定,流程起点为StartEvent,由EndEvent结束
    • Task(任务):任务之间通过连线形成整个的工作流
    • Geteway(构建复杂流程):网关
    • 其它

    如下为一个完整的流程:

    请添加图片描述

    网关

    简单的流程用activiti提供的按钮及连线便可确定,但是复杂的流程如:请假未超三天组长批,超过总监批这种流程则需要网关来处理

    • 排他网关(解析条件,IF else逻辑)

      img

    • 并行网关(互不影响,并行网关结束时需要聚合)

      img

    • 包含网关(使用最多,和并行网关一样,有分散聚合的功能)

    包含网关可以看做是排他网关和并行网关的结合体。

    和排他网关一样,你可以在外出顺序流上定义条件,包含网关会解析它们。 但是主要的区别是包含网关可以选择多于一条顺序流,这和并行网关一样。

    img

    • 事件网关(不常用)

    Activiti入门

    基本步骤

    • 部署Activiti

    Activiti是一个工作流引擎(其实就是一堆jarAPI),将工作流环境与业务系统的环境集成。

    • 流程定义

    使用Activiti流程建模工具(activity-designer)画业务流程(.bpmn文件,实质就是一堆xml文件) 。 bpmn文件就是业务流程定义文件,通过xml定义业务流程。

    • 部署流程定义

    使用Activiti提供的API把流程定义内容存储,在Activiti执行过程汇总可查询定义内容。Activiti通过数据库来存储业务流程

    • 启动一个流程实例

    流程实例也叫:ProcessInstance启动一个流程实例表示开始一次业务流程的运行。多个流程互不影响。

    • 用户查询待办任务(Task)

    因为现在系统的业务流程已经交给Activiti管理,通过Activiti就可以查询当前流程执行到哪了,当前用户需要办理什么任务了,这些Activiti帮我们管理了,而不需要开发人员自己编写在sql语句查询。

    • 用户办理任务

    用户查询待办任务后,就可以办理某个任务,如果这个任务办理完成还需要其它用户办理,就可由Activiti将工作流流程往后推动

    • 流程结束

    当任务办理完成没有下一个任务结点了,这个流程实例就完成了。

    基本类介绍

    类关系图:

    5b3cd01744b3c18f9aef6ff2a842c3b9.png

    • activiti.cfg.xml:activiti的引擎配置文件,包括:ProcessEngineConfiguration的定义、数据源定义、事务管理器等,此文件其实就是一个spring配置文件。

      官方提供代码:

      <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="jdbcUrl" value="jdbc:h2:mem:activiti;DB_CLOSE_DELAY=1000" />
            
          <property name="jdbcDriver" value="org.h2.Driver" />
            
          <property name="jdbcUsername" value="sa" />
            
          <property name="jdbcPassword" value="" />
       
            
          <property name="databaseSchemaUpdate" value="true" />
       
          <property name="jobExecutorActivate" value="false" />
          <property name="asyncExecutorEnabled" value="true" />
          <property name="asyncExecutorActivate" value="false" />
       
          <property name="mailServerHost" value="mail.my-corp.com" />
          <property name="mailServerPort" value="5025" />
        bean>
       
      beans>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
    • 流程引擎的配置类(ProcessEngineConfiguration),通过ProcessEngineConfiguration可以创建工作流引擎ProceccEngine

      ProcessEngineConfiguration类读取我们创建的activiti.cfg.xml文件,,源码如下:
      请添加图片描述
      请添加图片描述
      请添加图片描述

    • 工作流引擎(ProcessEngine),相当于一个门面接口,通过ProcessEngineConfiguration创建processEngine,通过ProcessEngine创建各个service接口。

    创建工作流引擎(ProcessEngine

    创建ProcessEngine 时会自动创建表

    有两种方式:

    1. 默认创建

       //使用classpath下的activiti.cfg.xml中的配置创建processEngine
              ProcessEngine processEngine01 = ProcessEngines.getDefaultProcessEngine();
              System.out.println(processEngine01);
      
      • 1
      • 2
      • 3
    2. 一般创建方式

    直接使用activiti提供的工具类ProcessEngines,会默认读取classpath下的activiti.cfg.xml文件,读取其中的数据库配置,创建 ProcessEngine

    @Test
    public void test(){
        // 引擎配置
        ProcessEngineConfiguration pec=ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml");
        // 获取流程引擎对象,会创建数据库
        ProcessEngine processEngine=pec.buildProcessEngine();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    数据库由代码动态生成25张表:

    1. act_re开头代表Repository,存储流程图的相关信息和数据。
    2. act_hi开头,history 历史记录,存储各种历史记录
    3. act_ru开头,存储runtime 运行时的数据(临时数据),流程跑完自动删除
    创建 Servcie服务接口

    ProcessEngine 源码中已有各个service:

    请添加图片描述

    所以直接用get方法创建各个service:

    请添加图片描述

    service总览:

    请添加图片描述

    • RepositoryService:是activiti的资源管理类,提供了管理和控制流程发布包和流程定义的操作
    • RuntimeService:Activiti的流程运行管理类。可以从这个服务类中获取很多关于流程执行相关的信息
    • TaskService:Activiti的任务管理类。可以从这个类中获取任务的信息
    • HistoryService:Activiti的历史管理类,可以查询历史信息,执行流程时,引擎会保存很多数据(根据配置),比如流程实例启动时间,任务的参与者, 完成任务的时间,每个流程实例的执行路径,等等。此服务主要通过查询功能来获得这些数据。
    • ManagementService:Activiti的引擎管理类,提供了对 Activiti 流程引擎的管理和维护功能,这些功能不在工作流驱动的应用程序中使用,主要用于Activiti 系统的日常维护

    流程基本操作

    1. 流程定义

    请添加图片描述

    在idea中画好对应的流程图,流程id为act_re_procdef表的key,用来标识流程的唯一性,生成.bpmn,是指就是xml文件

    请添加图片描述

    2. 使用RepositoryService进行流程部署:

    分为两种方式:

    • 通过调用activitiAPI将流程定义的bpmnpng两个文件一个一个添加部署到activiti

      public class ActivitiDemo {
          /**
           * 部署流程定义
           */
          @Test
          public void testDeployment(){
      //        1、创建ProcessEngine
              ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
      //        2、得到RepositoryService实例
              RepositoryService repositoryService = processEngine.getRepositoryService();
      //        3、使用RepositoryService进行部署
              Deployment deployment = repositoryService.createDeployment()
                      .addClasspathResource("bpmn/evection.bpmn") // 添加bpmn资源
                      .addClasspathResource("bpmn/evection.png")  // 添加png资源
                      .name("出差申请流程")
                      .deploy();
      //        4、输出部署信息
              System.out.println("流程部署id:" + deployment.getId());
              System.out.println("流程部署名称:" + deployment.getName());
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
    • 压缩包部署方式

      @Test
       public void deployProcessByZip() {
        // 定义zip输入流
        InputStream inputStream = this
          .getClass()
          .getClassLoader()
          .getResourceAsStream(
            "bpmn/evection.zip");
        ZipInputStream zipInputStream = new ZipInputStream(inputStream);
        // 获取repositoryService
        RepositoryService repositoryService = processEngine
          .getRepositoryService();
        // 流程部署
        Deployment deployment = repositoryService.createDeployment()
          .addZipInputStream(zipInputStream)
          .deploy();
        System.out.println("流程部署id:" + deployment.getId());
        System.out.println("流程部署名称:" + deployment.getName());
       }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19

    执行此操作后activiti会将上边代码中指定的bpm文件和图片文件保存在activiti数据库。

    流程定义部署后操作activiti的3张表如下:

    • act_re_deployment 流程定义部署表,每部署一次增加一条记录
    • act_re_procdef 流程定义表,部署每个新的流程定义都会在这张表中增加一条记录
    • act_ge_bytearray 流程资源表
    3. 使用runtimeService 启动流程实例

    可获取流程基本信息

    /**
         * 启动流程实例
         */
        @Test
        public void testStartProcess(){
    //        1、创建ProcessEngine
            ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    //        2、获取RunTimeService
            RuntimeService runtimeService = processEngine.getRuntimeService();
    //        3、根据流程定义Id启动流程
            ProcessInstance processInstance = runtimeService
                    .startProcessInstanceByKey("myEvection");
    //        输出内容
            System.out.println("流程定义id:" + processInstance.getProcessDefinitionId());
            System.out.println("流程实例id:" + processInstance.getId());
            System.out.println("当前活动Id:" + processInstance.getActivityId());
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    操作数据表

    • act_hi_actinst 流程实例执行历史
    • act_hi_identitylink 流程的参与用户历史信息
    • act_hi_procinst 流程实例历史信息
    • act_hi_taskinst 流程任务历史信息
    • act_ru_execution 流程执行信息
    • act_ru_identitylink 流程的参与用户信息
    • act_ru_task 任务信息
    4. 使用TaskService 进行任务操作
    • 查询任务

    可获取任务基本信息

    流程启动后,任务的负责人就可以查询自己当前需要处理的任务,查询出来的任务都是该用户的待办任务。

    /**
         * 查询当前个人待执行的任务
          */
        @Test
        public void testFindPersonalTaskList() {
    //        任务负责人
            String assignee = "zhangsan";
            ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    //        创建TaskService
            TaskService taskService = processEngine.getTaskService();
    //        根据流程key 和 任务负责人 查询任务
            List<Task> list = taskService.createTaskQuery()
                    .processDefinitionKey("myEvection") //流程Key
                    .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());
     
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 处理任务

      任务负责人查询待办任务,选择任务进行处理,完成任务。

    // 完成任务
        @Test
        public void completTask(){
    //        获取引擎
            ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    //        获取taskService
            TaskService taskService = processEngine.getTaskService();
     
    //        根据流程key 和 任务的负责人 查询任务
    //        返回一个任务对象
            Task task = taskService.createTaskQuery()
                    .processDefinitionKey("myEvection") //流程Key
                    .taskAssignee("zhangsan")  //要查询的负责人
                    .singleResult();
     
    //        完成任务,参数:任务id
            taskService.complete(task.getId());
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    5. 使用RepositoryService流程定义信息查询

    查询流程相关信息,包含流程定义,流程部署,流程定义版本

    /**
         * 查询流程定义
         */
        @Test
        public void queryProcessDefinition(){
            //        获取引擎
            ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    //        repositoryService
            RepositoryService repositoryService = processEngine.getRepositoryService();
    //        得到ProcessDefinitionQuery 对象
            ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
    //          查询出当前所有的流程定义
    //          条件:processDefinitionKey =evection
    //          orderByProcessDefinitionVersion 按照版本排序
    //        desc倒叙
    //        list 返回集合
            List<ProcessDefinition> definitionList = processDefinitionQuery.processDefinitionKey("myEvection")
                    .orderByProcessDefinitionVersion()
                    .desc()
                    .list();
    //      输出流程定义信息
            for (ProcessDefinition processDefinition : definitionList) {
                System.out.println("流程定义 id="+processDefinition.getId());
                System.out.println("流程定义 name="+processDefinition.getName());
                System.out.println("流程定义 key="+processDefinition.getKey());
                System.out.println("流程定义 Version="+processDefinition.getVersion());
                System.out.println("流程部署ID ="+processDefinition.getDeploymentId());
            }
     
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    6. 使用RepositoryService流程定义信息查询
    public void deleteDeployment() {
      // 流程部署id
      String deploymentId = "1";
      
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        // 通过流程引擎获取repositoryService
      RepositoryService repositoryService = processEngine
        .getRepositoryService();
      //删除流程定义,如果该流程定义已有流程实例启动则删除时出错
      repositoryService.deleteDeployment(deploymentId);
      //设置true 级联删除流程定义,即使该流程有流程实例启动也可以删除,设置为false非级别删除方式,如果流程
      //repositoryService.deleteDeployment(deploymentId, true);
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    7. 使用HistoryService

    即使流程定义已经删除了,流程执行的历史信息通过前面的分析,依然保存在activiti的act_hi_*相关的表中。所以我们还是可以查询流程执行的历史信息,可以通过HistoryService来查看相关的历史记录。

    /**
         * 查看历史信息
         */
        @Test
        public void findHistoryInfo(){
    //      获取引擎
            ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    //        获取HistoryService
            HistoryService historyService = processEngine.getHistoryService();
    //        获取 actinst表的查询对象
            HistoricActivityInstanceQuery instanceQuery = historyService.createHistoricActivityInstanceQuery();
    //        查询 actinst表,条件:根据 InstanceId 查询
    //        instanceQuery.processInstanceId("2501");
    //        查询 actinst表,条件:根据 DefinitionId 查询
            instanceQuery.processDefinitionId("myEvection:1:4");
    //        增加排序操作,orderByHistoricActivityInstanceStartTime 根据开始时间排序 asc 升序
            instanceQuery.orderByHistoricActivityInstanceStartTime().asc();
    //        查询所有内容
            List<HistoricActivityInstance> activityInstanceList = instanceQuery.list();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    流程进阶操作

    1. 多流程实例

    流程定义部署在activiti后,就可以在系统中通过activiti去管理该流程的执行,执行流程表示流程的一次执行。

    当多实例启动的时候,指定businesskey作为业务标识,就会在act_ru_execution 流程实例的执行表中存储businesskey,其与流程实例一一对应

    businesskey作为业务标识可以是出差单id或者采购单id等由业务系统发来的数据同activi数据产生联系

        /**
         * 启动流程实例,添加businessKey
         */
        @Test
        public void addBusinessKey(){
    //        1、得到ProcessEngine
            ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    //        2、得到RunTimeService
            RuntimeService runtimeService = processEngine.getRuntimeService();
    //        3、启动流程实例,同时还要指定业务标识businessKey,也就是出差申请单id,这里是1001
            ProcessInstance processInstance = runtimeService.
                    startProcessInstanceByKey("myEvection","1001");
    //        4、输出processInstance相关属性
            System.out.println("业务id=="+processInstance.getBusinessKey());
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    2. 查询流程实例

    流程在运行过程中可以查询流程实例的状态,当前运行结点等信息。

    @Test
    	public void queryProcessInstance() {
    		// 流程定义key
    		String processDefinitionKey = "evection";
            ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    		// 获取RunTimeService
    		RuntimeService runtimeService = processEngine.getRuntimeService();
    		List<ProcessInstance> list = runtimeService
    				.createProcessInstanceQuery()
    				.processDefinitionKey(processDefinitionKey)//
    				.list();
    
    		for (ProcessInstance processInstance : list) {
    			System.out.println("----------------------------");
    			System.out.println("流程实例id:"
    					+ processInstance.getProcessInstanceId());
    			System.out.println("所属流程定义id:"
    					+ processInstance.getProcessDefinitionId());
    			System.out.println("是否执行完成:" + processInstance.isEnded());
    			System.out.println("是否暂停:" + processInstance.isSuspended());
    			System.out.println("当前活动标识:" + processInstance.getActivityId());
    		}
    	}
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    注意:

    当系统查询流程实例时若是要同时显示比如出差天数,报销金额等处于业务系统数据库中的数据,则可以通过获取activiti数据中的businessKey,并以此当作主键,在业务表中进行查询

    3. 挂起、激活流程实例
  • 相关阅读:
    折半查找(C语言)
    uni-app去除页面头部的标题栏
    @Profile注解的作用是什么?
    汽车智能计算平台公司「地平线」在 Ingress Controller 的探索和实践
    二,几何相交-5,BO算法实现--(3)事件和操作
    LeetCode Python - 31.下一个排列
    P1025 [NOIP2001 提高组] 数的划分 题解
    PostgreSQL修炼之道笔记之基础篇(八)
    【 OpenGauss源码学习 —— 列存储(CU)(三)】
    canvas实现百度AI图片多主体识别效果
  • 原文地址:https://blog.csdn.net/qq_47267252/article/details/126630200