【工作流引擎】一文搞定Activiti核心知识点

Activiti

1.Activiti工作流介绍

1.1.工作流概念简介

(1)工作流系统

​ 工作流(Workflow),就是通过计算机对业务流程自动化执行管理。它主要解决的是“使在多个参与者之间按照某种预定义的规则自动进行传递文档、信息或任务的过程,从而实现某个预期的业务目标,或者促使此目标的实现”。

​ 一个软件系统中具有工作流的功能,我们把它称为工作流系统,一个系统中工作流的功能是什么?就是对系统的业务流程进行自动化管理,所以工作流是建立在业务流程的基础上,所以一个软件的系统核心根本上还是系统的业务流程,工作流只是协助进行业务流程管理。即使没有工作流业务系统也可以开发运行,只不过有了工作流可以更好的管理业务流程,提高系统的可扩展性。

​ 消费品行业,制造业,电信服务业,银证险等金融服务业,物流服务业,物业服务业,物业管理,大中型进出口贸易公司,政府事业机构,研究院所及教育服务业等,特别是大的跨国企业和集团公司。

(2)工作流的具体应用

  • 关键业务流程:订单、报价处理、合同审核、客户电话处理、供应链管理等

  • 行政管理类:出差申请、加班申请、请假申请、用车申请、各种办公用品申请、购买申请、日报周报等凡是原来手工流转处理的行政表单。

  • 人事管理类:员工培训安排、绩效考评、职位变动处理、员工档案信息管理等。

  • 财务相关类:付款请求、应收款处理、日常报销处理、出差报销、预算和计划申请等。

  • 客户服务类:客户信息管理、客户投诉、请求处理、售后服务管理等。

  • 特殊服务类:ISO系列对应流程、质量管理对应流程、产品数据信息管理、贸易公司报关处理、物流公司货物跟踪处理等各种通过表单逐步手工流转完成的任务均可应用工作流软件自动规范地实施。

(3)没有工作流之前流程的实现方式

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

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

  • 这是一种最为原始的方式。通过状态字段虽然做到了流程控制,但是当我们的流程发生变更的时候,这种方式所编写的代码也要进行调整。

1.2.Activiti概念简介

(1)什么是Activiti

Activiti是一个工作流引擎, activiti可以将业务系统中复杂的业务流程抽取出来,使用专门的建模语言BPMN2.0进行定义,业务流程按照预先定义的流程进行执行,实现了系统的流程由activiti进行管理,减少业务系统由于流程变更进行系统升级改造的工作量,从而提高系统的健壮性,同时也减少了系统开发维护成本。

官方网站:https://www.activiti.org

在这里插入图片描述

(2)什么是BPM软件

BPM(Business Process Management),即业务流程管理,是一种规范化的构造端到端的业务流程,以持续的提高组织业务效率。常见商业管理教育如EMBA、MBA等均将BPM包含在内。

BPM软件就是根据企业中业务环境的变化,推进人与人之间、人与系统之间以及系统与系统之间的整合及调整的经营方法与解决方案的IT工具。

通过BPM软件对企业内部及外部的业务流程的整个生命周期进行建模、自动化、管理监控和优化,使企业成本降低,利润得以大幅提升。

BPM软件在企业中应用领域广泛,凡是有业务流程的地方都可以BPM软件进行管理,比如企业人事办公管理、采购流程管理、公文审批流程管理、财务管理等。

(3)什么是BPMN

BPMN(Business Process Model AndNotation)- 业务流程模型和符号 是由BPMI(BusinessProcess Management Initiative)开发的一套标准的业务流程建模符号,使用BPMN提供的符号可以创建业务流程。

2004年5月发布了BPMN1.0规范.BPMI于2005年9月并入OMG(The Object Management Group对象管理组织)组织。OMG于2011年1月发布BPMN2.0的最终版本。

BPMN 是目前被各 BPM 厂商广泛接受的 BPM 标准。Activiti 就是使用 BPMN 2.0 进行流程建模、流程执行管理,它包括很多的建模符号。

BPMN常用的节点如图所示:

在这里插入图片描述

startEvent:开始节点,一个流程的开始,开始节点只能有一个。
endEvent:结束节点,一个流程的结束,可以有多个,不能没有(没有结束节点的流程,程序能运行,但是肯定不合理)。
flow:线节点,连接task,event,gateway这些节点,主要是控制流程的扭转。
task:节点,核心节点,表示一个审批节点,流程进度会停滞在这些task节点,需要调用API来推动流程的进行。
gateway:网关节点,其一个流程控制的作用,比如所有节点的下一个是网关节点的节点审核之后,会停在这个节点当且仅当这些节点全部审核结束,网关节点才会继续往后推动,网关节点也可以有多个出口。
3.Activiti使用步骤

(1)部署activiti

Activiti是一个工作流引擎(其实就是一堆jar包API),业务系统访问(操作)activiti的接口,就可以方便的操作流程相关数据,这样就可以把工作流环境与业务系统的环境集成在一起。

(2)定义流程

使用activiti流程建模工具(activity-designer)定义业务流程(.bpmn文件) 。

.bpmn文件就是业务流程定义文件,通过xml定义业务流程。

(3)流程定义部署

activiti部署业务流程定义(.bpmn文件)。

使用activiti提供的api把流程定义内容存储起来,在Activiti执行过程中可以查询定义的内容,Activiti执行把流程定义内容存储在数据库中。

(4)启动一个流程实例

流程实例也叫:ProcessInstance。

启动一个流程实例表示开始一次业务流程的运行。

在员工请假流程定义部署完成后,如果张三要请假就可以启动一个流程实例,如果李四要请假也启动一个流程实例,两个流程的执行互相不影响。

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

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

(6)用户办理任务

用户查询待办任务后,就可以办理某个任务,如果这个任务办理完成还需要其它用户办理,比如采购单创建后由部门经理审核,这个过程也是由activiti帮我们完成了。

(7)流程结束

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

2.Activiti的基本使用

2.1.创建项目整合Activiti

(1)创建Maven项目添加Activiti相关依赖

在这里插入图片描述

