Activiti7&请假流程开发

《单人审批请假流程》开发

到此,我们已经准备好了课程要使用到的环境,接下我们就先通过一个简单的《单人审批请假流程》来快速入门Activiti,通过案例来重点掌握:

  • Activiti相关术语概念
  • Activiti的开发步骤
  • Activiti相关表结构

单人审批请假流程:

image-20230819113436637

首选看一下Activiti相关术语概念:

基本概念

Activiti是一款主流的工作流 流程引擎,再此我们就先从工作流、流程引擎概念说起。

什么是工作流

工作流( Workflow ):是一项将系统任务或操作抽取,再按照预定业务流程进行组织、协调、并自动化执行的技术。

我们也可以简单理解==工作流是一种简化审批业务流程开发的技术==,其主要是解决一些业务场景硬编码方式的成本高、效率低、变更慢等问题的技术。

复杂的业务流程,实现复杂度比较高,因此开发周期长,开发成本高,效率也低,如果发生需求变更,对之前复杂的代码进行修改,同样需要更长的时间,因此响应变更的速度呢也表现得慢。

image-20230819122330430

工作流技术来实现类似复杂的业务,只需要集成流程引擎、画出流程中使用的表单、最后画出整个业务流程图,交给流程引擎3步,即可完成需求的开发。在整个实现过程中,流程引擎是一个三方框架,不需要我们过多编码,而画表单、画流程就是在页面中进行一些拖拉拽的操作。因此整个过程下来我们编码量极少,需要的开发时间也极少,通过此方式呢就轻松的解决了开发周期长、成本高、效率低的问题,如果需求变更了,工作流技术只需要修复之前的表单或图即可,因此变更响应速度自然也必硬编码开发的速度要快。

image-20230819122620945

什么是流程引擎

流程引擎:是用于驱动或控制业务流程每一步自动执行的代码框架。

目前市面上主流的流程引擎框架有jBPMN、Activiti、Flowable、Camunda4款,其中Activiti开源度最大,因此是当下主流的首选流程引擎。

本课程则选择Activiti7作为我们的学习流程引擎,主要因素有:

  • Activiti开源度大,企业使用多
  • Activiti成熟度好,文档资料完善
  • 学习完成Activiti之后,可以快速上手Flowable、Camunda等基于Activitii二次开源的框架

image-20240801174522258

Activiti介绍

Activiti:是一个开源的轻量级工作流和业务流程管理与执行的引擎。2010年基于jBPM4实现首次开源,旨在帮助企业自动化和优化各种业务流程。

官网地址:https://www.activiti.org/

Activiti 的主要特点和优势:

  • 开源和跨平台:Activiti 是一个开源项目,可以在各种操作系统和平台上运行,提供灵活的部署选项。

  • 高度可定制:Activiti 提供了可视化的流程设计器,使用户能够轻松设计和定制各种业务流程。流程定义可以基于 BPMN 2.0 标准进行创建。

  • 强大的任务管理:Activiti 允许用户定义任务、分配任务给不同的用户或组,并支持任务的优先级和截止日期。

  • 工作流引擎:Activiti 提供了强大的工作流引擎,能够自动执行流程、处理任务、执行规则等。它支持顺序流、条件流、并行网关等流程控制元素。

  • 事件驱动:Activiti 支持事件驱动的流程,可以在特定的事件发生时触发流程的不同分支。

  • 可扩展性:Activiti 允许开发人员通过编写自定义 Java 类来扩展其功能,满足特定的业务需求。

  • 数据持久化:Activiti 使用关系型数据库来存储流程定义、流程实例、任务等信息,保证了数据的持久性和可靠性。

  • 集成支持:Activiti 可以与其他技术和系统集成,如 Spring、RESTful Web Services、JavaEE 等。

  • 监控和报告:Activiti 提供了流程实例的监控、报告和分析功能,使用户能够了解流程的状态和性能。

尽管Activiti很强大,其还是有一些劣势需要完善:

  • 提供的设计器只能开发者使用,而且体验不是很好
  • 不支持直接面向业务人员使用
  • 不支持表单
  • 不支持国产功能,比如会签、跳转等功能
  • 不支持流程状态图
  • 与SpringClound微服务集成方式不优雅
  • 使用门槛、成本比较高

image-20230819130820243

那么针对这些劣势的解决,本课程开源的hiss项目,都可以解决。课程最终除了让大家掌握Activiti技术,也会把hiss项目交给大家。让大家带着知识和工具到企业中帮助企业解决这些问题,成为企业需要的人才,进而提升同学们自身的就业竞争力!

需求说明

有了刚才的知识,接下来我们就开始分析《单人审批请假流程》的需求以及使用Activiti对其进行开发的步骤。

需求分析

在企业中,员工如果有事需要请假,一般都需要向上级请假,得到批准后,方可离开公司。而整个过程在企业中需要在系统中完成,以信息化员工的出勤信息,便于工资、奖金的结算,因此需要开发此功能,需求如下:

  • 员工:请假的员工需要先填写请假单,填写的字段有:请假人、请假天数、开始请假时间、请假事由;
  • 经理:审批员工的请假单,如果不同意,则需要说明不同意的理由;

image-20230819113436637

实现步骤

在这里我们主要是来了解一下基于Activiti如何来实现上述需求,其主要的实现步骤是什么?

在前面我们有提到工作流只需要下面3步即可,在此需求中虽然用到了表单,但在此需求实现过程中,我们先不学画表单,这步放到后面一点去学习。

image-20230819140812914

因此这个需求只需要集成流程引擎、画流程两步实现即可,但是之前我们有介绍到Activiti目前也有些功能还需要完善,在这个流程中我们要用到的一些功能它不支持:

  • 画好的流程图不支持直接保存,需要用代码来保存
  • 画好的流程图直接运行,需要用代码来发布执行
  • 不支持同意、不同意国产功能,需要自己设计开发

==那么此需求用Activiti的实现步骤呢,就变成以下5步:==

  • 1、集成流程引擎
  • 2、画流程
  • 3、保存流程图
  • 4、部署和运行流程图
  • 5、开发同意和不同意审批功能

在此我们学习呢也将按照上面5步,逐步展开对Activiti的知识学习。最后要补充说明一下,上述5步,在Hiss项目中,只需要前两步即可,后3步已经封装好了,因此和我们开始说的步骤是一致的。至于怎么样封装的,在稍后课程中会教给大家。

