目录

Life in Flow

知不知,尚矣;不知知,病矣。
不知不知,殆矣。

X

Activiti7

ll# 工作流引擎 Activiti7

   多数互联网和 IT 公司里面用的技术,钉钉、飞书等效能工具、企业 OA、ERP、CRM

需求背景
   公司规定连续加班 3 天,去按摩可以报销一定比例的费用。

image.png

什么是工作流(WorkFlow)

  • 就是通过计算机对 业务流程自动化执行管理
  • 主要解决的是 使在多个参与者之间按照某种预定义的规则自动进行传递文档或任务的过程,促使此目标的实现
  • 企业日常中很多工作流程,比如:请假流程、报销流程、报价处理、合同审核
  • 使用行业
    • 消费品行业,制造业,电信服务业,银证险等金融服务业,物流服务业,物业服务业
    • 物业管理,大中型进出口贸易公司,政府事业机构,研究院所及教育服务业等,特别是大的跨国企业和集团公司。
  • 具体应用
    • 关键业务流程:合同审核、客户电话处理、供应链管理等
    • 行政管理类:出差申请、加班申请、请假申请、用车申请、各种办公用品申请 等凡是原来手工流转处理的行政表单。
    • 财务相关类:付款请求、应收款处理、日常报销处理、出差报销、预算和计划申请等
    • 特殊服务类:贸易公司报关处理、物流公司货物跟踪处理等各种通过表单逐步手工流转均可应用工作流软件自动规范地实施
  • 基于上面的需求,就出现了工作流系统,就是系统中包括了工作流的功能

什么是工作流引擎

  • 就是实现了工作流功能的框架,比如 Activiti, Flowable 等
  • 不用工作流引擎是否可以实现工作流审批?肯定可以的啊,只不过用了框架更方便。 比如:操作数据库,可以JDBC直接操作,也可以用封装好的MybaitsPlus去操作,就看效率了

自己实现工作流功能的优缺点

id 姓名 报销费用 审批人 状态
1 老王 按摩费用 1688 元 张三 0
2 大钊 沐足费用 998 元 张三 0
3 老帆 加班打的费用 100 元 冰冰 0
  • 审批流程:发起人-> 主管->HR-> 财务-> 结束
  • 操作流程
    • 发起流程后往表里面插入记录,状态为 0
    • 轮到主管审批人的时候就查找数据库状态,通过就是 1,拒绝就是-1
    • 轮到 HR 审批人的时候就查找数据库状态,通过就是 2,拒绝就是-1
    • 轮到财务审批人的时候就查找数据库状态,通过就是 3,拒绝就是-1

问题

  • 上面代码可以很好控制,但是如果调整了流程,不用 HR 审批,怎么办?代码是不是要调整 就不通用
  • 而且考虑也不全面,一个流程是这样,假如大集团,几千几万个流程模版,那代码就没法看了
  • 所以就可以通过工作流引擎来解决上面的问题!!

BPM 和 BPMN

什么是 BPM

   Business Process Management业务流程管理,它是一种涵盖流程建模、执行、监控、优化和自动化的综合【​方法论​】。

   关键部分包括如下:

  • 流程设计​:创建流程的蓝图。
  • 流程实施​:执行设计好的流程。
  • 流程监控​:实时跟踪流程的性能。
  • 流程分析​:评估流程的效果并找出改进点。
  • 流程优化​:基于分析结果改进流程。

什么是 BPMN
   Business Process Model and Notation 是一种业务流程建模符号,【一种标准的方式】来表示业务流程。

   BPMN 的目的是要 让业务分析师、开发人员和业务流程管理者能够轻松地理解、设计、执行和改进业务流程

   统一标准,类似 UML 一样,有多类型的符号来表示工作流中的各个节点含义

   bpmn 图形其实是通过​XML 表示业务流程​,.bpmn 文件使用文本编辑器打开

关键元素

  • 事件​(Events):开始事件、结束事件、中间事件。
  • 活动Activities)​:任务、子流程
  • 网关Gateways)​:排他网关、并行网关。
  • 流程路径Flows)​:序列流、条件流。

image.png

主流工作流引擎对比

   在选择工作流引擎时,需要考虑项目的具体需求团队的技术栈和经验、以及对性能和稳定性的要求

   ActivitiFlowable 因其与 Spring 的良好集成而在 Java 生态比较多

工作流引擎 介绍 优点 缺点 适用场景
Activiti Activiti 是一个开源的工作流引擎,基于 BPMN 2.0,与 Spring 框架集成良好。 轻量级,易于集成 Spring 支持,活跃的社区和文档 版本更新可能导致兼容性问题 适用于中大企业和需要快速集成工作流的场景
Flowable Flowable 是从 Activiti 分叉出来的工作流引擎,修复了 Activiti 的一些已知问题。 修复了 Activiti 的一些 bug,支持最新的 BPMN 规范,良好的文档和社区支持 相对于 Activiti,社区规模较小 适用于需要遵循最新工作流标准和规范的项目
jbpm jbpm 是一个历史悠久的工作流引擎,集成了规则引擎 Drools。 强大的规则引擎集成- 支持复杂的决策逻辑 学习曲线陡峭,社区支持比较低 适用于需要复杂规则和决策逻辑的项目

工作流引擎 Activiti7

  • 是一个工作流引擎,遵循 BPMN 2.0 标准,提供了模块化设计、强大的任务管理、高度定制化等功能。
  • Activiti 可以将业务系统中复杂的业务流程抽取出来
  • 使用专门的建模语言 BPMN2.0 进行定义,业务流程按照预先定义的流程进行执行,实现了系统的流程由 activiti 进行管理,
  • 减少业务系统由于流程变更进行系统升级改造的工作量,从而提高系统的健壮性,同时也减少了系统开发维护成本。
  • Activiti7 与之前的 Activiti5 和 Activiti6 在架构和定位上有显著不同,目前主要使用 7.X 版本

优点

  • 开源与社区支持 Activiti7 是开源的,拥有广泛的社区支持和丰富的文档资源
  • 强大的任务管理 提供丰富的任务管理功能,支持复杂业务流程的自动化处理,提高工作效率
  • 模块化与灵活性 模块化设计使得系统易于维护和扩展,可以根据实际需求加载和使用不同的组件

缺点

  • 技术门槛较高 由于 Activiti7 采用模块化设计,对开发者的技术要求较高,需要掌握相关的云原生技术和 Java 开发技能
  • 对 BPMN 2.0 元素的支持有限 Activiti7 对 BPMN 2.0 中某些元素的支持较少,需要开发者通过其他方式实现特定功能。
  • 学习曲线陡峭 对于初学者来说,Activiti7 的学习曲线可能较为陡峭,需要投入较多的时间和精力进行学习和实践

如何使用工作流引擎

  • 流程定义(画图)
    image.png
  • 部署流程模版(往数据库里插入流程模版)
  • 启动流程实例(根据模版创建流程实例,比如通过 Java 类 创建对象)
    image.png
  • 领取任务节点处理 (不同的节点处理流程任务,比如 你老板审批你的请假 )
  • 结束流程实例(审批流程走完了)

使用工作流引擎的步骤

顺序流程 说明
添加依赖(创建工程) 创建Java工程 工作流引擎其实就是一堆jar包API,和普通框架依赖包一样添加进来
流程定义(画图) 使用activiti流程建模工具定义业务流程(.bpmn文件) ,就是通过xml定义业务流程。
流程定义部署 部署业务流程定义,就是把 .bpmn文件,使用activiti提供的api把流程定义内容存储到数据库
启动流程实例(流程模板)化流程实例 ProcessInstance 启动流程实例表示开始一次业务流程的运行,比如:员工报销流程定义部署完成后,如果张三要报销就可以启动一个流程实例,如果李四要报销也启动一个流程实例,两个流程的执行互相不影响
查询待办任务(Task 节点) 业务流程已经交给 activiti 管理,通过 activiti 的 API 就可以查询当前流程执行到哪,当前用户需要办理什么任务。工作流引擎帮我们管理了这些事情,不需要开发人员自己编写在 SQL 语句查询。
处理任务 Task 节点 用户查询待办任务后,就可以办理对应任务,比如通过审批,拒绝,或者增加备注等
流程结束 当工作流程的任务节点办理完成,没有下一个任务结点了,这个流程实例就结束了

工作流引擎 Activiti7 环境搭建(JDK21)

