Loading

Activiti7入门手册

一、工作流介绍

1.1、概念

工作流(Workflow),指“业务过程的部分或整体在计算机应用环境下的自动化”。是对工作流程及其各操作步骤之间业务规则的抽象、概括描述。在计算机中,工作流属于计算机支持的协同工作(CSCW)的一部分。后者是普遍地研究一个群体如何在计算机的帮助下实现协同工作的。

工作流主要解决的主要问题是:为了实现某个业务目标,利用计算机在多个参与者之间按某种预定规则自动传递文档、信息或者任务。

1.2、工作流系统

工作流管理系统(Workflow Management System, WfMS)是一个软件系统,它完成工作量的定义和管理,按照在系统中预先定义好的工作流逻辑进行工作流实例的执行。

工作流管理系统不是企业的业务系统,而是为企业的业务系统的运行提供了一个软件的支撑环境。

工作流管理联盟(WfMC,Workflow Management Coalition)给出的关于工作流管理系统的定义是:工作流管理系统是一个软件系统,它完成工作流的定义和管理,并按照在计算机中预先定义好的工作流逻辑推进工作流实例的执行。

1.3、使用行业

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

1.4、具体应用

  • 关键业务流程:订单、报价处理、合同审核、客户电话处理、供应链管理等。
  • 行政管理类:出差申请、加班申请、请假申请、用车申请、各种办公用品申请、购买申请、日报周报等凡是原来手工流转处理的行政表单。
  • 人事管理类:员工培训安排、绩效考评、职位变动处理、员工档案信息管理等。
  • 财务相关类:付款请求、应收款处理、日常报销处、出差报销、预算和计划申请等。
  • 客户服务类:客户信息管理、客户投诉、请求处理、售后财务管理等。
  • 特殊服务类: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 进行管理,减少业务系统由于流程变更进行系统升级改造的工作量,从而提高系统的健壮性,同时也减少了系统开发维护成本。

  • 官方网站

2.2、BPM

  • BPM(Business Process Management),即业务流程管理,是一种以规范化的构造端到端的卓越业务流程为中心,以持续的提高组织业务绩效为目的的系统化方法,常见商业管理教育如EMBA、MBA等均将BPM包含在内。
  • 企业流程管理主要是对企业内部改革,改变企业只能管理机构重叠、中间层次多、流程不闭环等,做到机构不重叠、业务不复杂,达到缩短流程周期、节约运作资本、提高企业效益的作用。
  • 比较下面的两个人事加薪流程,那个效率更高?
    • 流程一:

      graph LR F[人事加薪流程] A[制定加新计划] -->B[部门经理审批] B --> C[总经理审批] C -->D[财务经理审批]
    • 流程二:

      graph LR F[人事加薪流程] A[制定加新计划] -->B[部门经理审批] B --> C{1万元} C -->|1万元以内|D[财务经理审批] C -->|1万元以上|E[总经理审批]
    • 上面的两个流程的区别在于第二个流程在执行时,如果本次加薪金额在一万元内不再由总经理审批将比第一个流程缩短流程周期,从而提高效率。

2.3、BPM软件

  • BPM软件就是根据企业中业务环境的变化,推进人和人之间、人和系统之间以及系统和系统之间的整合及调整的经营方法和解决方案的IT工具。通常以Internet方式实现信息传、数据同步、业务监控和企业业务流程的持续升级优化,从而实现跨应用、跨部门、夸合作伙伴和客户的企业运作。
  • 通过BPM软件对企业内部及外部的业务流程的整个生命周期进行建模、自动化、管理监控和优化,使企业成本降低,利润得以大幅度提升。
  • BPM软件在企业中应用领域广泛,凡是有业务流程的地方都可以用BPM软件进行管理,比如企业人事办公管理、采购流程管理、公文审批流程管理、财务管理等。

2.4、BPMN

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

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

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

  • 一个BPMN的例子:

    • 首先当事人发起一个请假单;
    • 其次他所在部门的经理对请假单进行审核;
    • 然后人事经理进行复核并进行备案;
    • 最后请假流程结束。

diagram-5808583

<?xml version="1.0" encoding="utf-8"?>

