activiti7关于SpringBoot前后端分离项目的详细教程
文章目录
本篇文章是从网上购买的教程然后整理出来的笔记,供大家一起学习参考。
一、Activiti工作流概述
这一章节就是对工作流的基础概述,对此比较熟悉的可以直接跳过。
1.1 什么是工作流
工作流的简单概念就是用于流程审批的。
如:请假审批流程、报销审批流程、出差审批流程、合同审批流程等。
工作流(Workflflow),就是“业务过程的部分或整体在计算机应用环境下的自动化”,它主要解决的是“使在多个参与者 之间按照某种预定义的规则传递文档、信息或任务的过程自动进行,从而实现某个预期的业务目标,或者促使此目标的实现”。 工作流引擎,主要是为了帮忙我们实现流程自动化控制,对应的Activiti引擎就可以实现自动化控制。 工作流管理系统是一个软件系统,它完成工作量的定义和管理,并按照在系统中预先定义好的工作流规则进行工作
1.2 工作流应用场景
工作流应用场景:
- 业务类:合同审批流程、订单处理流程、出入库审批流程等。
- 行政类:请假流程、出差流程、用车流程、办公用品申请流程等。
- 财务类:报销流程、支付流程等。
- 客户服务类:售后跟踪、客户投诉等。
1.3 什么是Activiti
Activiti 是由 jBPM (BPM,Business Process Management 即业务流程管理) 的创建者 Tom Baeyens 离开 JBoss 之后建立的项目,构建在开发 jBPM 版本 1 到 4 时积累的多年经验的基础之上,旨在创建下一代的 BPM 解 决方案。 Activiti 是一个开源的工作流引擎,它实现了BPMN 2.0规范,可以发布设计好的流程定义,并通过api进行流程调 度。 Activiti 作为一个遵从 Apache 许可的工作流和业务流程管理开源平台,其核心是基于Java的超快速、超稳定的BPMN2.0 流程引擎,强调流程服务的可嵌入性和可扩展性,同时更加强调面向业务人员。
Activiti 流程引擎重点关注在系统开发的易用性和轻量性上。每一项 BPM 业务功能 Activiti 流程引擎都以服务的形 式提供给开发人员。通过使用这些服务,开发人员能够构建出功能丰富、轻便且高效的 BPM 应用程序。
Activiti是一个针对企业用户、开发人员、系统管理员的轻量级工作流业务管理平台,其核心是使用Java开发的快 速、稳定的BPMN 2.0流程引擎。Activiti是在ApacheV2许可下发布的,可以运行在任何类型的Java程序中,例如服 务器、集群、云服务等。Activiti可以完美地与Spring集成。同时,基于简约思想的设计使Activiti非常轻量级。
1.4 Activiti开发流程
1、画流程定义模型:
遵守BPMN的流程规范,使用BPMN的流程定义工具,通过 流程符号 把整个业务流程定义出来,可以将流程定义文件字节流保存到模型数据表中(Model)。 其实用可视化工具画出来的每一个流程图都是一个.bpmn文件,在idea中也可以随时使用插件把bpmn文件进行可视化查看。
2、部署流程定义:
加载画好的流程定义文件,将它转换成流程定义数据(ProcessDefifinition),保存到流程定义数据表中。
3、启动流程(提交流程申请):
生成流程实例数据(ProcessInstance),生成第1个节点的任务数据(Task);
4、处理人审批流程节点任务:
完成任务审批,生成审批结果,生成下一节点任务数据。直至满足条件,流程走到结束节点。
1.5 BPMN 2.0规范是什么
其实就是使用bpmn的规范来定义画流程图,最终会形成一个bpmn文件。
业务流程模型注解(Business Process Modeling Notation - BPMN)是业务流程模型的一种标准图形注解。这个 标准是由对象管理组(Object Management Group - OMG)维护的。
标准的早期版本(1.2版以及之前)仅仅限制在模型上, 目标是在所有的利益相关者之间形成通用的理解, 在文 档,讨论和实现业务流程之上。 BPMN标准证明了它自己,现在市场上许多建模工具都使用了BPMN标准中的元素和结构。 BPMN规范的2.0版本,当前已经处于最终阶段了, 允许添加精确的技术细节在BPMN的图形和元素中, 同时制定 BPMN元素的执行语法。 通过使用XML语言来指定业务流程的可执行语法, BPMN规范已经演变为业务流程的语 言, 可以执行在任何兼容BPMN2的流程引擎中, 同时依然可以使用强大的图形注解。 目BPMN2.0是最新的版本,它用于在BPM上下文中进行布局和可视化的沟通。BPMN 2.0是使用一些符号来明确 业务流程设计流程图的一整套符号规范,它能增进业务建模时的沟通效率。
1.6 BPMN 2.0 基本流程符号
先了解在流程设计中常见的符号:
事件Event

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

网关 Gateway
网关用来处理决策:

排他网关(x)
只有一条路径会被选择。流程执行到该网关时,按照输出流的顺序逐个计算,当条件的计算结果为true时,继
续执行当前网关的输出流; 如果多条线路计算结果都是 true,则会执行第一个值为 true 的线路。如果所有网关计算结果没有true,则引 擎会抛出异常。 排他网关需要和条件顺序流结合使用,default 属性指定默认顺序流,当所有的条件不满足时会执行默认顺序流。
并行网关(+)
所有路径会被同时选择 。
分支: 并行执行所有输出顺序流,为每一条顺序流创建一个并行执行线路。
汇聚 :所有从并行网关拆分并执行完成的线路均在此等候,直到所有的线路都执行完成才继续向下执行。
包容网关(o)
可以同时执行多条线路,也可以在网关上设置条件。
分支:计算每条线路上的表达式,当表达式计算结果为true时,创建一个并行线路并继续执行
汇聚:所有从并行网关拆分并执行完成的线路均在此等候,直到所有的线路都执行完成才继续向下执行。
事件网关(o+)
专门为中间捕获事件设置的,允许设置多个输出流指向多个不同的中间捕获事件。当流程执行到事件网关后,流程处于等待状态,需要等待抛出事件才能将等待状态转换为活动状态。
定时器事件

- 开始定时器事件:可以设置时间,定时开始启动流程实例。
- 中间定时器事件:设定延迟时间,当完成任务1后,到达延时时间,流程才会走向任务2。
- 边界定时器事件:用于向某节点上添加边界定时事件。在设定时间内没有完成,流程实例则自动走向下一节点。
二、Activiti环境搭建和流程基础入门
2.1 框架版本号
依赖版本Spring Boot2.5.0Activiti7.1.0.M6
2.2 JDK
JDK1.8
2.2 MySQL
MySQL5.7
Activiti 通过数据库的数据表来进行控制业务流程,对应支持的数据库如下:
Activiti数据库类型JDBC连接示例备注h2jdbc:h2:tcp://localhost/activiti默认配置的数据库mysqljdbc:mysql://localhost:3306/activiti?autoReconnect=true使用mysql-connector- java数据库驱动程序进行 测试oraclejdbc:oracle:thin:@localhost:1521:xepostgresjdbc:postgresql://localhost:5432/activitidb2jdbc:db2://localhost:50000/activitimssqljdbc:sqlserver://localhost:1433;databaseName=activiti使用Microsoft JDBC驱动程序4.0(sqljdbc4.jar)和JTDS驱动程序进行了测
在mysql服务器中创建一个activiti01数据库。注意配置好数据库之后,执行流程引擎是会自动创建数据库表的。
2.3 Maven 3.9配置
这里就不粘贴maven的配置了,网上都有各种教程。
2.4 基于 mave 构建项目
- 使用 idea 创建一个空工程 study-activiti


- 创建基于maven的项目,项目名: activiti-demo1