pom.xml文件加入依赖

    <properties>
        <slf4j.version>1.6.6</slf4j.version>
        <log4j.version>1.2.12</log4j.version>
        <activiti.version>7.0.0.Beta1</activiti.version>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </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>
        <!-- bpmn 模型处理 -->
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-bpmn-model</artifactId>
            <version>${activiti.version}</version>
        </dependency>
        <!-- bpmn 转换 -->
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-bpmn-converter</artifactId>
            <version>${activiti.version}</version>
        </dependency>
        <!-- bpmn json数据转换 -->
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-json-converter</artifactId>
            <version>${activiti.version}</version>
        </dependency>
        <!-- bpmn 布局 -->
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-bpmn-layout</artifactId>
            <version>${activiti.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>com.github.jgraph</groupId>
                    <artifactId>jgraphx</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- activiti 云支持 -->
        <dependency>
            <groupId>org.activiti.cloud</groupId>
            <artifactId>activiti-cloud-services-api</artifactId>
            <version>${activiti.version}</version>
        </dependency>
        <!-- mysql驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.40</version>
        </dependency>
        <!-- mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.5</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>
        <!-- log start -->
        <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>

(2)添加日志文件log4j.properties

log4j.rootCategory=debug, CONSOLE, LOGFILE
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r[%15.15t] %-5p %30.30c %x - %m\n
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=/Users/mac/IdeaProjects/activiti-demo/activiti-demo/logs/activiti.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r[%15.15t] %-5p %30.30c %x - %m\n

(3)添加Activiti配置文件

Activiti的默认的使用方式是要求我们在resources下创建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 class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration" id="processEngineConfiguration">
        <property name="jdbcDriver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="jdbcUrl" value="jdbc:mysql://ip:端口/activiti?characterEncoding=utf-8&amp;nullCatalogMeansCurrent=true&amp;serverTimezone=UTC" />

        <property name="jdbcUsername" value="root" />
        <property name="jdbcPassword" value="123456" />
        <property name="databaseSchemaUpdate" value="true" />
        <!--<property name="dataSource" ref="dataSource" />-->
    </bean>
    <bean class="org.apache.commons.dbcp.BasicDataSource" id="dataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
                <property name="url" value="jdbc:mysql://ip:端口/activiti?characterEncoding=utf-8&amp;nullCatalogMeansCurrent=true&amp;serverTimezone=UTC" />

        <property name="username" value="root"/>
        <property name="password" value="123456"/>
        <property name="maxActive" value="3" />
        <property name="maxIdle" value="2" />
    </bean>
</beans>

(4)部署MySQL8,创建activiti数据库

在这里插入图片描述

在这里插入图片描述

(5)初始化数据库

创建一个工具类,调用Activiti的工具类来生成activiti需要的表结构

/**
 * @author lixiang
 * @date 2023/12/17 22:34
 */
public class ActivitiTest {
    
    
    /**
     * 生成Activiti的相关的表结构
     */
    @Test
    public void test(){
    
    
        // 使用classpath下的activiti.cfg.xml中的配置来创建 ProcessEngine对象
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        System.out.println(engine);
    }
}

在这里插入图片描述

在这里插入图片描述

2.2.Activiti表结构介绍

(1) 表的命名规则和作用

Activiti 的表都以 ACT_ 开头。

ACT_RE:'RE’表示 repository。 这个前缀的表包含了流程定义和流程静态资源 (图片,规则,等等)。

ACT_RU:'RU’表示 runtime。 这些运行时的表,包含流程实例,任务,变量,异步任务,等运行中的数据。 Activiti 只在流程实例执行过程中保存这些数据, 在流程结束时就会删除这些记录。 这样运行时表可以一直很小速度很快。

ACT_HI:'HI’表示 history。 这些表包含历史数据,比如历史流程实例, 变量,任务等等。

ACT_GE : GE 表示 general。 通用数据, 用于不同场景下 。

(2)Activiti数据表介绍

  • 一般数据
ACT_GE_BYTEARRAY:通用的流程定义和流程资源
ACT_GE_PROPERTY:系统相关属性
  • 流程历史记录
ACT_HI_ACTINST:历史的流程实例
ACT_HI_ATTACHMENT:历史的流程附件
ACT_HI_COMMENT:历史的说明性信息
ACT_HI_DETAIL:历史的流程运行中的细节信息
ACT_HI_IDENTITYLINK:历史的流程运行过程中用户关系
ACT_HI_PROCINST:历史的流程实例
ACT_HI_TASKINST:历史的任务实例
ACT_HI_VARINST:历史的流程运行中的变量信息
  • 流程定义表
ACT_RE_DEPLOYMENT:部署单元信息
ACT_RE_MODEL:模型信息
ACT_RE_PROCDEF:已部署的流程定义
  • 运行实例表
ACT_RU_EVENT_SUBSCR:运行时事件
ACT_RU_EXECUTION:运行时流程执行实例
ACT_RU_IDENTITYLINK:运行时用户关系信息,存储任务节点与参与者的相关信息
ACT_RU_JOB:运行时作业
ACT_RU_TASK:运行时任务
ACT_RU_VARIABLE:运行时变量表
2.3.ProcessEngine创建方式
		/**
     * 自定义的方式来加载配置文件
     */
    @Test
    public void test(){
    
    
        // 首先创建ProcessEngineConfiguration对象
        ProcessEngineConfiguration configuration =
                ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml");
        // 通过ProcessEngineConfiguration对象来创建 ProcessEngine对象
        ProcessEngine processEngine = configuration.buildProcessEngine();
    }
2.4.Servcie服务接口
service名称 service作用
RepositoryService activiti的资源管理类
RuntimeService activiti的流程运行管理类
TaskService activiti的任务管理类
HistoryService activiti的历史管理类
ManagerService activiti的引擎管理类

(1)RepositoryService

Activiti的资源管理类,提供了管理和控制流程发布包和流程定义的操作。使用工作流建模工具设计的业务流程图需要使用此service将流程定义文件的内容部署到计算机。

除了部署流程定义以外还可以:查询引擎中的发布包和流程定义。

暂停或激活发布包,对应全部和特定流程定义。 暂停意味着它们不能再执行任何操作了,激活是对应的反向操作。获得多种资源,像是包含在发布包里的文件, 或引擎自动生成的流程图。

获得流程定义的pojo版本, 可以用来通过java解析流程,而不必通过xml。

(2)RuntimeService

Activiti的流程运行管理类。可以从这个服务类中获取很多关于流程执行相关的信息

(3)TaskService

Activiti的任务管理类。可以从这个类中获取任务的信息。

(4)HistoryService

Activiti的历史管理类,可以查询历史信息,执行流程时,引擎会保存很多数据(根据配置),比如流程实例启动时间,任务的参与者, 完成任务的时间,每个流程实例的执行路径,等等。 这个服务主要通过查询功能来获得这些数据。

(5)ManagementService

Activiti的引擎管理类,提供了对 Activiti 流程引擎的管理和维护功能,这些功能不在工作流驱动的应用程序中使用,主要用于 Activiti 系统的日常维护。

2.5.流程图标介绍

BPMN 2.0是业务流程建模符号2.0的缩写。

它由Business Process Management Initiative这个非营利协会创建并不断发展。作为一种标识,BPMN 2.0是使用一些符号来明确业务流程设计流程图的一整套符号规范,它能增进业务建模时的沟通效率。

目前BPMN2.0是最新的版本,它用于在BPM上下文中进行布局和可视化的沟通。

接下来我们先来了解在流程设计中常见的 符号。

BPMN2.0的基本符合主要包含:

(1)事件 Event

在这里插入图片描述

(2)活动 Activity

活动是工作或任务的一个通用术语。一个活动可以是一个任务,还可以是一个当前流程的子处理流程; 其次,你还可以为活动指定不同的类型。常见活动如下:

在这里插入图片描述

(3)网关 GateWay

网关用来处理决策,有几种常用网关需要了解:

在这里插入图片描述

排他网关 (x)

只有一条路径会被选择。流程执行到该网关时,按照输出流的顺序逐个计算,当条件的计算结果为true时,继续执行当前网关的输出流;

如果多条线路计算结果都是 true,则会执行第一个值为 true 的线路。如果所有网关计算结果没有true,则引擎会抛出异常。

排他网关需要和条件顺序流结合使用,default 属性指定默认顺序流,当所有的条件不满足时会执行默认顺序流。

并行网关 (+)

所有路径会被同时选择

拆分 —— 并行执行所有输出顺序流,为每一条顺序流创建一个并行执行线路。

合并 —— 所有从并行网关拆分并执行完成的线路均在此等候,直到所有的线路都执行完成才继续向下执行。

包容网关 (+)

可以同时执行多条线路,也可以在网关上设置条件

拆分 —— 计算每条线路上的表达式,当表达式计算结果为true时,创建一个并行线路并继续执行

合并 —— 所有从并行网关拆分并执行完成的线路均在此等候,直到所有的线路都执行完成才继续向下执行。

事件网关 (+)

专门为中间捕获事件设置的,允许设置多个输出流指向多个不同的中间捕获事件。当流程执行到事件网关后,流程处于等待状态,需要等待抛出事件才能将等待状态转换为活动状态。

(4)流向 Flow

流是连接两个流程节点的连线。常见的流向包含以下几种:

在这里插入图片描述

3.Activiti流程操作

3.1.流程的部署

(1)IDEA增加插件

在这里插入图片描述

(2)绘制流程图

首先创建LeaveApprovalProcess.bpmn20.xml文件,点击右键进行流程图的绘制。

在这里插入图片描述

然后进行流程图的绘制,右键添加元素。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

设置完成后,右键可以保存成图片。

在这里插入图片描述

(3)部署bpmn20.xml和png文件

  • 单文件部署
   @Test
    public void test1(){
    
    
        // 1.创建ProcessEngine对象
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        // 2.获取RepositoryService进行部署操作
        RepositoryService repositoryService = engine.getRepositoryService();
        // 3.使用RepositoryService进行部署
        Deployment deploy = repositoryService.createDeployment()
                // 添加bpmn资源
                .addClasspathResource("bpmn/LeaveApprovalProcess.bpmn20.xml")
                // 添加png资源
                .addClasspathResource("bpmn/LeaveApprovalProcess.png")
                //禁止校验文件
                .disableSchemaValidation()
                .name("请假申请流程")
                .deploy();//部署流程
        // 4.查看部署结果
        System.out.println("流程部署的id:" + deploy.getId());
        System.out.println("流程部署的名称:" + deploy.getName());
    }
  • 压缩包部署
		@Test
    public void test2(){
    
    
        // 1.定义zip文件的输入流
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("bpmn/evection.zip");
        // 2.对 inputStream 做装饰
        assert inputStream != null;
        ZipInputStream zipInputStream = new ZipInputStream(inputStream);
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        // 3.使用RepositoryService进行部署
        RepositoryService repositoryService = engine.getRepositoryService();
        Deployment deploy = repositoryService.createDeployment()
                .addZipInputStream(zipInputStream)
                //禁止校验文件
                .disableSchemaValidation()
                .name("请假申请流程")
                .deploy();
        // 4.输出流程部署的信息
        System.out.println("流程部署的id:" + deploy.getId());
        System.out.println("流程部署的名称:" + deploy.getName());
    }

在这里插入图片描述

(4)操作的数据表

流程定义部署后操作activiti中的三张表:

  • ACT_RE_DEPLOYMENT:流程定义部署表,每部署一次就增加一条记录。

在这里插入图片描述

  • ACT_RE_PROCDEF:流程定义表,部署每个新的流程定义都会在这表中插入一条数据。

在这里插入图片描述

  • ACT_GE_BYTEARRAY:流程资源表,流程部署的bpmn文件和png图片都一保存在该表中。

在这里插入图片描述

3.2.启动流程实例

流程定义部署在Activiti后就可以通过工作流管理业务流程,也就是说上边部署的请假申请流程可以使用了。

针对该流程,启动一个流程表示发起一个新的出差申请单,这就相当于Java类和Java对象的关系,类定义好了后需要new创建一个对象使用,当然可以new出多个对象来,对于出差申请流程,张三可以发起一个出差申请单需要启动一个流程实例。

		@Test
    public void test3(){
    
    
        // 1.创建ProcessEngine对象
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        // 2.获取RuntimeService对象
        RuntimeService runtimeService = engine.getRuntimeService();
        // 3.根据流程定义的ID启动流程
        String id = "LeaveApprovalProcess";
        Map<String,Object> assignee = new HashMap<>();
        assignee.put("assignee1","zhangsan");
        assignee.put("assignee2","lisi");
        assignee.put("assignee3","wangwu");
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(id,assignee);
        // 4.输出相关的流程实例信息
        System.out.println("流程定义的ID:" + processInstance.getProcessDefinitionId());
        System.out.println("流程实例的ID:" + processInstance.getId());
        System.out.println("当前活动的ID:" + processInstance.getActivityId());
    }

在这里插入图片描述

启动流程实例涉及到的表结构:

  • ACT_HI_ACTINST 流程实例执行历史

在这里插入图片描述

  • ACT_HI_IDENTITYLINK 流程的参与用户的历史信息

在这里插入图片描述

  • ACT_HI_PROCINST 流程实例历史信息

在这里插入图片描述

  • ACT_HI_TASKINST 流程任务历史信息

在这里插入图片描述

  • ACT_RU_EXECUTION 流程执行信息

在这里插入图片描述

  • ACT_RU_IDENTITYLINK 流程的参与用户信息

在这里插入图片描述

  • ACT_RU_TASK 任务信息

在这里插入图片描述

3.3.流程任务查找

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

    @Test
    public void test4(){
    
    
        String assignee ="lisi";
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        // 任务查询 需要获取一个 TaskService 对象
        TaskService taskService = engine.getTaskService();
        // 根据流程的key和任务负责人 查询任务
        List<Task> list = taskService.createTaskQuery()
                .processDefinitionKey("LeaveApprovalProcess")
                .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());
        }
    }