<bpmn2:definitions xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="sample-diagram" targetNamespace="http://bpmn.io/schema/bpmn" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd">
  <bpmn2:process id="process1596086637078" name="流程1596086637078">
    <bpmn2:startEvent id="StartEvent_01ydzqe" name="开始">
      <bpmn2:outgoing>SequenceFlow_13g4uaz</bpmn2:outgoing>
    </bpmn2:startEvent>
    <bpmn2:userTask id="UserTask_1lf9x0v" name="填写请假单">
      <bpmn2:incoming>SequenceFlow_13g4uaz</bpmn2:incoming>
      <bpmn2:outgoing>SequenceFlow_0yljdna</bpmn2:outgoing>
    </bpmn2:userTask>
    <bpmn2:sequenceFlow id="SequenceFlow_13g4uaz" sourceRef="StartEvent_01ydzqe" targetRef="UserTask_1lf9x0v"/>
    <bpmn2:userTask id="UserTask_0xa95fp" name="部门经理审批">
      <bpmn2:incoming>SequenceFlow_0yljdna</bpmn2:incoming>
      <bpmn2:outgoing>SequenceFlow_0wc7wvy</bpmn2:outgoing>
    </bpmn2:userTask>
    <bpmn2:sequenceFlow id="SequenceFlow_0yljdna" sourceRef="UserTask_1lf9x0v" targetRef="UserTask_0xa95fp"/>
    <bpmn2:userTask id="UserTask_1p57lka" name="人事复核">
      <bpmn2:incoming>SequenceFlow_0wc7wvy</bpmn2:incoming>
      <bpmn2:outgoing>SequenceFlow_0o4nsg1</bpmn2:outgoing>
    </bpmn2:userTask>
    <bpmn2:sequenceFlow id="SequenceFlow_0wc7wvy" sourceRef="UserTask_0xa95fp" targetRef="UserTask_1p57lka"/>
    <bpmn2:endEvent id="EndEvent_05yb7io" name="结束">
      <bpmn2:incoming>SequenceFlow_0o4nsg1</bpmn2:incoming>
    </bpmn2:endEvent>
    <bpmn2:sequenceFlow id="SequenceFlow_0o4nsg1" sourceRef="UserTask_1p57lka" targetRef="EndEvent_05yb7io"/>
  </bpmn2:process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_1">
    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="process1596086637078">
      <bpmndi:BPMNShape id="StartEvent_01ydzqe_di" bpmnElement="StartEvent_01ydzqe">
        <dc:Bounds x="382" y="82" width="36" height="36"/>
        <bpmndi:BPMNLabel>
          <dc:Bounds x="389" y="58" width="22" height="14"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="UserTask_1lf9x0v_di" bpmnElement="UserTask_1lf9x0v">
        <dc:Bounds x="490" y="60" width="100" height="80"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="SequenceFlow_13g4uaz_di" bpmnElement="SequenceFlow_13g4uaz">
        <di:waypoint x="418" y="100"/>
        <di:waypoint x="490" y="100"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="UserTask_0xa95fp_di" bpmnElement="UserTask_0xa95fp">
        <dc:Bounds x="700" y="60" width="100" height="80"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="SequenceFlow_0yljdna_di" bpmnElement="SequenceFlow_0yljdna">
        <di:waypoint x="590" y="100"/>
        <di:waypoint x="700" y="100"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="UserTask_1p57lka_di" bpmnElement="UserTask_1p57lka">
        <dc:Bounds x="910" y="60" width="100" height="80"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="SequenceFlow_0wc7wvy_di" bpmnElement="SequenceFlow_0wc7wvy">
        <di:waypoint x="800" y="100"/>
        <di:waypoint x="910" y="100"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="EndEvent_05yb7io_di" bpmnElement="EndEvent_05yb7io">
        <dc:Bounds x="1152" y="82" width="36" height="36"/>
        <bpmndi:BPMNLabel>
          <dc:Bounds x="1159" y="125" width="22" height="14"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="SequenceFlow_0o4nsg1_di" bpmnElement="SequenceFlow_0o4nsg1">
        <di:waypoint x="1010" y="100"/>
        <di:waypoint x="1152" y="100"/>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</bpmn2:definitions>

在线BPMN网址https://bpmn.io

三、Activiti如何使用

3.1、整合Activiti

  • Activiti是一个工作流引擎,业务系统使用Activiti来对系统的业务流程进行自动化管理,为了方便业务系统访问(操作)Activiti的接口或功能,通常将Activiti和业务系统的环境集成在一起。

3.2、实现业务流程建模,使用BPMN实现业务流程图

  • 使用Activiti流程建模工具定义业务流程(.bpmn文件)。
  • .bpmn文件就是业务流程定义文件,通过xml定义业务流程。
  • 如果使用其他公司开发的工作引擎一般都提供了可视化的建模工具(Process Designer)用于生成流程定义文件,建模工具操作直观,一般都支持图形化拖拽方式、多窗口的用户界面、丰富的过程图形元素、过程元素拷贝、粘贴、删除等功能。