第一步:SpringBoot集成Activiti7

SpringBoot官方未提供直接的starter与Activiti集成,但是Activiti官网有帮我们提供,因此集成的过程也还是比较简单,按照下面4步即可完成。

PS:1、导依赖—>2、配置属性—>3、配置Bean—>4、启动测试。这4步是SpringBoot与任何框架集成的标准步骤,同学们可以牢记。

导入依赖

在hiss-test模块下创建demo-activiti工程,用于Activiti7与SpringBoot整合的测试与学习。

导入父POM

Activiti提供了自己POM,以简化我们使用activiti的依赖。在此我们选择了7.10.0版本,在hiss-center根下的pom.xml文件中导入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<properties>
<activiti.version>7.10.0</activiti.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-dependencies</artifactId>
<version>${activiti.version}</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
设定Activiti仓库地址

Activiti有些jar包,未上传到中央仓库,因此使用的时候需要添加Activiti的仓库,这里推荐直接在hiss-center的pom.xml文件中添加,避免对其它项目影响:

1
2
3
4
5
6
<repositories>
<repository>
<id>activiti-releases</id>
<url>https://artifacts.alfresco.com/nexus/content/repositories/activiti-releases</url>
</repository>
</repositories>
导入Activiti依赖

有了前面2不小步,在要使用的Activiti的模块中直接导入下面starter即可导入Activiti的依赖。在这里我们在test模块下,建立一个demo-activiti的工程,然后引入下面的依赖。

1
2
3
4
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
</dependency>

另外Activiti需要使用到数据库,它支持Mysq、Oracle、Postgres等多种数据库,本课程选用常见的Mysql,因此还需要导入数据库驱动和Mybatis-Plus ORM依赖。

1
2
3
4
5
6
7
8
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>

配置属性

配置属性就是指在application.yml或者bootstrap.yml文件中配置引入框架的常用属性,而在这里Activiti7的常用配置属性如下:

1
2
3
4
5
spring:
activiti:
database-schema-update: true #自动创建数据库
db-history-used: true # 开启流程历史记录
history-level: full # 历史流程数据全部记录到历史表中,支持none、activity、audit、full,4种粒度的数据,常用的就是full

配置数据源:

注意:url中的nullCatalogMeansCurrent参数需要设置成true,表示Activiti优先在url中指定的数据库中检查是否有创建过表,如果为false,则会遍历整个数据源中的每个数据库,检查是否有创建过activiti的表。

1
2
3
4
5
6
spring:
datasource:
password: 87sdhf298TYUUIz2!
username: root
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.126.128:3306/hiss-dev-demo?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&nullCatalogMeansCurrent=true

并新创建一个数据库:hiss-dev-demo

image-20230819163024408

配置Bean

在Activiti7中,它默认集成了SpringSecurity,内部还使用到了SpringSecurity系列的上下文代码,不能直接排除掉,因此在此还需要给定一些SpringSecurity的初始配置Bean。

image-20230819162416535

其实往往我们不想使用SpringSecurity,这里就强制我们使用,显得非常臃肿、不优雅。那么在本项目中,将合理避开对SpringSecurity的使用,因此直接把SpringSecurity相关配置置空即可,既把下面的Bean拷贝到启动类中,什么都不用改:

  • UserGroupManager:用于流程中获取用户的组、角色等信息
  • SecurityManager:用于获取当前操作授权的用户、组或角色等信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@Bean
public UserGroupManager userGroupManager() {
return new UserGroupManager() {
@Override
public List<String> getUserGroups(String s) {
return null;
}

@Override
public List<String> getUserRoles(String s) {
return null;
}

@Override
public List<String> getGroups() {
return null;
}

@Override
public List<String> getUsers() {
return null;
}
};
}

@Bean
public SecurityManager securityManager() {
return new SecurityManager() {
@Override
public String getAuthenticatedUserId() {
return "miukoo-user-1";
}

@Override
public List<String> getAuthenticatedUserGroups() throws SecurityException {
return null;
}

@Override
public List<String> getAuthenticatedUserRoles() throws SecurityException {
return null;
}
};
}

注意在这里可能会报错:

image-20240802170224872

解决方法:

1.删除

2.增加启动配置以我们的bean为主

image-20240802170643153

启动测试

到此,我们就完成了集成的主要工作,接下来就是先验证一下集成结果。针对Activiti7的集成验证,只需要启动服务,看看是否报错,是否创建成功表,如果控制台没有报错,并且数据库中也成功创建了表,那么就算集成成功了!

控制台:未报错,并且有输出创建表的日志

image-20230819164618161

数据库:成功的创建了相关25张表,这些表我们稍后去给大家介绍,这里只关注是否创建成功。

image-20230819164843010

第二步:画流程

项目集成完成Activiti7之后,就可以进行第二步,画流程了,在正式画之前,有必要给大家先介绍一下这里画涉及到的知识点BPMN。

BPMN介绍

BPMN:业务流程建模与标记语言(Business Process Model and Notation),是一种用于图形化表示和描述业务流程的标准化标记语言。目前主流的版本是BPMN2.0,由 Object Management Group(OMG)制定,并于2011年发布,发展至今,已被主流的流程引擎支持,并作为默认。

官网:https://www.bpmn.org/

讲白了,BPMN和HTML类似都是标记语言,都是通过XML的格式来组织数据,供后端解析,也可转成图形化方式直观展示给用户看。

例:BPMN标准的流程文件内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="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:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bioc="http://bpmn.io/schema/bpmn/biocolor/1.0" xmlns:tns="http://www.activiti.org/testm1685269119653" xmlns:xsd="http://www.w3.org/2001/XMLSchema" id="hiss_flow_1695088765609" name="" targetNamespace="http://www.activiti.org/testm1685269119653">
<process id="hiss_process_12_1695088765717" name="hiss_process" isExecutable="true">
<startEvent id="StartEvent_1" />
<userTask id="Activity_1xi315o">
<incoming>Flow_109kst5</incoming>
</userTask>
<sequenceFlow id="Flow_109kst5" sourceRef="StartEvent_1" targetRef="Activity_1xi315o" />
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="hiss_process_12_1695088765717">
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds x="173" y="102" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_1xi315o_di" bpmnElement="Activity_1xi315o">
<dc:Bounds x="330" y="80" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_109kst5_di" bpmnElement="Flow_109kst5" bioc:stroke="#060606" bioc:fill="none">
<di:waypoint x="209" y="120" />
<di:waypoint x="330" y="120" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>

