Activiti工作流引擎

一、工作流介绍:

1.1 概念:

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

1.2 工作流系统:

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

1.3 适用行业:

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

1.4 具体应用:

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

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

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

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

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

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

1.5 实现方式:

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

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

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

        那么有没有专业的方式来实现工作流的管理呢?并且可以做到业务流程变化之后,我们的程序可以不用改变,如果可以实现这样的效果,那么我们的业务系统的适应能力就得到了极大提升。

二、Activiti7概述:

2.1 介绍:

Alfresco软件在2010年5月17日宣布Activiti业务流程管理(BPM)开源项目的正式启动,其首席架构师由业务流程管理BPM的专家 Tom Baeyens担任,Tom Baeyens就是原来jbpm的架构师,而jbpm是一个非常有名的工作流引擎,当然activiti也是一个工作流引擎。

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

官方网站:Open Source Business Automation | Activiti

2.1.1 BPM

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

2.1.2 BPM软件

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

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

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

2.1.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 进行流程建模、流程执行管理,它包括很多的建模符号,比如:

Event

用一个圆圈表示,它是流程中运行过程中发生的事情。

活动用圆角矩形表示,一个流程由一个活动或多个活动组成

Bpmn图形其实是通过xml表示业务流程,下面的流程图对应的.bpmn文件:

<?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="dada" name="dada" isExecutable="true">
    <userTask id="sid-24636d5c-845f-49f7-8908-277e4c514cfc" name="审批"/>
    <startEvent id="sid-562e9b40-22dc-478c-87ce-37f3c71d9b1c" name="开始">
      <documentation>请假</documentation>
    </startEvent>
    <sequenceFlow id="sid-d7593067-5640-44ba-b5b0-2e84d47da691" sourceRef="sid-562e9b40-22dc-478c-87ce-37f3c71d9b1c" targetRef="sid-24636d5c-845f-49f7-8908-277e4c514cfc"/>
    <sequenceFlow id="sid-0b9cb70d-fb6b-46e2-a55f-63387c66e78f" sourceRef="sid-24636d5c-845f-49f7-8908-277e4c514cfc" targetRef="sid-a65e5897-617c-4ad0-bfb0-bc59a3ac16ef"/>
    <endEvent id="sid-2a093332-4b54-4d03-b111-0c90167ac8e2"/>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_dada">
    <bpmndi:BPMNPlane bpmnElement="dada" id="BPMNPlane_dada">
      <bpmndi:BPMNShape id="shape-d50fde8a-a1ad-40f0-ae21-1274e3a333b0" bpmnElement="sid-24636d5c-845f-49f7-8908-277e4c514cfc">
        <omgdc:Bounds x="30.0" y="-45.0" width="100.0" height="80.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="shape-3e155278-f28f-459f-8a74-d1d76697b248" bpmnElement="sid-562e9b40-22dc-478c-87ce-37f3c71d9b1c">
        <omgdc:Bounds x="-85.0" y="-20.0" width="30.0" height="30.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-ebc41fd2-4d1c-4f65-be6f-75f9660fe442" bpmnElement="sid-d7593067-5640-44ba-b5b0-2e84d47da691">
        <omgdi:waypoint x="-55.0" y="-5.0"/>
        <omgdi:waypoint x="30.0" y="-5.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="edge-796740fe-375c-4514-b554-76b6637e6d90" bpmnElement="sid-0b9cb70d-fb6b-46e2-a55f-63387c66e78f">
        <omgdi:waypoint x="130.0" y="-5.0"/>
        <omgdi:waypoint x="195.0" y="-5.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-d04621e7-970c-4138-91aa-f7b372a3be9c" bpmnElement="sid-2a093332-4b54-4d03-b111-0c90167ac8e2">
        <omgdc:Bounds x="195.0" y="-20.0" width="30.0" height="30.0"/>
      </bpmndi:BPMNShape>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

2.2 使用步骤

部署activiti

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

流程定义

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

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

流程定义部署

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

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

        Activiti执行把流程定义内容存储在数据库中

启动一个流程实例

        流程实例也叫:ProcessInstance

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

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

用户查询待办任务(Task)

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

用户办理任务

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

流程结束

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

三、Activiti环境:

3.1 开发环境

Jdk1.8或以上版本

Mysql 5及以上的版本

Tomcat8.5

IDEA