3.3、部署业务流程到Activiti

  • 向Activiti部署业务流程定义(.bpmn文件)。
  • 使用Activiti提供的API向Activiti中部署.bpmn文件(一般情况下还需要一起部署业务流程的图片.png)。

3.4、启动流程实例

  • 启动一个流程实例表示开始一次业务流程的运行,比如员工请假流程部署完成,如果张三要请假就可以启动一个流程实例,如果李四要请假也需要启动一个流程实例,两个流程的执行互不影响,就好比定义一个Java类,实例化两个Java对象一样,部署的流程就好比Java类,启动一个流程实例就好比new一个Java对象。

3.5、查询待办任务(Task)

  • 因为现在系统的业务流程已经交给Activiti管理,通过Activiti就可以查询当前流程执行到哪里了,当前用户需要办理什么任务了,这些Activiti帮我们管理了,而不像传统方式中需要我们在SQL语句中的WHERE条件中指定当前查询的状态值是多少。。

3.6、处理待办任务

  • 用户查询待办任务后,就可以办理某个任务,如果这任务办理完成还需要其他用户办理,比如采购单创建后由部门经理审核,这个过程也是由Activiti帮我们完成了,不需要我们在代码中硬编码指定下一个任务办理人了。

3.7、结束流程

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

四、Activiti环境

4.1、数据库支持

CREATE DATABASE activiti DEFAULT CHARACTER SET utf8mb4;

4.2、创建Java工程

  • 通过IDEA创建Maven的Java工程。