转成直观的图:

image-20230819170453556

BPMN设计器

虽然BPMN是一个标记语言,但是在实际中,我们很少直接去用它语法进行开发,而是直接使用流程设计器来画,而画用前端技术即可实现,在前端开源项目中有一个叫bpmn-js的开源项目,现在几乎成了画流程的标准,市面上的流程设计器基本都是基于它改造的。比如Activiti7官网提供的:https://github.com/activiti/activiti-modeling-app设计器,就是基于bpmn-js实现的。

img

但是因为其是英文版,使用习惯是按照西方人设计的,在使用体验上不好,因此在这里我们使用hiss产品中自研的设计器,也是基于bpmn-js实现的。它的面板介绍如下:

image-20230819173006479

绘制《单人审批请假流程》

在BPMN2.0中定义了很多图例,也就是主键面板上的那些图标,每种图例表示着不同的含义,那么在本案例中,我们需要用到以下3种图例:

  • 开始图例:表示一个流程的开始,一个流程可以有多个开始,但是至少需要一个开始图例
  • 结束图例:表示一个流程的结束,一个流程可以有多个结束,但是至少需要一个结束图例
  • 用户图例:也称用户任务,表示当前节点是需要人工参与的,比如需要人工审批、人工填写表单等

image-20230819174539575

有了上面的了解,那么就可以开始绘制:开始节点和填写请假单节点:

(画的方式一:拖拉画)拖拉组件面板中的图例,到画图区中,并配置办理人和连线即可。

图中表示请假单由张三填写。

image-20230819174133639

接着画经理审批和结束节点:

(画的方式二:快捷插入画)选中节点后,会在节点上弹出快捷操作,点击结束图例,即可快速在节点后面增加一个结束节点并自动连线。

图中经理审批节点的办理人为李四,表示张三填写的请假单由李四来审批。

image-20230819175745551

解读BPMN的xml文件

通过上面的操作我们就画出了《单人审批请假流程》,接下来我们来了解一下BPMN2.0的xml文件内容,做一个大概了解。

点击右上角的【设计模式】即可切换到【源码模式】,看见类似下图的界面。界面中就是BPMN2.0的xml内容,可见包括的内容如下:

  • 整个xml的根元素是definitions,它有两个子元素:
    - 流程节点信息使用process元素定义
    - 每种图例对应到xml中都是不同的元素,比如:
    - 开始图例对应startEvent元素
    - 结束图例对应endEvent元素
    - 用户任务对应userTask元素
    - 流程图中的线也是一种图例,对应sequenceFlow元素
    - 每种图例元素都一个唯一id属性
    - 在图中配置的办理人,其实是userTask元素的 activiti:assignee属性值

    • 流程展示的图形信息使用bpmndi:BPMNDiagram元素定义

image-20230819181018807

第三步:保存流程图

到此画完的流程图,还需要保存到磁盘或者数据库中,不然刷新网页就丢失了。而Activiti7在数据库层面给我们已经设计好了相关的表和API,直接调用即可。

保存到磁盘

在磁盘任意位置,新建一个bpmn-01.xml文件,并把上一步画的流程图的xml保存到改文件中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="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:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:activiti="http://activiti.org/bpmn" xmlns:bioc="http://bpmn.io/schema/bpmn/biocolor/1.0" xmlns:tns="http://www.activiti.org/testm1685269119653" xmlns:xsd="http://www.w3.org/2001/XMLSchema" id="hiss_flow_1692440641378" name="" targetNamespace="http://www.activiti.org/testm1685269119653">
<process id="hiss_process_12_1692440641431" name="hiss_process" isExecutable="true">
<startEvent id="StartEvent_1" />
<userTask id="Activity_1xi315o" name="填写请假单" activiti:assignee="张三">
<incoming>Flow_109kst5</incoming>
<outgoing>Flow_0wd9dr1</outgoing>
</userTask>
<sequenceFlow id="Flow_109kst5" sourceRef="StartEvent_1" targetRef="Activity_1xi315o" />
<userTask id="Activity_0xmqcac" name="经理审批" activiti:assignee="李四">
<incoming>Flow_0wd9dr1</incoming>
<outgoing>Flow_1s2a4e5</outgoing>
</userTask>
<sequenceFlow id="Flow_0wd9dr1" sourceRef="Activity_1xi315o" targetRef="Activity_0xmqcac" />
<endEvent id="Event_1qwztvn">
<incoming>Flow_1s2a4e5</incoming>
</endEvent>
<sequenceFlow id="Flow_1s2a4e5" sourceRef="Activity_0xmqcac" targetRef="Event_1qwztvn" />
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="hiss_process_12_1692440641431">
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds x="173" y="102" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_1xi315o_di" bpmnElement="Activity_1xi315o">
<dc:Bounds x="330" y="80" width="100" height="80" />
<bpmndi:BPMNLabel />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0xmqcac_di" bpmnElement="Activity_0xmqcac">
<dc:Bounds x="560" y="80" width="100" height="80" />
<bpmndi:BPMNLabel />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_1qwztvn_di" bpmnElement="Event_1qwztvn">
<dc:Bounds x="792" y="102" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_109kst5_di" bpmnElement="Flow_109kst5" bioc:stroke="#060606" bioc:fill="none">
<di:waypoint x="209" y="120" />
<di:waypoint x="330" y="120" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0wd9dr1_di" bpmnElement="Flow_0wd9dr1" bioc:stroke="#060606" bioc:fill="none">
<di:waypoint x="430" y="120" />
<di:waypoint x="560" y="120" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1s2a4e5_di" bpmnElement="Flow_1s2a4e5" bioc:stroke="#060606" bioc:fill="none">
<di:waypoint x="660" y="120" />
<di:waypoint x="792" y="120" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>

保存到数据库

创建测试类

在demo-activiti模块的test代码中新建ProcessSaveTest类,注意包名需要和启动类包名一致。

image-20230820091511697

在新建的类中初始化下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.itcast.hiss;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class ProcessSaveTest {

/**
* 流程保存测试
*/
@ParameterizedTest
@ValueSource(strings = {"C:\\Users\\lenovo\\Desktop\\bpmn-01.xml"})
public void testSaveFromFile(String file){

}

}