注意:activiti的流程定义工具插件可以安装在IDEA下,也可以安装在Eclipse工具下

3.2 Activiti环境

我们使用:Activiti7.0.0.Beta1 默认支持spring5

3.2.1 下载activiti7

Activiti下载地址:Redirecting…icon-default.png?t=N7T8http://activiti.org/download.htmlMaven的依赖如下:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-dependencies</artifactId>
            <version>7.0.0.Beta1</version>
            <scope>import</scope>
            <type>pom</type>
        </dependency>
    </dependencies>
</dependencyManagement>

1) Database

        activiti运行需要有数据库的支持,支持的数据库有:h2, mysql, oracle, postgres, mssql, db2。

3.2.2 流程设计器bpmn-js:

https://demo.bpmn.io/new.bpmnicon-default.png?t=N7T8https://demo.bpmn.io/new.bpmn

3.2.3 流程设计器IDEA下安装:

在IDEA的File菜单中找到子菜单”Settings”,后面我们再选择左侧的“plugins”菜单,如下图所示:

此时我们就可以搜索到activit BPMN 插件,它就是Activiti Designer的IDEA版本,我们点击Install安装。

新建.bpmn文件:

打开编辑器:

3.3 Activiti的数据库支持:

Activiti 在运行时需要数据库的支持,使用25张表,把流程定义节点内容读取到数据库表中,以供后续使用。

3.3.1 Activiti 支持的数据库:

activiti 支持的数据库和版本如下:

数据库类型版本JDBC连接示例说明
h21.3.168jdbc:h2:tcp://localhost/activiti默认配置的数据库
mysql5.1.21jdbc:mysql://localhost:3306/activiti?autoReconnect=true使用 mysql-connector-java 驱动测试
oracle11.2.0.1.0jdbc:oracle:thin:@localhost:1521:xe
postgres8.1jdbc:postgresql://localhost:5432/activiti
db2DB2 10.1 using db2jcc4jdbc:db2://localhost:50000/activiti
mssql2008 using sqljdbc4jdbc:sqlserver://localhost:1433/activiti

四、三种方式初始化MySQL数据库:

4.1 创建数据库:

创建 mysql 数据库 activiti (名字任意):

CREATE DATABASE activiti DEFAULT CHARACTER SET utf8;

4.2 使用java代码生成表

1) 创建 java 工程:

使用idea 创建 java 的maven工程,取名:activiti01。

2) 加入 maven 依赖的坐标(jar 包):

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.9</version>
</parent>

<dependencies>
    <!--Activiti工作流引擎的Spring Boot依赖-->
    <dependency>
        <groupId>org.activiti</groupId>
        <artifactId>activiti-spring-boot-starter</artifactId>
        <version>7.1.0.M6</version>
    </dependency>
    <!--MySQL的JDBC驱动依赖-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.33</version>
    </dependency>
    <!--Spring Boot的Web依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--Spring Boot的测试依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>
    <!--JUnit 5的测试依赖-->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

代码方式:

@Test
void initiMySql(){
    ProcessEngineConfiguration configuration = ProcessEngineConfiguration.createStandaloneProcessEngineConfiguration();
    //连接数据库的配置
    configuration.setJdbcDriver("com.mysql.cj.jdbc.Driver");
    configuration.setJdbcUrl("jdbc:mysql://localhost:3306/activiti?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true");
    configuration.setJdbcUsername("root");
    configuration.setJdbcPassword("123456");

    //DB_SCHEMA_UPDATE_TRUE 检查数据库有没有这个表 ,没有就创建,有就不管。
    //DB_SCHEMA_UPDATE_CREATE_DROP 每次都会重新创建
    //DB_SCHEMA_UPDATE_FALSE 不管有没有都不会创建
    configuration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);

    //工作流的核心对象,ProcessEngine对象
    ProcessEngine processEngine = configuration.buildProcessEngine();

    System.out.println("processEngine = " + processEngine);

}

可以通过配置类进行注入

配置文件方式:

在resource文件下创建: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">
    <!--默认id对应的值 为processEngineConfiguration-->
    <!--processEngine Activiti的流程引擎-->
    <bean id="processEngineConfiguration"
          class="org.activiti.engine.impl.cfg.StandaloneInMemProcessEngineConfiguration">
        <property name="jdbcDriver" value="com.mysql.cj.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/activiti2?useUnicode=true&amp;characterEncoding=utf8&amp;zeroDateTimeBehavior=convertToNull&amp;useSSL=true&amp;serverTimezone=GMT%2B8&amp;nullCatalogMeansCurrent=true"/>
        <property name="jdbcUsername" value="root"/>
        <property name="jdbcPassword" value="123456"/>
        <!--activiti 数据库表处理策略-->
        <property name="databaseSchemaUpdate" value="true"/>
    </bean>
</beans>

代码:自动加载配置文件创建表结构

@Test
void initiMysql2(){
    //这里默认就会去读取activiti.cfg.xml配置文件:创建数据库
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    System.out.println("processEngine = " + processEngine);
}

手动导入方式:

直接在项目输入:activiti-engine 找到对应的maven依赖找到你使用的数据库对应类型的sql文件

以上三种方式执行完成后我们查看数据库, 创建了 25 张表,结果如下:

五、表结构介绍:

5.1 表的命名规则和作用:

Activiti的表都以ACT_开头。

Activiti 使用到的表都是ACT_开头的。表明的第二部分用两个字母表明表的用途。

  • ACT_GE_(GE)表示general 全局通用数据及设置,各种情况都使用的数据。
  • ACT_HI_ (HI)表示histroy 历史数据库表,包含这程序执行的历史相关数据,如结束的流程实例,变量,任务,等等。
  • ACT_ID_(ID)表示identity 组织机构,用户记录,流程中使用到的用户和组。这些表包含标识的信息,如用户,用户组,等等。
  • ACT_RE_(RE)表示repository 存储,包含的是静态信息,如,流程定义,流程的资源(图片,规则等)。
  • ACT_RU_(RU)表示runtime 运行时,运行时的流程变量,用户任务,变量,职责(job)等运行的数据。Activiti 只存储实例执行期间的运行时数据,当流程实例结束时,将删除这些记录。这就保证了这些运行时的表小且快。

5.2 数据表介绍:

表分类表名解释
一般数据
[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]运行时变量表

六、创建流程:

6.1 工作流的开发步骤:

1、使用流程设计器设计流程

2、部署流程

3、发起流程

4、执行流程

6.2 示例一:负者人固定

        6.2.1 定义流程:

Assignee:表示任务负责人、办理人。

        6.2.2 生成图片:

七、流程执行:

7.1 部署流程的两种方式:

1、直接读取xml文件

2、读取xml+图片:方便整合到项目中查看流程执行节点

        7.1.1 直接读取xml文件:

/**
 * 1、设计器设计流程文件xml/png
 * 2、部署流程
 * 3、发起流程
 * 4、执行流程
 */
@Test
void testDeploy(){
    //1、获取流程引擎
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    //2、通过流程引擎获得 RepositoryService
    RepositoryService repositoryService = processEngine.getRepositoryService();

    //3、加载文件【xml/zip】
    Deployment deployment = repositoryService
            .createDeployment()
            .addClasspathResource("process/test01.bpmn20.xml")
            .name("测试第一个流程部署")
            .deploy();//deploy:部署流程


    System.out.println("deployment.getId() = " + deployment.getId());
    System.out.println("deployment.getName() = " + deployment.getName());
    System.out.println("deployment.getKey() = " + deployment.getKey());
}

       7.1.2 部署流程会执行的SQL:

insert into ACT_RE_PROCDEF

insert into ACT_RE_DEPLOYMENT

insert into ACT_GE_BYTEARRAY

        7.1.3 从磁盘读取xml文件:

//输入流方式
@Test
void testDeployInputStream() throws FileNotFoundException {
    //1、获取流程引擎
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    //2、通过流程引擎获得 RepositoryService
    RepositoryService repositoryService = processEngine.getRepositoryService();

    //3、加载文件【xml/zip】
    Deployment deployment = repositoryService
            .createDeployment()
            .addInputStream("test01",new FileInputStream("C:\\Users\\Lenovo\\OneDrive\\桌面\\activiti01\\src\\main\\resources\\process\\test01.bpmn20.xml"))
            .name("从磁盘读取xml文件流程部署")
            .deploy();//deploy:部署流程


    System.out.println("deployment.getId() = " + deployment.getId());
    System.out.println("deployment.getName() = " + deployment.getName());
    System.out.println("deployment.getKey() = " + deployment.getKey());
}

        7.1.4 读取压缩包:

        将xml和png打成zip

