前后端分离Springboot 整合使用activiti7教程(一)(全网最细)

news2024/11/27 4:19:21

activiti7关于SpringBoot前后端分离项目的详细教程

文章目录

  • activiti7关于SpringBoot前后端分离项目的详细教程
    • 一、Activiti工作流概述
      • 1.1 什么是工作流
      • 1.2 工作流应用场景
      • 1.3 什么是Activiti
      • 1.4 Activiti开发流程
      • 1.5 BPMN 2.0规范是什么
      • 1.6 BPMN 2.0 基本流程符号
    • 二、Activiti环境搭建和流程基础入门
      • 2.1 框架版本号
      • 2.2 JDK
      • 2.2 MySQL
      • 2.3 Maven 3.9配置
      • 2.4 基于 mave 构建项目
      • 2.7 pom.xml添加依赖坐标
      • 2.8 activiti.cfg.xml核心配置
      • 2.9 log4j.properties配置日志
      • 2.10 创建ProcessEngine流程引擎实例和数据表
      • 2.11 Activiti 25张数据表分析
      • 2.12 Activiti API服务接口
    • 三、Activiti流程实操入门
      • 3.1 IDEA安装actiBPM插件
      • 3.2 绘制流程定义模型
      • 3.3 解决中文乱码问题
      • 3.4 部署流程定义
      • 3.5 查询流程定义
      • 3.6 启动流程实例代码
      • 3.7 查询办理人待办任务
      • 3.8 完成待办任务
      • 3.9 查询流程实例历史节点信息
    • 四、SpringBoot 与 Activiti7 整合
      • 4.1 创建模块 activiti-boot
      • 4.2 pom.xml 依赖坐标
      • 4.3 创建application.yml配置文件
      • 4.4 创建数据库activiti-boot
      • 4.5 整合SpringSecurity
      • 4.6 创建启动类
      • 4.7 测试Activiti流程
    • 五、整合流程模型设计器Activiti Modeler
      • 5.1 下载源码
      • 5.2 拷贝目标源码
      • 5.3 修改上下文路径
      • 5.4 汉化Activiti Modeler
      • 5.5 创建空的流程模型
      • 5.6 绘制请假流程定义模型
    • 六、流程定义模型管理Model
      • 6.1 查询已绘制的所有流程模型数据:
      • 6.2 删除流程模型
      • 6.3 导出下载模型图zip压缩包
      • 6.4 导出下载模型 xml 文件
    • 七、流程定义部署管理 Deployment
      • 7.1 通过 .zip 压缩包 部署 流程定义
      • 7.2 通过 .bpmn 或 .bpmn.xml 文件 部署 流程定义
      • 7.3 删除流程定义部署信息
    • 八、流程定义管理 ProcessDefifinition
      • 8.1 分页条件查询流程定义
      • 8.2 激活或挂起流程定义
      • 8.3 下载流程定义的 xml 和 png 文件

本篇文章是从网上购买的教程然后整理出来的笔记,供大家一起学习参考。

一、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非常轻量级。

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

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.0
Activiti7.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:xe
postgresjdbc:postgresql://localhost:5432/activiti
db2jdbc:db2://localhost:50000/activiti
mssqljdbc:sqlserver://localhost:1433;databaseName=activiti使用Microsoft JDBC驱动程序4.0(sqljdbc4.jar)和JTDS驱动程序进行了测

在mysql服务器中创建一个activiti01数据库。注意配置好数据库之后,执行流程引擎是会自动创建数据库表的。

2.3 Maven 3.9配置

这里就不粘贴maven的配置了,网上都有各种教程。

2.4 基于 mave 构建项目

  1. 使用 idea 创建一个空工程 study-activiti

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 创建基于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 文件