在这里插入图片描述

3.4.流程任务处理

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

    @Test
    public void test5(){
    
    
        String assignee ="zhangsan";
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        // 1.任务查询 需要获取一个 TaskService 对象
        TaskService taskService = engine.getTaskService();
        // 2.根据流程的key和任务负责人 查询任务
        Task process = taskService.createTaskQuery()
                .processDefinitionKey("LeaveApprovalProcess")
                .taskAssignee(assignee)
                .singleResult();
        // 3.完成任务
        taskService.complete(process.getId());
    }

zhangsan处理了这个操作后,流程就流转到了 lisi处。

在这里插入图片描述

然后就是不同的用户登录,然后查询任务处理任务,直到任务流程走完。

3.5.流程定义的查询

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

    @Test
    public void test6(){
    
    
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        RepositoryService repositoryService = engine.getRepositoryService();
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
        List<ProcessDefinition> definitionList = processDefinitionQuery
                // 指定key
                .processDefinitionKey("LeaveApprovalProcess")
                // 安装版本排序
                .orderByProcessDefinitionVersion()
                // 倒序
                .desc()
                .list();
        //输出流程定义的信息
        definitionList.forEach(obj->{
    
    
            System.out.println("流程定义的ID:"+obj.getId());
            System.out.println("流程定义的name:"+obj.getName());
            System.out.println("流程定义的key:"+obj.getKey());
            System.out.println("流程定义的version:"+obj.getVersion());
            System.out.println("流程部署的ID:"+obj.getDeploymentId());
        });
    }