项目环境中自动导入了springboot的test依赖,所集成junit版本是5.8.2,支持一种参数化的测试(Parameterized Tests,简单理解就是测试的方法可以动态传参),这在企业中也是非常常用的一种写法,上述的代码就是参数化测试代码的一段实现:

  • @ParameterizedTest 注解标注testSaveFromFile方法是一个参数化测试方法
  • @ValueSource 注解标注要传给testSaveFromFile方法的参数值有那些,参数值的类型支持基本类型+String、Class

@ParameterizedTest不仅有标注的功能,还会依据@ValueSource的值的数量,动态为每个参数值生成一个测试案例:

例如:

1
2
3
4
5
@ParameterizedTest
@ValueSource(strings = {"1","2","3"})
public void test(String num){
System.out.println(num);
}

传入了3个值,则生成了3个案例,在企业中常常用来测试一段代码在多种输入值的情况下执行结果是否正确:

image-20230820092724945

代码实现&运行

在Activiti7中对于设计好的流程图,被称为模型(Model),保存代码实现参考以下,相关说明如下:

  • RepositoryService是非常常用的API,主要用来管理(CRUD)和操作模型、发布、流程定义等资源;
  • Model是一个接口不能直接new,其对应实现类是ModelEntityImpl,而repositoryService.newModel()方法正是返回一个新的ModelEntityImpl实例;
  • 保存时,除了xml内容之外,还可以附加一些名称、分类等信息;
  • 在Activiti7底层表设计原则是:所有大字段值都保存到一张表中,而Model的xml内容属于大字段,因此下面代码分别调用了saveModel(保存基本信息)和addModelEditorSource(保存大字段信息)方法;
  • assertNotNull是junit的断言方法,主要用来判断方法执行的结果是否符合预期,在这里预期是:插入成功则model的id应该不为null
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.itcast.hiss;

import cn.hutool.core.io.FileUtil;
import lombok.extern.slf4j.Slf4j;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Model;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import static org.junit.jupiter.api.Assertions.*;

@SpringBootTest
@Slf4j
public class ProcessSaveTest {

@Autowired
RepositoryService repositoryService;

/**
* 流程保存测试
*/
@ParameterizedTest
@ValueSource(strings = {"C:\\Users\\lenovo\\Desktop\\bpmn-01.xml"})
public void testSaveFromFile(String file){
String xml = FileUtil.readUtf8String(file);
Model model = repositoryService.newModel();
model.setName("单人审批请假流程"); // 设置名称
model.setCategory("学习"); // 设置分类
repositoryService.saveModel(model);// 保存基本信息
repositoryService.addModelEditorSource(model.getId(),xml.getBytes());// 保存xml信息
assertNotNull(model.getId()); // 断言插入成功
log.info("new model id : {}", model.getId());
}

}

运行上述测试方法,出现下面情况,既模型数据就保存成功

image-20230820094524600

表分析

在上述保存方法执行成功后,数据实际上保存到了数据库中的act_re_model和act_ge_bytearray表中。

act_re_model

act_re_model表是用于存储流程模型(就是流程图)信息的表:

image-20230820101031997

序号 字段名 类型 长度 是否主键 注释
1 ID_ varchar 64 主键
2 REV_ int 10 数据更新乐观锁
3 NAME_ varchar 255 名称
4 KEY_ varchar 255 自定义业务主键
5 CATEGORY_ varchar 255 分类
6 CREATE_TIME_ timestamp 创建时间
7 LAST_UPDATE_TIME_ timestamp 更新时间
8 VERSION_ int 10 模型版本
9 META_INFO_ varchar 4000 附加信息
10 DEPLOYMENT_ID_ varchar 64 部署ID
11 EDITOR_SOURCE_VALUE_ID_ varchar 64 xml文件管理存储的数据ID
关联act_ge_bytearray中的ID
12 EDITOR_SOURCE_EXTRA_VALUE_ID_ varchar 64 附加信息存储的数据ID
关联act_ge_bytearray中的ID
13 TENANT_ID_ varchar 255 租户ID
act_ge_bytearray

在Activiti7底层大字段的存储原则是都存储到act_ge_bytearray表的BYTES_字段中。

image-20230820101212056

序号 字段名 类型 长度 是否主键 注释
1 ID_ varchar 64 主键
2 REV_ int 10 数据更新乐观锁
3 NAME_ varchar 255 名称
4 DEPLOYMENT_ID_ varchar 64 部署ID
5 BYTES_ longblob 4294967295 大字段内容
6 GENERATED_ tinyint 3 标识是否自动生成的记录

第四步:部署和运行流程图

在之前我们使用SpringBoot开发完一个web程序打成jar,然后需要把这个jar拷贝到测试或者生产环境,既部署,最后就可在不同机器上运行多个进程实例,如下图蓝色部分。而在Activiti中操作流程图的过程与其过程类似,需要先开发一个模型(model,也见流程图),然后把model的内容拷贝一份到Deployment对象中,这个过程也叫部署,最后就可以运行部署的流程,同时也支持运行多个流程实例。

一个流程图(模型)画好之后,需要发布(部署)之后才可以运行,这样的好处在于,流程图部署运行后,在去修改,不会影响到正在执行中的流程,是一种非常优秀的设计思想,具体实现呢,同学们也可以好好学习一下。

image-20230820113016984

创建测试类

在demo-activiti模块的test代码中新建ProcessRunTest类,注意包名需要和启动类包名一致。

image-20230820091511697

在新建的类中初始化下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.itcast.hiss;

import lombok.extern.slf4j.Slf4j;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
@Slf4j
public class ProcessRunTest {

@Autowired
RepositoryService repositoryService;

@Autowired
RuntimeService runtimeService;

/**
* 流程部署测试
*/
@Test
public void testDeployment(){

}

/**
* 运行测试
*/
@ParameterizedTest
@ValueSource(strings = {"流程定义key"})
public void testRun(String key){

}

}

部署实现