@ Test
public void testDeployZip () {
    //1、获取流程引擎
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    //2、通过流程引擎获得 RepositoryService
    RepositoryService repositoryService = processEngine.getRepositoryService();

    InputStream in = this.getClass().getClassLoader().getResourceAsStream("process/test01.zip");
    ZipInputStream zipInputStream = new ZipInputStream(in);

    //3、加载文件【xml/zip】
    Deployment deploy = repositoryService.createDeployment()
            .addZipInputStream(zipInputStream)
            //.addClasspathResource("process/test01.bpmn20.xm1")
            .name("读取zip压缩包流程部署")
            .deploy();//是一个流程部署的行为可以部署多个流程定义的
    System.out.println("deploy.getId()=" + deploy.getId());
    System.out.println("deploy.getName()=" + deploy.getName());
}

        7.1.5 读取xml和png部署流程:

    @ Test
    public void testDeployXMLPNG () {
        //1、获取流程引擎
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //2、通过流程引擎获得 RepositoryService
        RepositoryService repositoryService = processEngine.getRepositoryService();

        //3、加载文件【xml/zip】
        Deployment deploy = repositoryService.createDeployment()
                .addClasspathResource("process/test01.bpmn20.xml")
                .addClasspathResource("process/test01.png")
                .name("读取xml和png部署流程")
                .deploy();//是一个流程部署的行为可以部署多个流程定义的
        System.out.println("deploy.getId()=" + deploy.getId());
        System.out.println("deploy.getName()=" + deploy.getName());
    }

        7.1.6 生成SVG图片:

@Test
void testGenerateSVG() throws IOException {
    //1、获取流程引擎
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    //2、通过流程引擎获得 RepositoryService
    RepositoryService repositoryService = processEngine.getRepositoryService();

    BpmnModel bpmnModel = repositoryService.getBpmnModel("test01:3:12504");//对应ACT_RE_PROCDEF表得id

    DefaultProcessDiagramGenerator generator = new DefaultProcessDiagramGenerator();

    //InputStream inputStream = generator.generateDiagram(bpmnModel, lastTask, Collection.emptyList(), "宋体", "宋体", "宋体");
    InputStream inputStream = generator.generateDiagram(bpmnModel, "endEvent", "宋体", "宋体");

    String imageName = "image-"+ Instant.now().getEpochSecond()+ ".svg";

    FileUtils.copyInputStreamToFile(inputStream, new File("processes/"+ imageName));

}

        7.1.7 查询流程:

    @Test
    public void testQueryDefinition() {
        //1、获取流程引擎
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //2、通过流程引擎获得 RepositoryService
        RepositoryService repositoryService = processEngine.getRepositoryService();
        //流程部署信息
        List<Deployment> deployments = repositoryService.createDeploymentQuery().list();
        System.out.println("********************流程部署信息********************");
        for (Deployment deployment:deployments) {
            System.out.println("deployment.getId()=" + deployment.getId());
            System.out.println("deployment.getName()=" + deployment.getName());
        }

        System.out.println("*********************流程定义信息*********************");
        List<ProcessDefinition> processDefinitions = repositoryService.createProcessDefinitionQuery().list();
        for (ProcessDefinition processDefinition:processDefinitions) {
            System.out.println("processDefinition.getId()=" + processDefinition.getId());
            System.out.println("processDefinition.getKey()=" + processDefinition.getKey());
            System.out.println("processDefinition.getDeploymentId()=" + processDefinition.getDeploymentId());

        }

    }

        7.1.8 删除流程:

    @Test
    public void testDeleteDeployment() {
        //1、获取流程引擎
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //2、通过流程引擎获得 RepositoryService
        RepositoryService repositoryService = processEngine.getRepositoryService();
        //流程部署信息
        List<Deployment> deployments = repositoryService.createDeploymentQuery().list();
        System.out.println("********************流程部署信息********************");
        for (Deployment deployment:deployments) {
            repositoryService.deleteDeployment(deployment.getId());
        }
    }

        7.1.9 发起任务:

    void testStartProcess(){
        //获取流程引擎
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //通过流程引擎获得 RuntimeService
        RuntimeService runtimeService = processEngine.getRuntimeService();
        //通过流程定义ID启动一个流程实例
        //流程定义ID:test01:4:15004
        //流程定义key:test01
        ProcessInstance processInstance = runtimeService.startProcessInstanceById("test01:4:15004");

        System.out.println("processInstance.getId() = " + processInstance.getId());
        System.out.println("processInstance.getName() = " + processInstance.getName());
        System.out.println("processInstance.getProcessInstanceId() = " + processInstance.getProcessInstanceId());
    }

        会添加一下5张表:

        insert into ACT_HI_TASKINST

        insert into ACT_HI_PROCINST

        insert into ACT_HI_ACTINST

        insert into ACT_RU_EXECUTION

        insert into ACT_RU_TASK

        7.1.10 查询任务:

    @Test
    void testQueryTask(){
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //任务处理接口
        TaskService taskService = processEngine.getTaskService();
        //查询所有任务:后面我们会学习到根据办理人、流程定义Id查
        List<Task> tasks = taskService.createTaskQuery().list();
        for (Task task : tasks) {
            System.out.println("task.getId() = " + task.getId());
            System.out.println("task.getName() = " + task.getName());
            System.out.println("task.getProcessInstanceId() = " + task.getProcessInstanceId());
            System.out.println("task.getProcessDefinitionId() = " + task.getProcessDefinitionId());
        }
    }

        7.1.11 查询当前任务节点:

    //查询当前任务节点
    @Test
    void testQueryCurrentNode(){
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //任务处理接口
        TaskService taskService = processEngine.getTaskService();
        //查询所有任务:后面我们会学习到根据办理人、流程定义Id查
        List<Task> lists = taskService.createTaskQuery().list();
        for (Task task : lists) {
            System.out.println("task.getId() = " + task.getId());
            System.out.println("task.getName() = " + task.getName());
            System.out.println("task.getProcessInstanceId() = " + task.getProcessInstanceId());
            System.out.println("task.getProcessDefinitionId() = " + task.getProcessDefinitionId());
        }

        //处理人;张三 的所有任务
        List<Task> tasks = taskService.createTaskQuery().taskAssignee("张三").list();
        for (Task task : lists) {
            System.out.println("task.getId() = " + task.getId());
            System.out.println("task.getName() = " + task.getName());
            System.out.println("task.getDescription() = " + task.getDescription());
        }
    }

        7.1.12 查询当前任务节点图:

        导入依赖:
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-image-generator</artifactId>
            <version>7.1.0.M6</version>
        </dependency>

        <dependency>
            <groupId>dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>1.6.1</version>
        </dependency>
    @Test
    void generateImage() throws IOException {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = processEngine.getTaskService();
        RuntimeService runtimeService = processEngine.getRuntimeService();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        HistoryService historyService = processEngine.getHistoryService();
        List<Task> tasks = taskService.createTaskQuery()
                .active()
                .list();
        Task task = tasks.get(0);
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(task.getProcessInstanceId())
                .singleResult();
        BpmnModel model = repositoryService.getBpmnModel(task.getProcessDefinitionId());

        ProcessDiagramGenerator generator = new DefaultProcessDiagramGenerator();

        //全部经过的站点
        List<HistoricActivityInstance> historicActivityInstances = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processInstance.getId())
                .orderByHistoricActivityInstanceId()
                //        .asc()
                .desc()
                .list();
        List<String> highlightedActivityIds = historicActivityInstances.stream()
                .map(HistoricActivityInstance::getActivityId)
                .collect(Collectors.toList());

        //lastTask是当前任务执行到的位置
        List<HistoricActivityInstance> lastTasks = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processInstance.getId())
                .orderByHistoricActivityInstanceStartTime()
                .desc()
                .list();
        List<String> lastTask = lastTasks.stream()
                .map(HistoricActivityInstance::getActivityId)
                .limit(1)
                .collect(Collectors.toList());
        lastTask.add(lastTasks.get(0).getActivityId());


        InputStream inputStream = generator.generateDiagram(
                model, lastTask,Collectors.emptyList(), "宋体", "宋体", "宋体", true, "test");
        )
        String imageName = "image-" + Instant.now().getEpochSecond() + ".svg";
        FileUtils.copyInputStreamToFile(inputStream, new File("processes/" + imageName));


    }

        7.1.13 完成任务:

    @Test
    void testCompleteTask(){
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = processEngine.getTaskService();
        //条件查询任务
        Task task = taskService.createTaskQuery().processDefinitionKey("test02").singleResult();
        //完成任务
        taskService.complete(task.getId());


    }

        7.1.14 查询历史流程实例:

    @Test
    void testQueryHistoryProcessInstance(){
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        HistoryService historyService = processEngine.getHistoryService();
        List<HistoricProcessInstance> historicProcessInstances = historyService.createHistoricProcessInstanceQuery().list();
        for (HistoricProcessInstance historicProcessInstance : historicProcessInstances) {
            System.out.println("historicProcessInstance.getId() = " + historicProcessInstance.getId());
            System.out.println("historicProcessInstance.getProcessDefinitionId() = " + historicProcessInstance.getProcessDefinitionId());
            System.out.println("historicProcessInstance.getProcessDefinitionKey() = " + historicProcessInstance.getProcessDefinitionKey());
        }

    }

        7.1.15 查询历史任务:

    @Test
    void testQueryHistoryTask(){
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        HistoryService historyService = processEngine.getHistoryService();
        List<HistoricTaskInstance> historicTaskInstances = historyService.createHistoricTaskInstanceQuery().list();

        for (HistoricTaskInstance historicTaskInstance : historicTaskInstances) {
            System.out.println("historicTaskInstance.getId() = " + historicTaskInstance.getId());
            System.out.println("historicTaskInstance.getProcessInstanceId() = " + historicTaskInstance.getProcessInstanceId());
            System.out.println("historicTaskInstance.getStartTime() = " + historicTaskInstance.getStartTime());
            System.out.println("historicTaskInstance.getEndTime() = " + historicTaskInstance.getEndTime());
            System.out.println("historicTaskInstance.getName() = " + historicTaskInstance.getName());

        }
    }

        7.1.16 查询历史活动:

    //历史活动
    @Test
    void testQueryActivityInstance(){
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        HistoryService historyService = processEngine.getHistoryService();
        List<HistoricActivityInstance> historicActivityInstances = historyService.createHistoricActivityInstanceQuery().list();
        for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
            System.out.println("historicActivityInstance.getProcessDefinitionId() = " + historicActivityInstance.getProcessDefinitionId());
            System.out.println("historicActivityInstance.getActivityName() = " + historicActivityInstance.getActivityName());
            System.out.println("historicActivityInstance.getActivityType() = " + historicActivityInstance.getActivityType());
        }
    }

        7.1.17设置任务办理人:

        固定分配:

                在进行业务流程建模时指定固定的任务负责人, 如图:

    //根据办理人查询任务
    @Test
    void testQueryByAssigneeTask(){
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = processEngine.getTaskService();
        List<Task> tasks = taskService.createTaskQuery()
                .taskAssignee("张三")//代办人:张三
                .active()//活动状态
                .list();
    }
        表达式分配:

                由于固定分配方式,任务只管一步一步执行任务,执行到每一个任务将按照 bpmn 的配置去分配任 务负责人。

        UEL 表达式:

                Activiti 使用 UEL 表达式, UEL 是 java EE6 规范的一部分, UEL(Unified Expression Language)即 统一表达式语言, activiti 支持两个 UEL 表达式: UEL-value 和 UEL-method。

                1)UEL-value 定义:

                        如图:assignee 这个变量是 activiti 的一个流程变量,

        或者使用这种方式定义:

        如图:user 也是 activiti 的一个流程变量, user.assignee 表示通过调用 user 的 getter 方法获取值

                2)UEL-method 方式:

                如图:userBean 是 spring 容器中的一个 bean,表示调用该 bean 的 getUserId()方法。

                3)UEL-method 与 UEL-value 结合:

                再比如: ${ldapService.findManagerForEmployee(emp)} ldapService 是 spring 容器的一个 bean,findManagerForEmployee 是该 bean 的一个方法,emp 是 activiti 流程变量, emp 作为参数传到 ldapService.findManagerForEmployee 方法中。

                4)其它:

                表达式支持解析基础类型、 bean、 list、 array 和 map,也可作为条件判断。 如下: ${order.price > 100 && order.price < 250}

        编写代码配置负责人:
                1)定义任务分配流程变量:

                2)设置流程变量:
    /**
     * 设置流程负责人
     */
    @Test
    public void assigneeUEL(){
        //获取流程引擎
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //获取 RuntimeService
        RuntimeService runtimeService = processEngine.getRuntimeService();
        //设置assignee的取值,用户可以在界面上设置流程的执行
        Map<String,Object> assigneeMap = new HashMap<>();
        assigneeMap.put("assignee0","张三");
        assigneeMap.put("assignee1","李经理");
        assigneeMap.put("assignee2","王总经理");
        assigneeMap.put("assignee3","赵财务");
        //启动流程实例,同时还要设置流程定义的assignee的值
        runtimeService.startProcessInstanceByKey("myEvection1",assigneeMap);
        //输出
        System.out.println(processEngine.getName());
    }