在这里插入图片描述

3.6.流程的删除
    @Test
    public void test7(){
    
    
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        RepositoryService repositoryService = engine.getRepositoryService();
        // 删除流程定义,如果该流程定义已经有了流程实例启动则删除时报错
        // 设置为TRUE 级联删除流程定义,及时流程有实例启动,也可以删除,设置为false 非级联删除操作.
        repositoryService.deleteDeployment("10001",true);
    }
3.7.流程历史信息查看

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

    @Test
    public void test8(){
    
    
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        HistoryService historyService = engine.getHistoryService();
        HistoricActivityInstanceQuery historicActivityInstanceQuery = historyService.createHistoricActivityInstanceQuery();
        List<HistoricActivityInstance> instanceList = historicActivityInstanceQuery
                .processDefinitionId("LeaveApprovalProcess:1:17504")
                .orderByHistoricActivityInstanceStartTime()
                .desc()
                .list();
        //输出查询的结果
        instanceList.forEach(obj->{
    
    
            System.out.println("activityId:"+obj.getActivityId());
            System.out.println("activityName:"+obj.getActivityName());
            System.out.println("activityType:"+obj.getActivityType());
            System.out.println("assignee:"+obj.getAssignee());
            System.out.println("processDefinitionId:"+obj.getProcessDefinitionId());
            System.out.println("processInstanceId:"+obj.getProcessInstanceId());
            System.out.println("-----------------------");
        });
    }

在这里插入图片描述

4.Activiti进阶操作

4.1.流程实例操作

(1)什么是流程实例

流程实例(ProcessInstance)代表流程定义的执行实例。

一个流程实例包括了所有的运行节点,我们可以利用这个对象来了解当前流程实例的进度等信息。

例如:用户或者程序安装流程定义的内容发起了一个流程,这个就是一个流程实例。

在这里插入图片描述

(2)业务管理

流程定义部署在Activiti后,我们就可以在系统中通过Activiti去管理流程的执行,但是如果我们要将我们的流程实例和业务数据关联,这时我们需要使用到Activiti中预留的BusinessKey(业务标识)来关联。

比如:请假流程启动一个流程实例,就可以将单的id作为业务标识存储到activiti中,将来查询activiti的流程实例信息就可以获取出差单的id从而关联查询业务系统数据库得到出差单信息。

		@Test
    public void test9(){
    
    
        // 1.创建ProcessEngine对象
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        // 2.获取RuntimeService对象
        RuntimeService runtimeService = engine.getRuntimeService();
        // 3.根据流程定义的ID启动流程
        String id = "LeaveApprovalProcess";
        Map<String,Object> assignee = new HashMap<>();
        assignee.put("assignee1","zhangsan");
        assignee.put("assignee2","lisi");
        assignee.put("assignee3","wangwu");
        // 4.定义业务ID
        String businessKey = "100010";
        ProcessInstance processInstance = runtimeService.startProcessInstanceById(id,businessKey,assignee);
        // 5.输出相关的流程实例信息
        System.out.println("流程定义的ID:" + processInstance.getProcessDefinitionId());
        System.out.println("流程实例的ID:" + processInstance.getId());
        System.out.println("当前活动的ID:" + processInstance.getActivityId());
        System.out.println("businessKey = "+processInstance.getBusinessKey());
    }

在这里插入图片描述

(3)流程实例的挂起和激活

在实际场景中可能由于流程变更需要将当前运行的流程暂停而不是删除,流程暂停后将不能继续执行。

  • 全部流程挂起

操作流程的定义为挂起状态,该流程定义下边所有的流程实例全部暂停。