准备工作 说明
MySQL 数据库创建 Activiti7 工作流相关定义和流程实例是存储到数据库,有 25 张表,可以配置自动创建
Java 工程创建 采用 JDK21+Maven3.9 版本 +Mysql8.X

安装 Mysql8.x
   则创建数据库 CREATE DATABASE activiti DEFAULT CHARACTER SET utf8

 1#创建目录
 2mkdir -p /home/data/mysql/
 3#创建配置文件
 4touch /home/data/mysql/my.cnf
 5
 6docker run \
 7    -p 3306:3306 \
 8    -e MYSQL_ROOT_PASSWORD=123456 \
 9    -v /home/data/mysql/conf:/etc/mysql/conf.d \
10    -v /home/data/mysql/data:/var/lib/mysql:rw \
11    -v /home/data/mysql/my.cnf:/etc/mysql/my.cnf \
12    --name mysql \
13    --restart=always \
14    -d mysql:8.0

Maven 项目工程创建和依赖添加

   在线创建 Spring 项目 https://start.spring.io/

 1<dependencies>
 2        <dependency>
 3            <groupId>org.springframework.boot</groupId>
 4            <artifactId>spring-boot-starter</artifactId>
 5        </dependency>
 6
 7        <dependency>
 8            <groupId>org.springframework.boot</groupId>
 9            <artifactId>spring-boot-starter-test</artifactId>
10            <scope>test</scope>
11        </dependency>
12
13        <!-- https://mvnrepository.com/artifact/org.activiti/activiti-spring-boot-starter -->
14        <dependency>
15            <groupId>org.activiti</groupId>
16            <artifactId>activiti-spring-boot-starter</artifactId>
17            <version>7.1.0.M6</version>
18        </dependency>
19        <!-- mysql驱动 -->
20        <dependency>
21            <groupId>mysql</groupId>
22            <artifactId>mysql-connector-java</artifactId>
23            <version>8.0.26</version>
24        </dependency>
25        <!-- mybatis -->
26        <dependency>
27            <groupId>org.mybatis</groupId>
28            <artifactId>mybatis</artifactId>
29            <version>3.4.5</version>
30        </dependency>
31        <!-- 连接池 -->
32        <dependency>
33            <groupId>commons-dbcp</groupId>
34            <artifactId>commons-dbcp</artifactId>
35            <version>1.4</version>
36        </dependency>
37        <dependency>
38            <groupId>junit</groupId>
39            <artifactId>junit</artifactId>
40            <version>4.11</version>
41            <scope>test</scope>
42        </dependency>
43        <!-- log start -->
44        <dependency>
45            <groupId>log4j</groupId>
46            <artifactId>log4j</artifactId>
47            <version>1.2.12</version>
48        </dependency>
49
50        <dependency>
51            <groupId>org.projectlombok</groupId>
52            <artifactId>lombok</artifactId>
53        </dependency>
54    </dependencies>

日志文件配置

 1log4j.rootLogger=DEBUG,console,file
 2
 3log4j.appender.console = org.apache.log4j.ConsoleAppender
 4log4j.appender.console.Target = System.out
 5log4j.appender.console.Threshold=DEBUG
 6log4j.appender.console.layout = org.apache.log4j.PatternLayout
 7log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
 8
 9log4j.appender.file = org.apache.log4j.RollingFileAppender
10log4j.appender.file.File=./log/soulboy.log
11log4j.appender.file.MaxFileSize=10mb
12log4j.appender.file.Threshold=DEBUG
13log4j.appender.file.layout=org.apache.log4j.PatternLayout
14log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
15
16log4j.logger.org.mybatis=DEBUG
17log4j.logger.java.sql=DEBUG
18log4j.logger.java.sql.Statement=DEBUG
19log4j.logger.java.sql.ResultSet=DEBUG
20log4j.logger.java.sql.PreparedStatement=DEBUG

数据库连接配置
   测试链接没问题,则创建数据库 CREATE DATABASE xd_activiti_demo DEFAULT CHARACTER SET utf8坑:避免数据库有多个名称含有 activiti, 不然会报错

activiti.cfg.xml 文件
   要求在 resources 下创建 activiti.cfg.xml 文件,默认方式目录和文件名不能修改,activiti 的源码中已经设置固定了

 1<?xml version="1.0" encoding="UTF8"?>
 2<beans xmlns="http://www.springframework.org/schema/beans"
 3       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4       xmlns:context="http://www.springframework.org/schema/context"
 5       xmlns:tx="http://www.springframework.org/schema/tx"
 6       xsi:schemaLocation="http://www.springframework.org/schema/beans
 7                    http://www.springframework.org/schema/beans/spring-beans.xsd
 8http://www.springframework.org/schema/contex
 9http://www.springframework.org/schema/context/spring-context.xsd
10http://www.springframework.org/schema/tx
11http://www.springframework.org/schema/tx/spring-tx.xsd">
12
13    <!--配置数据源-->
14    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
15        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
16        <property name="url" value="jdbc:mysql://192.168.1.11:3306/xd_activiti_demo"/>
17        <property name="username" value="root"/>
18        <property name="password" value="123456"/>
19        <property name="maxActive" value="10"/>
20        <property name="maxIdle" value="4"/>
21    </bean>
22    <!--配置流程引擎对象,默认方式下 bean的id,固定processEngineConfiguration-->
23    <bean id="processEngineConfiguration"
24          class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
25        <!--引入上面配置的数据库链接池-->
26        <property name="dataSource" ref="dataSource"/>
27        <!--actviti数据库表在生成时的策略,支持多种方式 true,false,create-drop,drop-create
28         默认是false 不会自动创建表;推荐是true常用的是如果数据库中已经存在相应的表,那直接使用,如果不存在,则会创建-->
29        <property name="databaseSchemaUpdate" value="true"/>
30    </bean>
31
32</beans>

单元测试验证
   src/test/java/ActivitiTest.java

 1import org.activiti.engine.ProcessEngines;
 2import org.junit.Test;
 3
 4public class ActivitiTest {
 5    @Test
 6    public void testV1(){
 7        //  创建流程引擎 processEngine,会检查相关库表情况,如果没有,会自动创建
 8        var processEngine = ProcessEngines.getDefaultProcessEngine();
 9        System.out.println(processEngine);
10    }
11}

image.png

工作流引擎 Activiti7 可视化插件

   安装 BPMN 工作流定义插件,https://plugins.jetbrains.com/plugin/15222-activiti-bpmn-visualizer/versions

image.png

image.png

image.png

image.png

image.png

image.png

src/main/resources/bpmn/testV1.bpmn20.xml

 1<?xml version="1.0" encoding="UTF-8"?>
 2<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">
 3  <process id="testV1" name="报销流程定义" isExecutable="true">
 4    <startEvent id="sid-96c7cbd5-959f-43be-9ada-c8c5ec2634e5"/>
 5    <userTask id="sid-0bcc0d21-c654-4d6f-9a9b-86d1665405e1" name="主管审批" activiti:assignee="张三"/>
 6    <userTask id="sid-2879de4c-1035-43ab-bf00-4cf53fc52a0e" name="财务审批" activiti:assignee="李四"/>
 7    <endEvent id="sid-e27667fc-3883-4c8f-a2eb-1d6943658f21"/>
 8    <sequenceFlow id="sid-1f6894c4-cbbd-4a0f-9a3b-c532257b70f9" sourceRef="sid-96c7cbd5-959f-43be-9ada-c8c5ec2634e5" targetRef="sid-0bcc0d21-c654-4d6f-9a9b-86d1665405e1"/>
 9    <sequenceFlow id="sid-dc1bad4f-563d-4e3a-82aa-5967975e7f30" sourceRef="sid-0bcc0d21-c654-4d6f-9a9b-86d1665405e1" targetRef="sid-2879de4c-1035-43ab-bf00-4cf53fc52a0e"/>