2.7 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.mengxuegu</groupId><artifactId>activiti-demo1</artifactId><version>1.0-SNAPSHOT</version><dependencies><!--activiti核心依赖--><dependency><groupId>org.activiti</groupId><artifactId>activiti-engine</artifactId><version>7.1.0.M6</version></dependency><!--mysql驱动包--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.23</version></dependency><!--mybatis--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.4.5</version></dependency><!-- 日志 --><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId><version>1.7.26</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>jcl-over-slf4j</artifactId><version>1.7.26</version></dependency><!--单元测试--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.6</version></dependency></dependencies></project>
2.8 activiti.cfg.xml核心配置
Activiti流程引擎通过名为的XML文件进行配置
activiti.cfg.xml
。
在 resources 目录下创建 activiti.cfg.xml 文件
<beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><!--默认方式下,bean的id必须是processEngineConfiguration --><beanid="processEngineConfiguration"class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration"><!-- 配置数据源 --><propertyname="jdbcUrl"value="jdbc:mysql://127.0.0.1:3306/activiti01?characterEncoding=utf8&nullCatalogMeansCurrent=true"/><propertyname="jdbcDriver"value="com.mysql.cj.jdbc.Driver"/><propertyname="jdbcUsername"value="root"/><propertyname="jdbcPassword"value="root"/><!-- activiti 数据库表生成策略 --><!--
自动更新数据库结构
true:适用开发环境,默认值。activiti会对数据库中所有表进行更新操作。如果表不存在,则自动创建
false:适用生产环境。activiti在启动时,对比数据库表中保存的版本,如果没有表或者版本不匹配,将抛出异常
create_drop: 在activiti启动时创建表,在关闭时删除表(必须手动关闭引擎,才能删除表)
drop-create: 在activiti启动时删除原来的旧表,然后在创建新表(不需要手动关闭引擎)
--><propertyname="databaseSchemaUpdate"value="true"/></bean></beans>
注意:防止插入中文数据乱码,要加上字符集 characterEncoding=utf8
如果报错:Table ‘activiti01.act_ge_property’ doesn’t exist
解决:jdbcUrl的后面加上 nullCatalogMeansCurrent=true
2.9 log4j.properties配置日志
如果想在控制台打印执行的sql日志,需要配置日志输出格式等,在 resources 目录下创建 log4j.properties 文件: 注意:记得修改日志文件路径。
log4j.rootCategory=debug, CONSOLE, LOGFILE
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{HH:mm:ss.SSS} %p [%t] %C.%M(%L) | %m%n
# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=D:/03-projectCode/ideaProject/study-activiti/activiti.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
2.10 创建ProcessEngine流程引擎实例和数据表
加载类路径上的 activiti.cfg.xml ,并根据该文件中的配置构造一个流程引擎,和创建数据表。
方式如下:
importorg.activiti.engine.ProcessEngine;importorg.activiti.engine.ProcessEngines;importorg.junit.Test;publicclassActivitiTest01{/*** 创建ProcessEngine流程引擎,自动创建 activiti 数据表 */@TestpublicvoidgetProcessEngine(){// 方式一:使用activiti提供的工具类ProcessEngines, // 调用 getDefaultProcessEngine 会默认读取resource下的activiti.cfg.xml文件, // 并创建 Activiti 流程引擎 和 创建数据库表 ProcessEngine processEngine =ProcessEngines.getDefaultProcessEngine();System.out.println(processEngine);// 方式二:以编程方式创建ProcessEngineConfiguration对象 // 1. 等同于方式一 //ProcessEngineConfiguration configuration =ProcessEngineConfiguration.createProcessEngineConfigurationFromResourceDefault(); //ProcessEngine processEngine = configuration.buildProcessEngine(); // 2. 自定义配置路径和文件名, 但流程引擎bean的id要等于processEngineConfiguration //ProcessEngineConfiguration configuration = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml"); // 3.自定义:配置路径、文件名和流程引擎bean的id //ProcessEngineConfiguration configuration =ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml", "processEngineConfiguration"); } }
2.11 Activiti 25张数据表分析
Activiti 会自动创建25张数据表。(可能不同版本生成的表的数量也会不同。)
数据库表命名
Acitiviti数据库中表的命名都是以 ACT_ 开头的。第二部分是一个两个字符用例表的标识。此用例大体与服务API是
匹配的。
ACT_GE_ :* GE 表示 general 。通用数据,各种情况都使用的数据 ,如存放资源文件(图片,规则等)。
ACT_HI_xxx : HI 表示history。就是这些表包含着历史的相关数据,如结束的流程实例(变量,任务等)。
ACT_RE_xxx : RE 表示repository。带此前缀的表包含的是静态信息,如,流程定义,流程的资源(图片,规则 等)。 Activiti只在流程实例执行过程中保存这些数据, 在流程结束时就会删除这些记录。 这样运行时表可以一直 很小速度很快。
ACT_RU_xxx : RU 表示 runtime。这是运行时的表存储着流程变量,用户任务,变量,职责(job)等运行时的 数据。Activiti只存储实例执行期间的运行时数据,当流程实例结束时,将删除这些记录。这就保证了这些运行时的表小且快。
ACT_EVT_ :*EVT表示EVENT,流程引擎的通用事件日志记录表,方便管理员跟踪处理。
表分类表名说明通用数据act_ge_bytearray二进制数据表(流程图)act_ge_property属性数据表,存储整个流程引擎级别的数据,初始化表结构时,会插入版本号信息等历史信息act_hi_actinst历史节点表act_hi_attachment历史附件表act_hi_comment历史意见表act_hi_detail历史详情表,提供历史变量的查询act_hi_identitylink历史流程人员表,主要存储任务节点与参与者的相关信息act_hi_procinst历史流程实例表act_hi_taskinst历史任务实例表act_hi_varinst历史变量表流程定义 部署表act_re_deployment部署信息表act_re_model流程设计模型表act_re_procdef流程定义数据表流程运行数据表act_ru_deadletter_job作业死亡信息表,如果作业失败超过重试次数,则写入到此表act_ru_event_subscrthrowEvent、catchEvent时间监听信息表act_ru_execution运行时流程执行实例表act_ru_identitylink运行时流程人员表,主要存储任务节点与参与者的相关信息act_ru_integration运行时积分表act_ru_job定时异步任务数据表act_ru_suspended_job运行时作业暂停表, 比如流程中有一个定时任务,如果把这个任务停止工作了,这个任务写入到此表中act_ru_task运行时任务节点表act_ru_timer_job运行时定时器作业表act_ru_variable运行时流程变量数据表其他表act_procdef_info流程定义的动态变更信息act_evt_log流程引擎的通用事件日志记录表
完善表字段
- Activiti 7的M4以上版本,部署流程定义时,报错如下:
报错: MySQLSyntaxErrorException: Unknown column ‘VERSION_’ in ‘field list’
解决:因为 ACT_RE_DEPLOYMENT 表缺少 VERSION_ 和 PROJECT_RELEASE_VERSION_ 字段,执行以下语句
添加
ALTERTABLE ACT_RE_DEPLOYMENT ADDCOLUMN VERSION_ VARCHAR(255);ALTERTABLE ACT_RE_DEPLOYMENT ADDCOLUMN PROJECT_RELEASE_VERSION_ VARCHAR(255);
在 Activiti7.1.0.M6已经有了这些,不用执行上面sql。
2.12 Activiti API服务接口
前言
Activiti
Process Engine API****和服务
引擎 API 是与 Activiti 交互的最常见方式。 您可以从ProcessEngine中获取包含工作流/ BPM方法的各种服务。 ProcessEngine和服务对象是线程安全的。因此,您可以为整个服务器保留对其中之一的引用。
Service 是工作流引擎提供用于进行工作流部署、执行、管理的服务接口,我们使用对应Service接口可以操作对应的数据表。也就是说它提供了几个基础的service,里面包含的api供我们对流程进行操作。

Activiti7的Servcie核心接口
Service接口说明RuntimeService运行时 Service,可以处理所有正在运行状态的流程实例和任务等RepositoryService流程仓库 Service,主要用于管理流程仓库,比如流程定义的控制管理(部 署、删除、挂起、激活…)DynamicBpmnServiceRepositoryService可以用来部署流程定义(使用xml形式定义好的),一旦 部署到Activiti(解析后保存到DB),那么流程定义就不会再变了,除了修改xml定义文件内容;而DynamicBpmnService就允许我们在程序运行过程中去修改流程定义,例如:修改流程定义中的分配角色、优先级、流程流转的条件。TaskService任务 Service,用于管理和查询任务,例如:签收、办理等HistoryService历史Service,可以查询所有历史数据,例如:流程实例信息、参与者信息、完成时间…ManagementService引擎管理Service,和具体业务无关,主要用于对Activiti流程引擎的管理和维护。
- 核心 Service 接口实例获取方式如下:
// 会在首次调用时初始化并构建一个流程引擎,此后始终返回相同的流程引擎。
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 引擎管理类
ManagementService managementService = processEngine.getManagementService();
// 动态修改流程管理类
DynamicBpmnService dynamicBpmnService = processEngine.getDynamicBpmnService();
// 流程运行管理类
RuntimeService runtimeService = processEngine.getRuntimeService();
// 流程仓库管理类
RepositoryService repositoryService = processEngine.getRepositoryService();
// 任务管理类
TaskService taskService = processEngine.getTaskService();
// 历史管理类
HistoryService historyService = processEngine.getHistoryService();
// activiti 7 没有IdentityService和FormService接口
//IdentityService identityService = processEngine.getIdentityService();
// FormService formService = processEngine.getFormService();
三、Activiti流程实操入门
3.1 IDEA安装actiBPM插件


官网下载地址


3.2 绘制流程定义模型
- 在 /resources 目录下创建 processes 目录,用于存放流程图
- 创建BPMN文件:右击 processes 目录,点击【New】–>【BpmnFile】
- 输入文件名称 leave ,点击【OK】按钮

绘制请假申请流程图
将 xxxx.bpmn 文件放在 /resources/processes/ 目录下,即 /resources/processes/leave.bpmn
- 鼠标左键拖拽右侧 开始事件 符号,将其拖下左侧界面上,同样的方式再拖拽其他图标

- 添加人工任务符号,修改 Name 节点名称 领导审批,和 Assignee 节点处理人:meng

- 添加人工任务符号:修改 Name 节点名称:总经理审批,和 Assignee 节点处理人:xue

- 添加结束事件符号

- 流程连线:点击图标的中心,会变成黑白色扇形,拖拽到另一图标,即可连接

- 点击流程图的空白处(不是点击流程符号):
设置当前流程图唯一标签 ID (也称为:流程的KEY):leaveProcess ,流程名称:请假流程。

- 最后检查,每个人工任务节点的 Name 和 Assignee 是否也上面一致。
3.3 解决中文乱码问题
- 打开 Settings 窗口,点击 Editor > File Encodings > 将字符编码均设置为 UTF-8

- 打开idea安装目录,在 idea.exe.vmoptions (32位系统)和 idea64.exe.vmoptions (64位系统)文件最后
追加一行命令。 (
C:\User\mengxuegu\.IntelliJIdea\config
)
注意:不能有空格,不然idea重启后打不开
-Dfile.encoding=UTF-8
- 如果上面修改后,重启idea重新打开bpmn文件,还是乱码,则查找:
C:\Users\Administrator\AppData\Roaming\JetBrains\IntelliJIdea2020.

生成png图片
将 bpmn 文件不方便随处打开提供给用户看,为了方便将流程图转成图片格式,方便随处打开演示;
并在部署流程时也需要 png 图片。
- 将 leave.bpmn 文件复制一份为 leave.xml
- 右击 leave.xml 文件,选择【Diagrams】–>【Show BPMN 2.0 Diagrams…】
- 如果找不到 Diagrams ,则重启 IDEA 工具

- 打开后,点击上方导出按钮,保存即可。

- 将生成的 leave.png 图片拷贝到 resources/processes 目录下

- 可以把 leave.xml 文件删除
3.4 部署流程定义
概述
将上面在设计器中定义的流程部署到activiti数据库中,就是流程定义部署。
一般情况下,流程定义(Process Definition)是在流程设计和开发阶段完成的,它描述了工作流程的规则、流程节点、流程步骤、流程之间的转移条件等信息。流程定义通常以一种特定的格式或者规范进行定义,如 BPMN(Business Process Model and Notation)等。
而流程部署(Process Deployment)是在流程准备运行阶段完成的,它将流程定义部署到流程引擎中以便执行。在流程部署过程中,流程定义会被加载到流程引擎中,并在流程引擎中生成对应的流程实例。
通常流程部署与流程定义是一对多的关系,比如一组相关联的业务可以做成多个流程图定义,然后把他们打包成zip后统一部署。
通过调用activiti的api将流程定义的 .bpm 和 png 两个文件一个一个添加部署到activiti中,也可以将两个文件打成 zip包进行部署。
.bpmn****流程定义文件部署
- 代码如下:
packagecom.mengxuegu.test;importorg.activiti.engine.*;importorg.activiti.engine.impl.util.ReflectUtil;importorg.activiti.engine.repository.Deployment;importorg.activiti.engine.runtime.ProcessInstance;importorg.activiti.engine.task.Task;importorg.activiti.engine.task.TaskQuery;importorg.junit.Test;importjava.io.IOException;importjava.io.InputStream;importjava.util.HashMap;importjava.util.List;importjava.util.Map;importjava.util.zip.ZipInputStream;publicclassActivitiTest02{/*** 部署流程:
* 1. ACT_RE_DEPLOYMENT 流程部署表,每执行一次部署,会插入一条记录
* 2. ACT_RE_PROCDEF 生成流程定义信息
* 其中 ACT_RE_DEPLOYMENT 与 ACT_RE_PROCDEF 表是一对多的关系,
* ACT_RE_PROCDEF 每条记录对应一个流程的定义信息(如:小梦、小谷请假申请)
* 3. ACT_GE_BYTEARRAY 流程资源表,插入资源数据,当前插入两条记录(.bpmn和.png资源) */@TestpublicvoiddeployByFile(){// 1. 实例化流程引擎实例 ProcessEngine processEngine =ProcessEngines.getDefaultProcessEngine();// 2. 获取流程定义和部署对象相关的ServiceRepositoryService repositoryService = processEngine.getRepositoryService();// 3. 创建部署对象进行流程的部署,定义一个流程的名字,把 .bpmn 和 .png 部署到数据库中 Deployment deployment = repositoryService.createDeployment().name("请假申请流程").addClasspathResource("processes/leave.bpmn").addClasspathResource("processes/leave.png").deploy();// 4. 输出部署信息 System.out.println("部署ID:"+ deployment.getId());System.out.println("部署名称:"+ deployment.getName());}}
解决报错找不到字段
解决: ACT_RE_DEPLOYMENT 表缺少 VERSION_ 和 PROJECT_RELEASE_VERSION_ 字段,执行下面添加表字
段
ALTERTABLE ACT_RE_DEPLOYMENT ADDCOLUMN VERSION_ VARCHAR(255);ALTERTABLE ACT_RE_DEPLOYMENT ADDCOLUMN PROJECT_RELEASE_VERSION_ VARCHAR(255);
.zip****流程定义压缩包部署
将 leave.bpmn和 leave.png 压缩成 leave.zip ,放在类路径下的 processes/leave.zip 。 (所以正如上面所说,部署与定义是一对多的关系)
* 通过zip压缩包部署流程定义 */@TestpublicvoiddeployByZip(){// 1. 实例化流程引擎实例 ProcessEngine processEngine =ProcessEngines.getDefaultProcessEngine();// 2. 获取流程定义和部署对象相关的ServiceRepositoryService repositoryService = processEngine.getRepositoryService();// 3. 流程部署 // 读取zip资源包,构成 InputStream 输入流 InputStream inputStream =ReflectUtil.getResourceAsStream("processes/leave.zip");// 封装 ZipInputStream 输入流进行流程部署 ZipInputStream zipInputStream =newZipInputStream(inputStream);Deployment deployment = repositoryService.createDeployment().addZipInputStream(zipInputStream).name("请假申请流程-压缩包").deploy();// 4. 输出部署信息 System.out.println("部署ID:"+ deployment.getId());System.out.println("部署名称:"+ deployment.getName());}
运行后,压缩包中的 .bpm 文件和 图片文件会保存在activiti数据表中。
部署涉及的数据表
流程定义部署会涉及 activiti 的3张表:
- act_re_deployment 流程定义部署表,每部署一次增加一条记录
- act_re_procdef 流程定义表,部署每个新的流程定义都会在这张表中增加一条记录
注意:表中字段 KEY 是不同流程定义的唯一标识。
- act_ge_bytearray 流程资源表,bpmn的 xml 串和 流程图片,每次增加对应资源数据
注意:
act_re_deployment 和 act_re_procdef 一对多关系,一次部署在流程部署表生成一条记录,但一次部署可以部署 多个流程定义,每个流程定义在流程定义表生成一条记录。每一个流程定义在 act_ge_bytearray 会存在两个资源 记录,bpmn 和 png。
建议:
一次部署一个流程,这样部署表和流程定义表是一对一有关系,方便读取流程部署及流程定义信息。
3.5 查询流程定义
查询部署的流程定义数据 ACT_RE_PROCDEF :
/*** 查询部署的流程定义数据 ACT_RE_PROCDEF */@TestpublicvoidgetProcessDefinitionList(){// 1. 实例化流程引擎实例 ProcessEngine processEngine =ProcessEngines.getDefaultProcessEngine();// 2. 获取 RepositoryService RepositoryService repositoryService = processEngine.getRepositoryService();// 3. 获取 ProcessDefinitionQuery 这里的leaveProcess就是画流程图时指定的唯一的keyProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery();List<ProcessDefinition> definitionList = query.processDefinitionKey("leaveProcess").orderByProcessDefinitionVersion()// 按版本号排序 .desc()// 降序 .list();for(ProcessDefinition pd : definitionList){System.out.println("流程部署ID:"+ pd.getDeploymentId());System.out.println("流程定义ID:"+ pd.getId());System.out.println("流程定义Key:"+ pd.getKey());System.out.println("流程定义名称:"+ pd.getName());System.out.println("流程定义版本号:"+ pd.getVersion());}}
输出内容:
流程部署ID:1
流程定义ID:leaveProcess:1:4
流程定义Key:leaveProcess
流程定义名称:请假流程
流程定义版本号:1
启动流程实例(提交申请)
流程定义部署后,然后可以通过 activiti 工作流管理业务流程了。例如上面部署好了请假流程,可以申请请假了。 针对部署好的流程定义,每次用户发起一个新的请假申请,就对应的启动一个新的请假流程实例;
类似于 java 类与 java 对象(实例)的关系,定义好类后,使用 new 创建一个对象(实例)使用,当然可以 new 多个对象(实例)。
例如:请假流程,小梦发起一个请假申请单,对应的就启动一个针对小梦的新流程实例;小王发起一个请假申请 单,也启动针对小王的新流程实例。
3.6 启动流程实例代码
/** * 启动流程实例(提交申请) */@TestpublicvoidstartProcessInstance(){// 1. 实例化流程引擎实例 ProcessEngine processEngine =ProcessEngines.getDefaultProcessEngine();// 2. 获取 RuntimeService RuntimeService runtimeService = processEngine.getRuntimeService();// 开启流程实例 (流程设计图唯一标识key) ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("leaveProcess");System.out.println("流程定义id:"+ processInstance.getProcessDefinitionId());System.out.println("流程实例id:"+ processInstance.getId());}
输出内容:
流程定义id:leaveProcess:1:4
流程实例id:2501
流程实例涉及的数据表
- act_hi_actinst 流程实例执行的节点历史信息
- act_hi_identitylink 流程的参与用户历史信息
- act_hi_procinst 流程实例历史信息
- act_hi_taskinst 流程实例的任务历史信息
- act_ru_execution 流程运行中执行信息
- act_ru_identitylink 流程运行中参与用户信息
- act_ru_task 流程运行中任务信息
3.7 查询办理人待办任务
启动流程实例后,每个任务的办理人就可以查询自己当前的待办任务,然后进行办理任务。
/*** 查询指定人员的待办任务 */@TestpublicvoidtaskListByAssignee(){// 1. 实例化流程引擎实例ProcessEngine processEngine =ProcessEngines.getDefaultProcessEngine();// 2. 获取 TaskService TaskService taskService = processEngine.getTaskService();// 3. 根据流程唯一标识 key 和 任务办理人 查询任务 List<Task> list = taskService.createTaskQuery().processDefinitionKey("leaveProcess").taskAssignee("meng").list();for(Task task : list){System.out.println("流程实例id:"+ task.getProcessInstanceId());System.out.println("任务id:"+ task.getId());System.out.println("任务名称:"+ task.getName());System.out.println("任务办理人:"+ task.getAssignee());}}
输出内容:
流程实例id:2501
任务id:2505
任务负责人:meng
任务名称:领导审批
3.8 完成待办任务
任务办理人查询待办任务,将待办任务进行完成。
- 先查询 meng 办理人任务,然后完成任务
- 再查询 xue 办理人任务,然后完成任务
- 这样此流程实例就完成结束。
/*** 完成待办任务 */@TestpublicvoidcompleteTask(){// 1. 实例化流程引擎实例 ProcessEngine processEngine =ProcessEngines.getDefaultProcessEngine();// 2. 获取 TaskService TaskService taskService = processEngine.getTaskService();// 3. 根据流程唯一标识 key 和 任务办理人 查询任务 Task task = taskService.createTaskQuery().processDefinitionKey("leaveProcess")// 流程 Key .taskAssignee("meng")// 查询 meng 的任务 .taskAssignee("xue").singleResult();// 目前只有一条任务,则可以只获取一条 // 4. 完成任务(任务id)
taskService.complete(task.getId());}
3.9 查询流程实例历史节点信息
查询流程办理历史信息,通过 HistoryService 历史数据对象来获取 HistoricActivityInstanceQuery 历史节点查询 对象。
/*** 查看流程办理历史信息 */@TestpublicvoidhistoryInfo(){// 1. 实例化流程引擎实例 ProcessEngine processEngine =ProcessEngines.getDefaultProcessEngine();// 2. 获取 HistoryService HistoryService historyService = processEngine.getHistoryService();// 3. 获取节点历史记录查询对象 ACT_HI_ACTINST 表 HistoricActivityInstanceQuery query = historyService.createHistoricActivityInstanceQuery();// 实例 id String processInstanceId ="10001";List<HistoricActivityInstance> list = query.processInstanceId(processInstanceId).orderByHistoricActivityInstanceStartTime()// 根据开始时间排序 asc 升序 .asc().list();for(HistoricActivityInstance hi : list){System.out.print("流程定义ID: "+ hi.getProcessDefinitionId());System.out.print(",流程实例ID: "+ hi.getProcessInstanceId());System.out.print(",节点ID: "+ hi.getActivityId());System.out.print(",节点名称: "+ hi.getActivityName());System.out.print(",任务办理人:"+ hi.getAssignee());System.out.print(",开始时间:"+ hi.getStartTime());System.out.println("结束时间:"+ hi.getEndTime());}}
四、SpringBoot 与 Activiti7 整合
测试完基础的功能,终于到了重头戏了,好多网上的文档斌没有教你如何把activi7如何整合到你的实际项目中。
4.1 创建模块 activiti-boot
模块名:activiti-boot

4.2 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.mengxuegu</groupId><artifactId>activiti-boot</artifactId><version>1.0-SNAPSHOT</version><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.5.0</version><relativePath/></parent><properties><activiti.version>7.1.0.M6</activiti.version><mybatis-plus.version>3.3.1</mybatis-plus.version></properties><dependencies><!--Activiti--><dependency><groupId>org.activiti</groupId><artifactId>activiti-spring-boot-starter</artifactId><version>${activiti.version}</version></dependency><!-- java绘制activiti流程图 --><dependency><groupId>org.activiti</groupId><artifactId>activiti-image-generator</artifactId><version>${activiti.version}</version></dependency><!-- activiti json转换器--><dependency><groupId>org.activiti</groupId><artifactId>activiti-json-converter</artifactId><version>${activiti.version}</version></dependency><!-- svg转png图片工具--><dependency><groupId>org.apache.xmlgraphics</groupId><artifactId>batik-all</artifactId><version>1.10</version></dependency><!-- web启动器--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!--mybatis-plus启动器--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>${mybatis-plus.version}</version></dependency><!--SpringSecurity--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!--<dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.6</version></dependency>--></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>
4.3 创建application.yml配置文件
- 创建 application.yml 文件,activiti 7 相关配置如下:
server:port:8080servlet:context-path: /workflow
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/activiti-boot?nullCatalogMeansCurrent=true&useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8&allowMultiQueries=trueusername: root
password: root
# activiti配置activiti:#自动更新数据库结构# true:适用开发环境,默认值。activiti会对数据库中所有表进行更新操作。如果表不存在,则自动创建# false:适用生产环境。activiti在启动时,对比数据库表中保存的版本,如果没有表或者版本不匹配,将抛出异常# create_drop: 在activiti启动时创建表,在关闭时删除表(必须手动关闭引擎,才能删除表)# drop-create: 在activiti启动时删除原来的旧表,然后在创建新表(不需要手动关闭引擎)database-schema-update:true# activiti7与springboot整合后默认不创建历史表,需要手动开启db-history-used:true# 记录历史等级 可配置的历史级别有none, activity, audit, full# none:不保存任何的历史数据,因此,在流程执行过程中,这是最高效的。# activity:级别高于none,保存流程实例与流程行为,其他数据不保存。# audit:除activity级别会保存的数据外,还会保存全部的流程任务及其属性。# full:保存历史数据的最高级别,除了会保存audit级别的数据外,还会保存其他全部流程相关的细节数据,包括一些流程参数等。history-level: full
# 是否自动检查resources下的processes目录的流程定义文件check-process-definitions:false# smtp服务器地址mail-server-host: smtp.qq.com
# SSL端口号mail-server-port:465# 开启ssl协议mail-server-use-ssl:true# 默认的邮件发送地址(发送人),如果activiti流程定义中没有指定发送人,则取这个值mail-server-default-from: [email protected]
# 邮件的用户名mail-server-user-name: [email protected]
# qq的smtp服务相关的授权码mail-server-password: xxx填写自己qq邮箱的smtp授权码
# 关闭不自动添加部署数据 SpringAutoDeployment#deployment-mode: never-fail# 日志级别是debug才能显示SQL日志logging:level:org.activiti.engine.impl.persistence.entity: debug
4.4 创建数据库activiti-boot
只有你自己创建了数据库建立了驱动连接,才会自动生成工作流的表。这里就不讲解怎么创建数据库了。
4.5 整合SpringSecurity
- activiti7 与 SpringSecurity 强耦合,
创建安全认证配置类 com.mengxuegu.config.SpringSecurityConfig 类
注意:activiti7 任务办理人,必须是当前可查询到的用户名
packagecom.mengxuegu.workflow.config;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.security.config.annotation.web.builders.HttpSecurity;importorg.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;importorg.springframework.security.core.authority.SimpleGrantedAuthority;importorg.springframework.security.core.userdetails.User;importorg.springframework.security.core.userdetails.UserDetailsService;importorg.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;importorg.springframework.security.crypto.password.PasswordEncoder;importorg.springframework.security.provisioning.InMemoryUserDetailsManager;importorg.springframework.security.provisioning.UserDetailsManager;importjava.util.Arrays;importjava.util.List;importjava.util.stream.Collectors;@ConfigurationpublicclassSpringSecurityConfigextendsWebSecurityConfigurerAdapter{privateLogger logger =LoggerFactory.getLogger(SpringSecurityConfig.class);/**
* 内存 UserDetailsManager
*/@BeanpublicUserDetailsServicemyUserDetailsService(){InMemoryUserDetailsManager inMemoryUserDetailsManager =newInMemoryUserDetailsManager();// 初始化账号角色数据addGroupAndRoles(inMemoryUserDetailsManager);return inMemoryUserDetailsManager;}privatevoidaddGroupAndRoles(UserDetailsManager userDetailsManager){// 注意:后面流程办理人,必须是当前存在的用户 username,这些都是为了后面指定流程班里人做准备String[][] usersGroupsAndRoles ={{"meng","123456","ROLE_ACTIVITI_USER","GROUP_activitiTeam"},{"xue","123456","ROLE_ACTIVITI_USER","GROUP_activitiTeam"},{"gu","123456","ROLE_ACTIVITI_USER","GROUP_activitiTeam"},{"小梦","123456","ROLE_ACTIVITI_ADMIN","GROUP_otherTeam"},{"小学","123456","ROLE_ACTIVITI_ADMIN","GROUP_otherTeam"},{"小谷","123456","ROLE_ACTIVITI_ADMIN","GROUP_otherTeam"}};for(String[] user : usersGroupsAndRoles){List<String> authoritiesStrings =Arrays.asList(Arrays.copyOfRange(user,2, user.length));
logger.info("> Registering new user: "+ user[0]+" with the following Authorities["+ authoritiesStrings +"]");
userDetailsManager.createUser(newUser(user[0],passwordEncoder().encode(user[1]),
authoritiesStrings.stream().map(s ->newSimpleGrantedAuthority(s)).collect(Collectors.toList())));}}@Overrideprotectedvoidconfigure(HttpSecurity http)throwsException{
http.csrf().disable();}@BeanpublicPasswordEncoderpasswordEncoder(){returnnewBCryptPasswordEncoder();}}
4.6 创建启动类
创建项目启动类 com.mengxuegu.workflflow.WorkFlowApplication
packagecom.mengxuegu.workflow;importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublicclassWorkFlowApplication{publicstaticvoidmain(String[] args){SpringApplication.run(WorkFlowApplication.class, args);}}
4.7 测试Activiti流程
相关的 Activiti 服务接口,都直接依赖注入即可使用。
- 将上面的 activiti-demo1 工程中绘制的bpmn流程图文件,拷贝如下工程的 resources 目录下

- 在 src/test 目录下创建测试类 com.mengxuegu.workflflow.test.ActivitiTest01
packagecom.mengxuegu.workflow.test;importorg.activiti.engine.ProcessEngine;importorg.activiti.engine.RepositoryService;importorg.activiti.engine.repository.Deployment;importorg.junit.jupiter.api.Test;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.boot.test.context.SpringBootTest;@SpringBootTestpublicclassActivitiTest01{@AutowiredProcessEngine processEngine;@AutowiredRepositoryService repositoryService;/*** 获取 ProcessEngine 流程引擎,自动创建 activiti 数据表 */@TestpublicvoidgetProcessEngine(){// 获取 Activiti 流程引擎 和 创建数据库表, // 并在 ACT_RE_DEPLOYMENT 插入一条无用的数据 SpringAutoDeploymentSystem.out.println(processEngine);}/*** 部署流程: */@TestpublicvoiddeployByFile(){// 1. 创建部署对象进行流程的部署,定义一个流程的名字,把 .bpmn 和 .png 部署到数据库中 Deployment deployment = repositoryService.createDeployment().name("请假申请流程").addClasspathResource("processes/leave.bpmn").addClasspathResource("processes/leave.png").deploy();// 2. 输出部署信息 System.out.println("部署ID:"+ deployment.getId());System.out.println("部署名称:"+ deployment.getName());}}
如果报错如下:
Cause: java.sql.SQLSyntaxErrorException: Unknown column 'VERSION_' in 'field list'
Cause: java.sql.SQLSyntaxErrorException: Unknown column 'PROJECT_RELEASE_VERSION_' in 'field
解决: ACT_RE_DEPLOYMENT 表缺少 VERSION_ 和 PROJECT_RELEASE_VERSION_ 字段,执行下面添加表字
段
ALTERTABLE ACT_RE_DEPLOYMENT ADDCOLUMN VERSION_ VARCHAR(255);ALTERTABLE ACT_RE_DEPLOYMENT ADDCOLUMN PROJECT_RELEASE_VERSION_ VARCHAR(255);
五、整合流程模型设计器Activiti Modeler
为什么要整合
上面我们在 idea 上设计的流程模型,而每次要设计都要打开 idea 或其他设计流程工具来进行操作,这样不太方 便。 而实际上 Activiti 官方提供了 Web 版的流程设计工具 Activiti Modeler,可以直接整合到我们项目中。
在 Activiti 5.10 版本把原本独立的 Activiti Modeler 模块整合到了 Activiti Explorer 模块中,两者相结合使用起来 很方便, 通过 Modeler 设计的流程模型可以直接部署到引擎,也可以把已经部署的流程转换为Model从而在Modeler中编 辑。 在实际应用中也有这样的需求,把 Modeler 整合到业务系统中可以供管理员使用,或者作为BPM流程管理平台的 一部分存在。
但是在 Activiti 官方没有给出如何整合Modeler的文档,要我们自己整合。

5.1 下载源码
首先需要从 Github下载 Activiti 5.22 版本源码
访问:https://github.com/Activiti/Activiti/releases/tag/activiti-5.22.0
下载 zip 格式的压缩包:https://github.com/Activiti/Activiti/archive/activiti-5.22.0.zip

如果解压过程中出现文件名过程问题可以换一个解压软件,比如7Zip。
5.2 拷贝目标源码
- 解压 Activiti-activiti-5.22.0.zip ,然后进入 Activiti-activiti-5.22.0/modules 目录,
- 复制 activiti-webapp-explorer2 工程中如下图画红框的文件夹和文件,

粘贴到 activiti-boot 工程的 resources/static 目录下(static 目录创建)

- 找到 activiti-modeler 工程下的 3个类,如下:

拷贝到 com.mengxuegu.workflflow.activiti 包下面,如下图:

5.3 修改上下文路径
- 找到 static/editor-app/confifiguration/url-confifig.js ,修改文件中的项目上下文路径,这样才能请求到上面的3 个接口
上下文路径对应 application.yml 文件中配置的 server.servlet.context-path=/workflow
在前端请求接口路径配置文件可参见:static/editor-app/confifiguration/url-confifig.js,
ACTIVITI.CONFIG = {'contextRoot': '/workflow' // '/activiti-explorer/service',};

5.4 汉化Activiti Modeler
汉化文件为两个json文件
stencilset.json
和
zh-CN.json
需要的自取:
链接:https://pan.baidu.com/s/1az50zQQ6U-o-owqOfgt3RQ
提取码:1111
- 汉化页面文字,*在*resources/static/目录下添加stencilset.json**文件**。
- 需要修改
StencilsetRestResource.java类中stencilset.json为static/stencilset.json(最前面不
要有 / );
InputStream stencilsetStream =this.getClass().getClassLoader().getResourceAsStream("static/stencilset.json");

- 汉化按钮文字,添加
zh-CN.json文件
在
resources/static/editor-app/i18n
目录下添加
zh-CN.json
文件

修改 resources/static/editor-app/app.js 文件,将第51行的 $translateProvider.preferredLanguage(‘en’); 替换为以下内容:
// $translateProvider.preferredLanguage('en'); // 多语言支持 if("zh-CN"== navigator.language){
$translateProvider.preferredLanguage('zh-CN');}else{
$translateProvider.preferredLanguage('en');}
启动项目时报错:
Error:Kotlin: Module was compiled with an incompatible version of Kotlin. The binary version of
its metadata is 1.5.1, expected version is 1.1.16.
解决:如下图重新编译下,然后再启动项目

5.5 创建空的流程模型
- 创建一个 ModelController 模型控制器,用于创建空流程模型和跳转到模型设计页面 modeler.html
packagecom.mengxuegu.workflow.controller;importcom.fasterxml.jackson.databind.ObjectMapper;importcom.fasterxml.jackson.databind.node.ObjectNode;importorg.activiti.editor.constants.ModelDataJsonConstants;importorg.activiti.engine.RepositoryService;importorg.activiti.engine.repository.Model;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Controller;importorg.springframework.web.bind.annotation.*;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;/*** 流程模型管理 */@Controller@RequestMapping("/model")publicclassModelController{@AutowiredRepositoryService repositoryService;@AutowiredObjectMapper objectMapper;/*** 创建空模型窗口:
* 创建模型对象
* 设置对象值
* 存储模型对象(表act_re_model)
* 存储模型对象基础数据(表act_ge_bytearray)
* 跳转到ActivitiModeler,编辑流程图,存储流程图片和流程定义等(表act_ge_bytearray)
*/@GetMapping("/create")publicvoidcreate(HttpServletRequest request,HttpServletResponse response){try{String name ="请假流程模型";String key ="leaveProcess";String desc ="请输入描述信息~";int version =1;//初始化一个空模型 Model model = repositoryService.newModel();
model.setName(name);
model.setKey(key);
model.setVersion(version);// 封裝模型json对象 ObjectNode modelObjectNode = objectMapper.createObjectNode(); modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, name); modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION,0); modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, desc); model.setMetaInfo(modelObjectNode.toString());// 存储模型对象(表 ACT_RE_MODEL )
repositoryService.saveModel(model);// 封装模型对象基础数据json串 {"id":"canvas","resourceId":"canvas","stencilset": {"namespace":"http://b3mn.org/stencilset/bpmn2.0#"},"properties":{"process_id":"未定义"}} ObjectNode editorNode = objectMapper.createObjectNode();//editorNode.put("resourceId", "canvas"); ObjectNode stencilSetNode = objectMapper.createObjectNode(); stencilSetNode.put("namespace","http://b3mn.org/stencilset/bpmn2.0#"); editorNode.replace("stencilset", stencilSetNode);// 标识key ObjectNode propertiesNode = objectMapper.createObjectNode(); propertiesNode.put("process_id", key);
editorNode.replace("properties", propertiesNode);// 存储模型对象基础数据(表 ACT_GE_BYTEARRAY )
repositoryService.addModelEditorSource(model.getId(), editorNode.toString().getBytes("utf-8"));// 编辑流程模型时,只需要直接跳转此url并传递上modelId即可
response.sendRedirect(request.getContextPath()+"/modeler.html?modelId="+ model.getId());}catch(Exception e){
e.printStackTrace();}}}
- 重启mengxuegu-activiti-boot项目,
- 访问 http://localhost:8080/workflflow/model/create ,
会重定向到 http://localhost:8080/workflflow/modeler.html?modelId=xxxxxxxx

如果按钮文字是英文,而不是中文,则清除缓存重新打开浏览器访问 。
5.6 绘制请假流程定义模型
绘制流程定义模型涉及表
- ACT_RE_MODEL 流程模型基本信息表
- ACT_GE_BYTEARRAY 流程模型描述 json 串(注意不是xml串)和 流程图字节码。
1、绘制请假流程:
领导审批:办理人 meng
总经理审批:办理人 xue



- 点击 保存 按钮,会将绘制的 流程图 提交给后台进行保存操作(当前会报400错误,提交不成功)
保存请求路径:http://localhost:8080/workflflow/model/{modelId}/save
- 上面点击保存,会报400错误,
原因: ModelSaveRestResource 类中使用 @RequestBody MultiValueMap 接收参数无法接收到。
解决:将 @RequestBody MultiValueMap 改为 @RequestParam MultiValueMap ,如下图:

- 重启项目,再次保存测试 ok
六、流程定义模型管理Model
上面已经整合Web 端Activiti Modeler流程模型设计器,并完成创建和保存流程模型设计图。 下面对流程定义模型进行查询、删除、导出模型zip压缩包、xml 文件、部署流程定义。
6.1 查询已绘制的所有流程模型数据:
packagecom.mengxuegu.workflow.test;importorg.activiti.engine.RepositoryService;importorg.activiti.engine.repository.Model;importorg.activiti.engine.repository.ModelQuery;importorg.junit.jupiter.api.Test;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.boot.test.context.SpringBootTest;importjava.util.List;@SpringBootTestpublicclassActivitiTest02Model{@AutowiredRepositoryService repositoryService;/*** 查询所有流程模型 ACT_RE_MODEL */@TestpublicvoidmodelList(){// 获取模型查询对象 ModelQuery query = repositoryService.createModelQuery();// 按模型创建时间 降序 排列 List<Model> list = query.orderByCreateTime().desc().list();for(Model model : list){System.out.print("模型id:"+ model.getId());System.out.print(",模型名称:"+ model.getName());System.out.print(",模型描述:"+ model.getMetaInfo());System.out.print(",模型标识key:"+ model.getKey());System.out.print(",模型版本号:"+ model.getVersion());System.out.print(",创建时间:"+ model.getCreateTime());System.out.println("更新时间:"+ model.getLastUpdateTime());}}}
6.2 删除流程模型
通过模型ID删除模型:
/*** 删除模型: * 涉及表:ACT_RE_MODEL、ACT_GE_BYTEARRAY */@TestpublicvoiddeleteModel(){// 模型id String id ="f2cd384f-c826-11eb-bbf0-2abb00fc727d"; repositoryService.deleteModel(id);System.out.println("删除成功");}
6.3 导出下载模型图zip压缩包
导出下载模型图zip压缩包,压缩包中有 .bpmn20.xml 流程描述和 .png 图片资源, 在流程部署时,可以使用上传流程模型图zip压缩包进行部署.
/*** 导出下载模型图zip压缩包(.bpmn20.xml流程描述和.png图片资源) */@TestpublicvoidexportZip()throwsException{// 模型id String id ="1c3b7d73-c833-11eb-98fd-ee5b4f08d062";// 查询模型信息 Model model = repositoryService.getModel(id);if(model !=null){// 获取流程图 json 字节码 byte[] bpmnJsonBytes = repositoryService.getModelEditorSource(id);// 流程图 json 字节码转 xml 字节码byte[] xmlBytes =bpmnJsonToXmlBytes(bpmnJsonBytes);if(xmlBytes ==null){System.out.println("模型数据为空-请先设计完整流程-再导出");}else{// 压缩包文件名 String zipName = model.getName()+"."+ model.getKey()+".zip";// 文件输出流 File file =newFile("D:/"+ zipName);FileOutputStream outputStream =newFileOutputStream(file);// 实例化zip压缩对象输出流 ZipOutputStream zipos =newZipOutputStream(outputStream);// 指定压缩包里的 name.bpmn20.xml 文件名
zipos.putNextEntry(newZipEntry(model.getName()+".bpmn20.xml"));// 将xml写入压缩流
zipos.write(xmlBytes);// 查询png图片, byte[] pngBytes = repositoryService.getModelEditorSourceExtra(id);if(pngBytes !=null){// 指定压缩包里的 name.key.png 文件名
zipos.putNextEntry(newZipEntry(model.getName()+"."+ model.getKey()+".png"));// 图片文件写到压缩包中
zipos.write(pngBytes);}
zipos.closeEntry();
zipos.close();System.out.println("导出成功");}}else{System.out.println("模型不存在");}}@AutowiredObjectMapper objectMapper;/*** 流程图保存的时候是json串,引擎认识的却是符合bpmn2.0规范的xml,
* json 字节码转 xml 字节码
* @param jsonBytes * @return
*/privatebyte[]bpmnJsonToXmlBytes(byte[] jsonBytes)throwsIOException{if(jsonBytes ==null){returnnull;}// json转回BpmnModel对象 JsonNode modelNode = objectMapper.readTree(jsonBytes);BpmnModel bpmnModel =newBpmnJsonConverter().convertToBpmnModel(modelNode);if(bpmnModel.getProcesses().size()==0){returnnull;}// BpmnModel对象转xml字节数组 returnnewBpmnXMLConverter().convertToXML(bpmnModel);}
运行后,生成 请假流程模型.leaveProcess.zip 压缩后,解压后如下:

6.4 导出下载模型 xml 文件
在流程部署时,可以只上传流程模型图 xml 文件进行部署
/*** 导出下载模型 xml 文件 */@TestpublicvoidexportXml()throwsException{// 模型id String id ="1c3b7d73-c833-11eb-98fd-ee5b4f08d062";ByteArrayInputStream in =null;// 获取流程图 json 字节码 byte[] bytes = repositoryService.getModelEditorSource(id);// json转xml字节数组 String filename =null;if(bytes !=null){JsonNode modelNode = objectMapper.readTree(bytes);BpmnModel bpmnModel =newBpmnJsonConverter().convertToBpmnModel(modelNode);if(bpmnModel.getProcesses().size()!=0){// 转xml字节数组 byte[] bpmnBytes =newBpmnXMLConverter().convertToXML(bpmnModel); in =newByteArrayInputStream(bpmnBytes);// 如果流程名称为空,则取流程定义key
filename =StringUtils.isEmpty(bpmnModel.getMainProcess().getName())? bpmnModel.getMainProcess().getId(): bpmnModel.getMainProcess().getName();}}if(filename ==null){
filename ="模型数据为空,请先设计流程,再导出";
in =newByteArrayInputStream(filename.getBytes("UTF-8"));}// 文件输出流 FileOutputStream out =newFileOutputStream(newFile("D:/"+ filename +".bpmn20.xml"));// 输入流,输出流的转换 IOUtils.copy(in, out);// 关闭流
out.close();
in.close();System.out.println("下载模型 xml 文件成功");}
运行后,生成文件: 请假申请流程.bpmn20.xml

通过模型数据 部署 流程定义
注意事项:
每个流程定义模型可以多次流程定义部署,activiti 通过流程定义模型中的标识 key 来判断是否为同一流程模型, 相同标识key则视为同一流程定义模型。
相同的标识key流程定义模型,每部署一次对应的新增一条流程定义数据,对应流程定义版本号会基于之前的加1。
上面注意事项,针对下一章讲解的通过 zip 和 xml 文件进行部署流程定义也是要一样的。
/**
* 通过流程模型 进行 流程定义部署
* 流程图保存的时候是json串,引擎认识的却是符合bpmn2.0规范的xml,
* 所以在首次的部署的时候要将json串转换为BpmnModel,
* 再将BpmnModel转换成xml保存进数据库,以后每次使用就直接将xml转换成BpmnModel,
* 这套操作确实有点啰嗦,实际项目中如果不用activiti自带的设计器,可以考虑用插件,直接生成的是 xml,
* 或者自己开发设计器,在后端生成节点及其属性,引擎有现成的节点实体,如:开始节点StartEvent, 线SequenceFlow等。
* 涉及表:
* ACT_RE_PROCDEF 新增数据: 流程定义数据
* ACT_RE_DEPLOYMENT 新增数据: 流程部署数据
* ACT_GE_BYTEARRAY 新增数据:将当前流程图绑定到此流程定义部署数据上
* ACT_RE_MODEL 更新部署id
*/@Testpublicvoiddeploy()throwsException{// 模型id String id ="1c3b7d73-c833-11eb-98fd-ee5b4f08d062";// 获取流程图 json 字节码 byte[] jsonBytes = repositoryService.getModelEditorSource(id);if(jsonBytes ==null){System.out.println("模型数据为空,请先设计流程并成功保存,再进行发布。");return;}// 转xml字节数组byte[] xmlBytes =bpmnJsonToXmlBytes(jsonBytes);if(xmlBytes ==null){System.out.println("数据模型不符要求,请至少设计一条主线流程。");return;}// 流程图片字节码 byte[] pngBytes = repositoryService.getModelEditorSourceExtra(id);// 获取模型Model model = repositoryService.getModel(id);// 流程定义xml名称String processName = model.getName()+".bpmn20.xml";// 流程定义png名称String pngName = model.getName()+"."+ model.getKey()+".png";// 流程部署 Deployment deployment = repositoryService.createDeployment().name(model.getName()).addString(processName,newString(xmlBytes,"UTF-8"))// xml文件 .addBytes(pngName, pngBytes )// 图片.deploy();// 更新 部署id 到模型对象(将模型与部署数据绑定)
model.setDeploymentId(deployment.getId());
repositoryService.saveModel(model);System.out.println("部署完成");}
部署流程定义涉及表:
- ACT_RE_PROCDEF 新增数据: 流程定义数据
- ACT_RE_DEPLOYMENT 新增数据: 流程部署数据
- ACT_GE_BYTEARRAY 新增数据:流程定义 xml 和 png 保存下来,对应绑定到此流程定义数据上
- ACT_RE_MODEL 更新部署id
部署流程报错:
- 报错:元素‘sequenceFlow’中必须包含属性****‘targetRef’
解决:流程唯一标识 不能以数字开头,以字母开头

- 报错:元素’sequenceFlow’ 中必须包含属性’sourceRef’
解决:两个图标箭头连线,箭头来源和目标多拉一点到图标上。
(Ctrl+A全选流程设计图,移动一下哪根连线没动说明这根有问题)
七、流程定义部署管理 Deployment
概要
通过流程定义模型,进行部署流程定义,部署后会生成流程定义数据(相当于java类),此时生成的流程定义数据
主要用于生成流程实例(相当于java对象),一个流程定义 Java 类对应的可以创建无数个 java 流程实例对象。
7.1 通过 .zip 压缩包 部署 流程定义
packagecom.mengxuegu.workflow.test;importorg.activiti.engine.RepositoryService;importorg.activiti.engine.repository.DeploymentBuilder;importorg.junit.jupiter.api.Test;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.boot.test.context.SpringBootTest;importjava.io.*;importjava.util.zip.ZipInputStream;@SpringBootTestpublicclassActivitiTest03Deployment{@AutowiredRepositoryService repositoryService;/*** 通过 .zip 流程压缩包进行部署的流程定义 */@TestpublicvoiddeployByZip()throwsException{File file =newFile("D:/请假流程模型.leaveProcess.zip");String filename = file.getName();// 压缩包输入流ZipInputStream zipis =newZipInputStream(newFileInputStream(file));// 创建部署实例 DeploymentBuilder deployment = repositoryService.createDeployment();// 添加zip流
deployment.addZipInputStream(zipis);// 部署名称
deployment.name(filename.substring(0, filename.indexOf(".")));// 执行部署流程定义
deployment.deploy();System.out.println("zip压缩包方式部署流程定义完成");}
部署流程定义涉及表:
ACT_RE_PROCDEF 新增数据: 流程定义数据
ACT_RE_DEPLOYMENT 新增数据: 流程部署数据
ACT_GE_BYTEARRAY 新增数据:将当前流程图绑定到此流程定义部署数据上
ACT_RE_MODEL 更新部署id
7.2 通过 .bpmn 或 .bpmn.xml 文件 部署 流程定义
/**
* 通过 .bpmn 或 .bpmn20.xml 流程文件进行部署的流程定义
* 缺陷:没有png流程图
*/@TestpublicvoiddeployByBpmnFile()throwsException{// .bpmn 文件 File file =newFile("D:/leave.bpmn");// .bpmn20.xml 文件 //File file = new File("D:/请假流程模型.bpmn20.xml"); String filename = file.getName();// 输入流 FileInputStream input =newFileInputStream(file);// 创建部署实例DeploymentBuilder deployment = repositoryService.createDeployment();// bpmn20.xml 或 .bpmn (activiti5.10版本以上支持)
deployment.addInputStream(filename, input);// 部署名称//deployment.name(filename.substring(0, filename.indexOf("."))); // 执行流程定义部署
deployment.deploy();System.out.println("通过 .bpmn 或 .bpmn20.xml 部署完成");}
7.3 删除流程定义部署信息
/**
* 根据部署ID删除流程定义部署信息:
* ACT_GE_BYTEARRAY、
* ACT_RE_DEPLOYMENT、
* ACT_RU_IDENTITYLINK、
* ACT_RE_PROCDEF、
* ACT_RU_EVENT_SUBSCR
*/@Testpublicvoiddelete(){try{// 部署ID String deploymentId ="9a14702d-c996-11eb-8eef-02466359b284";// 不带级联的删除:如果有正在执行的流程,则删除失败抛出异常;不会删除 ACT_HI_和 历史表数据
repositoryService.deleteDeployment(deploymentId);// 级联删除:不管流程是否启动,都能可以删除;并删除历史表数据。 //repositoryService.deleteDeployment(deploymentId, true);System.out.println("删除流程定义部署信息成功");}catch(Exception e){
e.printStackTrace();if(e.getMessage().indexOf("a foreign key constraint fails")>0){System.out.println("有正在执行的流程,不允许删除");}else{System.out.println("删除失败,原因:"+ e.getMessage());}}}
八、流程定义管理 ProcessDefifinition
概要
部署好流程定义后,则可以进行查询、激活(启动)、挂起(暂停)、删除流程定义数据(上面讲的删除流程定义 部署信息就是),下载流程定义对应的 xml文件和 png 文件。
8.1 分页条件查询流程定义
packagecom.mengxuegu.workflow.test;importorg.activiti.engine.RepositoryService;importorg.activiti.engine.repository.ProcessDefinition;importorg.activiti.engine.repository.ProcessDefinitionQuery;importorg.junit.jupiter.api.Test;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.boot.test.context.SpringBootTest;importjava.util.List;@SpringBootTestpublicclassActivitiTest04ProcessDefinition{@AutowiredRepositoryService repositoryService;/**
* 查询部署的流程定义数据 ACT_RE_PROCDEF
* 需求:如果有多个相同流程定义标识key的流程时,只查询其最新版本
*/@TestpublicvoidgetProcessDefinitionList(){// 1. 获取 ProcessDefinitionQuery ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery();// 条件查询
query.processDefinitionNameLike("%请假%");// 有多个相同标识key的流程时,只查询其最新版本
query.latestVersion();// 按流程定义key升序排列
query.orderByProcessDefinitionKey().asc();// 当前查询第几页 int current =1;// 每页显示多少条数据 int size =5;// 当前页第1条数据下标 int firstResult =(current-1)* size;// 开始分页查询List<ProcessDefinition> definitionList = query.listPage(firstResult, size);for(ProcessDefinition pd : definitionList){System.out.print("流程部署ID:"+ pd.getDeploymentId());System.out.print(",流程定义ID:"+ pd.getId());System.out.print(",流程定义Key:"+ pd.getKey());System.out.print(",流程定义名称:"+ pd.getName());System.out.print(",流程定义版本号:"+ pd.getVersion());System.out.println(",状态:"+(pd.isSuspended()?"挂起(暂停)":"激活(开启)"));}// 用于前端显示页面,总记录数long total = query.count();System.out.println("满足条件的流程定义总记录数:"+ total);}}
8.2 激活或挂起流程定义
- 流程定义被挂起:此流程定义下的所有流程实例不允许继续往后流转了,就被停止了。
- 流程定义被激活:此流程定义下的所有流程实例允许继续往后流转。
- 为什么会被挂起?可能当前公司的请假流程发现了一些不合理的地方,然后就把此流程定义挂起。
流程不合理解决办法:
- 方式一:可以先挂起流程定义,然后更新流程定义,然后激活流程定义。
- 方式二:挂起了就不激活了,重新创建一个新的请假流程定义。
/**
* 通过流程定义id,挂起或激活流程定义
*/@TestpublicvoidupdateProcessDefinitionState(){// 流程定义IDString definitionId ="leaveProcess:2:b9227310-c8f4-11eb-acbf-aa2e2e85fc05";// 流程定义对象ProcessDefinition processDefinition =repositoryService.createProcessDefinitionQuery().processDefinitionId(definitionId).singleResult();// 获取当前状态是否为:挂起 boolean suspended = processDefinition.isSuspended();if(suspended){// 如果状态是:挂起,将状态更新为:激活, // 参数1: 流程定义id;参数2:是否级联激活该流程定义下的流程实例;参考3:设置什么时间激活这 个流程定义,如果 null 则立即激活)
repositoryService.activateProcessDefinitionById(definitionId,true,null);}else{// 如果状态是:激活,将状态更新为:挂起 // 参数 (流程定义id,是否挂起,激活时间)
repositoryService.suspendProcessDefinitionById(definitionId,true,null);}}
对应 act_re_procdef 表中的 SUSPENSION_STATE_ 字段,1是激活,2是挂起
8.3 下载流程定义的 xml 和 png 文件
下载流程定义的 xml 和 png 文件,方便用户浏览当前流程定义是怎样的。
/**
* 导出下载流程定义想着文件(.bpmn20.xml流程描述或.png图片资源)
*/@TestpublicvoidexportProcessDefinitionFile()throwsException{// 流程定义ID String definitionId ="leaveProcess:2:b9227310-c8f4-11eb-acbf-aa2e2e85fc05";// 查询流程定义数据ProcessDefinition processDefinition = repositoryService.getProcessDefinition(definitionId);// xml 文件名//String filename = processDefinition.getResourceName(); // png 图片名String filename = processDefinition.getDiagramResourceName();// 获取对应文件输入流 InputStream input = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), filename);// 创建输出流File file =newFile("/Users/mengxuegu/"+ filename);FileOutputStream output =newFileOutputStream(file);// 流拷贝IOUtils.copy(input, output);// 关闭流
input.close();
output.close();System.out.println("流程定义文件导出成功:"+ filename);}
由于文章字数限制,后续会有继续的更新,请关注订阅专栏~
版权归原作者 厌世小晨宇yu. 所有, 如有侵权,请联系我们删除。