流程定义为挂起状态,该流程定义将不允许启动新的流程实例,同时该流程定义下的所有的流程实例都将全部挂起暂停执行。

    @Test
    public void test10(){
    
    
        // 1.获取ProcessEngine对象
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        // 2.获取RepositoryService对象
        RepositoryService repositoryService = engine.getRepositoryService();
        // 3.查询流程定义的对象
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionKey("LeaveApprovalProcess")
                .singleResult();
        // 4.获取当前流程定义的状态
        boolean suspended = processDefinition.isSuspended();
        String id = processDefinition.getId();
        // 5.如果挂起就激活,如果激活就挂起
        if(suspended){
    
    
            // 表示当前定义的流程状态是 挂起的
            repositoryService.activateProcessDefinitionById(
                    id // 流程定义的id
                    ,true // 是否激活
                    ,null // 激活时间
            );
            System.out.println("流程定义:" + id + ",已激活");
        }else{
    
    
            // 非挂起状态,激活状态 那么需要挂起流程定义
            repositoryService.suspendProcessDefinitionById(
                    id // 流程id
                    ,true // 是否挂起
                    ,null // 挂起时间
            );
            System.out.println("流程定义:" + id + ",已挂起");
        }
    }

在这里插入图片描述

挂起流程定义后,对于的实例对象中的状态会修改为2。

在这里插入图片描述

然后再去操作对于的流程实例会抛异常信息。

在这里插入图片描述

我们再将挂起的流程转变为激活状态,对于的状态值会从2更新为1。

然后就是业务流程可以正常处理了

  • 单个实例挂起
    /**
     * 单个流程实例挂起与激活
     */
    @Test
    public void test03(){
    
    
        // 1.获取ProcessEngine对象
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        // 2.获取RuntimeService
        RuntimeService runtimeService = engine.getRuntimeService();
        // 3.获取流程实例对象
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId("20001")
                .singleResult();
        // 4.获取相关的状态操作
        boolean suspended = processInstance.isSuspended();
        String id = processInstance.getId();
        if(suspended){
    
    
            // 挂起--》激活
            runtimeService.activateProcessInstanceById(id);
            System.out.println("流程定义:" + id + ",已激活");
        }else{
    
    
            // 激活--》挂起
            runtimeService.suspendProcessInstanceById(id);
            System.out.println("流程定义:" + id + ",已挂起");
        }

    }

在这里插入图片描述

4.2.个人任务操作

(1)分配任务责任人

  • 固定分配

在进行业务流程建模的时候指定固定的任务负责人

在这里插入图片描述

在Properties视图中,填写Assiginee项为任务负责人。

  • 表达式分配

在这里插入图片描述

案例请看《3.2.启动流程实例》

(2)查询任务

在 activiti 实际应用时,查询待办任务可能要显示出业务系统的一些相关信息。

比如:查询待审批出差任务列表需要将出差单的日期、 出差天数等信息显示出来。

出差天数等信息在业务系统中存在,而并没有在 activiti 数据库中存在,所以是无法通过 activiti 的 api 查询到出差天数等信息。

在查询待办任务时,通过 businessKey(业务标识 )关联查询业务系统的出差单表,查询出出差天数等信息。

		@Test
    public void findProcessInstance(){
    
    
        //获取processEngine
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //获取TaskService
        TaskService taskService = processEngine.getTaskService();
        //获取RuntimeService
        RuntimeService runtimeService = processEngine.getRuntimeService();
        //查询流程定义的对象
        Task task = taskService.createTaskQuery()
                .processDefinitionKey("LeaveApprovalProcess")
                .taskAssignee("zhangsan")
                .processInstanceId("25001")
                .singleResult();
        //使用task对象获取实例id
        String processInstanceId = task.getProcessInstanceId();
        //使用实例id,获取流程实例对象
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();
        //使用processInstance,得到 businessKey
        String businessKey = processInstance.getBusinessKey();
        System.out.println("businessKey=="+businessKey);
    }

在这里插入图片描述

(3)办理任务

注意:在实际应用中,完成任务前需要校验任务的负责人是否具有该任务的办理权限 。

		@Test
    public void completeTask() {
    
    
        //任务id
        String taskId = "25001";
        //任务负责人
        String assingee = "zhangsan";
        //获取processEngine
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //创建TaskService
        TaskService taskService = processEngine.getTaskService();
        //完成任务前,需要校验该负责人可以完成当前任务
        //校验方法:根据任务id和任务负责人查询当前任务,如果查到该用户有权限,就完成
        Task task = taskService.createTaskQuery()
                .taskId(taskId)
                .taskAssignee(assingee)
                .singleResult();
        if(task != null){
    
    
            taskService.complete(taskId);
            System.out.println("完成任务");
        }
    }
4.3.流程变量操作

(1)什么是流程变量

流程变量在 activiti 中是一个非常重要的角色,流程运转有时需要靠流程变量,业务系统和 activiti结合时少不了流程变量,流程变量就是 activiti 在管理工作流时根据管理需要而设置的变量。

比如:在请假申请流程流转时如果出差天数大于 3 天则由总监审核,否则由经理直接审核, 出差天数就可以设置为流程变量,在流程流转时使用。

注意:虽然流程变量中可以存储业务数据可以通过activiti的api查询流程变量从而实现 查询业务数据,但是不建议这样使用,因为业务数据查询由业务系统负责,activiti设置流程变量是为了流程执行需要而创建。

(2)流程变量类型

如果将 pojo 存储到流程变量中,必须实现序列化接口 serializable,为了防止由于新增字段无法反序列化,需要生成 serialVersionUID。

(3)流程变量作用域

流程变量的作用域可以是一个流程实例(processInstance),或一个任务(task),或一个执行实例(execution)。

globa变量:流程变量的默认作用域是流程实例。当一个流程变量的作用域为流程实例时,可以称为 global 变量。

  • Global变量:userId(变量名)、zhangsan(变量值)。

  • global 变量中变量名不允许重复,设置相同名称的变量,后设置的值会覆盖前设置的变量值。

local变量:任务和执行实例仅仅是针对一个任务和一个执行实例范围,范围没有流程实例大, 称为 local 变量。

  • Local 变量由于在不同的任务或不同的执行实例中,作用域互不影响,变量名可以相同没有影响。Local 变量名也可以和 global 变量名相同,没有影响。

(4)globa变量使用

员工创建请假申请单,3天以下由经理直接申批,3天以上先由经理审批,经理审批通过后再由总监审批。