4.3、加入相关jar包的Maven坐标

  • 在Java工程中加入ProcessEngine所需要的jar包,包括:
    • activiti-engine-7.0.0.GA.jar
    • activiti依赖的jar包:mybatis、slf4j、log4j等
    • activiti依赖的spring的jar包
    • 数据库驱动
    • 第三方数据库连接池dbcp
    • 单元测试junit
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>activiti_project</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <activiti.version>7.1.0.M6</activiti.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-engine</artifactId>
            <version>7.1.0.M6</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.activiti/activiti-spring -->
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-spring</artifactId>
            <version>7.1.0.M6</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.activiti/activiti-bpmn-model -->
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-bpmn-model</artifactId>
            <version>7.1.0.M6</version>
        </dependency>
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-bpmn-converter</artifactId>
            <version>${activiti.version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.activiti/activiti-json-converter -->
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-json-converter</artifactId>
            <version>7.1.0.M6</version>
        </dependency>

        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-bpmn-layout</artifactId>
            <version>${activiti.version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.activiti.cloud/activiti-cloud-services-api -->
        <dependency>
            <groupId>org.activiti.cloud</groupId>
            <artifactId>activiti-cloud-services-api</artifactId>
            <version>7-201802-EA</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.25</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.22</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <!-- log start -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.14.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.13.3</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.30</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.14.0</version>
        </dependency>


        <!-- log end -->
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.6</version>
        </dependency>


        <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-dbcp2 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-dbcp2</artifactId>
            <version>2.8.0</version>
        </dependency>



    </dependencies>

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

</project>

4.4、log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration后面的status用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,可以看到log4j2内部各种详细输出-->
<configuration status="INFO">
    <!--先定义所有的appender-->
    <appenders>
        <!--输出日志信息到控制台-->
        <console name="Console" target="SYSTEM_OUT">
            <!--控制日志输出的格式-->
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        </console>
    </appenders>
    <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效-->
    <!--root:用于指定项目的根日志,如果没有单独指定Logger,则会使用root作为默认的日志输出-->
    <loggers>
        <root level="info">
            <appender-ref ref="Console"/>
        </root>
    </loggers>
</configuration>

4.5、activiti.cfg.xml

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

    <!--  配置数据源 -->
    <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="username" value="root"/>
        <property name="url" value="jdbc:mysql://127.0.0.1:3306/activiti?useUnicode=true&amp;characterEncoding=UTF-8&amp;autoReconnect=true&amp;useSSL=false&amp;serverTimezone=GMT%2B8&amp;allowPublicKeyRetrieval=true"/>
        <property name="password" value="root"/>
        <property name="maxIdle" value="1"/>
    </bean>

    <!-- Activiti单独运行的ProcessEngine配置 -->
    <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
        <!-- 数据源 -->
        <property name="dataSource" ref="dataSource"/>
        <!--
            activiti数据库表处理策略
                false(默认值):检查数据库的版本和依赖库的版本,如果不匹配就抛出异常
                true:构建流程引擎时,执行检查,如果需要就执行更新。如果表不存在,就创建。
                create-drop:构建流程引擎时创建数据库报表,关闭流程引擎时就删除这些表。
                drop-create:先删除表再创建表。
                create:构建流程引擎时创建数据库表,关闭流程引擎时不删除这些表
        -->
        <property name="databaseSchemaUpdate" value="true"/>
    </bean>
</beans>

4.5、测试

  • 创建ProcessEngineConfiguration,通过ProcessEngineConfiguration创建ProcessEngine,在创建ProcessEngine的同时会自动创建数据库。
  • 示例:
package com.sunxiaping;

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngineConfiguration;

/**
 * @author <a href="mailto:1900919313@qq.com">weiwei.xu</a>
 * @version 1.0
 * 2020-07-31 6:41
 */
public class ActivitiTest {

    public static void main(String[] args) {
        //创建ProcessEngineConfiguration对象
        ProcessEngineConfiguration configuration = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti-cfg.xml");
        //创建ProcessEngine对象
        ProcessEngine processEngine = configuration.buildProcessEngine();
        System.out.println("processEngine = " + processEngine);
    }

}

也可以这样

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngineConfiguration;

/**
 * @author <a href="mailto:1900919313@qq.com">weiwei.xu</a>
 * @version 1.0
 * 2020-07-31 6:41
 */

public class ActivitiTest {

    public static void main(String[] args) {
        ProcessEngineConfiguration config = ProcessEngineConfiguration.createStandaloneProcessEngineConfiguration();
        config.setJdbcDriver("com.mysql.cj.jdbc.Driver");
        config.setJdbcUrl("jdbc:mysql://localhost:3306/activiti?createDatabaseIfNotExist=true");
        config.setJdbcUsername("root");
        config.setJdbcPassword("root");
        config.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
        ProcessEngine engine = config.buildProcessEngine();
        System.out.println(engine);
    }

}

此时我们查看数据库,发现25张表

五、Activiti服务架构

5.1、概述

5.2、 Service

5.2.1、Service的创建方式

  • 通过ProcessEngine创建Service,Service是工作流引擎提供用于进行工作流部署、指定、管理的服务接口。
  • 创建方式如下:
package com.sunxiaping;

import org.activiti.engine.*;

/**
 * @author <a href="mailto:1900919313@qq.com">weiwei.xu</a>
 * @version 1.0
 * 2020-07-31 11:55
 */
public class ActivitiTest {

    public static void main(String[] args) {
        //创建ProcessEngineConfiguration对象
        ProcessEngineConfiguration configuration = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti-cfg.xml");
        //创建ProcessEngine对象
        ProcessEngine processEngine = configuration.buildProcessEngine();

        RepositoryService repositoryService = processEngine.getRepositoryService();
        RuntimeService runtimeService = processEngine.getRuntimeService();
        TaskService taskService = processEngine.getTaskService();
        HistoryService historyService = processEngine.getHistoryService();
        ManagementService managementService = processEngine.getManagementService();

        System.out.println("repositoryService = " + repositoryService);
        System.out.println("runtimeService = " + runtimeService);
        System.out.println("taskService = " + taskService);
        System.out.println("historyService = " + historyService);
        System.out.println("managementService = " + managementService);

    }
}

5.2.2、Service总览

Service接口 说明
RepositoryService Activiti的资源管理接口
RuntimeService Activiti的流程运行管理接口
TaskService Activiti的任务管理接口
HistoryService Activiti的历史管理接口
ManagementService Activiti的引擎管理接口

5.2.3、RepositoryService

  • RepositoryService,是Activiti的资源管理接口,提供了管理和控制流程发布包和流程定义的操作。使用工作流建模工具设计的业务流程图需要使用此Service将流程定义文件的内容部署到计算机中。
  • 除了流程部署定义以外还可以做如下的操作:
    • 查询引擎中的发布包和流程定义。
    • 暂停或激活发布包以及对应全部和特定流程定义。暂停意味着它们不能再在执行任务操作了,激活是对应的反向操作。
    • 获取多种资源,像包含在发布包中的文件获引擎自动生成的流程图。
    • 获取流程定义的POJO,可以用解析流程,而不必通过XML。

5.2.4、RuntimeService

  • RuntimeService是Activiti的流程运行管理接口,可以从这个接口中获取很多关于流程执行相关的信息。

5.2.5、TaskService

  • TaskService是Activiti的任务管理接口,可以从这个接口中获取任务的信息。

5.2.6、HistoryService

  • HistoryService是Activiti的历史管理类,可以查询历史信息,执行流程时,引擎会包含很多数据(根据配置),比如流程实例启动时间,任务的参与者,完成任务的时间,每个流程实例的执行路径,等等。

5.2.7、ManagementService

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

六、流程定义

6.1、画板

在IDEA中安装对应的Activiti-Designer已停止更新了。

https://gitee.com/jjjxxx/activiti-designer

6.1.1、介绍

基于bpmn.js 的 activiti 模型设计器

6.1.2、安装教程

  1. 切换到项目目录下运行 npm install 安装依赖
  2. 运行 npm run dev 启动

6.1.3、使用说明

  1. 可在页面进行拖拽设计activiti的流程图,支持导出bpmn流程图和svg图片
  2. 运行截图

6.2、绘制流程

image-20210317131433207

选中可执行文件

image-20210317131528861

把请假申请任务分配给张三

image-20210317131604581

导出diagram.bpmn

6.3、部署流程定义

将刚才生成的holiday.bpmn和holiday.png拷贝到项目的resources目录下。

image-20210317131919691

测试

    @Test
    public void test() {
        //创建ProcessEngineConfiguration对象
        ProcessEngineConfiguration configuration = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml");
        ProcessEngine processEngine = configuration.buildProcessEngine();
        //获取RepositoryService对象
        RepositoryService repositoryService = processEngine.getRepositoryService();
        //进行部署

        Deployment deployment = repositoryService.createDeployment()
                .addClasspathResource("diagram.bpmn") //添加bpmn资源
                .addClasspathResource("diagram.svg")
                .name("请假申请程")
                .deploy(); //部署
        //输出部署的一些信息
        String id = deployment.getId();
        System.out.println("流程部署id = " + id);
        String name = deployment.getName();
        System.out.println("流程部署名称 = " + name);
    }

运行结果:

image-20210317132125717

6.4、启动一个流程实例

  • 流程定义部署在Activiti中之后就可以通过工作流管理业务流程了。
  • 针对该流程,启动一个流程表示发起一个新的请假申请单,这就相当于Java类和Java对象的关系,类定义好之后需要new创建一个对象使用,当然,也可以new多个对象。
  • 对于请假申请流程,张三发起一个请假申请单需要启动一个流程实例,李四发起一个请求申请单也需要启动一个流程实例。

关系: 流程定义(BPMN文件)-->流程部署(Activiti的三张表)。
流程实例-->启动流程实例。

类比:
流程定义类似于Java中的类,流程实例类似于Java中的一个实例(对象),所以一个流程定义key对应多个不同的流程实例。

  @Test
    public void test2() {
        //创建ProcessEngineConfiguration对象
        ProcessEngineConfiguration configuration = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml");
        ProcessEngine processEngine = configuration.buildProcessEngine();
        //获取RuntimeService对象
        RuntimeService runtimeService = processEngine.getRuntimeService();
        //根据流程定义的key启动流程实例
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("holiday");
        //获取流程实例的相关信息
        String processDefinitionId = processInstance.getProcessDefinitionId();
        System.out.println("流程定义的id = " + processDefinitionId);
        String deploymentId = processInstance.getDeploymentId();
        System.out.println("流程部署的id = " + deploymentId);
        String id = processInstance.getId();
        System.out.println("流程实例的id = " + id);
        String activityId = processInstance.getActivityId();
        System.out.println("当前活动的id = " + activityId);

    }

运行结果:

image-20210317132729509

6.5、任务查询

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

    @Test
    public void test3() {
        //任务负责人
        String assignee = "张三";
        //创建ProcessEngineConfiguration对象
        ProcessEngineConfiguration configuration = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml");
        ProcessEngine processEngine = configuration.buildProcessEngine();
        //获取TaskService对象
        TaskService taskService = processEngine.getTaskService();
        //查询任务列表,根据流程定义的key和任务负责人
        List<Task> taskList = taskService.createTaskQuery().processDefinitionKey("holiday").taskAssignee(assignee).list();
        //遍历任务列表
        for (Task task : taskList) {
            String processDefinitionId = task.getProcessDefinitionId();
            System.out.println("流程定义id = " + processDefinitionId);
            String processInstanceId = task.getProcessInstanceId();
            System.out.println("流程实例id = " + processInstanceId);
            String assignee1 = task.getAssignee();
            System.out.println("任务负责人 = " + assignee1);
            String id = task.getId();
            System.out.println("任务id = " + id);
            String name = task.getName();
            System.out.println("任务名称 = " + name);
        }
    }

image-20210317132906653

6.6、任务处理

  • 任务负责人查询待办任务,选择任务进行处理,完成任务。
  • 示例:
@Test
    public void test4() {
        //任务负责人
        String assignee = "张三";
        //创建ProcessEngineConfiguration对象
        ProcessEngineConfiguration configuration = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml");
        ProcessEngine processEngine = configuration.buildProcessEngine();
        //获取TaskService对象
        TaskService taskService = processEngine.getTaskService();
        //查询任务列表,根据流程定义的key和任务负责人
        List<Task> taskList = taskService.createTaskQuery().processDefinitionKey("holiday").taskAssignee(assignee).list();
        //遍历任务列表
        for (Task task : taskList) {
            String id = task.getId();
            //完成任务
            taskService.complete(id);
            System.out.println("完成任务"+task.getName());
        }
    }

image-20210317133535923

七、流程定义

7.1 流程定义概述

7.1.1、什么是流程定义?

  • 流程定义是按照BPMN2.0标准去描述业务流程,通常使用Activiti-explorer(Activiti控制台)或Activiti-eclipse-designer(Activiti的eclipse设计器)插件对业务流程进行建模,这两种方式都遵循BPMN2.0标准。
  • 如果使用Activiti-eclipse-designer插件完成业务流程建模。可以生成两个文件:.bpmn和.png文件。

7.1.2、bpmn文件

  • .bpmn文件其实就是XML文件。
<?xml version="1.0" encoding="UTF-8"?>
<bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:activiti="http://activiti.org/bpmn" id="sample-diagram" targetNamespace="http://activiti.org/bpmn" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd">
  <bpmn2:process id="holiday" isExecutable="true">
    <bpmn2:startEvent id="StartEvent_1">
      <bpmn2:outgoing>SequenceFlow_0rn9i9a</bpmn2:outgoing>
    </bpmn2:startEvent>
    <bpmn2:userTask id="UserTask_0ch0apa" name="填写请假申请" activiti:assignee="张三">
      <bpmn2:incoming>SequenceFlow_0rn9i9a</bpmn2:incoming>
      <bpmn2:outgoing>SequenceFlow_12ujysh</bpmn2:outgoing>
    </bpmn2:userTask>
    <bpmn2:sequenceFlow id="SequenceFlow_0rn9i9a" sourceRef="StartEvent_1" targetRef="UserTask_0ch0apa" />
    <bpmn2:userTask id="UserTask_0vnu4us" name="部门经理审批">
      <bpmn2:incoming>SequenceFlow_12ujysh</bpmn2:incoming>
      <bpmn2:outgoing>SequenceFlow_002nzo9</bpmn2:outgoing>
    </bpmn2:userTask>
    <bpmn2:sequenceFlow id="SequenceFlow_12ujysh" sourceRef="UserTask_0ch0apa" targetRef="UserTask_0vnu4us" />
    <bpmn2:userTask id="UserTask_1jz9b0z" name="总经理审批">
      <bpmn2:incoming>SequenceFlow_002nzo9</bpmn2:incoming>
      <bpmn2:outgoing>SequenceFlow_0uv53v4</bpmn2:outgoing>
    </bpmn2:userTask>
    <bpmn2:sequenceFlow id="SequenceFlow_002nzo9" sourceRef="UserTask_0vnu4us" targetRef="UserTask_1jz9b0z" />
    <bpmn2:endEvent id="EndEvent_0ampl6l">
      <bpmn2:incoming>SequenceFlow_0uv53v4</bpmn2:incoming>
    </bpmn2:endEvent>
    <bpmn2:sequenceFlow id="SequenceFlow_0uv53v4" sourceRef="UserTask_1jz9b0z" targetRef="EndEvent_0ampl6l" />
  </bpmn2:process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_1">
    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="holiday">
      <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
        <dc:Bounds x="402" y="392" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="UserTask_0ch0apa_di" bpmnElement="UserTask_0ch0apa">
        <dc:Bounds x="490" y="370" width="100" height="80" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="SequenceFlow_0rn9i9a_di" bpmnElement="SequenceFlow_0rn9i9a">
        <di:waypoint x="438" y="410" />
        <di:waypoint x="490" y="410" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="UserTask_0vnu4us_di" bpmnElement="UserTask_0vnu4us">
        <dc:Bounds x="650" y="370" width="100" height="80" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="SequenceFlow_12ujysh_di" bpmnElement="SequenceFlow_12ujysh">
        <di:waypoint x="590" y="410" />
        <di:waypoint x="650" y="410" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="UserTask_1jz9b0z_di" bpmnElement="UserTask_1jz9b0z">
        <dc:Bounds x="810" y="370" width="100" height="80" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="SequenceFlow_002nzo9_di" bpmnElement="SequenceFlow_002nzo9">
        <di:waypoint x="750" y="410" />
        <di:waypoint x="810" y="410" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="EndEvent_0ampl6l_di" bpmnElement="EndEvent_0ampl6l">
        <dc:Bounds x="972" y="392" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="SequenceFlow_0uv53v4_di" bpmnElement="SequenceFlow_0uv53v4">
        <di:waypoint x="910" y="410" />
        <di:waypoint x="972" y="410" />
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</bpmn2:definitions>
  • .bpmn文件的根节点是definitions节点。这个元素中,可以定义多个流程定义(建议每个文件只包含一个流程定义,可以简化开发过程中的维护难度)。注意,definitions元素最少也要包含xmlns和targetNamespace的声明。targetNamespace可以是任何值,它用来对流程实例进行分类。
  • 流程定义部分:定义了流程每个结点的描述和结点之间的流程流转。
  • 流程布局定义:定义流程每个结点在流程图上的位置坐标等信息。

7.2 流程定义部署

7.2.1、什么是流程定义部署

  • 将生成的流程定义部署到Activiti的数据库中,这就是流程定义部署,通过调用Activiti的API将流程定义的bpmn和png两个文件一个一个添加部署到Activiti中,也可以将两个文件打成zip包进行部署。

7.2.2、单个文件部署方式

  • 分别将bpmn文件和png图片文件部署。
  • 示例:
    @Test
    public void test() {
        //创建ProcessEngineConfiguration对象
        ProcessEngineConfiguration configuration = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti-cfg.xml");
        ProcessEngine processEngine = configuration.buildProcessEngine();
        //获取RepositoryService对象
        RepositoryService repositoryService = processEngine.getRepositoryService();
        //进行部署
        Deployment deployment = repositoryService.createDeployment()
                .addClasspathResource("diagram/holiday.bpmn") //添加bpmn资源
                .addClasspathResource("diagram/holiday.svg")
                .name("请假申请流程")
                .deploy(); //部署
        //输出部署的一些信息
        String id = deployment.getId();
        System.out.println("流程部署id = " + id);
        String name = deployment.getName();
        System.out.println("流程部署名称 = " + name);
    }

7.2.3、压缩包部署方式

将.bpmn文件和.png图片压缩成一个zip包。

image-20210318005121149

@Test
    public void test() {
        //创建ProcessEngineConfiguration对象
        ProcessEngineConfiguration configuration = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti-cfg.xml");
        ProcessEngine processEngine = configuration.buildProcessEngine();
        //获取RepositoryService对象
        RepositoryService repositoryService = processEngine.getRepositoryService();

        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("diagram/holiday.zip");

        ZipInputStream zipInputStream = new ZipInputStream(inputStream);

        //进行部署
        Deployment deployment = repositoryService.createDeployment()
                .addZipInputStream(zipInputStream)
                .name("请假申请流程")
                .deploy(); //部署
        //输出部署的一些信息
        String id = deployment.getId();
        System.out.println("流程部署id = " + id);
        String name = deployment.getName();
        System.out.println("流程部署名称 = " + name);
    }

7.2.4、操作数据表

  • 流程定义部署后,会操作三张表:act_re_deploymentact_re_procdefact_ge_bytearray
  • act_re_deployment是流程定义部署表,记录流程部署信息。
  • act_re_procdef是流程定义表,记录流程定义信息。
  • act_ge_bytearray是资源表,将.bpmn文件和.png图片存入到这个表。

说明:
act_re_deploymentact_re_procdef是一对多的关系,一次部署在流程部署表生成一条记录,但一次流程部署可以部署多个流程定义,每个流程定义在流程定义表生成一条记录。每一个流程定义在act_ge_bytearray会存在两个资源记录.bpmn文件和.png图片。

建议:
一次部署一个流程,这样部署表和流程定义表示一对一的关系,方便读取流程部署和流程定义信息。

7.3、流程定义查询

  • 示例:
    public void test5() {
        //创建ProcessEngineConfiguration对象
        ProcessEngineConfiguration configuration = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml");
        ProcessEngine processEngine = configuration.buildProcessEngine();
        //获取RepositoryService对象
        RepositoryService repositoryService = processEngine.getRepositoryService();
        //得到ProcessDefinitionQuery对象
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
        //设置条件,并查询出当前的所有流程定义
        List<ProcessDefinition> processDefinitionList = processDefinitionQuery.processDefinitionKey("holiday")
                .orderByProcessDefinitionVersion()
                .desc()
                .list();
        //输出流程定义信息
        for (ProcessDefinition processDefinition : processDefinitionList) {
            System.out.println("流程定义的id = " + processDefinition.getId());
            System.out.println("流程定义的name = " + processDefinition.getName());
            System.out.println("流程定义的key = " + processDefinition.getKey());
            System.out.println("流程定义的version = " + processDefinition.getVersion());
        }
    }

结果显示为:

image-20210317221454265

7.4、流程定义删除

  • 示例:
@Test
    public void test6() {
        //创建ProcessEngineConfiguration对象
        ProcessEngineConfiguration configuration = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml");
        ProcessEngine processEngine = configuration.buildProcessEngine();
        //获取RepositoryService对象
        RepositoryService repositoryService = processEngine.getRepositoryService();
        //删除已经部署的流程定义

        //得到ProcessDefinitionQuery对象
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
        //设置条件,并查询出当前的所有流程定义
        List<ProcessDefinition> processDefinitionList = processDefinitionQuery.processDefinitionKey("holiday")
                .orderByProcessDefinitionVersion()
                .desc()
                .list();
        for (ProcessDefinition processDefinition : processDefinitionList) {
            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());
        }
        String deploymentId = "22501";
        repositoryService.deleteDeployment(deploymentId,true);
    }

image-20210318000806159

在数据库中访问DEPLOYMENT_ID表可以发现22501的部署数据已经消失。

7.5、流程定义资源查询

  • 示例:通过查询流程定义对象获取流程定义资源,即bpmn和png
   @Test
    public void test7() throws IOException {
        //创建ProcessEngineConfiguration对象
        ProcessEngineConfiguration configuration = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml");
        ProcessEngine processEngine = configuration.buildProcessEngine();
        //获取RepositoryService对象
        RepositoryService repositoryService = processEngine.getRepositoryService();
        //获取ProcessDefinitionQuery对象
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
        //设置查询条件,执行查询操作
        List<ProcessDefinition> processDefinitionList = processDefinitionQuery.processDefinitionKey("holiday")
                .orderByProcessDefinitionVersion()
                .desc()
                .list();


        //遍历查询结果
        for (ProcessDefinition processDefinition : processDefinitionList) {
            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());
            //获取资源名称,即png图片的名称
            String resourceName = processDefinition.getResourceName();
            //获取图表资源,即bpmn图片的名称
            String diagramResourceName = processDefinition.getDiagramResourceName();
            //获取资源的输入流,即png图片的输入流
            InputStream resourceNameInputStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), resourceName);
            InputStream diagramResourceNameInputStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), diagramResourceName);

            String resourcePath =  "/Users/wzx/Downloads/"+resourceName;
            File file = new File(resourcePath);
            if (!file.exists()) {
                file.getParentFile().mkdirs();
            }

            String diagramResourcePath =  "/Users/wzx/Downloads/"+diagramResourceName;
            file = new File(diagramResourcePath);
            if (!file.exists()) {
                file.getParentFile().mkdirs();
            }

            //复制文件
            FileCopyUtils.copy(resourceNameInputStream, new FileOutputStream(resourcePath));
            FileCopyUtils.copy(diagramResourceNameInputStream, new FileOutputStream(diagramResourcePath));
        }
    }