10    <sequenceFlow id="sid-ac464ee7-39a4-48ef-a4e0-23a4bdb762a6" sourceRef="sid-2879de4c-1035-43ab-bf00-4cf53fc52a0e" targetRef="sid-e27667fc-3883-4c8f-a2eb-1d6943658f21"/>
11  </process>
12  <bpmndi:BPMNDiagram id="BPMNDiagram_testV1">
13    <bpmndi:BPMNPlane bpmnElement="testV1" id="BPMNPlane_testV1">
14      <bpmndi:BPMNShape id="shape-1873d429-85f6-40c2-a418-d424d257165c" bpmnElement="sid-96c7cbd5-959f-43be-9ada-c8c5ec2634e5">
15        <omgdc:Bounds x="-115.0" y="-95.0" width="30.0" height="30.0"/>
16      </bpmndi:BPMNShape>
17      <bpmndi:BPMNShape id="shape-be9e33f2-08ba-4703-98d6-e1097f42db5d" bpmnElement="sid-0bcc0d21-c654-4d6f-9a9b-86d1665405e1">
18        <omgdc:Bounds x="-35.0" y="-120.0" width="100.0" height="80.0"/>
19      </bpmndi:BPMNShape>
20      <bpmndi:BPMNShape id="shape-48ae18cb-747a-497f-b77b-2b1e979deb61" bpmnElement="sid-2879de4c-1035-43ab-bf00-4cf53fc52a0e">
21        <omgdc:Bounds x="115.0" y="-120.0" width="100.0" height="80.0"/>
22      </bpmndi:BPMNShape>
23      <bpmndi:BPMNShape id="shape-3dfd601e-1a1a-4771-8316-fcb6922198b8" bpmnElement="sid-e27667fc-3883-4c8f-a2eb-1d6943658f21">
24        <omgdc:Bounds x="275.0" y="-95.0" width="30.0" height="30.0"/>
25      </bpmndi:BPMNShape>
26      <bpmndi:BPMNEdge id="edge-64d40d0e-e08b-460e-9d02-238d13404908" bpmnElement="sid-1f6894c4-cbbd-4a0f-9a3b-c532257b70f9">
27        <omgdi:waypoint x="-85.0" y="-80.0"/>
28        <omgdi:waypoint x="-35.0" y="-80.0"/>
29      </bpmndi:BPMNEdge>
30      <bpmndi:BPMNEdge id="edge-60f150fd-a1ee-4108-a755-2531626e3099" bpmnElement="sid-dc1bad4f-563d-4e3a-82aa-5967975e7f30">
31        <omgdi:waypoint x="65.0" y="-80.0"/>
32        <omgdi:waypoint x="115.0" y="-80.0"/>
33      </bpmndi:BPMNEdge>
34      <bpmndi:BPMNEdge id="edge-e1cf31c1-d1b7-4c70-b784-d3289131296d" bpmnElement="sid-ac464ee7-39a4-48ef-a4e0-23a4bdb762a6">
35        <omgdi:waypoint x="215.0" y="-80.0"/>
36        <omgdi:waypoint x="275.0" y="-80.0"/>
37      </bpmndi:BPMNEdge>
38    </bpmndi:BPMNPlane>
39  </bpmndi:BPMNDiagram>
40</definitions>

流程定义部署

   定义了业务工作流程,就需要部署,即解析 XML 存储到数据库里面

 1/**
 2     * 流程定义部署到数据库
 3     */
 4    @Test
 5    public void testDeploy(){
 6        // 创建流程引擎 processEngine,会检查相关库表情况,如果没有,会自动创建
 7        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
 8        // 获取资源服务
 9        RepositoryService repositoryService = processEngine.getRepositoryService();
10        //部署流程定义到数据库
11        Deployment deployed = repositoryService.createDeployment().addClasspathResource("bpmn/testV1.bpmn20.xml").deploy();
12        System.out.println("id=" + deployed.getId());
13        System.out.println("name=" + deployed.getName());
14        System.out.println("key=" + deployed.getKey());
15        System.out.println("deploymentTime=" + deployed.getDeploymentTime());
16    }

image.png

Activiti7 核心 Service 类

   Activiti 将不同生命周期的服务封装在不同的 Service 中,这些 Service 通过 ProcessEngine 获取,主要的服务类包括

服务名称 功能描述
RepositoryService 部署流程定义,提供了对 repository 的存取服务,查询部署包和流程定义。通过服务将设计好的流程图(BPMN 文件)部署到 Activiti 引擎。操作流程定义的状态,比如挂起和暂停等
RuntimeService 提供了启动流程、查询流程实例、设置获取流程实例变量等功能。
TaskService 提供了对用户任务相关的操作,如运行时任务查询、领取、完成、删除以及变量设置等。
HistoryService 获取流程的历史数据的服务,比如历史流程实例和任务的查询
ManagementService 提供了对 Activiti 流程引擎的管理和维护功能,主要用于 Activiti 系统的日常维护

image.png

image.png

Activiti7 库表资源介绍

   Activiti7 的表结构是其数据库架构的核心组成部分,用于存储和管理与工作流引擎相关的各种数据。

   Activiti7 的表结构通常包含多个表,这些表根据功能和用途进行分类,并遵循一定的命名规则,Activiti7 的表名都以 ACT_ 为前缀,后跟两个字母的缩写标识,用于表示表的用途,如下

  • GE:表示 general(通用),用于存放一些通用的数据。
  • RE:表示 repository(存储库),包含流程定义和流程静态资源(如图片、规则等)。
  • RU:表示 runtime(运行时),包含流程实例、任务、变量等运行中的数据。
  • HI:表示 history(历史),包含历史流程实例、变量、任务等数据。
  • EVT:表示 event(事件),用于记录事件日志。
  • PROCDEF:表示 process define(流程定义),用于记录流程定义信息。
表结构分类 表名字 说明
通用数据表(ACT_GE_*) ACT_GE_BYTEARRAY 用于保存与流程引擎相关的资源,如流程文件、流程图片等。资源被转换为 byte 数组存储在表中。
ACT_GE_PROPERTY 存储整个流程引擎级别的数据,如属性名称和属性值等。
流程定义和部署信息表(ACT_RE_*) ACT_RE_DEPLOYMENT 记录部署信息,包括部署的名称、时间等。
ACT_RE_MODEL 流程设计模型部署表
ACT_RE_PROCDEF 记录流程定义信息,如流程定义的名称、key、分类等。
运行时数据表(ACT_RU_*) ACT_RU_EXECUTION 记录流程实例和执行流的信息,包括流程实例 ID、执行流 ID、父执行流 ID 等。
ACT_RU_TASK 记录任务数据,包括任务名称、描述、优先级、指派人等。
ACT_RU_VARIABLE 存放流程中的参数,包括流程实例参数、执行流参数和任务参数等
历史数据表(ACT_HI_*) ACT_HI_ACTINST 记录历史活动信息,即流程流转过的所有节点。
ACT_HI_ATTACHMENT 记录历史的流程附件。
ACT_HI_COMMENT 记录历史的说明性信息。
ACT_HI_DETAIL 记录流程中产生的变量详细信息。
ACT_HI_IDENTITYLINK 记录任务参与者数据,主要存储历史节点参与者的信息。
ACT_HI_PROCINST 记录历史流程实例信息。
ACT_HI_TASKINST 记录历史任务实例信息。
ACT_HI_VARINST 记录历史变量信息。

   除了上述主要分类的表外,Activiti7 还可能包含其他表,如事件日志表(ACT_EVT_*)等,用于记录系统事件。

流程图绘制-部署和库表数据分析

流程图绘制

  • 指定流程定义的 Key,即 ID

  • 指定任务负责人,即 Assignee 分配负责人

    image.png

流程定义部署

 1@Test
 2    public void testDeploy2(){
 3        //  创建流程引擎 processEngine,会检查相关库表情况,如果没有,会自动创建
 4        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
 5        //获取资源service
 6        RepositoryService repositoryService = processEngine.getRepositoryService();
 7        // 部署流程定义
 8        Deployment deployed = repositoryService.createDeployment()
 9                .addClasspathResource("bpmn/test2.bpmn20.xml")
10                .name("汽车报废补贴申请流程")
11                .key("testDayKey")
12                .deploy();
13        System.out.println("id=" + deployed.getId());
14        System.out.println("name=" + deployed.getName());
15        System.out.println("key=" + deployed.getKey());
16        System.out.println("deploymentTime=" + deployed.getDeploymentTime());
17    }

数据库表变化

  • act_re_deployment:流程定义部署表,每部署一次增加一条记录
    image.png
  • act_re_procdef:流程定义表,部署每个新的流程定义都会在这张表中增加一条记录
    image.png
  • act_ge_bytearray:流程资源表,里面存储 XML 相关信息
    image.png

工作流核心操作

启动流程实例

   基于部署的流程定义,发起一个流程实例

image.png

数据库表变化