为避免画流程图时对运行流程影响,在部署时需要把画好的流程图复制一份到部署信息中这也是下面DeploymentBuilder对象所做的事情。除此之外还有几点说明如下:

  • repositoryService.createModelQuery()创建了一个针对act_re_model表查询的查询器,可以查询其表中的任何数据;
  • DeploymentBuilder部署资源中必须指定一个部署的流程图资源信息,并且资源信息的名称必须以*.bpmn20.xml结尾,否则部署的流程将不生效
  • 在act_re_model表中有一个DEPLOYMENT_ID_字段记录了部署ID,因此部署成功后需要修改Model表
  • RuntimeService:是非常常用的API,主要用于对流程实例(也就是运行中的流程)管理;
  • 在运行流程时,可以直接使用流程定义key运行一个流程,这个流程定义key可以在act_re_procdef表的KEY_中拷贝;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package com.itcast.hiss;

import lombok.extern.slf4j.Slf4j;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.repository.Model;
import org.activiti.engine.runtime.ProcessInstance;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import static org.junit.jupiter.api.Assertions.assertNotNull;

@SpringBootTest
@Slf4j
public class ProcessRunTest {

@Autowired
RepositoryService repositoryService;

@Autowired
RuntimeService runtimeService;

/**
* 流程部署测试
*/
@Test
public void testDeployment(){
// 通过名称查询出之前保存的流程图
Model model = repositoryService.createModelQuery().modelName("单人审批请假流程").singleResult();
assertNotNull(model); // 断言查询成功
byte[] xml = repositoryService.getModelEditorSource(model.getId());// 获取内容
String xmlName = "itcast.bpmn20.xml";
DeploymentBuilder deploymentBuilder = repositoryService.createDeployment()
.key(model.getKey())
.name(model.getName())
.category(model.getCategory())
.enableDuplicateFiltering()// 自动去重
.tenantId(model.getTenantId())
.addBytes(xmlName, xml);// 设置部署的xml名称、内容
Deployment deploy = deploymentBuilder.deploy(); // 部署
assertNotNull(deploy.getId()); // 断言插入成功
model.setDeploymentId(deploy.getId());
repositoryService.saveModel(model); // 部署的id更新回Model对象
log.info("new deployment id : {}", model.getId());
}

/**
* 运行测试
*/
@ParameterizedTest
@ValueSource(strings = {"ID"})
public void testRun(String key){
}

}

运行上述testDeployment方法,出现下面情况,既部署成功

image-20230820104922522

运行上述testRun方法,出现下面情况,既运行成功

image-20230820113409998

部署表分析

在上述部署testDeployment方法执行成功后,数据实际上保存到了数据库中的act_re_deployment、act_re_procdef、act_ru_identitylink、act_ge_bytearray、act_re_model表中。

act_ge_bytearray和act_re_model表之前我们已经介绍过了,在此看一下数据即可:

act_ge_bytearray:表中增加了一条数据,BYTES_字段内容一模一样,不一样的就是NAME_字段,并且DEPLOYMENT_ID_字段存储了部署ID

image-20230820114619650

act_re_model:表中DEPLOYMENT_ID_字段也存储了部署ID

image-20230820114857638

act_re_deployment

act_re_deployment表是用于存储部署流程图信息:

image-20230820122639131

序号 字段名 类型 长度 是否主键 注释
1 ID_ varchar 64 主键
2 NAME_ varchar 255 名称
3 CATEGORY_ varchar 255 分类
4 KEY_ varchar 255 业务key
5 TENANT_ID_ varchar 255 租户ID
6 DEPLOY_TIME_ timestamp 部署时间
7 ENGINE_VERSION_ varchar 255 引擎版本,一般为空
8 VERSION_ int 10 部署版本
9 PROJECT_RELEASE_VERSION_ varchar 255 项目版本,一般为空
act_re_procdef

act_re_procdef表用于存储流程图中解析得到的流程信息:

image-20230820123040466

序号 字段名 类型 长度 是否必填 注释
1 ID_ varchar 64 主键
2 REV_ int 10 数据更新乐观锁
3 CATEGORY_ varchar 255 分类
4 NAME_ varchar 255 名称
5 KEY_ varchar 255 流程定义key
6 VERSION_ int 10 流程定义版本
7 DEPLOYMENT_ID_ varchar 64 关联部署ID
8 RESOURCE_NAME_ varchar 4000 流程定义存在的资源名称
9 DGRM_RESOURCE_NAME_ varchar 4000 流程图文件的资源名称,一般为空
10 DESCRIPTION_ varchar 4000 流程定义描述
11 HAS_START_FORM_KEY_ tinyint 3 是否启动表单,1是,0否,一般为0
12 HAS_GRAPHICAL_NOTATION_ tinyint 3 是否图形标记,1是,0否,可以理解成
xml中节点坐标等信息是否有
13 SUSPENSION_STATE_ int 10 挂起状态,1挂起,2未挂起
14 TENANT_ID_ varchar 255 租户ID
15 ENGINE_VERSION_ varchar 255 引擎版本,一般不用
16 APP_VERSION_ int 10 APP版本,一般不用

act_ru_identitylink表用于存储资源(比如:流程定义、流程实例、任务等资源)授权信息,拥有授权信息才可以使用对应资源:

这条数据可以理解为:ID为XXX的流程定义被授权给任意分组使用,既没有使用限定。

image-20230820123932599

序号 字段名 类型 长度 是否主键 注释
1 ID_ varchar 64 主键
2 REV_ int 10 数据更新乐观锁
3 GROUP_ID_ varchar 255 授权的分组ID
4 TYPE_ varchar 255 授权类型
5 USER_ID_ varchar 255 授权的用户ID
6 TASK_ID_ varchar 64 任务资源ID
7 PROC_INST_ID_ varchar 64 流程实例资源ID
8 PROC_DEF_ID_ varchar 64 流程定义资源ID

运行实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package com.itcast.hiss;

import lombok.extern.slf4j.Slf4j;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.repository.Model;
import org.activiti.engine.runtime.ProcessInstance;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import static org.junit.jupiter.api.Assertions.assertNotNull;

@SpringBootTest
@Slf4j
public class ProcessRunTest {

@Autowired
RepositoryService repositoryService;

@Autowired
RuntimeService runtimeService;

/**
* 流程部署测试
*/
@Test
public void testDeployment(){
}

/**
* 运行测试
*/
@ParameterizedTest
@ValueSource(strings = {"hiss_process_12_1692440641431"})
public void testRun(String key){
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(key);
assertNotNull(processInstance.getId()); // 断言插入成功
log.info("new processInstance id : {}", processInstance.getId());
}

}