7.6、流程历史信息查询

  • 示例:
    @Test
    public void test() {
        //创建ProcessEngineConfiguration对象
        ProcessEngineConfiguration configuration = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti-cfg.xml");
        //获取ProcessEngine对象
        ProcessEngine processEngine = configuration.buildProcessEngine();
        //获取HistoryService对象
        HistoryService historyService = processEngine.getHistoryService();
        //获取HistoricActivityInstanceQuery查询对象
        HistoricActivityInstanceQuery historicActivityInstanceQuery = historyService.createHistoricActivityInstanceQuery();
        List<HistoricActivityInstance> historicActivityInstanceList = historicActivityInstanceQuery.list();
        for (HistoricActivityInstance historicActivityInstance : historicActivityInstanceList) {
            String activityId = historicActivityInstance.getActivityId();
            System.out.println("activityId = " + activityId);
            String activityName = historicActivityInstance.getActivityName();
            System.out.println("activityName = " + activityName);
            String processDefinitionId = historicActivityInstance.getProcessDefinitionId();
            System.out.println("processDefinitionId = " + processDefinitionId);
            String processInstanceId = historicActivityInstance.getProcessInstanceId();
            System.out.println("processInstanceId = " + processInstanceId);
        }

    }

参考转自

Activiti7的基本原理和使用

Activiti7工作流的使用

posted @ 2021-03-18 00:48  xine  阅读(9142)  评论(6编辑  收藏  举报