在这里插入图片描述

  • 部署流程
		@Test
    public void test1(){
    
    
        // 1.创建ProcessEngine对象
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        // 2.获取RepositoryService进行部署操作
        RepositoryService repositoryService = engine.getRepositoryService();
        // 3.使用RepositoryService进行部署
        Deployment deploy = repositoryService.createDeployment()
                // 添加bpmn资源
                .addClasspathResource("bpmn/LeaveApprovalProcess.bpmn20.xml")
                // 添加png资源
                .addClasspathResource("bpmn/LeaveApprovalProcess.png")
                //禁止校验文件
                .disableSchemaValidation()
                .name("请假申请流程(流程变量)")
                .deploy();//部署流程
        // 4.查看部署结果
        System.out.println("流程部署的id:" + deploy.getId());
        System.out.println("流程部署的名称:" + deploy.getName());
    }
  • 启动时设置流程变量

在启动流程时设置流程变量,变量的作用域是整个流程实例。

    /**
     * 启动流程实例,设置流程变量
     */
		@Test
    public void test3(){
    
    
        // 1.创建ProcessEngine对象
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        // 2.获取RuntimeService对象
        RuntimeService runtimeService = engine.getRuntimeService();
        // 3.根据流程定义的ID启动流程
        String id = "LeaveApprovalProcess";
        Map<String,Object> assignee = new HashMap<>();
        assignee.put("assignee1","zhangsan");
        assignee.put("assignee2","lisi");
        assignee.put("assignee3","wangwu");
        //设置num天数
        assignee.put("num",2);
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(id,assignee);
        // 4.输出相关的流程实例信息
        System.out.println("流程定义的ID:" + processInstance.getProcessDefinitionId());
        System.out.println("流程实例的ID:" + processInstance.getId());
        System.out.println("当前活动的ID:" + processInstance.getActivityId());
    }

		/**
     * 完成任务
     */
    @Test
    public void test03(){
    
    
        String key = "LeaveApprovalProcess";
        String assignee = "zhangsan";
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = engine.getTaskService();
        Task task = taskService.createTaskQuery()
                .processDefinitionKey(key)
                .taskAssignee(assignee)
                .singleResult();
        if(task != null){
    
    
            taskService.complete(task.getId());
            System.out.println("任务执行完成...");
        }
    }

通过startProcessInstanceByKey方法设置流程变量的作用域是一个流程实例,流程变量使用Map存储,同一个流程实例map中的key相同,后者会覆盖前者。

  • 任务办理时设置

在完成任务时设置流程变量,该流程变量只有在该任务完成后其它结点才可使用该变量,它的作用域是整个流程实例,如果设置的流程变量的key在流程实例中已存在相同的名字则后设置的变量替换前边设置的变量。

这里需要在创建出差单任务完成时设置流程变量。

		/**
     * 启动流程实例,设置流程变量
     */
    @Test
    public void test02(){
    
    
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        RuntimeService runtimeService = engine.getRuntimeService();
        // 流程定义key
        String key = "LeaveApprovalProcess";
        // 创建变量集合
        Map<String,Object> variables = new HashMap<>();
        
        // 设置assignee的取值
        variables.put("assignee1","zhangsan");
        variables.put("assignee2","lisi");
        variables.put("assignee3","wangwu");
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(key, variables);
        // 输出信息
        System.out.println("获取流程实例名称:"+processInstance.getName());
        System.out.println("流程定义ID:" + processInstance.getProcessDefinitionId());
    }

    /**
     * 完成任务
     */
    @Test
    public void test03(){
    
    
        String key = "LeaveApprovalProcess";
        String assignee = "zhangsan";
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = engine.getTaskService();
        Task task = taskService.createTaskQuery()
                .processDefinitionKey(key)
                .taskAssignee(assignee)
                .singleResult();
        Map<String,Object> variables = new HashMap<>();
        variables.put("num",4);
        if(task != null){
    
    
            taskService.complete(task.getId(),variables);
            System.out.println("任务执行完成...");
        }
    }

通过当前任务设置流程变量,需要指定当前任务id,如果当前执行的任务id不存在则抛出异常。

任务办理时也是通过map<key,value>设置流程变量,一次可以设置多个变量。

  • 当前流程实例设置

通过流程实例id设置全局变量,该流程实例必须未执行完成。

    @Test
    public void setGlobalVariableByExecutionId() {
    
    
        //当前流程实例执行 id,通常设置为当前执行的流程实例
        String executionId = "2601";
        //获取processEngine
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //获取RuntimeService
        RuntimeService runtimeService = processEngine.getRuntimeService();
        //通过流程实例 id设置流程变量
        runtimeService.setVariable(executionId, "num", 3);
        //一次设置多个值
        //runtimeService.setVariables(executionId, variables)
    }

executionId必须当前未结束 流程实例的执行id,通常此id设置流程实例 的id。也可以通runtimeService.getVariable()获取流程变量。

  • 当前任务设置
    @Test
    public void setGlobalVariableByTaskId(){
    
    
        //当前待办任务id
        String taskId="1404";
        //获取processEngine
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = processEngine.getTaskService();
        //通过任务设置流程变量
        taskService.setVariable(taskId, "num", 3);
        //一次设置多个值
        //taskService.setVariables(taskId, variables)
    }

任务id必须是当前待办任务id,act_ru_task中存在。如果该任务已结束,会报错

也可以通过taskService.getVariable()获取流程变量。

(5)local变量使用

任务办理时设置local流程变量,当前运行的流程实例只能在该任务结束前使用,任务结束该变量无法在当前流程实例使用,可以通过查询历史任务查询。

    @Test
    public void completeTask() {
    
    
        //任务id
        String taskId = "1404";
        //获取processEngine
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = processEngine.getTaskService();
        //定义流程变量
        Map<String, Object> variables = new HashMap<>();
        //变量名是holiday,变量值是holiday对象
        variables.put("num", 3);
        //设置local变量,作用域为该任务
        taskService.setVariablesLocal(taskId, variables);
        //完成任务
        taskService.complete(taskId);
    }

注意:任务id必须是当前待办任务id,act_ru_task中存在。

4.4.组任务操作

在流程定义中在任务结点的 assignee 固定设置任务负责人,在流程定义时将参与者固定设置在.bpmn 文件中,如果临时任务负责人变更则需要修改流程定义,系统可扩展性差。

针对这种情况可以给任务设置多个候选人,可以从候选人中选择参与者来完成任务。

(1)设置任务候选人

在流程图中任务节点的配置中设置 candidate-users(候选人),多个候选人之间用逗号分开。

在这里插入图片描述