数据表 功能描述
act_hi_procinst 流程实例的历史信息
act_hi_taskinst 流程任务的历史信息,记录所有任务节点的历史信息
act_ru_task 流程实例的任务节点信息,启动流程实例,流程当前执行到第一个任务结点,此表会插入一条记录表示当前任务的执行情况。=,如果对应的节点任务完成,则记录会删除。
  • act_hi_procinst 流程实例的历史信息

示例代码

 1@Test
 2    public void testStart(){
 3        //创建流程引擎 processEngine,会检查相关库表情况,如果没有,会自动创建
 4        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
 5        //得到 RuntimeService 实例
 6        RuntimeService runtimeService = processEngine.getRuntimeService();
 7        //根据流程定义的key启动流程
 8        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("testV1");
 9        //流程定义Key=testV1
10        System.out.println("流程定义Key="+processInstance.getProcessDefinitionKey());
11        //流程定义id=testV1:1:3
12        System.out.println("流程定义id="+processInstance.getProcessDefinitionId());
13        //流程实例id=2501
14        System.out.println("流程实例id="+processInstance.getId());
15    }

查询任务

   流程发起后,任务的负责人就可以查询自己需要处理的任务

示例代码

 1@Test
 2    public void testQuery(){
 3        //  创建流程引擎 processEngine,会检查相关库表情况,如果没有,会自动创建
 4        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
 5        // 得到 TaskService 实例
 6        TaskService taskService = processEngine.getTaskService();
 7        //  查询代办任务 select * from ACT_RU_TASK where ASSIGNEE_ = '张三'
 8        List<Task> list = taskService.createTaskQuery()
 9                .processDefinitionKey("testV1")
10                .taskAssignee("张三")
11                //.singleResult() //返回单个结果
12                .list();
13        //打印任务
14        for (Task task : list) {
15            System.out.println("————————————————");
16            //流程定义id=testV1:1:3
17            System.out.println("流程定义id="+task.getProcessDefinitionId());
18            //流程实例id=2501
19            System.out.println("流程实例id="+task.getProcessInstanceId());
20            //任务id=2505
21            System.out.println("任务id="+task.getId());
22            //任务负责人=张三
23            System.out.println("任务负责人="+task.getAssignee());
24            //任务名称=主管审批
25            System.out.println("任务名称="+task.getName());
26        }
27    }

完成任务

   查询出来的任务就是当前负责人的代办任务, 完成对应的任务

示例代码

   张三审批

 1@Test
 2    public void testQuery(){
 3        //  创建流程引擎 processEngine,会检查相关库表情况,如果没有,会自动创建
 4        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
 5        // 得到 TaskService 实例
 6        TaskService taskService = processEngine.getTaskService();
 7        //  查询代办任务 select * from ACT_RU_TASK where ASSIGNEE_ = '张三'
 8        List<Task> list = taskService.createTaskQuery()
 9                .processDefinitionKey("testV1")
10                .taskAssignee("张三")
11                //.singleResult() //返回单个结果
12                .list();
13        //打印任务
14        for (Task task : list) {
15            System.out.println("————————————————");
16            //流程定义id=testV1:1:3
17            System.out.println("流程定义id="+task.getProcessDefinitionId());
18            //流程实例id=2501
19            System.out.println("流程实例id="+task.getProcessInstanceId());
20            //任务id=2505
21            System.out.println("任务id="+task.getId());
22            //任务负责人=张三
23            System.out.println("任务负责人="+task.getAssignee());
24            //任务名称=主管审批
25            System.out.println("任务名称="+task.getName());
26            
27            //完成任务!!!
28            taskService.complete(task.getId());
29        }
30    }

image.png

   李四审批

 1@Test
 2    public void testQuery(){
 3        //  创建流程引擎 processEngine,会检查相关库表情况,如果没有,会自动创建
 4        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
 5        // 得到 TaskService 实例
 6        TaskService taskService = processEngine.getTaskService();
 7        //  查询代办任务 select * from ACT_RU_TASK where ASSIGNEE_ = '张三'
 8        List<Task> list = taskService.createTaskQuery()
 9                .processDefinitionKey("testV1")
10                .taskAssignee("李四")
11                //.singleResult() //返回单个结果
12                .list();
13        //打印任务
14        for (Task task : list) {
15            System.out.println("————————————————");
16            //流程定义id=testV1:1:3
17            System.out.println("流程定义id="+task.getProcessDefinitionId());
18            //流程实例id=2501
19            System.out.println("流程实例id="+task.getProcessInstanceId());
20            //任务id=2505
21            System.out.println("任务id="+task.getId());
22            //任务负责人=张三
23            System.out.println("任务负责人="+task.getAssignee());
24            //任务名称=主管审批
25            System.out.println("任务名称="+task.getName());
26
27            //完成任务
28            taskService.complete(task.getId());
29        }
30    }

image.png

添加审批意见处理

    增加审批意见

 1@Test
 2    public void testQuery(){
 3        //  创建流程引擎 processEngine,会检查相关库表情况,如果没有,会自动创建
 4        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
 5        // 得到 TaskService 实例
 6        TaskService taskService = processEngine.getTaskService();
 7        //  查询代办任务 select * from ACT_RU_TASK where ASSIGNEE_ = '张三'
 8        List<Task> list = taskService.createTaskQuery()
 9                .processDefinitionKey("testV1")
10                .taskAssignee("张三")
11                //.singleResult() //返回单个结果
12                .list();
13        //打印任务
14        for (Task task : list) {
15            System.out.println("————————————————");
16            //流程定义id=testV1:1:3
17            System.out.println("流程定义id="+task.getProcessDefinitionId());
18            //流程实例id=2501
19            System.out.println("流程实例id="+task.getProcessInstanceId());
20            //任务id=2505
21            System.out.println("任务id="+task.getId());
22            //任务负责人=张三
23            System.out.println("任务负责人="+task.getAssignee());
24            //任务名称=主管审批
25            System.out.println("任务名称="+task.getName());
26
27            //添加审批意见:第一个参数是任务id,第二个参数是流程实例id,第三个参数是批注内容
28            taskService.addComment(task.getId(),task.getProcessInstanceId(),task.getName()+" 通过,但是不用去太多次");
29
30            //完成任务
31            taskService.complete(task.getId());
32        }
33    }

image.png

历史信息查询实战

   Activiti 会在 act_hi_* 相关表中保留执行过的流程的历史数据,需要查询相关的历史信息

数据库表 说明描述
ACT_HI_TASKINST(历史任务信息表) TASKINST 只记录 usertask 内容,专注于存储历史任务实例的信息。它主要记录用户任务(User Task)类型的任务节点信息,即那些需要用户参与并执行的任务。开始一个任务,不仅在act_ru_task表插入记录,也在历史任务表插入一条记录,任务完成此表记录不删除
ACT_HI_ACTINST(历史流程实例表) 主要用于存储流程实例执行过程中的历史活动信息。它记录了流程流转过的所有节点,包括启动节点、结束节点、网关、调用子流程、服务类任务等……活动包括任务,此表中不仅记录了任务,还记录了流程执行过程的其它活动,比如开始事件、结束事件等

历史任务查询
   ACT_HI_TASKINST

image.png

 1/**
 2     * 历史任务查询  ACT_HI_TASKINST
 3     * 如何使用Activiti流程引擎的历史服务来查询历史任务实例
 4     * 主要步骤包括:获取默认流程引擎、创建历史任务实例查询、打印查询结果
 5     */
 6    @Test
 7    public void testQueryHistoryTask(){
 8        // 创建流程引擎 processEngine,会检查相关库表情况,如果没有,会自动创建
 9        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
10        // 从流程引擎中获取历史服务,用于操作历史数据
11        HistoryService historyService = processEngine.getHistoryService();
12        // 创建历史任务实例查询,查询库表是 ACT_HI_TASKINST
13        List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery()
14                //.processDefinitionKey("test1")
15                //.taskAssignee("张三")
16                .list();
17        System.out.println("数量="+list.size());
18        for (HistoricTaskInstance task : list) {
19            System.out.println("------------");
20            System.out.println("任务id="+task.getId());
21            System.out.println("任务负责人="+task.getAssignee());
22            System.out.println("任务名称="+task.getName());
23            System.out.println("任务开始时间="+task.getStartTime());
24            System.out.println("任务结束时间="+task.getEndTime());
25            System.out.println("任务耗时="+task.getDurationInMillis());
26        }
27    }