运行表分析

  • 任务实例:就是流程图中的每个需要人工参与的节点
  • 流程实例:流程定义运行起来的流程被称为流程实例
  • 活动实例:就是流程图中的每个节点
  • 执行器:记录当前执行节点的信息

对部署好的流程图运行之后,得到流程实例(ProcessInstance),这个运行中的流程实例信息存储到了act_ru_identitylink、act_ru_execution、act_ru_task表中。

act_ru_identitylink:多出了一条记录,是因为我们启动流程后,开始节点会自动执行并到下一个节点既【填写请假单】,而请假单需要张三这个参与人来办理,因此在此表中就多出一条记录,表示流程实例ID为XXX的流程授权给张三操作。

这里通过观察我们还可发现,在之前设计流程图中,还有一步李四【经理审批】,而此表中没有出现李四的授权信息,则说明流程执行到那个节点才会在此表中添加授权信息

image-20230820124823258

image-20230819174133639

act_ru_execution

act_ru_execution用于存储运行中流程的节点信息。不仅存储流程实例信息,还存储流程实例的待执行节点信息。

刚才运行的流程实例,首选会在此表中创建一条流程实例信息(特点:PARENT_ID_字段为空),比如下图中的第1行数据。接着流程流转到【填写请假单】节点,因此在此表中还创建了一条待执行节点的信息,比如下图中的第2行数据。我们也可以通过ACT_ID_字段值与xml中节点元素的ID值比对印证。

同时我们通过观察还可以发现,在之前设计流程图中,还有一步李四【经理审批】,而此表中没有出现相关节点信息,则说明流程执行到那个节点才会在此表中添加节点信息

image-20230820125930243

序号 字段名 类型 长度 注释
1 ID_ varchar 64 主键
2 REV_ int 10 数据更新乐观锁
3 PROC_INST_ID_ varchar 64 流程实例ID
4 BUSINESS_KEY_ varchar 255 业务自定义key
5 PARENT_ID_ varchar 64 父执行ID
6 PROC_DEF_ID_ varchar 64 流程定义ID
7 ROOT_PROC_INST_ID_ varchar 64 根流程实例ID
8 ACT_ID_ varchar 255 正在活跃的活动ID,节点可以任务是时间或任务,ID对应画图时的ID
9 IS_ACTIVE_ tinyint 3 是否激活,1激活,2挂起
10 IS_CONCURRENT_ tinyint 3 是否是并行分支,1是,0否
11 IS_SCOPE_ tinyint 3 是否独立执行,1是,0不是,独立空间执行会有独立的变量等信息
12 IS_EVENT_SCOPE_ tinyint 3 是否独立事件执行,1是,0不是
13 IS_MI_ROOT_ tinyint 3 是否多实例,1是,0不是
14 SUSPENSION_STATE_ int 10 挂起状态,1激活,2挂起
15 CACHED_ENT_STATE_ int 10 缓存实体状态
16 TENANT_ID_ varchar 255 租户ID
17 NAME_ varchar 255 流程实例名称
18 START_TIME_ datetime 开始时间
19 START_USER_ID_ varchar 255 开始用户
20 LOCK_TIME_ timestamp 锁住的时间
21 IS_COUNT_ENABLED_ tinyint 3 是否能够计数
22 EVT_SUBSCR_COUNT_ int 10 订阅的事件数量
23 TASK_COUNT_ int 10 任务数量
24 JOB_COUNT_ int 10 作业数量
25 TIMER_JOB_COUNT_ int 10 定时作业数量,activiti自带定时作业功能
26 SUSP_JOB_COUNT_ int 10 挂了的作业数量
27 DEADLETTER_JOB_COUNT_ int 10 延迟任务数量
28 VAR_COUNT_ int 10 变量数量
29 APP_VERSION_ int 10 应用版本
act_ru_task

act_ru_task用于存储需要办理的任务信息。

在刚才运行的流程中,【填写请假单】节点需要张三办理,因此在此表中可以查到相关信息。另外在此表我们还是可以发现未执行节点,不会在此表中创建任务

image-20230820132031343

序号 字段名 类型 长度 注释
1 ID_ varchar 64 主键
2 REV_ int 10 数据修改乐观锁
3 EXECUTION_ID_ varchar 64 执行实例ID
4 PROC_INST_ID_ varchar 64 流程实例ID
5 PROC_DEF_ID_ varchar 64 流程定义ID
6 NAME_ varchar 255 当前任务名称
7 BUSINESS_KEY_ varchar 255 业务自定义KEY
8 PARENT_TASK_ID_ varchar 64 父任务ID
9 DESCRIPTION_ varchar 4000 任务描述,对应画图时的document
10 TASK_DEF_KEY_ varchar 255 任务定义KEY,流程图中的id字段值
11 OWNER_ varchar 255 任务拥有者
12 ASSIGNEE_ varchar 255 任务办理人
13 DELEGATION_ varchar 64 任务委托状态,任务被委托时,为PENDING,委托任务被解决后为RESOLVED
14 PRIORITY_ int 10 优先级,默认50
15 CREATE_TIME_ timestamp 创建时间
16 DUE_DATE_ datetime 截止时间
17 CATEGORY_ varchar 255 任务类别
18 SUSPENSION_STATE_ int 10 挂起状态,1激活,2挂起
19 TENANT_ID_ varchar 255 租户
20 FORM_KEY_ varchar 255 表单key,一般不用
21 CLAIM_TIME_ datetime 任务被认领的时间
22 APP_VERSION_ int 10 应用版本

-

第五步:开发同意和不同意审批功能

创建测试类

在demo-activiti模块的test代码中新建ProcessHandleTest类,注意包名需要和启动类包名一致。

image-20230820142607089

在新建的类中初始化下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package com.itcast.hiss;

import lombok.extern.slf4j.Slf4j;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
@Slf4j
public class ProcessHandleTest {

@Autowired
RepositoryService repositoryService;

@Autowired
RuntimeService runtimeService;

@Autowired
TaskService taskService;

/**
* 完成填写清单
*/
@ParameterizedTest
@ValueSource(strings = {"这里是任务ID"})
public void testCompleteNode2(String taskId){

}

/**
* 同意
*/
@ParameterizedTest
@ValueSource(strings = {"这里是任务ID"})
public void testAgree(String taskId){

}

/**
* 不同意,既拒绝
*/
@ParameterizedTest
@ValueSource(strings = {"这里是流程实例ID"})
public void testReject(String processInstanceId){

}

}

