系列博客介绍

在常用的ERP系统、OA系统的开发中,工作流引擎是一个必不可少的工具。现在很多公司、企业办公都使用大公司的产品比如阿里巴巴的钉钉、腾讯的企业微信、头条的飞书等。但是对于我们程序员来说,掌握好 Activiti 这门技术,是有必要的。

标准工作流的图:

springboot activiti工作流校验 springboot activiti流程设计器_xml

为啥要用工作流?

在没有专门的工作流引擎之前,我们之前为了实现流程控制,通常的做法就是采用状态字段的值来跟踪流程的变化情况。这样不用角色的用户,通过状态字段的取值来决定记录是否显示。
针对有权限可以查看的记录,当前用户根据自己的角色来决定审批是否合格的操作。如果合格将状态字段设置一个值,来代表合格;当然如果不合格也需要设置一个值来代表不合格的情况。
这是一种最为原始的方式。通过状态字段虽然做到了流程控制,但是当我们的流程发生变更的时候,这种方式所编写的代码也要进行调整。


 


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

 

OK,少说理论,直接进入主题。这篇博客是基于一定的 Java 基础上的,只说重点,细节需要自己去掌握。

 

 

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、流程结束

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

 

 

Activiti 环境入门

JDK1.8,IDEA,MySQL5.7 数据库。

1、IDEA 下载 actiBPM 插件。

先下载 actiBPM 插件,重启 IDEA。

springboot activiti工作流校验 springboot activiti流程设计器_xml_02

为了防止后面使用时 bpmn 中文乱码,请先配置以下几点:

1、修改 IDEA 默认编码 UTF-8

springboot activiti工作流校验 springboot activiti流程设计器_spring_03

2、进入到 IDEA 安装目录,进入到 bin 目录下,找到这2个 .vmoptions 的文件,用记事本打开。

springboot activiti工作流校验 springboot activiti流程设计器_spring_04

修改这两个文件,在其尾部增加一句话:-Dfile.encoding=UTF-8

一定注意,不要有空格,否则重启IDEA时会打不开,然后 重启IDEA。

3、如果无效,那么则找到你的 idea 配置文件,如果你默认安装,有可能在 C 盘下,比如:C:\Users\XXX\.IntelliJIdea2018.2\config,找到后进入config目录,然后在这个目录下的idea64.exe.vmoptions文件尾部增加    -Dfile.encoding=UTF-8。

最后,重启 IDEA 。 

 

2、创建 Maven 项目,生成 Activiti 的 25 张表

创建 maven 项目,pom.xml 如下:

<?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>com.study</groupId>
    <artifactId>study-activiti</artifactId>
    <version>1.0-SNAPSHOT</version>

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


    <dependencies>
        <!--Spring boot 集成包-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!-- SpringBoot整合Web组件 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- 阿里巴巴的druid数据库连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.20</version>
        </dependency>
        <!-- 该 starter 会扫描配置文件中的 DataSource,然后自动创建使用该 DataSource 的 SqlSessionFactoryBean,并注册到 Spring 上下文中 -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.0.0</version>
        </dependency>
        <!-- 数据库MySQL 依赖包 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.48</version>
        </dependency>
        <!-- mybatis 依赖包 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.6</version>
        </dependency>
         <!-- lombok 依赖 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!--添加 Activiti工作流的支持-->
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-spring-boot-starter</artifactId>
            <version>7.0.0.Beta2</version>
        </dependency>

    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Finchley.SR4</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

 

使用 activiti 提供的默认方式来创建mysql的表。

默认方式的要求是在 resources 下创建 activiti.cfg.xml 文件,注意:默认方式目录和文件名不能修改,因为activiti的源码中已经设置,到固定的目录读取固定文件名的文件。

默认方式要在在 activiti.cfg.xml 中bean的名字叫 processEngineConfiguration,名字不可修改