image.png

控制台输出

 1数量=4
 2------------
 3任务id=10005
 4任务负责人=张三
 5任务名称=主管审批
 6任务开始时间=Sat Oct 19 08:22:47 CST 2024
 7任务结束时间=Sat Oct 19 08:30:31 CST 2024
 8任务耗时=464039
 9------------
10任务id=12503
11任务负责人=李四
12任务名称=财务审批
13任务开始时间=Sat Oct 19 08:30:31 CST 2024
14任务结束时间=null
15任务耗时=null
16------------
17任务id=2505
18任务负责人=张三
19任务名称=主管审批
20任务开始时间=Sat Oct 19 05:42:15 CST 2024
21任务结束时间=Sat Oct 19 07:05:19 CST 2024
22任务耗时=4983614
23------------
24任务id=5002
25任务负责人=李四
26任务名称=财务审批
27任务开始时间=Sat Oct 19 07:05:19 CST 2024
28任务结束时间=Sat Oct 19 08:19:10 CST 2024
29任务耗时=4431020

历史流程查询
   ACT_HI_ACTINST

image.png

 1/**
 2     * 历史流程实例的方法 ACT_HI_ACTINST
 3     * 本方法用于演示如何查询历史流程实例,并打印相关任务信息
 4     */
 5    @Test
 6    public void testQueryHistoryInstance(){
 7        // 创建流程引擎 processEngine,会检查相关库表情况,如果没有,会自动创建
 8        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
 9        // 获取历史服务历史服务
10        HistoryService historyService = processEngine.getHistoryService();
11        // 创建历史活动实例查询对象,针对库表 ACT_HI_ACTINST
12        HistoricActivityInstanceQuery instanceQuery = historyService.createHistoricActivityInstanceQuery();
13        // 此处注释掉的代码是通过指定流程实例ID查询,还可以增加更多条件
14        // List<HistoricActivityInstance> list = instanceQuery.processInstanceId("10001").list();
15        // 使用instanceQuery直接查询所有历史活动实例
16        List<HistoricActivityInstance> list = instanceQuery.list();
17
18        // 打印查询到的历史活动实例的数量
19        System.out.println("数量="+list.size());
20        // 遍历查询结果,打印每个任务的详细信息
21        for (HistoricActivityInstance activityInstance : list) {
22            System.out.println("------------");
23            System.out.println("任务id="+activityInstance.getId());
24            System.out.println("任务负责人="+activityInstance.getAssignee());
25            System.out.println("任务名称="+activityInstance.getActivityName());
26            System.out.println("任务开始时间="+activityInstance.getStartTime());
27            System.out.println("任务结束时间="+activityInstance.getEndTime());
28            System.out.println("任务持续时间="+activityInstance.getDurationInMillis());
29            System.out.println("任务类型="+activityInstance.getActivityType());
30        }
31    }

控制台输出

 1数量=7
 2------------
 3任务id=10003
 4任务负责人=null
 5任务名称=null
 6任务开始时间=Sat Oct 19 08:22:47 CST 2024
 7任务结束时间=Sat Oct 19 08:22:47 CST 2024
 8任务持续时间=1
 9任务类型=startEvent
10------------
11任务id=10004
12任务负责人=张三
13任务名称=主管审批
14任务开始时间=Sat Oct 19 08:22:47 CST 2024
15任务结束时间=Sat Oct 19 08:30:31 CST 2024
16任务持续时间=464061
17任务类型=userTask
18------------
19任务id=12502
20任务负责人=李四
21任务名称=财务审批
22任务开始时间=Sat Oct 19 08:30:31 CST 2024
23任务结束时间=null
24任务持续时间=null
25任务类型=userTask
26------------
27任务id=2503
28任务负责人=null
29任务名称=null
30任务开始时间=Sat Oct 19 05:42:15 CST 2024
31任务结束时间=Sat Oct 19 05:42:15 CST 2024
32任务持续时间=1
33任务类型=startEvent
34------------
35任务id=2504
36任务负责人=张三
37任务名称=主管审批
38任务开始时间=Sat Oct 19 05:42:15 CST 2024
39任务结束时间=Sat Oct 19 07:05:19 CST 2024
40任务持续时间=4983639
41任务类型=userTask
42------------
43任务id=5001
44任务负责人=李四
45任务名称=财务审批
46任务开始时间=Sat Oct 19 07:05:19 CST 2024
47任务结束时间=Sat Oct 19 08:19:10 CST 2024
48任务持续时间=4431047
49任务类型=userTask
50------------
51任务id=7501
52任务负责人=null
53任务名称=null
54任务开始时间=Sat Oct 19 08:19:10 CST 2024
55任务结束时间=Sat Oct 19 08:19:10 CST 2024
56任务持续时间=0
57任务类型=endEvent

流程定义查询

   查看对应的表 ACT_RE_PROCDEF

   如果同个流程定义 KEY 重复部署,每部署一次则 Version 版本号会变化 +1

示例代码

 1@Test
 2    public void testQueryDefinition(){
 3        //  创建流程引擎 processEngine,会检查相关库表情况,如果没有,会自动创建
 4        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
 5        //获取资源service
 6        RepositoryService repositoryService = processEngine.getRepositoryService();
 7        //创建流程定义查询
 8        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
 9        List<ProcessDefinition> list = processDefinitionQuery.processDefinitionKey("testV1")
10                //orderByProcessDefinitionVersion 按照版本排序
11                .orderByProcessDefinitionVersion()
12                //desc倒叙
13                .desc()
14                // list 返回集合
15                .list();
16        for (ProcessDefinition processDefinition : list) {
17            System.out.println("-----------");
18            //流程定义id=testV1:1:3
19            System.out.println("流程定义id="+processDefinition.getId());
20            //流程定义key=testV1
21            System.out.println("流程定义key="+processDefinition.getKey());
22            //流程定义name=报销流程定义
23            System.out.println("流程定义name="+processDefinition.getName());
24            //流程定义版本=1
25            System.out.println("流程定义版本="+processDefinition.getVersion());
26        }
27    }

控制台输出

1流程定义id=testV1:1:3
2流程定义key=testV1
3流程定义name=报销流程定义
4流程定义版本=1

image.png

image.png

流程定义删除

   删除流程定义通过 repositoryService 不会移除历史数据,如果无活动实例的流程定义可直接删除。

   如果存在活动实例时,普通删除将失败,则 需要级联删除来清除流程及其所有相关数据。

   应 优先清除未完成的流程节点 后才能彻底移除流程定义

示例代码

 1@Test
 2    public void testDelDefinition() {
 3        // 创建流程引擎 processEngine,会检查相关库表情况,如果没有,会自动创建
 4        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
 5        //获取资源service
 6        RepositoryService repositoryService = processEngine.getRepositoryService();
 7        
 8        //删除流程定义 deploymentId,如果该流程定义已有流程实例启动并且在运行(ACT_RU_TASK表中有该流程定义模板的实例)
 9        //则会抛出异常 【物理外键】
10        repositoryService.deleteDeployment("1");
11        
12        //级联删除:设置true,,如果流程实例启动并且在运行,也会删除
13        //repositoryService.deleteDeployment("1",true);
14    }

image.png

多版本流程定义进行

需求

  • 同个流程定义,不同版本之前发起流程实例的时候是采用哪个?
  • 比如:如果旧流程还在进行中,又有新的流程定义,则如何进行

image.png

image.png

答案

  • 老王的发起的时候是旧的流程,走到了主管大钊的节点,则按照旧的流程定义继续进行
  • 冰冰是新发起的,则采用最新的流程定义进行

示例代码

  • 使用旧流程定义发起一个流程实例
  • 修改旧的流程定义,修改里面的节点,需要使用同个 Key
  • 使用修改后的流程定义,发起一个流程实例,验证流转

image.png

部署新的流程定义模板

 1@Test
 2    public void testDeployNew(){
 3        //  创建流程引擎 processEngine,会检查相关库表情况,如果没有,会自动创建
 4        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
 5        //获取资源service
 6        RepositoryService repositoryService = processEngine.getRepositoryService();
 7        // 部署流程定义
 8        Deployment deploy = repositoryService.createDeployment().addClasspathResource("bpmn/testV1.bpmn20.xml")
 9                .name("报销申请-新版").deploy();
10        System.out.println(deploy.getId());
11    }

ACT_RE_DEPLOYMENT

image.png