<beans xmlns="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 -->
    <bean id="processEngineConfiguration"
          class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
        <!-- 配置数据源 -->
        <property name="jdbcUrl"
                  value="jdbc:mysql://127.0.0.1:3306/activiti01?characterEncoding=utf8&amp;nullCatalogMeansCurrent=true" />
        <property name="jdbcDriver" value="com.mysql.cj.jdbc.Driver" />
        <property name="jdbcUsername" value="root" />
        <property name="jdbcPassword" value="root" />

        <!-- activiti 数据库表生成策略 -->
        <!--
            自动更新数据库结构
            true:适用开发环境,默认值。activiti会对数据库中所有表进行更新操作。如果表不存在,则自动创建
            false:适用生产环境。activiti在启动时,对比数据库表中保存的版本,如果没有表或者版本不匹配,将抛出异常
            create_drop: 在activiti启动时创建表,在关闭时删除表(必须手动关闭引擎,才能删除表)
            drop-create: 在activiti启动时删除原来的旧表,然后在创建新表(不需要手动关闭引擎)
        -->
        <property name="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 ,并根据该文件中的配置构造一个流程引擎,和创建数据表。

方式如下:

import org.activiti.engine.ProcessEngine; import org.activiti.engine.ProcessEngines; import org.junit.Test; 
public class ActivitiTest01 { 
 /*** 创建ProcessEngine流程引擎,自动创建 activiti 数据表 */ @Test public void getProcessEngine() { 
     // 方式一:使用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流程引擎的通用事件日志记录表

完善表字段

  1. Activiti 7的M4以上版本,部署流程定义时,报错如下:

报错: MySQLSyntaxErrorException: Unknown column ‘VERSION_’ in ‘field list’

解决:因为 ACT_RE_DEPLOYMENT 表缺少 VERSION_ 和 PROJECT_RELEASE_VERSION_ 字段,执行以下语句

添加

ALTER TABLE ACT_RE_DEPLOYMENT ADD COLUMN VERSION_ VARCHAR(255); ALTER TABLE ACT_RE_DEPLOYMENT ADD COLUMN 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流程引擎的管理和维护。
  1. 核心 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 绘制流程定义模型

  1. 在 /resources 目录下创建 processes 目录,用于存放流程图

  2. 创建BPMN文件:右击 processes 目录,点击【New】–>【BpmnFile】

  3. 输入文件名称 leave ,点击【OK】按钮

绘制请假申请流程图

将 xxxx.bpmn 文件放在 /resources/processes/ 目录下,即 /resources/processes/leave.bpmn

  1. 鼠标左键拖拽右侧 开始事件 符号,将其拖下左侧界面上,同样的方式再拖拽其他图标

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

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

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

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

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 添加结束事件符号

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

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

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 点击流程图的空白处(不是点击流程符号):

设置当前流程图唯一标签 ID (也称为:流程的KEY):leaveProcess ,流程名称:请假流程。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 最后检查,每个人工任务节点的 Name 和 Assignee 是否也上面一致。

3.3 解决中文乱码问题

  1. 打开 Settings 窗口,点击 Editor > File Encodings > 将字符编码均设置为 UTF-8

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 打开idea安装目录,在 idea.exe.vmoptions (32位系统)和 idea64.exe.vmoptions (64位系统)文件最后

追加一行命令。 (C:\User\mengxuegu\.IntelliJIdea\config )

注意:不能有空格,不然idea重启后打不开

-Dfile.encoding=UTF-8
  1. 如果上面修改后,重启idea重新打开bpmn文件,还是乱码,则查找: C:\Users\Administrator\AppData\Roaming\JetBrains\IntelliJIdea2020.

生成png图片

将 bpmn 文件不方便随处打开提供给用户看,为了方便将流程图转成图片格式,方便随处打开演示;

并在部署流程时也需要 png 图片。

  1. 将 leave.bpmn 文件复制一份为 leave.xml

  2. 右击 leave.xml 文件,选择【Diagrams】–>【Show BPMN 2.0 Diagrams…】

  3. 如果找不到 Diagrams ,则重启 IDEA 工具

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

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

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

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 可以把 leave.xml 文件删除

3.4 部署流程定义

概述

将上面在设计器中定义的流程部署到activiti数据库中,就是流程定义部署。

一般情况下,流程定义(Process Definition)是在流程设计和开发阶段完成的,它描述了工作流程的规则、流程节点、流程步骤、流程之间的转移条件等信息。流程定义通常以一种特定的格式或者规范进行定义,如 BPMN(Business Process Model and Notation)等。

而流程部署(Process Deployment)是在流程准备运行阶段完成的,它将流程定义部署到流程引擎中以便执行。在流程部署过程中,流程定义会被加载到流程引擎中,并在流程引擎中生成对应的流程实例。

通常流程部署与流程定义是一对多的关系,比如一组相关联的业务可以做成多个流程图定义,然后把他们打包成zip后统一部署。

通过调用activiti的api将流程定义的 .bpm 和 png 两个文件一个一个添加部署到activiti中,也可以将两个文件打成 zip包进行部署。

.bpmn 流程定义文件部署

  1. 代码如下:
package com.mengxuegu.test; 

import org.activiti.engine.*; 
import org.activiti.engine.impl.util.ReflectUtil; 
import org.activiti.engine.repository.Deployment; 
import org.activiti.engine.runtime.ProcessInstance; 
import org.activiti.engine.task.Task; 
import org.activiti.engine.task.TaskQuery; 
import org.junit.Test; 
import java.io.IOException; 
import java.io.InputStream; 

import java.util.HashMap; 
import java.util.List; 
import java.util.Map; 
import java.util.zip.ZipInputStream; 
public class ActivitiTest02 { 
    /*** 部署流程: 
    * 1. ACT_RE_DEPLOYMENT 流程部署表,每执行一次部署,会插入一条记录 
    * 2. ACT_RE_PROCDEF 生成流程定义信息 
    * 其中 ACT_RE_DEPLOYMENT 与 ACT_RE_PROCDEF 表是一对多的关系, 
    * ACT_RE_PROCDEF 每条记录对应一个流程的定义信息(如:小梦、小谷请假申请) 
    * 3. ACT_GE_BYTEARRAY 流程资源表,插入资源数据,当前插入两条记录(.bpmn和.png资源) */ 
    @Test public void deployByFile() {
        // 1. 实例化流程引擎实例 
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); 
        // 2. 获取流程定义和部署对象相关的
        Service RepositoryService 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_ 字段,执行下面添加表字

ALTER TABLE ACT_RE_DEPLOYMENT ADD COLUMN VERSION_ VARCHAR(255); ALTER TABLE ACT_RE_DEPLOYMENT ADD COLUMN PROJECT_RELEASE_VERSION_ VARCHAR(255);

.zip 流程定义压缩包部署

将 leave.bpmn和 leave.png 压缩成 leave.zip ,放在类路径下的 processes/leave.zip 。 (所以正如上面所说,部署与定义是一对多的关系)

* 通过zip压缩包部署流程定义 */ 
    @Test public void deployByZip() { 
    // 1. 实例化流程引擎实例 
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); 
    // 2. 获取流程定义和部署对象相关的Service
    RepositoryService repositoryService = processEngine.getRepositoryService(); 
    // 3. 流程部署 
    // 读取zip资源包,构成 InputStream 输入流 
    InputStream inputStream = ReflectUtil.getResourceAsStream("processes/leave.zip"); 
    // 封装 ZipInputStream 输入流进行流程部署 
    ZipInputStream zipInputStream = new ZipInputStream(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 */ 
@Test public void getProcessDefinitionList() { 
    // 1. 实例化流程引擎实例 
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); 
    // 2. 获取 RepositoryService 
    RepositoryService repositoryService = processEngine.getRepositoryService(); 
    // 3. 获取 ProcessDefinitionQuery 这里的leaveProcess就是画流程图时指定的唯一的key
    ProcessDefinitionQuery 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 启动流程实例代码

/** * 启动流程实例(提交申请) */
@Test public void startProcessInstance() {
    // 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 查询办理人待办任务

启动流程实例后,每个任务的办理人就可以查询自己当前的待办任务,然后进行办理任务。

/*** 查询指定人员的待办任务 */ 
@Test public void taskListByAssignee() { 
// 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 完成待办任务

任务办理人查询待办任务,将待办任务进行完成。

  1. 先查询 meng 办理人任务,然后完成任务

  2. 再查询 xue 办理人任务,然后完成任务

  3. 这样此流程实例就完成结束。

/*** 完成待办任务 */ @Test public void completeTask() { 
    // 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 历史节点查询 对象。

/*** 查看流程办理历史信息 */ 
@Test public void historyInfo(){ 
    // 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配置文件

  1. 创建 application.yml 文件,activiti 7 相关配置如下:
server:
  port: 8080
  servlet:
    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=true
    username: 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: w736486962@qq.com
    # 邮件的用户名
    mail-server-user-name: w736486962@qq.com
    # 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

  1. activiti7 与 SpringSecurity 强耦合,

创建安全认证配置类 com.mengxuegu.config.SpringSecurityConfig 类

注意:activiti7 任务办理人,必须是当前可查询到的用户名

package com.mengxuegu.workflow.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.provisioning.UserDetailsManager;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    private Logger logger = LoggerFactory.getLogger(SpringSecurityConfig.class);

    /**
     * 内存 UserDetailsManager
     */
    @Bean
    public UserDetailsService myUserDetailsService() {
        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
        // 初始化账号角色数据
        addGroupAndRoles(inMemoryUserDetailsManager);
        return inMemoryUserDetailsManager;
    }

    private void addGroupAndRoles(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(new User(user[0], passwordEncoder().encode(user[1]),
                    authoritiesStrings.stream().map(s -> new SimpleGrantedAuthority(s)).collect(Collectors.toList())));
        }
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

}

4.6 创建启动类

创建项目启动类 com.mengxuegu.workflflow.WorkFlowApplication

package com.mengxuegu.workflow; 
import org.springframework.boot.SpringApplication; 
import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication 
public class WorkFlowApplication { 
    public static void main(String[] args) { 
        SpringApplication.run(WorkFlowApplication.class, args);                               } 
}

4.7 测试Activiti流程

相关的 Activiti 服务接口,都直接依赖注入即可使用。

  1. 将上面的 activiti-demo1 工程中绘制的bpmn流程图文件,拷贝如下工程的 resources 目录下

  1. 在 src/test 目录下创建测试类 com.mengxuegu.workflflow.test.ActivitiTest01
package com.mengxuegu.workflow.test; 
import org.activiti.engine.ProcessEngine; 
import org.activiti.engine.RepositoryService; import org.activiti.engine.repository.Deployment; 
import org.junit.jupiter.api.Test; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.boot.test.context.SpringBootTest; 
@SpringBootTest public class ActivitiTest01 {
    @Autowired ProcessEngine processEngine; 
    @Autowired RepositoryService repositoryService;
    /*** 获取 ProcessEngine 流程引擎,自动创建 activiti 数据表 */ 
    @Test public void getProcessEngine() { 
        // 获取 Activiti 流程引擎 和 创建数据库表, 
        // 并在 ACT_RE_DEPLOYMENT 插入一条无用的数据 
        SpringAutoDeployment System.out.println(processEngine); 
    }
    /*** 部署流程: */ 
    @Test public void deployByFile() { 
        // 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_ 字段,执行下面添加表字

ALTER TABLE ACT_RE_DEPLOYMENT ADD COLUMN VERSION_ VARCHAR(255); ALTER TABLE ACT_RE_DEPLOYMENT ADD COLUMN 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 拷贝目标源码

  1. 解压 Activiti-activiti-5.22.0.zip ,然后进入 Activiti-activiti-5.22.0/modules 目录,

  2. 复制 activiti-webapp-explorer2 工程中如下图画红框的文件夹和文件,

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

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

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

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

5.3 修改上下文路径

  1. 找到 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.jsonzh-CN.json

需要的自取:

链接:https://pan.baidu.com/s/1az50zQQ6U-o-owqOfgt3RQ
提取码:1111

  1. 汉化页面文字, resources/static/ 目录下添加 stencilset.json 文件

  2. 需要修改 StencilsetRestResource.java 类中 stencilset.jsonstatic/stencilset.json (最前面不

要有 / );

InputStream stencilsetStream = this.getClass().getClassLoader().getResourceAsStream("static/stencilset.json");

  1. 汉化按钮文字,添加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 创建空的流程模型

  1. 创建一个 ModelController 模型控制器,用于创建空流程模型和跳转到模型设计页面 modeler.html
package com.mengxuegu.workflow.controller; 
import com.fasterxml.jackson.databind.ObjectMapper; 
import com.fasterxml.jackson.databind.node.ObjectNode; 
import org.activiti.editor.constants.ModelDataJsonConstants; 
import org.activiti.engine.RepositoryService; 
import org.activiti.engine.repository.Model; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.stereotype.Controller; 
import org.springframework.web.bind.annotation.*; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse;
/*** 流程模型管理 */ 
@Controller 
@RequestMapping("/model") 
public class ModelController {
    @Autowired RepositoryService repositoryService;
    @Autowired ObjectMapper objectMapper;
    /*** 创建空模型窗口: 
    * 创建模型对象 
    * 设置对象值 
    * 存储模型对象(表act_re_model) 
    * 存储模型对象基础数据(表act_ge_bytearray) 
    * 跳转到ActivitiModeler,编辑流程图,存储流程图片和流程定义等(表act_ge_bytearray) 
    */ 
    @GetMapping("/create") 
    public void create(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(); 
        }
    }
}
  1. 重启mengxuegu-activiti-boot项目,

  2. 访问 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

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 点击 保存 按钮,会将绘制的 流程图 提交给后台进行保存操作(当前会报400错误,提交不成功)

保存请求路径:http://localhost:8080/workflflow/model/{modelId}/save

  1. 上面点击保存,会报400错误,

原因: ModelSaveRestResource 类中使用 @RequestBody MultiValueMap 接收参数无法接收到。

解决:将 @RequestBody MultiValueMap 改为 @RequestParam MultiValueMap ,如下图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 重启项目,再次保存测试 ok

六、流程定义模型管理Model

上面已经整合Web 端Activiti Modeler流程模型设计器,并完成创建和保存流程模型设计图。 下面对流程定义模型进行查询、删除、导出模型zip压缩包、xml 文件、部署流程定义。

6.1 查询已绘制的所有流程模型数据:

package com.mengxuegu.workflow.test; 

import org.activiti.engine.RepositoryService; 
import org.activiti.engine.repository.Model; 
import org.activiti.engine.repository.ModelQuery; 
import org.junit.jupiter.api.Test; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.boot.test.context.SpringBootTest; 
import java.util.List; 
@SpringBootTest 
public class ActivitiTest02Model { 
    @Autowired RepositoryService repositoryService;
    /*** 查询所有流程模型 ACT_RE_MODEL */ 
    @Test public void modelList() { 
        // 获取模型查询对象 
        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 */ 
@Test public void deleteModel(){ 
    // 模型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图片资源) */ 
@Test public void exportZip() throws Exception{ 
    // 模型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 = new File("D:/" + zipName); 
                FileOutputStream outputStream = new FileOutputStream(file); 
                // 实例化zip压缩对象输出流 
                ZipOutputStream zipos = new ZipOutputStream(outputStream); 
                // 指定压缩包里的 name.bpmn20.xml 文件名 
                zipos.putNextEntry(new ZipEntry(model.getName() + ".bpmn20.xml")); 
                // 将xml写入压缩流 
                zipos.write(xmlBytes); 
                // 查询png图片, 
                byte[] pngBytes = repositoryService.getModelEditorSourceExtra(id); if(pngBytes != null) { 
                // 指定压缩包里的 name.key.png 文件名 
                zipos.putNextEntry(new ZipEntry(model.getName() + "." + model.getKey() + ".png")); 
                // 图片文件写到压缩包中 
                zipos.write(pngBytes); 
       }
                zipos.closeEntry(); 
                zipos.close(); 
                System.out.println("导出成功"); 
            } 
    }else  { 
        System.out.println("模型不存在");
    } 
}


    @Autowired ObjectMapper objectMapper; 
/*** 流程图保存的时候是json串,引擎认识的却是符合bpmn2.0规范的xml, 
* json 字节码转 xml 字节码 
* @param jsonBytes * @return 
*/ 
private byte[] bpmnJsonToXmlBytes(byte[] jsonBytes) throws IOException { 
    if(jsonBytes == null) {
        return null; 
    }
    // json转回BpmnModel对象 
    JsonNode modelNode = objectMapper.readTree(jsonBytes); 
    BpmnModel bpmnModel = new BpmnJsonConverter().convertToBpmnModel(modelNode);            if(bpmnModel.getProcesses().size() == 0) {
        return null;
    }
    // BpmnModel对象转xml字节数组 
    return new BpmnXMLConverter().convertToXML(bpmnModel); 
}

运行后,生成 请假流程模型.leaveProcess.zip 压缩后,解压后如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

6.4 导出下载模型 xml 文件

在流程部署时,可以只上传流程模型图 xml 文件进行部署

/*** 导出下载模型 xml 文件 */ 
@Test public void exportXml() throws Exception{
    // 模型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 = new BpmnJsonConverter().convertToBpmnModel(modelNode);                 if(bpmnModel.getProcesses().size() != 0) { 
            // 转xml字节数组 
            byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(bpmnModel); in = new ByteArrayInputStream(bpmnBytes);
            // 如果流程名称为空,则取流程定义key 
            filename = StringUtils.isEmpty(bpmnModel.getMainProcess().getName()) ? bpmnModel.getMainProcess().getId() : bpmnModel.getMainProcess().getName(); 
        } 
    }if(filename == null) {
        filename = "模型数据为空,请先设计流程,再导出"; 
        in = new ByteArrayInputStream(filename.getBytes("UTF-8")); 
    }
    // 文件输出流 
    FileOutputStream out = new FileOutputStream(new File("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 
*/ 
@Test public void deploy() throws Exception {
    // 模型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, new String(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

部署流程报错:

  1. 报错:元素 ‘sequenceFlow’ 中必须包含属性 ‘targetRef’

解决:流程唯一标识 不能以数字开头,以字母开头

  1. 报错:元素’sequenceFlow’ 中必须包含属性’sourceRef’

解决:两个图标箭头连线,箭头来源和目标多拉一点到图标上。

(Ctrl+A全选流程设计图,移动一下哪根连线没动说明这根有问题)

七、流程定义部署管理 Deployment

概要

通过流程定义模型,进行部署流程定义,部署后会生成流程定义数据(相当于java类),此时生成的流程定义数据

主要用于生成流程实例(相当于java对象),一个流程定义 Java 类对应的可以创建无数个 java 流程实例对象。

7.1 通过 .zip 压缩包 部署 流程定义

package com.mengxuegu.workflow.test;
import org.activiti.engine.RepositoryService; 
import org.activiti.engine.repository.DeploymentBuilder; 
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.boot.test.context.SpringBootTest; 
import java.io.*; 
import java.util.zip.ZipInputStream; 
@SpringBootTest 
public class ActivitiTest03Deployment { 
    @Autowired RepositoryService repositoryService;
    /*** 通过 .zip 流程压缩包进行部署的流程定义 */ 
    @Test public void deployByZip() throws Exception { 
        File file = new File("D:/请假流程模型.leaveProcess.zip"); 
        String filename = file.getName();
        // 压缩包输入流
        ZipInputStream zipis = new ZipInputStream(new FileInputStream(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流程图 
*/ 
@Test
public void deployByBpmnFile() throws Exception { 
    // .bpmn 文件 
    File file = new File("D:/leave.bpmn"); 
    // .bpmn20.xml 文件 
    //File file = new File("D:/请假流程模型.bpmn20.xml"); 
    String filename = file.getName(); 
    // 输入流 
    FileInputStream input = new FileInputStream(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 
*/ 
@Test 
public void delete() {
    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 分页条件查询流程定义

package com.mengxuegu.workflow.test;
import org.activiti.engine.RepositoryService; 
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.junit.jupiter.api.Test; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List; 
@SpringBootTest 
public class ActivitiTest04ProcessDefinition {
    @Autowired RepositoryService repositoryService;
    /**
    * 查询部署的流程定义数据 ACT_RE_PROCDEF
    * 需求:如果有多个相同流程定义标识key的流程时,只查询其最新版本 
    */ 
    @Test 
    public void getProcessDefinitionList() { 
        // 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,挂起或激活流程定义
*/ 
@Test 
public void updateProcessDefinitionState() { 
    // 流程定义ID
    String 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图片资源) 
*/ 
@Test 
public void exportProcessDefinitionFile() throws Exception{ 
    // 流程定义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 = new File("/Users/mengxuegu/" + filename); 
    FileOutputStream output = new FileOutputStream(file); 
    // 流拷贝
    IOUtils.copy(input, output); 
    // 关闭流 
    input.close(); 
    output.close(); 
    System.out.println("流程定义文件导出成功:" + filename);
}

由于文章字数限制,后续会有继续的更新,请关注订阅专栏~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1557016.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java 实现扫描/识别图片文字 (OCR识别)

图片内容一般无法编辑&#xff0c;如果想要读取图片中的文本&#xff0c;我们需要用到OCR工具。本文将介绍如何在Java中实现OCR识别读取图片中的文字。 所需工具&#xff1a; IDEASpire.OCR for Java - Java OCR组件&#xff0c;支持识别多种语言、字体&#xff0c;可读取JPG、…

2024年大广赛联通沃派命题解析:赛题内容一览

2024大广赛又又又又又出新命题了&#xff0c;它就是助力青少年积极向上&#xff0c;乐观自信&#xff0c;探享多彩人生的5G时代潮牌——联通沃派&#xff0c;让我们来看看命题详情吧&#xff01; 联联通沃派是中国联通面向青少年群体推出的客户品牌&#xff0c;契合目标群体特…

module ‘numpy‘ has no attribute ‘int‘

在 NumPy 中&#xff0c;如果遇到了错误提示 "module numpy has no attribute int"&#xff0c;这通常意味着正在尝试以错误的方式使用 NumPy 的整数类型。从 NumPy 1.20 版本开始&#xff0c;numpy.int 已经不再是一个有效的属性&#xff0c;因为 NumPy 不再推荐使用…

win11 环境配置 之 Jmeter(JDK17版本)

一、安装 JDK 1. 安装 jdk 截至当前最新时间&#xff1a; 2024.3.27 jdk最新的版本 是 官网下载地址&#xff1a; https://www.oracle.com/java/technologies/downloads/ 建议下载 jdk17 另存为到该电脑的 D 盘下&#xff0c;新建jdk文件夹 开始安装到 jdk 文件夹下 2. 配…

不小心将某个分支的本地代码推到另外一个远程分支上

不小心将某个分支的本地代码推到另外一个远程分支上&#xff0c;是不会产生效果的&#xff0c;若提示了“Everything up-to-date ”&#xff0c;说明没有内容被提交到了远程&#xff0c;某个分支的本地代码仍然在本地仓库。 若想将改动的代码提交到另外一个远程分支上&#xf…

送朋友的生日祝福静态页面代码!(小白也能轻松GET!)

Hey亲爱的小白们&#xff01;&#x1f44b; 知道你们想给朋友一个独特又有心的生日祝福&#xff0c;却苦于没有编程基础吗&#xff1f;别担心&#xff0c;来白嫖&#xff01;&#x1f381; &#x1f680;【生日祝福静态页面代码】来啦&#xff01;只需简单几步&#xff0c;就能…

java多线程——概述,创建方式及常用方法

前言&#xff1a; 学习到多线程了&#xff0c;整理下笔记&#xff0c;daydayup!!! 多线程 什么是线程 线程&#xff08;Thread&#xff09;是一个程序内部的一条执行流程。若程序只有一条执行流程&#xff0c;那这个程序就是单线程的程序。 什么是多线程 多线程是指从软硬件上…

E5063A是德科技E5063A网络分析仪

181/2461/8938产品概述&#xff1a; Keysight E5063A 是一款低成本网络分析仪&#xff0c;可为测试天线、电缆、滤波器和 PCB 等简单无源元件提供优化的性能和功能。Keysight E5063A 为您的企业提供价格和性能之间的最佳平衡&#xff0c;以满足您的业务和技术要求。它利用行业…

【MySQL】DQL-基础查询-语句&演示(查询多个字段 / 所有字段/并设置别名/去重)

前言 大家好吖&#xff0c;欢迎来到 YY 滴MySQL系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C Linux的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; YY的《C》专栏YY的《C11》专栏YY的…

Arduino通过Wire库读取AS5600编码器数据

Arduino通过Wire库读取AS5600编码器数据 ✨在实际测试中&#xff0c;测试AS5600除了使用径向磁铁之外&#xff0c;球型的或者正四方体的强磁铁&#xff0c;也是可以准确的测量角度。测试高度的话&#xff0c;从板子&#xff08;芯片引脚底部&#xff09;到磁铁底部15毫米内&…

【MATLAB源码-第22期】基于matlab的手动实现的(未调用内置函数)CRC循环码编码译码仿真。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 循环码是线性分组码的一种&#xff0c;所以它具有线性分组码的一般特性&#xff0c;此外还具有循环性。循环码的编码和解码设备都不太复杂&#xff0c;且检(纠)错能力强。它不但可以检测随机的错误&#xff0c;还可以检错突发…

《论文阅读》PAGE:一个用于会话情绪原因蕴含基于位置感知的图模型 ICASSP 2023

《论文阅读》PAGE&#xff1a;一个用于会话情绪原因蕴含基于位置感知的图模型 ICASSP 2023 前言 简介任务定义模型构架Utterances Encoding with EmotionPosition-aware GraphCausal Classifier实验结果 前言 亲身阅读感受分享&#xff0c;细节画图解释&#xff0c;再也不用担…

【论文精读】CAM:基于上下文增强和特征细化网络的微小目标检测

文章目录 &#x1f680;&#x1f680;&#x1f680;摘要一、1️⃣ Introduction---介绍二、2️⃣Related Work---相关工作2.1 &#x1f393; 基于深度学习的对象检测器2.2 ✨多尺度特征融合2.3 ⭐️数据增强 三、3️⃣提议的方法3.1 &#x1f393; 具有上下文增强和特征细化的特…

c++使用类的一些注意事项

前言&#xff1a; 本篇内容为前面的补充&#xff0c;介绍了我们使用类时需要注意些什么以及一些编译器的优化&#xff0c;可能有些理解不到位或者错误&#xff0c;请斧正。 目录 前言&#xff1a; 1.再谈构造函数 2.&#xff08;c98&#xff09;隐式类型转换中的编译器的优…

牛客NC31 第一个只出现一次的字符【simple map Java,Go,PHP】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/1c82e8cf713b4bbeb2a5b31cf5b0417c 核心 Map参考答案Java import java.util.*;public class Solution {/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff0c;直接返回方法规定的值即可*…

Linux IRC

目录 入侵框架检测 检测流程图 账号安全 查找账号中的危险信息 查看保存的历史命令 检测异常端口 入侵框架检测 1、系统安全检查&#xff08;进程、开放端口、连接、日志&#xff09; 这一块是目前个人该脚本所实现的功能 2、Rootkit 建议使用rootkit专杀工具来检查&#…

在 Linux 中通过 SSH 执行远程命令时,无法自动加载环境变量(已解决)

问题场景 目前我的环境变量都存储在 /etc/profile 文件中&#xff0c;当我通过远程 SSH 执行一些命令时&#xff0c;提示命令找不到&#xff0c;如下所示&#xff1a; 问题出现原因 这里找到了一张出自尚硅谷的图片&#xff0c;很好的解释了该问题&#xff1a; 这是由于 Linu…

快速上手Spring Cloud 十七:深入浅出的学习之旅

快速上手Spring Cloud 一&#xff1a;Spring Cloud 简介 快速上手Spring Cloud 二&#xff1a;核心组件解析 快速上手Spring Cloud 三&#xff1a;API网关深入探索与实战应用 快速上手Spring Cloud 四&#xff1a;微服务治理与安全 快速上手Spring Cloud 五&#xff1a;Spring …

JAVAEE之网络编程

1.网络编程 网络编程&#xff0c;指网络上的主机&#xff0c;通过不同的进程&#xff0c;以编程的方式实现网络通信&#xff08;或称为网络数据传输&#xff09;。 当然&#xff0c;我们只要满足进程不同就行&#xff1b; 所以即便是同一个主机&#xff0c;只要是不同进程&am…