执行成功后,可以在act_ru_variable表中看到刚才map中的数据

        *注意:

        实际工作中有的时候我们在画.bpmn文件时不会设置每个UserTask对应的Assignee(即不写死也不设置变量)而是等到即将完成任务的时候设置负责人。这种情况常用于下个任务的负责人由上个任务完成时指定。

Map<String,Object> assigneeMap = new HashMap<>();
assigneeMap.put("assignee0","张三");
taskService.complete(task.getId(),assigneeMap);

********************************************************

taskService.setAssignee(String taskId,String UserId);
taskService.complete(taskId);

        7.1.18 添加审批人意见:

                添加审批意见:

                我们可以通过TaskService的addComment方法来给任务审批意见:

//任务ID、流程实例ID、审批意见
taskService.addComment(task.getId(),task.getProcessInstanceId(),"这里是添加的备注。。。。。");//type: comment
taskService.addComment(task.getId(),task.getProcessInstanceId(),"MyType","这里是通过type参加添加的备注");//type默认:comment
                审批意见存放的表:

             insert into ACT_HI_COMMENT

                查询任务审批意见:
    //查询审批意见
    @Test
    void testQueryComment(){
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        List<Comment> comments = processEngine.getTaskService().getCommentsByType("myType");
        for (Comment comment : comments) {
            System.out.println("comment.getId() = " + comment.getId());
            System.out.println("comment.getFullMessage() = " + comment.getFullMessage());
            System.out.println("comment.getType() = " + comment.getType());
        }
    }

    7.1.19 候选人:    

        候选人CandidateUsers        

        当任务可以被多个人处理的时候,可能会发生并发,所以添加候选人,让候选人去获取任务,之后就变成这个候选人的任务了。

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

        查看bpmn文件:

        候选人拾取任务:
    //候选人拾取任务
    @Test
    void testCalim(){
        //activiti候选人会被当做系统登录用户,activiti和 SpringSecurity整合,就是SpringSecurity的userName
        //根据其中的一个候选人去查询
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = processEngine.getTaskService();
        List<Task> tasks = taskService.createTaskQuery().taskCandidateUser("lisi").list();
        //如果是和 springboot 整合默认使用 springSecurity 这里就是会进行两步查询,将 candidateUser 作为 springSecurity 的用户名去查询
        //如果查询不到会报错 UsernameNotFoundException
        //springSecurity 重写 UserDetailsService 即可
        //下一步才会执行我们的activiti的候选人查询
        for (Task task : tasks) {
            System.out.println("task.getId() = " + task.getId());
            System.out.println("task.getName() = " + task.getName());
            System.out.println("task.getAssignee() = " + task.getAssignee());
            //候选人选中任务:拾取
            taskService.claim(task.getId()."lisi");
        }
    }
        *注意:

        如果是和springboot整合,那么会走到SpringSecurity登录拦截,要么排除SpringSecurity,要么模拟一个登录