ACT_RE_PROCDEF

image.png

再次发起新的流程实例

 1@Test
 2    public void test2Start(){
 3        //  创建流程引擎 processEngine,会检查相关库表情况,如果没有,会自动创建
 4        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
 5        // 得到 RuntimeService 实例
 6        RuntimeService runtimeService = processEngine.getRuntimeService();
 7        // 根据流程定义的key启动流程
 8        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("testV1");
 9        System.out.println("流程定义Key="+processInstance.getProcessDefinitionKey());
10        System.out.println("流程定义id="+processInstance.getProcessDefinitionId());
11        System.out.println("流程实例id="+processInstance.getId());
12    }

image.png

流程审批
   主管张三审批后是老板(超哥)

 1/**
 2     * 审批
 3     */
 4    @Test
 5    public void testQuery(){
 6        //  创建流程引擎 processEngine,会检查相关库表情况,如果没有,会自动创建
 7        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
 8        // 得到 TaskService 实例
 9        TaskService taskService = processEngine.getTaskService();
10        //  查询代办任务 select * from ACT_RU_TASK where ASSIGNEE_ = '张三'
11        List<Task> list = taskService.createTaskQuery()
12                .processDefinitionKey("testV1")
13                .taskAssignee("张三")
14                //.deploymentId("20001")
15                //.singleResult() //返回单个结果
16                .list();
17        //打印任务
18        for (Task task : list) {
19            System.out.println("————————————————");
20            //流程定义id=testV1:1:3
21            System.out.println("流程定义id="+task.getProcessDefinitionId());
22            //流程实例id=2501
23            System.out.println("流程实例id="+task.getProcessInstanceId());
24            //任务id=2505
25            System.out.println("任务id="+task.getId());
26            //任务负责人=张三
27            System.out.println("任务负责人="+task.getAssignee());
28            //任务名称=主管审批
29            System.out.println("任务名称="+task.getName());
30
31            //添加审批意见:第一个参数是任务id,第二个参数是流程实例id,第三个参数是批注内容
32            taskService.addComment(task.getId(),task.getProcessInstanceId(),task.getName()+" 通过,但是不用去太多次");
33
34            //完成任务
35            taskService.complete(task.getId());
36        }
37    }

ACT_RU_TASK

image.png

Businesskey 业务标识

   业务标识,是工作流(如 Activiti)中用于关联 流程实例业务系统数据关键字

   它通常是业务表的主键,用于 唯一标识一个业务实体(如请假单、出差单等)。

   BusinessKey与流程实例绑定使得通过【流程实例】可以查询到相关的业务数据

作用

作用 描述
数据关联 启动流程实例时,将 BusinessKey 存储在 act_ru_execution 表中,通过流程实例查询到对应的业务数据。
流程管理 在流程执行过程中,通过 BusinessKey 查询流程实例的状态、进度等信息,进行流程管理。
数据追溯 通过 BusinessKey,可以追溯流程实例的执行过程,查看历史记录等信息

image.png

如何使用 BusinessKey
   启动流程实例时添加 BusinessKey,在 启动流程实例时,指定BusinessKey

   这个 BusinessKey 通常 是业务系统的某个主键值,用于标识具体的业务实体

   查询流程实例时,获取任务节点,即 可获取BusinessKey

示例代码

 1/**
 2     * 启动流程实例 BusinessKey
 3     */
 4    @Test
 5    public void test2Start(){
 6        //  创建流程引擎 processEngine,会检查相关库表情况,如果没有,会自动创建
 7        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
 8        // 得到 RuntimeService 实例
 9        RuntimeService runtimeService = processEngine.getRuntimeService();
10        // 根据流程定义的key启动流程
11        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("testV1","88888888");
12        System.out.println("流程定义Key="+processInstance.getProcessDefinitionKey());
13        System.out.println("流程定义id="+processInstance.getProcessDefinitionId());
14        System.out.println("流程实例id="+processInstance.getId());
15    }

   启动流程实例时添加 BusinessKey,在 启动流程实例时,指定BusinessKey

   查看 act_ru_execution 表,发现 BusinessKey

image.png

通过流程实例(BusinessKey)查询业务数据库中的其他相关信息

 1/**
 2     * 查询BusinessKey案例
 3     * 本测试方法演示如何通过流程实例的BusinessKey来查询相关的业务数据
 4     */
 5    @Test
 6    public void testQueryBusinessKey(){
 7        // 初始化流程引擎
 8        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
 9        // 获取任务服务和运行时服务
10        TaskService taskService = processEngine.getTaskService();
11        RuntimeService runtimeService = processEngine.getRuntimeService();
12        // 查询当前所有的任务
13        List<Task> list = taskService.createTaskQuery().list();
14        // 遍历任务列表,查询每个任务所属流程实例的BusinessKey
15        for (Task task : list) {
16            // 获取流程实例ID
17            String processInstanceId = task.getProcessInstanceId();
18            // 根据流程实例ID查询流程实例
19            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
20            // 通过流程实例打印BusinessKey
21            // 这里可以扩展为通过BusinessKey 查询业务数据库的操作
22            System.out.println("业务id=" + processInstance.getBusinessKey());
23
24            // 通过BusinessKey可以进一步查询业务数据库中的其他相关信息 TODO ...
25            // BusinessKey可以查询到申请人的部门、薪资、电话、姓名、工龄等…… 
26            // 进而让审批者判断是否审批通过
27        }
28    }

流程定义挂起和激活

   流程定义的挂起(Suspend)与激活(Activate)是管理工作流执行状态的重要功能。通常用于在需要时暂停或恢复流程实例的执行,满足业务变更、系统维护或特定业务规则的需求。

   在某些业务场景下,可能需要临时中断流程的执行,比如:公司里面的财务主管贪污被撤岗了,需要暂停所有相关的业务流程

   有 100 个人的流程发起过了,80 个是已经完成了,20 个是还在进行中

   由于流程变更需要将当前运行的流程暂停不是直接删除流程暂停后将不会继续执行

   业务流程正常后,就需要激活流程实例意味着 恢复其执行,允许其继续按照定义中的流程逻辑执行

   激活可以是针对单个已挂起的流程实例,也可以是针对整个流程定义下所有已挂起的实例。

流程定义的状态

流程定义的状态 描述
挂起 流程定义为挂起状态,该流程定义下边所有的流程实例全部暂停,挂起操作不会影响已完成的任务或历史数据。
挂起可以是 针对单个流程实例,也可以是针对整个流程定义下的所有实例。
流程定义挂起后,如果发起流程实例则会报错,直到被重新激活。
激活 激活流程实例恢复其执行,允许其继续按照定义中的流程逻辑执行。
激活后,流程实例将从其挂起前的状态继续执行。如果有待办任务,这些任务将重新变为可处理状态。

SUSPENSION_STATE 字段含义

数字 含义
1 已激活
2 已挂起

环境准备

ACT_RE_PROCDEF(流程定义)

image.png

ACT_RU_TASK(流程定义下的多个实例)

image.png

流程定义的挂起和激活
    挂起某流程定义,ACT_RE_PROCDEF 表下(ID_ = testV1:1:45003 的流程定义)

 1/**
 2     * 流程定义挂起和激活
 3     */
 4    @Test
 5    public void testSuspendAndActivateInstance2Def(){
 6        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
 7        RepositoryService repositoryService = processEngine.getRepositoryService();
 8        //挂起某个流程定义
 9        //repositoryService.suspendProcessDefinitionById("testV1:1:45003");
10
11        //激活某个流程定义
12        repositoryService.activateProcessDefinitionById("testV1:1:45003");
13    }

ACT_RE_PROCDEF(流程定义)
image.png

流程定义(实例) 挂起和激活
   挂起某流程实例, ACT_RU_TASK 表下(PROC_INST_ID = 47501 的流程定义实例)

 1/**
 2     * 流程定义(实例) 挂起和激活
 3     */
 4    @Test
 5    public void testSuspendAndActivateInstance2DefInstance(){
 6        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
 7        //单个流程定义下的某流程实例挂起
 8        RuntimeService runtimeService = processEngine.getRuntimeService();
 9
10        //挂起
11        runtimeService.suspendProcessInstanceById("47501");
12
13        //激活
14        //runtimeService.activateProcessInstanceById("47505");
15        
16        //查询具体的流程实例对象
17        //ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId("47505").singleResult();
18
19        //检查流程实例是否已暂停
20        //boolean isSuspended = processInstance.isSuspended();
21
22        //获取流程实例ID
23        //String instanceId = processInstance.getProcessInstanceId();
24
25        // 是否已暂停执行激活或挂起操作
26//        if (isSuspended) {
27//            // 如果已暂停,执行激活操作
28//            runtimeService.activateProcessInstanceById(instanceId);
29//            System.out.println("流程实例:" + instanceId + " 已激活");
30//        } else {
31//            // 如果处于激活状态,执行挂起操作
32//            runtimeService.suspendProcessInstanceById(instanceId);
33//            System.out.println("流程实例:" + instanceId + " 已挂起");
34//        }
35    }