完成填写请假单任务

在上面运行的流程实例中,流程实例正等待填写请假单,那么假设我们此时填写完成了请假单,想继续下一步审批,该怎么办呢?

其实这个需求Activiti提供了相关API,可以直接标注某个任务执行完成,然后流程自动到下一步,相关实现代码如下:

  • TaskService:是一个常用的API,主要用来对任务进行各种操作
  • 调用complete方法,即可结束当前节点,并且流程会自动开启下一个节点的任务。同时在结束时还可以把填写的表单数据,作为流程变量(这些流程变量,后续再去讲解如何使用)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Autowired
TaskService taskService;

/**
* 完成填写清单
*/
@ParameterizedTest
@ValueSource(strings = {"这里替换成act_ru_task表中的id_"})
public void testCompleteNode2(String taskId){
// 把张三填写的请假单中的数据,作为流程变量,设置到流程中
Map<String, Object> variables = new HashMap<>();
variables.put("userName","张三");
variables.put("startDate","2024-1-1");
variables.put("days","1");
variables.put("reason","元旦回家");
// 标记任务完成
taskService.complete(taskId, variables);
}

执行上面testCompleteNode2方法后,可以在act_ru_execution表中发现,新增了【经理审批】待执行的节点,而完成的【填写请假单】节点被删除了。

image-20230820143520807

同时在act_ru_task表中,待办任务也变成了李四【经理审批】:

image-20230820143714186

代码中传入的表单数据,同时也作为流程变量存储到了act_ru_variable表中(详细字段含义,后续讲解)。

image-20230820144747919

经理同意

关于经理同意操作的实现,如果我们把【同意】看做是经理填写的【审批表单(包括:审批结果、审批意见)】中的一个字段,那么其实现和完成填写请假单任务的实现差不多,经理点击同意之后也需要完成当前节点执行下一结束节点。

image-20230819113436637

则同意操作的实现代码如下:

  • 调用complete方法时,把同意作为表单数据传入,也作为了流程变量数据。
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 同意
*/
@ParameterizedTest
@ValueSource(strings = {"这里替换成act_ru_task表中的id_"})
public void testAgree(String taskId){
// 把【同意】看做是填写的【审批表单(包括:审批结果、审批意见)】中的approvalStatus字段
Map<String, Object> variables = new HashMap<>();
variables.put("approvalStatus","同意");//
variables.put("approvalNote","");
// 标记任务完成
taskService.complete(taskId, variables);
}

上述代码执行成功后,到数据库中查看act_ru_execution、act_ru_task、act_ru_variable中的数据,你会发现里面没有了相关流程实例的数据,那是因为【经理审批】节点执行完成之后,后面的结束节点也会自动执行,因此流程就执行完成了。而act_ru_开头的表只存储运行中的流程信息,不会存储流程结束了的信息,结束了的相关信息都转到act_hi开头的表中存储了,相关表稍后我们再去认识。

经理不同意

经理不同意和同意都是审批操作,那么不同意的实现就和同意的实现一样?不然,在这里不同意的需求要比同意的复杂一点,不同意则流程会终止执行。比如下面流程中,如果经理1审批不同意,那么经理2就不用审批了,整个流程就应该直接结束。因此不同意,则应该是终止流程而不是完成节点。

image-20230820150955034

综上需求所述,则不同意的实现代码如下:

  • 在这里,要终止流程,因此操作的是流程实例,因此使用的是runtimeService API,这里用到的是流程实例的ID,不是任务ID
  • 在删除流程时,同时也把审批不同意及理由,作为流程变量存储到流程变量中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 不同意,既拒绝
*/
@ParameterizedTest
@ValueSource(strings = {"这里替换成act_ru_execution表中的流程实例的id_"})
public void testReject(String processInstanceId){
String reason = "这里是不同意的理由";
// 把【同意】看做是填写的【审批表单(包括:审批结果、审批意见)】中的approvalStatus字段
Map<String, Object> variables = new HashMap<>();
variables.put("approvalStatus","不同意");
variables.put("approvalNote",reason);
//记录流程变量
runtimeService.setVariables(processInstanceId, variables);
// 添加流程变量,表示任务被拒绝
runtimeService.deleteProcessInstance(processInstanceId, reason);
}

要运行上面代码,我们需要重新发起一个流程实例,并完成【填写请假单】节点后,再实现,具体操作:

  • 直接运行ProcessRunTest类中的testRun方法,新启动一个实例
  • 修改ProcessHandleTest类中testCompleteNode2方法上的taskId值,并运行testCompleteNode2方法
  • 最后修改testReject上的流程实例ID,然后运行testReject方法即可

上述代码执行成功后,同样在数据库中act_ru_execution、act_ru_task、act_ru_variable中的看不到数据,应为整个流程都被删除了,也结束了,因此信息也被转到了act_hi开头的表中。

到此,《单人审批请假流程》的整个流程就基本实现完成了。

表分析

通过上面的学习我们知道了act_ru_开头的表只存储运行中的流程信息,而流程结束后,则信息都存储到act_hi开头的表中了,其实act_hi开头的表不管是运行中还是结束的流程实例信息都存储了

接下来,我们就一起来看看这些表。

act_ru_variable

act_ru_variable用于存储运行中流程的变量信息。

image-20230820144747919

序号 字段名 类型 长度 注释
1 ID_ varchar 64 主键
2 REV_ int 10 乐观锁
3 TYPE_ varchar 255 变量类型json\string\integer
4 NAME_ varchar 255 变量名
5 EXECUTION_ID_ varchar 64 执行ID
6 PROC_INST_ID_ varchar 64 流程实例ID
7 TASK_ID_ varchar 64 任务ID
8 BYTEARRAY_ID_ varchar 64 二进制数据管理的ID,关联BYTEARRAY表
9 DOUBLE_ double 22 存储变量类型为Double
10 LONG_ bigint 19 存储变量类型为long
11 TEXT_ varchar 4000 存储变量值类型为String 如此处存储持久化对象时,值jpa对象的class
12 TEXT2_ varchar 4000 此处存储的是JPA持久化对象时,才会有值。此值为对象ID
act_hi_actinst

act_hi_actinst用于存储流程实例每个节点信息,在activiti中把每个节点都称为活动,因此此表也叫活动信息表。