八、任务监听器TaskListener:

        任务监听是处理业务逻辑的重要地方,当任务创建、设定负责人、完成任务时都可以监听的到从而来处理自己的业务。

        常用于监听Assignment事件,设置负责人给负责人发一个消息来通知提示。注意:任务监听器只能用在UserTask上使用。

        监听的事件类型:

                Create:任务创建后触发。常用于任务创建后设置任务负责人等。

                Assignment:任务分配后触发。常用于设置完负责人向负责人发邮箱、短信等通知一下。

                Delete:任务完成后触发。

                All:所有事件发生都触发。

*注意:需要实现TaskListener

九、流程监听器ExecutionListener:

        任务监听器只能监听UserTask,流程监听器用在流程的不同的阶段上:

                开始事件和结束事件的开始和结束

                经过输出顺序流

                流程活动的开始和结束

                流程网关的开始和结束

                中间事件的开始和结束

*注意:需要实现ExecutionListener

十、流程变量:

        简介:

        流程变量就是if(条件)中使用到的变量用于参与条件表达式的计算。

        变量都是有作用域的,在程序方法的内部变量是属于局部变量,方法外的变量属于全局变量,同样流程变量也分全局变量global(作用在整个流程实例范围)和本地变量local(作用在单个任务或者单个执行实例)。

        流程变量设置时机:

                启动实例时设置为全局变量:

HashMap<String, Object> variables = new HashMap<>();
variables.put("day",8);
runtimeService.startProcessInstanceByKey(task.getId(),variables )

                任务办理时设置变量:

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

        HashMap<String, Object> variables = new HashMap<>();
        variables.put("day",8);
        taskService.complete(task.getId(), variables);

                通过流程实例设置变量:   

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

                通过当前任务设置变量:

                办理任务前设置局部变量:

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

        条件分支:

        流程条件就是在连接线(sequeceFlow)增加一个Boolean类型的添加,当条件满足后就会对应的任务。

                当有多个条件满足时:只要满足条件的都执行。

                当所有条件都不成立时:抛异常,流程走不下去报错。

十一、网关Gateway:

        ExclusiveGateway 排它网关:

                所谓网关就是条件分支语句 if() else if () ,排它网关会执行所有条件找到一个为true的执行,如果有多个条件为true那么会执行优先定义的(Id较小的那个UserTask),如果条件都为false则抛出异常。

                排它网关和直接在连线上设置条件的区别:

                        条件分支不满条件抛出异常,如果都瞒足都执行

                        排它网关不满足条件抛出异常,如果都瞒足只执行Id较小的任务(即先绘制的任务)。

        HashMap<String, Object> variables = new HashMap<>();
        variables.put("day",8);
        taskService.complete(task.getId(), variables);

               

        ParallelGateway 并行网关:

                并行网关没有条件,写了条件也会被忽略,【全部都会执行,这里可以通过在人事审批、CTO审批上添加监听器,看监听器会发现全部执行】,前面做fork分支,后面做join汇聚。

当【经理审批】完接下来就会同时插入两条任务(人事审批、CTO审批),两个经理审核完后就进入【CEO审批】。

        InclusiveGateway 包含网关:

                包含网关:是同时包括ExclusiveGateway 排它网关 和 ParallelGateway 并行网关,如果你在连线上写条件就是排它网关,如果没写条件就是并行网关。

十二、任务委派:

        任务委派只是任务人将当前的任务交给接收人进行审批,完成任务后又重新回到任务身上,委派人查询任务与完成任务与正常的有区别。

//指定代办人
    @Test
    void delegateTask(){
        List<Task> tasks = taskService.createTaskQuery().list();
        String taskId = tasks.get(0).getId();
        taskService.delegateTask(taskId,"lisi");
    }

    被委派人处理任务:

十三、任务转办:

        任务转办和任务委派类似:

    //任务转办
    @Test
    void testSetAssignee(){
        List<Task> tasks = taskService.createTaskQuery().list();
        String taskId = tasks.get(0).getId();
        taskService.setAssignee(taskId,"lisi");
    }

十四、任务持有人:

        持有人:指定当前任务的执行人,他此时持有该任务。

    //设置任务持有人
    @Test
    void testSetOwner(){
        List<Task> tasks = taskService.createTaskQuery().list();
        String taskId = tasks.get(0).getId();
        taskService.setOwner(taskId,"lisi");
    }

十五、候选人组:

        候选人组:代表有权限对任务进行操作的人。

@Test
    void testQueryByCandidateGroups(){
        List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("审批角色").list();
        for (Task task : tasks) {
            System.out.println("task.getId() = " + task.getId());
            System.out.println("task.getAssignee() = " + task.getAssignee());
            taskService.claim(task.getId(),"XXXX");
        }
    }

十六:多种任务类型:

        ServiceTask:

        ScriptTask:

        ReceiveTask:

        CallActivityTask:

        ManualTask:

十七、子流程:

        创建子流程:

        并在子流程的【内部审核】上添加监听器

十八、流程驳回:

       通过变量控制来控制流程走向,达到拒绝效果。

十九、流程回退:

        

  • 27
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值