image.png

UEL 表达式应用(动态任务负责人)

   流程定义里面,任务节点负责人是固定的,但是很多场景下需要有切换不同的负责人。比如:流程任务中,集团里面,不同的分公司里面报销流程审批人不一样,广州分公司,深圳分公司等

image.png

UEL 表达式

   ​Unified Expression Language(UEL)​一种用于在 Java EE 应用中对表达式进行求值的语言。提供了简洁、灵活、强大的语法来表示和处理表达式。

   在 Activiti 工作流引擎中,UEL 表达式被广泛用于流程定义中,以实现动态的任务分配、条件判断、变量设置等功能

   在流程运行时,UEL 表达式可以 根据流程变量的变化,动态地计算表达式的值

示例

  • 流程定义画图,任务节点不配置负责人,采用表达式 ${XXX} 进行占位

image.png

  • 部署流程定义(deployment)
 1/**
 2     * 流程定义部署到数据库
 3     */
 4    @Test
 5    public void testDeploy(){
 6        // 创建流程引擎 processEngine,会检查相关库表情况,如果没有,会自动创建
 7        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
 8        // 获取资源服务
 9        RepositoryService repositoryService = processEngine.getRepositoryService();
10        //部署流程定义到数据库
11        Deployment deployed = repositoryService.createDeployment().addClasspathResource("bpmn/uel_demo_v1.bpmn20.xml").deploy();
12        System.out.println("id=" + deployed.getId());
13        System.out.println("name=" + deployed.getName());
14        System.out.println("key=" + deployed.getKey());
15        System.out.println("deploymentTime=" + deployed.getDeploymentTime());
16    }
User Task Name Assignee Map
主管审批 主管审批 ${p1} map.put("p1","张三 UEL");
财务审批 财务审批 ${p2} map.put("p2","李四 UEL");

image.png

image.png

image.png

image.png

  • 发起流程实例,这个时候可以动态配置相关参数执行成功后,可以在act_ru_variable表中看到刚才map中的数据
 1/**
 2     * 流程定义实例启动
 3     * 本方法主要用于启动一个流程实例,并设置相关变量
 4     */
 5    @Test
 6    public void testStart1(){
 7        //创建流程引擎 processEngine, 会检查库表,如果没有就自动创建
 8        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
 9        //获取运行时服务,通过该服务可以启动流程实例
10        RuntimeService runtimeService = processEngine.getRuntimeService();
11        //创建一个变量映射,用于在启动流程实例时传递变量
12        Map<String,Object> map = new HashMap<>();
13        //设置变量p1的值为"张三p"
14        map.put("p1","张三UEL");
15        //设置变量p2的值为"李四p"startProcessInstanceByKey("uel_demo_v1", "888", map);
16        map.put("p2","李四UEL");
17
18        //通过流程定义的key启动流程实例,并传递变量映射
19        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("uel_demo_v1", "888", map);
20
21        System.out.println("流程定义Key="+processInstance.getProcessDefinitionKey());
22        System.out.println("流程定义id="+processInstance.getProcessDefinitionId());
23        System.out.println("流程实例id="+processInstance.getId());
24        System.out.println("实例名称name="+processInstance.getId());
25    }

image.png

image.png

流程变量

   在流程处理中,有不同判断条件,需要切换不同的人处理,怎么操作。比如请假超过3天则需要总监同意,但是3天内的话就组长同意即可

什么是流程变量

   用于在流程运转过程中传递业务参数

   流程变量是 Activiti 在管理工作流时 根据管理需要而设置的变量,用于支持流程的逻辑判断和分支选择。 例如:在请假申请流程中,请假天数、请假原因等都可以设置为流程变量,在流程流转时根据这些变量的值进行不同的处理

   数据类型:包括基本数据类型(如 String、Integer、Boolean 等)和复杂数据类型(如实现了 Serializable 接口的自定义类)。

   作用域:可以是 一个流程实例(processInstance)、一个任务(task)或 一个执行实例(execution)。流程变量的默认作用域是流程实例当流程变量的作用域为流程实例时,可以称为global变量。global变量中的变量名不允许重复,设置相同名称的变量时,后设置的值会覆盖前设置的变量值。 如果任务和执行实例的作用域相对较小,仅针对一个任务或一个执行实例范围,称为local变量。local 变量名可以与 global 变量名相同,且在不同的任务或执行实例中互不影响。

使用方法

   在流程连线上设置 UEL 表达式 ${day > 3},其中 day 是一个流程变量名称,该表达式的执行结果是布尔类型,用于决定流程是否跳转到特定的分支

作用域 描述 使用方法
global 变量 流程变量的作用域为流程实例 在启动流程实例时设置流程变量
local 变量 如果任务和执行实例的作用域相对较小,仅针对一个任务或一个执行实例范围,称为 local 变量 通过 UEL 表达式使用流程变量

实例代码
   假设有一个请假申请流程,员工提交请假申请后,根据请假天数的不同,流程会流转到不同的审批节点。例如:如果请假天数大于3天,则需要总经理审批;否则,由部门经理直接审批

  • 定义流程变量​:在流程设计时,定义一个名为 day 的流程变量,用于存储请假天数。
  • 设置流程节点​:在流程中设置两个审批节点,分别对应部门经理审批和总经理审批。
  • 编写 UEL 表达式​:在部门经理审批节点和总经理审批节点间的连线上编写 UEL 表达式 ${day > 2},决定流程是否跳转到总经理审批节点。
  • 启动流程实例​:在启动流程实例时,通过 Map<String, Object> 设置流程变量 day 的值。
连线名称 Condition expression
请假大于 3 天 "${day > 3}"
请假小于等于 3 天 "${day <= 3}"
User Task Assignee
组长 张三
部门经理 李四
总经理 王五

image.png

部署流程定义到数据库

 1@Test
 2    public void testDeploy(){
 3        // 创建流程引擎 processEngine,会检查相关库表情况,如果没有,会自动创建
 4        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
 5        // 获取资源服务
 6        RepositoryService repositoryService = processEngine.getRepositoryService();
 7        //部署流程定义到数据库
 8        Deployment deployed = repositoryService.createDeployment().addClasspathResource("bpmn/var_day.bpmn20.xml").deploy();
 9        System.out.println("id=" + deployed.getId());
10        System.out.println("name=" + deployed.getName());
11        System.out.println("key=" + deployed.getKey());
12        System.out.println("deploymentTime=" + deployed.getDeploymentTime());
13    }

发起流程定义的实例

 1@Test
 2    public void testStartVar(){
 3        //创建流程引擎 processEngine, 会检查库表,如果没有就自动创建
 4        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
 5        //获取运行时服务,通过该服务可以启动流程实例
 6        RuntimeService runtimeService = processEngine.getRuntimeService();
 7        //创建一个变量映射,用于在启动流程实例时传递变量
 8        Map<String,Object> map = new HashMap<>();
 9        //传入变量
10        map.put("day","4");
11        //通过流程定义的key启动流程实例,并传递变量映射
12        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("var_day", "111", map);
13        System.out.println("流程定义Key="+processInstance.getProcessDefinitionKey());
14        System.out.println("流程定义id="+processInstance.getProcessDefinitionId());
15        System.out.println("流程实例id="+processInstance.getId());
16        System.out.println("实例名称name="+processInstance.getId());
17    }

image.png

image.png

审批任务

1/**
2     * 审批任务 ID_    60006
3     */
4    public void testCompleteTaskV3(){
5        //获取引擎
6        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
7        TaskService taskService = processEngine.getTaskService();
8        taskService.complete("");
9    }

image.png

image.png