PS:活动与任务的区别:流程图中每个节点都被称为活动,但是并不是所有活动在流程实现执行过程中都会创建任务,比如开始节点就不会任务。

image-20230820160527454

序号 字段名 类型 长度 是否主键 注释
1 ID_ varchar 64 主键
2 PROC_DEF_ID_ varchar 64 流程定义ID
3 PROC_INST_ID_ varchar 64 流程实例ID
4 EXECUTION_ID_ varchar 64 执行ID
5 ACT_ID_ varchar 255 xml中活动节点id
6 TASK_ID_ varchar 64 任务ID
7 CALL_PROC_INST_ID_ varchar 64 调用的流程实例ID
8 ACT_NAME_ varchar 255 xml中活动节点的name
9 ACT_TYPE_ varchar 255 xml中活动节点的元素名
10 ASSIGNEE_ varchar 255 办理人
11 START_TIME_ datetime 开始时间
12 END_TIME_ datetime 结束时间
13 DURATION_ bigint 19 耗时
14 DELETE_REASON_ varchar 4000 删除原因
15 TENANT_ID_ varchar 255 租户ID
act_hi_detail

act_hi_detail表记录了整个流程运行过程中,对流程变量、表单数据、备注等操作的操作明细。被称为流程运行明细表。

image-20230820161209252

序号 字段名 类型 长度 是否主键 注释
1 ID_ varchar 64 主键
2 TYPE_ varchar 255 操作类型支持变量、表单数据、备注
3 PROC_INST_ID_ varchar 64 流程实例
4 EXECUTION_ID_ varchar 64 执行ID
5 TASK_ID_ varchar 64 任务ID
6 ACT_INST_ID_ varchar 64 活动实例ID
7 NAME_ varchar 255 变量名
8 VAR_TYPE_ varchar 255 变量类型
9 REV_ int 10 数据更新乐观锁
10 TIME_ datetime 操作时间
11 BYTEARRAY_ID_ varchar 64 大字段关联act_ge_bytearray表的ID
12 DOUBLE_ double 22 double类型的变量值
13 LONG_ bigint 19 long类型的变量值
14 TEXT_ varchar 4000 String类型的变量值

act_hi_identitylink比act_ru_identitylink表少了一个REV_字段,其它字段都一样。

image-20230820161732133

序号 字段名 类型 长度 是否主键 注释
1 ID_ varchar 64 主键
2 PROC_DEF_ID_ varchar 64 流程定义资源ID
3 GROUP_ID_ varchar 255 授权的分组ID
4 TYPE_ varchar 255 授权类型
5 USER_ID_ varchar 255 授权的用户ID
6 TASK_ID_ varchar 64 任务资源ID
7 PROC_INST_ID_ varchar 64 流程实例资源ID
act_hi_procinst

act_hi_procinst流程实例信息表,此表和之前act_ru_execution不一样,只存储了流程实例的信息,流程实例节点的信息没像act_ru_execution表一样存储。

image-20230820162635209

序号 字段名 类型 长度 是否主键 注释
1 ID_ varchar 64 主键
2 PROC_INST_ID_ varchar 64 流程实例ID
3 BUSINESS_KEY_ varchar 255 业务自定义KEY
4 PROC_DEF_ID_ varchar 64 流程定义ID
5 START_TIME_ datetime 开始时间
6 END_TIME_ datetime 结束时间
7 DURATION_ bigint 19 耗时
8 START_USER_ID_ varchar 255 发起人ID
9 START_ACT_ID_ varchar 255 开始活动xmlID
10 END_ACT_ID_ varchar 255 结束活动xmlID
11 SUPER_PROCESS_INSTANCE_ID_ varchar 64 父流程实例ID
12 DELETE_REASON_ varchar 4000 删除原因
13 TENANT_ID_ varchar 255 租户ID
14 NAME_ varchar 255 流程名称
act_hi_taskinst

act_hi_taskinst基本和act_ru_taskinst表相同,但又改变了一些字段比如结束时间、耗时等,记录了更明细的task任务信息。

image-20230820163034215

序号 字段名 类型 长度 是否必填 注释
1 ID_ varchar 64 主键
2 PROC_DEF_ID_ varchar 64 流程定义ID
3 TASK_DEF_KEY_ varchar 255 任务定义KEY,流程图中的id字段值
4 PROC_INST_ID_ varchar 64 流程实例ID
5 EXECUTION_ID_ varchar 64 执行实例ID
6 NAME_ varchar 255 任务名称
7 PARENT_TASK_ID_ varchar 64 父任务ID
8 DESCRIPTION_ varchar 4000 任务描述,对应画图时的document
9 OWNER_ varchar 255 归属人
10 ASSIGNEE_ varchar 255 办理人
11 START_TIME_ datetime 开始时间
12 CLAIM_TIME_ datetime 认领时间
13 END_TIME_ datetime 结束时间
14 DURATION_ bigint 19 耗时
15 DELETE_REASON_ varchar 4000 删除理由
16 PRIORITY_ int 10 优先级
17 DUE_DATE_ datetime 截止时间
18 FORM_KEY_ varchar 255 表单KEY
19 CATEGORY_ varchar 255 分类
20 TENANT_ID_ varchar 255 租户ID
act_hi_varinst

act_hi_varinst存储了流程实例过程中的所有变量数据,比act_ru_variable多了CREATE_TIME_、LAST_UPDATED_TIME_两个字段。

image-20230820163855253

序号 字段名 类型 长度 注释
1 ID_ varchar 64 主键
2 REV_ int 10 乐观锁
3 TYPE_ varchar 255 变量类型json\string\integer
4 NAME_ varchar 255 变量名
5 EXECUTION_ID_ varchar 64 执行ID
6 PROC_INST_ID_ varchar 64 流程实例ID
7 TASK_ID_ varchar 64 任务ID
8 BYTEARRAY_ID_ varchar 64 二进制数据管理的ID,关联BYTEARRAY表
9 DOUBLE_ double 22 存储变量类型为Double
10 LONG_ bigint 19 存储变量类型为long
11 TEXT_ varchar 4000 存储变量值类型为String 如此处存储持久化对象时,值jpa对象的class
12 TEXT2_ varchar 4000 此处存储的是JPA持久化对象时,才会有值。此值为对象ID
13 CREATE_TIME_ datetime 创建时间
14 LAST_UPDATED_TIME_ datetime 更新时间