(2)组任务办理流程

  • 查询组任务

    • 指定候选人,查询该候选人当前的待办任务。
    • 候选人不能立即办理任务。
  • 拾取(claim)任务

    • 该组任务的所有候选人都能拾取。
    • 将候选人的组任务,变成个人任务。原来候选人就变成了该任务的负责人。
    • 如果拾取后不想办理该任务,需要将已经拾取的个人任务归还到组里边,将个人任务变成了组任务。
  • 查询个人任务

    • 查询方式同个人任务部分,根据assignee查询用户负责的个人任务。
  • 办理个人任务

(3)查询组任务

根据候选人查询组任务

		@Test
    public void test01(){
    
    
        String key = "LeaveApprovalProcessGroup";
        String candidateUser = "lisi";
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = engine.getTaskService();
        List<Task> list = taskService.createTaskQuery()
                .processDefinitionKey(key)
                .taskCandidateUser(candidateUser)
                .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());
        }
    }

在这里插入图片描述

(4)拾取组任务

候选人员拾取组任务后该任务变为自己的个人任务。

    @Test
    public void test04(){
    
    
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = engine.getTaskService();
        String taskId = "50002";
        // 候选人
        String userId = "lisi";
        // 拾取任务
        Task task = taskService.createTaskQuery()
                .taskId(taskId)
                .taskCandidateUser(userId) // 根据候选人查询
                .singleResult();
        if(task != null){
    
    
            // 可以拾取任务
            taskService.claim(taskId,userId);
            System.out.println("拾取成功");
        }
    }

在这里插入图片描述

lisi拾取之后查询个人待办任务,这会lisi就可以办理该任务了。

在这里插入图片描述

(5)归还组任务

如果个人不想办理该组任务,可以归还组任务,归还后该用户不再是该任务的负责人。

		@Test
    public void test06(){
    
    
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = engine.getTaskService();
        String taskId = "50002";
        String userId= "lisi";
        Task task = taskService.createTaskQuery()
                .taskId(taskId)
                .taskAssignee(userId)
                .singleResult();
        if(task != null){
    
    
            // 如果设置为null,归还组任务,任务没有负责人
            taskService.setAssignee(taskId,null);
            System.out.println("归还成功");
        }
    }

在这里插入图片描述

(6)任务交接

任务负责人将任务交给其他负责人来处理。

		@Test
    public void test07(){
    
    
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = engine.getTaskService();
        String taskId = "50002";
        String userId= "lisi";
        Task task = taskService.createTaskQuery()
                .taskId(taskId)
                .taskAssignee(userId)
                .singleResult();
        if(task != null){
    
    
            // 设置该任务的新的负责人
            taskService.setAssignee(taskId,"zhaoliu");
            System.out.println("任务移交给zhaoliu");
        }
    }

在这里插入图片描述

4.5.网关操作

(1) 排他网关ExclusiveGateway

排他网关,用来在流程中实现决策。 当流程执行到这个网关,所有分支都会判断条件是否为true,如果为true则执行该分支,

注意:排他网关只会选择一个为true的分支执行。如果有两个分支条件都为true,排他网关会选择id值较小的一条分支去执行。

在这里插入图片描述

    /**
     * 单个文件部署
     */
    @Test
    public void test1() {
    
    
        // 1.创建ProcessEngine对象
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        // 2.获取RepositoryService进行部署操作
        RepositoryService repositoryService = engine.getRepositoryService();
        // 3.使用RepositoryService进行部署
        Deployment deploy = repositoryService.createDeployment()
                // 添加bpmn资源
                .addClasspathResource("bpmn/CompensatoryLeave.bpmn20.xml")
                // 添加png资源
                .addClasspathResource("bpmn/CompensatoryLeave.png")
                //禁止校验文件
                .disableSchemaValidation()
                .name("调休申请流程")
                .deploy();//部署流程
        // 4.查看部署结果
        System.out.println("流程部署的id:" + deploy.getId());
        System.out.println("流程部署的名称:" + deploy.getName());
    }

    /**
     * 启动两个流程实例,一个为2天,一个为4天
     */
    @Test
    public void test3() {
    
    
        // 1.创建ProcessEngine对象
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        // 2.获取RuntimeService对象
        RuntimeService runtimeService = engine.getRuntimeService();
        // 3.根据流程定义的ID启动流程
        String id = "CompensatoryLeave";
        Map<String, Object> assignee = new HashMap<>();
        assignee.put("user1", "ZhangSan");
        assignee.put("user2", "LiSi");
        assignee.put("user3", "WangWu");
        assignee.put("user4", "ZhaoLiu");
      	// assignee.put("num", 2);
        assignee.put("num", 4);
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(id, assignee);
        // 4.输出相关的流程实例信息
        System.out.println("流程定义的ID:" + processInstance.getProcessDefinitionId());
        System.out.println("流程实例的ID:" + processInstance.getId());
        System.out.println("当前活动的ID:" + processInstance.getActivityId());
    }

    /**
     * 流程任务处理
     */
    @Test
    public void test5() {
    
    
      	// 设置审批人
        String assignee = "LiSi";
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        // 1.任务查询 需要获取一个 TaskService 对象
        TaskService taskService = engine.getTaskService();
        // 2.根据流程的key和任务负责人 查询任务
        Task process = taskService.createTaskQuery()
                .processDefinitionKey("CompensatoryLeave")
                .taskAssignee(assignee)
                .singleResult();
        // 3.完成任务
        taskService.complete(process.getId());
    }

在这里插入图片描述

如果从网关出去的线所有条件都不满足则系统抛出异常。

org.activiti.engine.ActivitiException: No outgoing sequence flow of the exclusive gateway 'exclusivegateway1' could be selected for continuing the process
   at org.activiti.engine.impl.bpmn.behavior.ExclusiveGatewayActivityBehavior.leave(ExclusiveGatewayActivityBehavior.java:85)

(2)并行网关ParallelGateway

并行网关允许将流程分成多条分支,也可以把多条分支汇聚到一起,并行网关的功能是基于进入和外出顺序流的:

fork分支:并行后的所有外出顺序流,为每个顺序流都创建一个并发分支。

join汇聚:所有到达并行网关,在此等待的进入分支, 直到所有进入顺序流的分支都到达以后, 流程就会通过汇聚网关。

注意,如果同一个并行网关有多个进入和多个外出顺序流, 它就同时具有分支和汇聚功能。 这时,网关会先汇聚所有进入的顺序流,然后再切分成多个并行分支。