任务候选人 Candidate

   在 Activiti 工作流中,任务候选人是指 潜在的任务处理者列表,可以有多个

   当任务到达某一阶段时,系统会从候选人列表中选择一人或多人来实际处理该任务

   多个人都可以看到任务,谁先看到就可以先处理任务

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

   在设置任务候选人时,应确保候选人的用户ID在系统中是唯一的,以避免混淆和错误

示例

  • 画图流程定义

image.png
image.png
image.png

  • 部署流程定义
 1@Test
 2    public void testDeploy(){
 3        // 创建流程引擎 processEngine,会检查相关库表情况,如果没有,会自动创建
 4        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
 5        // 获取资源服务
 6        RepositoryService repositoryService = processEngine.getRepositoryService();
 7        //部署流程定义到数据库
 8        Deployment deployed = repositoryService.createDeployment().addClasspathResource("bpmn/candidate_demo.bpmn20.xml").deploy();
 9        System.out.println("id=" + deployed.getId());
10        System.out.println("name=" + deployed.getName());
11        System.out.println("key=" + deployed.getKey());
12        System.out.println("deploymentTime=" + deployed.getDeploymentTime());
13    }

image.png

  • 发起流程实例:发起流程实例后,RU_TASK 表中,对应 ASIGNEE 负责人是空的
 1@Test
 2    public void testStart(){
 3        //创建流程引擎 processEngine,会检查相关库表情况,如果没有,会自动创建
 4        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
 5        //得到 RuntimeService 实例
 6        RuntimeService runtimeService = processEngine.getRuntimeService();
 7        //根据流程定义的key启动流程
 8        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("candidate_demo","168");
 9        System.out.println("流程定义Key="+processInstance.getProcessDefinitionKey());
10        System.out.println("流程定义id="+processInstance.getProcessDefinitionId());
11        System.out.println("流程实例id="+processInstance.getId());
12    }

image.png

  • 查询代办任务:候选人都可以查询
 1/**
 2     * 查询候选用户
 3     */
 4    @Test
 5    public void testCandidateUser(){
 6        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
 7        TaskService taskService = processEngine.getTaskService();
 8        List<Task> list = taskService.createTaskQuery().taskCandidateUser("张3").list();
 9        for (Task task : list){
10            System.out.println(task.getId()); //67505
11            System.out.println(task.getName());
12            System.out.println("=======");
13        }
14    }

image.png

  • 领取任务,领取后,RU_TASK 表中,对应 ASIGNEE 负责人就有了
1/**
2     * 领取任务  ACT_RU_TASK   ID_ 67505   李4
3     */
4    @Test
5    public void testClaim(){
6        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
7        TaskService taskService = processEngine.getTaskService();
8        taskService.claim("67505","李4");
9    }

image.png

  • 完成任务
 1/**
 2     * 审批任务 ACT_RU_TASK     ID_    67505
 3     */
 4    @Test
 5    public void testCompleteForCandidate(){
 6        //获取引擎
 7        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
 8        TaskService taskService = processEngine.getTaskService();
 9        taskService.complete("67505");
10    }

image.png

网关

   工作流中的网关是流程设计中非常重要的组件,它们用于控制流程的流向,实现流程的拆分、合并以及条件判断,有多个。满足一个怎么走?都满足怎么走?都不满足怎么走

排他网关(Exclusive Gateway)

  • 用于在流程中进行条件判断,根据条件选择不同的分支路径。
  • 当流程执行到排他网关时,会评估所有分支的条件,选择第一个条件为 true 的分支执行
  • 如果多个条件都为 true,则按照 XML 中定义的顺序(通常是第一个,即 ID 小的)执行
  • 如果没有任何条件满足,流程将异常结束
  • 应用场景​:根据特定条件选择不同的审批流程(如请假天数大于等于 3 天需要总经理审批,小于 3 天只需部门经理审批)

image.png

示例

User Task Assignee
HR 张三
部门经理 李四
总经理 超哥
连线 Condition expression
天数大于 3 "${day > 3}"
天数小于等于 3 "${day <= 3}"
  • 流程定义画图
    image.png
  • 流程定义部署
 1@Test
 2    public void testDeploy(){
 3        // 创建流程引擎 processEngine,会检查相关库表情况,如果没有,会自动创建
 4        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
 5        // 获取资源服务
 6        RepositoryService repositoryService = processEngine.getRepositoryService();
 7        //部署流程定义到数据库
 8        Deployment deployed = repositoryService.createDeployment().addClasspathResource("bpmn/exclusive.bpmn20.xml").deploy();
 9        System.out.println("id=" + deployed.getId());
10        System.out.println("name=" + deployed.getName());
11        System.out.println("key=" + deployed.getKey());
12        System.out.println("deploymentTime=" + deployed.getDeploymentTime());
13    }

image.png

  • 流程实例启动
 1@Test
 2    public void testStartVar(){
 3        //创建流程引擎 processEngine, 会检查库表,如果没有就自动创建
 4        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
 5        //获取运行时服务,通过该服务可以启动流程实例
 6        RuntimeService runtimeService = processEngine.getRuntimeService();
 7        //创建一个变量映射,用于在启动流程实例时传递变量
 8        Map<String,Object> map = new HashMap<>();
 9        //传入变量
10        map.put("day",2);
11        //通过流程定义的key启动流程实例,并传递变量映射
12        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("exclusive", "222", map);
13        System.out.println("流程定义Key="+processInstance.getProcessDefinitionKey());
14        System.out.println("流程定义id="+processInstance.getProcessDefinitionId());
15        System.out.println("流程实例id="+processInstance.getId());
16        System.out.println("实例名称name="+processInstance.getId());
17    }

image.png

  • 任务审批
 1/**
 2     * 审批任务  ACT_RU_TASK  ID_    80006
 3     */
 4    @Test
 5    public void testCompleteForVar(){
 6        //获取引擎
 7        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
 8        TaskService taskService = processEngine.getTaskService();
 9        taskService.complete("80006");
10    }

image.png

  • 验证任务走向
    image.png

并行网关

   用于将流程 拆分成多个并行分支,这些分支可以同时执行, ​当所有分支都执行完毕后​,流程才会继续向下执行。

   并行网关分为分支(fork)和汇聚(join)两种类型,分别用于拆分和合并流程

   应用场景​:需要多个部门或人员同时处理的任务(如请假流程需要项目经理和技术经理同时审批)。

image.png

示例

User Task Assignee
HR 张三
产品组长 李四
技术组长 王五
部门经理 赵六
  • 流程定义画图
    image.png
  • 流程定义部署
 1@Test
 2    public void testDeploy(){
 3        // 创建流程引擎 processEngine,会检查相关库表情况,如果没有,会自动创建
 4        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
 5        // 获取资源服务
 6        RepositoryService repositoryService = processEngine.getRepositoryService();
 7        //部署流程定义到数据库
 8        Deployment deployed = repositoryService.createDeployment().addClasspathResource("bpmn/par.bpmn20.xml").deploy();
 9        System.out.println("id=" + deployed.getId());
10        System.out.println("name=" + deployed.getName());
11        System.out.println("key=" + deployed.getKey());
12        System.out.println("deploymentTime=" + deployed.getDeploymentTime());
13    }

image.png

  • 流程实例启动
 1@Test
 2    public void testStart(){
 3        //创建流程引擎 processEngine,会检查相关库表情况,如果没有,会自动创建
 4        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
 5        //得到 RuntimeService 实例
 6        RuntimeService runtimeService = processEngine.getRuntimeService();
 7        //根据流程定义的key启动流程
 8        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("par","555");
 9        System.out.println("流程定义Key="+processInstance.getProcessDefinitionKey());
10        System.out.println("流程定义id="+processInstance.getProcessDefinitionId());
11        System.out.println("流程实例id="+processInstance.getId());
12    }

image.png

  • HR 审批完成
1@Test
2    public void testCompleteForVar(){
3        //获取引擎
4        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
5        TaskService taskService = processEngine.getTaskService();
6        taskService.complete("87505");
7    }

image.png

  • 产品组长审批通过
1@Test
2    public void testCompleteForVar(){
3        //获取引擎
4        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
5        TaskService taskService = processEngine.getTaskService();
6        taskService.complete("90004");
7    }

image.png

  • 技术组长审批通过
1@Test
2    public void testCompleteForVar(){
3        //获取引擎
4        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
5        TaskService taskService = processEngine.getTaskService();
6        taskService.complete("90007");
7    }

image.png

   

  • 等待部门经理审批
    image.png

   

   
   
   


作者:Soulboy