activiti.cfg.xml 完整代码如下:(需要创建 study_activiti 数据库,根据实际数据库账号密码配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                    http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/contex
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
    <!-- 默认id对应的值 为processEngineConfiguration -->
    <!-- processEngine Activiti的流程引擎 -->
    <bean id="processEngineConfiguration"
          class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
        <property name="jdbcDriver" value="com.mysql.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/study_activiti"/>
        <property name="jdbcUsername" value="root"/>
        <property name="jdbcPassword" value="root"/>
        <!-- activiti数据库表处理策略:自动更新 -->
        <property name="databaseSchemaUpdate" value="true"/>
    </bean>
</beans>

 

创建一个测试类,调用 activiti 的工具类,生成acitivti需要的数据库表。

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

package com.study.test;

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.junit.Test;

/**
 * @author biandan
 * @description
 * @signature 让天下没有难写的代码
 * @create 2021-06-13 上午 10:39
 */
public class MyTest {

    /**
     * 生成 Activiti 表
     */
    @Test
    public void testCreateDbTable() {
        //使用classpath下的activiti.cfg.xml中的配置创建processEngine
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        System.out.println(processEngine);
    }
}

点击运行。大概十几秒,就生成好了表。

springboot activiti工作流校验 springboot activiti流程设计器_Activiti_05

Activiti数据表介绍

看到刚才创建的表,我们发现Activiti 的表都以   act_   开头。 

第二部分是表示表的用途的两个字母标识。 用途也和服务的 API 对应。
**ACT_RE** :'RE'表示 repository。 这个前缀的表包含了流程定义和流程静态资源 (图片,规则,等等)。
**ACT_RU**:'RU'表示 runtime。 这些运行时的表,包含流程实例,任务,变量,异步任务,等运行中的数据。 Activiti 只在流程实例执行过程中保存这些数据, 在流程结束时就会删除这些记录。 这样运行时表可以一直很小速度很快。
**ACT_HI**:'HI'表示 history。 这些表包含历史数据,比如历史流程实例, 变量,任务等等。
 **ACT_GE** : GE 表示 general。 通用数据, 用于不同场景下 

springboot activiti工作流校验 springboot activiti流程设计器_xml_06

 

Activiti类关系图

springboot activiti工作流校验 springboot activiti流程设计器_Activiti_07

在新版本中,我们通过实验可以发现IdentityService,FormService两个Serivce都已经删除了。所以后面我们对于这两个Service也不讲解了,但老版本中还是有这两个Service

springboot activiti工作流校验 springboot activiti流程设计器_xml_08

 

Activiti 入门

BPM(Business Process Management),即业务流程管理,是一种规范化的构造端到端的业务流程,以持续的提高组织业务效率。

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

springboot activiti工作流校验 springboot activiti流程设计器_xml_09

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

springboot activiti工作流校验 springboot activiti流程设计器_业务流程_10

 

详细说明:

1、事件 Event

springboot activiti工作流校验 springboot activiti流程设计器_xml_11

2、活动 Activity

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

springboot activiti工作流校验 springboot activiti流程设计器_spring_12

3、网关 GateWay

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

springboot activiti工作流校验 springboot activiti流程设计器_Activiti_13

排他网关 (x) 

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

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

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

 

并行网关 (+) 

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

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

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

 

事件网关 (+) 

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

 

4、流向 Flow

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

springboot activiti工作流校验 springboot activiti流程设计器_SpringBoot_14

 

5、流程设计器使用

Palette(画板)

Connection—连接

Event---事件

Task---任务

Gateway---网关

Container—容器

Boundary event—边界事件

Intermediate event- -中间事件

流程图设计完毕保存生成.bpmn文件

 

首先选中存放图形的目录(选择resources下的bpmn目录),点击菜单:New  -> BpmnFile

springboot activiti工作流校验 springboot activiti流程设计器_业务流程_15

我们创建 apply.bpmn  请假流程。最左侧是配置区域,中间区域是绘图区,右侧区域是palette画板区域。可以拖动图标。

点击空白区域,指定流程定义的 key,我们这里叫做:myApply。然后每个节点都设置负责人。

springboot activiti工作流校验 springboot activiti流程设计器_SpringBoot_16

springboot activiti工作流校验 springboot activiti流程设计器_spring_17

 

创建请假申请,Assignee 输入负责人 zhangsan 

springboot activiti工作流校验 springboot activiti流程设计器_xml_18

 

然后主管审批负责人,lisi

springboot activiti工作流校验 springboot activiti流程设计器_业务流程_19

然后经理审批负责人,wangwu

springboot activiti工作流校验 springboot activiti流程设计器_xml_20

 

最后人资审批负责人,zhuliu。然后就结束了

springboot activiti工作流校验 springboot activiti流程设计器_spring_21

 

然后使用直线把这个流程串联起来。(点中中心的点,拉动)

效果图:

springboot activiti工作流校验 springboot activiti流程设计器_SpringBoot_22

 

然后,我们把这个画好的 bpmn 文件复制到桌面,使用记事本打开,其实就是 xml 文件,完整代码如下:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" 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" xmlns:tns="http://www.activiti.org/test" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" expressionLanguage="http://www.w3.org/1999/XPath" id="m1623556442463" name="" targetNamespace="http://www.activiti.org/test" typeLanguage="http://www.w3.org/2001/XMLSchema">
  <process id="myApply" isClosed="false" isExecutable="true" name="请假流程" processType="None">
    <startEvent id="_2" name="请假申请流程"/>
    <userTask activiti:assignee="zhangsan" activiti:exclusive="true" id="_3" name="创建请假申请"/>
    <userTask activiti:assignee="lisi" activiti:exclusive="true" id="_4" name="主管审批"/>
    <userTask activiti:assignee="wangwu" activiti:exclusive="true" id="_5" name="经理审批"/>
    <userTask activiti:assignee="zhuliu" activiti:exclusive="true" id="_6" name="人资审批"/>
    <endEvent id="_7" name="请假结束流程"/>
    <sequenceFlow id="_8" sourceRef="_2" targetRef="_3"/>
    <sequenceFlow id="_9" sourceRef="_3" targetRef="_4"/>
    <sequenceFlow id="_10" sourceRef="_4" targetRef="_5"/>
    <sequenceFlow id="_11" sourceRef="_5" targetRef="_6"/>
    <sequenceFlow id="_12" sourceRef="_6" targetRef="_7"/>
  </process>
  <bpmndi:BPMNDiagram documentation="background=#FFFFFF;count=1;horizontalcount=1;orientation=0;width=842.4;height=1195.2;imageableWidth=832.4;imageableHeight=1185.2;imageableX=5.0;imageableY=5.0" id="Diagram-_1" name="New Diagram">
    <bpmndi:BPMNPlane bpmnElement="myApply">
      <bpmndi:BPMNShape bpmnElement="_2" id="Shape-_2">
        <omgdc:Bounds height="32.0" width="32.0" x="165.0" y="25.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_3" id="Shape-_3">
        <omgdc:Bounds height="55.0" width="85.0" x="140.0" y="100.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_4" id="Shape-_4">
        <omgdc:Bounds height="55.0" width="85.0" x="135.0" y="185.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_5" id="Shape-_5">
        <omgdc:Bounds height="55.0" width="85.0" x="135.0" y="285.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_6" id="Shape-_6">
        <omgdc:Bounds height="55.0" width="85.0" x="135.0" y="375.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_7" id="Shape-_7">
        <omgdc:Bounds height="32.0" width="32.0" x="160.0" y="460.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="_12" id="BPMNEdge__12" sourceElement="_6" targetElement="_7">
        <omgdi:waypoint x="176.0" y="430.0"/>
        <omgdi:waypoint x="176.0" y="460.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_8" id="BPMNEdge__8" sourceElement="_2" targetElement="_3">
        <omgdi:waypoint x="181.0" y="57.0"/>
        <omgdi:waypoint x="181.0" y="100.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_9" id="BPMNEdge__9" sourceElement="_3" targetElement="_4">
        <omgdi:waypoint x="180.0" y="155.0"/>
        <omgdi:waypoint x="180.0" y="185.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_11" id="BPMNEdge__11" sourceElement="_5" targetElement="_6">
        <omgdi:waypoint x="177.5" y="340.0"/>
        <omgdi:waypoint x="177.5" y="375.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_10" id="BPMNEdge__10" sourceElement="_4" targetElement="_5">
        <omgdi:waypoint x="177.5" y="240.0"/>
        <omgdi:waypoint x="177.5" y="285.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>