与其他网关的主要区别是,并行网关不会解析条件。 即使顺序流中定义了条件,也会被忽略。

技术经理和项目经理是两个execution分支,在act_ru_execution表有两条记录分别是技术经理和项目经理,act_ru_execution还有一条记录表示该流程实例。

待技术经理和项目经理任务全部完成,在汇聚点汇聚,通过parallelGateway并行网关。

并行网关在业务应用中常用于会签任务,会签任务即多个参与者共同办理的任务。

在这里插入图片描述

		/**
     * 单个文件部署
     */
    @Test
    public void test1() {
    
    
        // 1.创建ProcessEngine对象
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        // 2.获取RepositoryService进行部署操作
        RepositoryService repositoryService = engine.getRepositoryService();
        // 3.使用RepositoryService进行部署
        Deployment deploy = repositoryService.createDeployment()
                // 添加bpmn资源
                .addClasspathResource("bpmn/BusinessTripApplication.bpmn20.xml")
                // 添加png资源
                .addClasspathResource("bpmn/BusinessTripApplication.png")
                //禁止校验文件
                .disableSchemaValidation()
                .name("出差申请流程")
                .deploy();//部署流程
        // 4.查看部署结果
        System.out.println("流程部署的id:" + deploy.getId());
        System.out.println("流程部署的名称:" + deploy.getName());
    }

    /**
     * 启动一个流程实例
     */
    @Test
    public void test3() {
    
    
        // 1.创建ProcessEngine对象
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        // 2.获取RuntimeService对象
        RuntimeService runtimeService = engine.getRuntimeService();
        // 3.根据流程定义的ID启动流程
        String id = "BusinessTripApplication";
        Map<String, Object> assignee = new HashMap<>();
        assignee.put("user1", "ZhangSan");
        assignee.put("user2", "LiSi");
        assignee.put("user3", "WangWu");
        assignee.put("user4", "ZhaoLiu");
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(id, assignee);
        // 4.输出相关的流程实例信息
        System.out.println("流程定义的ID:" + processInstance.getProcessDefinitionId());
        System.out.println("流程实例的ID:" + processInstance.getId());
        System.out.println("当前活动的ID:" + processInstance.getActivityId());
    }

    /**
     * 流程任务处理
     */
    @Test
    public void test5() {
    
    
        String assignee = "ZhangSan";
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        // 1.任务查询 需要获取一个 TaskService 对象
        TaskService taskService = engine.getTaskService();
        // 2.根据流程的key和任务负责人 查询任务
        Task process = taskService.createTaskQuery()
                .processDefinitionKey("BusinessTripApplication")
                .taskAssignee(assignee)
                .singleResult();
        // 3.完成任务
        taskService.complete(process.getId());
    }

在这里插入图片描述

在这里插入图片描述

(3)包含网关InclusiveGateway

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

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

包含网关的功能是基于进入和外出顺序流的:

分支:所有外出顺序流的条件都会被解析,结果为true的顺序流会以并行方式继续执行, 会为每个顺序流创建一个分支。

汇聚:所有并行分支到达包含网关,会进入等待状态, 直到每个包含流程token的进入顺序流的分支都到达。 这是与并行网关的最大不同。换句话说,包含网关只会等待被选中执行了的进入顺序流。 在汇聚之后,流程会穿过包含网关继续执行。

在这里插入图片描述

    /**
     * 单个文件部署
     */
    @Test
    public void test1() {
    
    
        // 1.创建ProcessEngine对象
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        // 2.获取RepositoryService进行部署操作
        RepositoryService repositoryService = engine.getRepositoryService();
        // 3.使用RepositoryService进行部署
        Deployment deploy = repositoryService.createDeployment()
                // 添加bpmn资源
                .addClasspathResource("bpmn/WorkOvertime.bpmn20.xml")
                // 添加png资源
                .addClasspathResource("bpmn/WorkOvertime.png")
                //禁止校验文件
                .disableSchemaValidation()
                .name("加班申请流程")
                .deploy();//部署流程
        // 4.查看部署结果
        System.out.println("流程部署的id:" + deploy.getId());
        System.out.println("流程部署的名称:" + deploy.getName());
    }

    /**
     * 启动一个流程实例
     */
    @Test
    public void test3() {
    
    
        // 1.创建ProcessEngine对象
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        // 2.获取RuntimeService对象
        RuntimeService runtimeService = engine.getRuntimeService();
        // 3.根据流程定义的ID启动流程
        String id = "WorkOvertime";
        Map<String, Object> assignee = new HashMap<>();
        assignee.put("user1", "ZhangSan");
        assignee.put("user2", "LiSi");
        assignee.put("user3", "WangWu");
        assignee.put("user4", "ZhaoLiu");
        assignee.put("user5", "LuQi");
        assignee.put("num",4);
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(id, assignee);
        // 4.输出相关的流程实例信息
        System.out.println("流程定义的ID:" + processInstance.getProcessDefinitionId());
        System.out.println("流程实例的ID:" + processInstance.getId());
        System.out.println("当前活动的ID:" + processInstance.getActivityId());
    }

    /**
     * 流程任务处理
     */
    @Test
    public void test5() {
    
    
        String assignee = "ZhangSan";
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        // 1.任务查询 需要获取一个 TaskService 对象
        TaskService taskService = engine.getTaskService();
        // 2.根据流程的key和任务负责人 查询任务
        Task process = taskService.createTaskQuery()
                .processDefinitionKey("WorkOvertime")
                .taskAssignee(assignee)
                .singleResult();
        // 3.完成任务
        taskService.complete(process.getId());
    }

在这里插入图片描述

(4)事件网关EventGateway

事件网关允许根据事件判断流向。网关的每个外出顺序流都要连接到一个中间捕获事件。 当流程到达一个基于事件网关,网关会进入等待状态:会暂停执行。与此同时,会为每个外出顺序流创建相对的事件订阅。

事件网关的外出顺序流和普通顺序流不同,这些顺序流不会真的"执行", 相反它们让流程引擎去决定执行到事件网关的流程需要订阅哪些事件。 要考虑以下条件:

  • 事件网关必须有两条或以上外出顺序流;
  • 事件网关后,只能使用intermediateCatchEvent类型(activiti不支持基于事件网关后连接ReceiveTask)
  • 连接到事件网关的中间捕获事件必须只有一个入口顺序流。

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_47533244/article/details/135327487