Activiti 工作流 学习笔记
环境信息:
IntelliJ IDEA 2021.3.3 (Ultimate Edition)
JDK 1.8
Activiti 7
文章目录
- Activiti 7
- 一、了解工作流
- 1.1 什么是工作流
- 1.2 工作流引擎
- 1.3 常见的工作流引擎
- 1.4 Activiti 概述
- 1.4.1 Activiti介绍
- 1.4.2 建模语言BPMN
- 1.4.3 Activiti使用流程
- 1.4.4 流程设计工具
- 1.4.5 下载activiti-explorer
- 1.4.6 解压获取部署包
- 1.4.7 部署 activiti-explorer.war
- 1.4.8 访问activiti-explorer
- 1.5 Activiti 7
- 1.5.1 Activiti7 新特性介绍
- 二、Activiti 流程操作
- 2.1 流程定义
- 2.1.1 新建模型
- 2.1.2 开始节点
- 2.1.3 任务节点
- 2.1.4 结束节点
- 2.1.5 设置节点属性
- 2.1.6 设置流程定义key
- 2.1.7保存流程定义模型
- 2.1.8 下载流程定义文件
- 2.1.9 下载流程定义图片
- 2.1.10 将资源文件放入项目
- 三、实践项目
- 3.1 创建 Spring Boot 项目:
- 3.2 配置文件:
- 3.3 引入依赖
- 3.4 新写一个Controller
- 3.5 添加Activiti Maven依赖
- 3.5.1 数据库
- 1、 表的命名规则和作用
- 2、Activiti 数据表介绍
- 3.6 Activiti 7 经典类
- 3.6.1 流程部署 Deployment
- 3.6.2 流程定义 ProcessDefinition
- 3.6.3 流程实例 ProcessInstance
- 3.6.4 任务处理Task
- 3.6.5 历史任务 HistoricTaskInstance
- A. 历史综合信息 HistoricTaskInstance
- B. 历史变量:HistoriVariableInstance
- 3.7 UEL表达式
- 3.8 BPMN 2.0 网关
- 3.9 Activiti 新特性
- 3.10 Spring Security
- 3.11 BPMN.js
- a. 下载BPMN.js
- b. 安装Node.js
一、了解工作流
1.1 什么是工作流
工作流(Workflow),就是通过计算机对业务流程自动化执行管理。它主要解决的是“使在多个参与者之间按照某种**预定义的规则(约定·好的)**自动进行传递文档、信息或任务的过程,从而实现某个预期的业务目标,或者促使此目标的实现”。通俗来讲,就是业务上一个完整的审批流程。 例如:员工的请假,出差,外出采购,合同审核等等,这些过程,都是一个工作流。
1.2 工作流引擎
对于工作流的处理,如果采用原始的方式,我们需要拿着各种文件到各个负责人那里去签字,需要在多个部门之间不断审批,这种方式费时费力。而我们可以借助软件系统来协助我们处理这些审批流程,这样就出现了工作流系统,使用工作流系统后可以极大的提高工作效率。
工作流模型:
填写请假单 -> 部门经理审批 -> 总经理审批 -> 人事备案
要实现上述的流程,我们自己可以通过字段标识来实现这个审批效果,在业务表中加个字段,比如填写请假单用1标识,部门经理用2标识,总经理用3标识,人事备案用4标识,好像看起来没啥问题,也实现了审批效果。可是一旦我们的流程出现变化,这个时候我们就需要改动我们的代码,这显然是不可取的,需要用专业的方式来实现工作流的管理,并且可以做到业务流程变化之后,我们的程序可以不用改变,流程自动完成。如果可以实现这样的效果,那么我们的业务系统的适应能力就得到了极大提升。在这样的背景下,就出现了工作流引擎。
为什么使用工作流引擎,能实现业务流程改变,不用修改代码,流程还能自动推进?
(1)我们先来说说为什么流程改变,不用修改代码:我们的工作流引擎都实现了一个规范,这个规范要求我们的流程管理与状态字段无关,始终都是读取业务流程图的下一个节点。当业务更新的时候我们只需要更新业务流程图就行了。这就实现了业务流程改变,不用修改代码。
(2)再来说说流程自动推进,这个原理就更简单了,就拿上面的请假模型来说,工作流引擎会用一张数据库表来记录当前处在的节点。当填写完请假单后肯定是要轮到部门经理来审批了,所以我们一旦完成了请假单填写那么这条记录将会被从这张表删除掉,并且会把下一个节点部门经理的信息插入到这张表中,当我们用部门经理的信息去这张表中查询的时候就能查出部门经理相关的审批的信息了,以此类推,这样层层递进,就实现了流程的自动递交了。
1.3 常见的工作流引擎
主流框架有:Activiti、JBPM、Camunda、Flowable,国产的盘古BPM、云程
1.4 Activiti 概述
1.4.1 Activiti介绍
Activiti是一个工作流引擎,可以将业务系统中复杂的业务流程抽取出来,使用专门的建模语言BPMN进行定义,业务流程按照预先定义的流程进行执行。实现了系统的流程由Activiti进行管理,减少业务系统由于流程变更进行系统升级改造的工作流量,从而提高系统的健壮性,同时也减少了系统开发维护成本。
官方网站:https://www.activiti.org

1.4.2 建模语言BPMN
BPM(Business Process Management)即业务流程管理,是一种规范化的构造端到端的业务流程,以持续提高组织业务效率。
BPM 软件就是根据企业中业务环境的变化,推进人与人之间、人与系统之间以及系统与系统之间的整理及调整的经营方法与解决方案的 IT 工具。使用 BPM 软件对企业内部及外部的业务流程的整个生命周期进行建模、自动化、管理监控和优化,可以降低企业成本,提高利润。
BPMN(Business Process Model And Notation)即业务流程模型和符号,是一套标准的业务流程建模符号,使用 BPMN 提供的符号可以创建业务流程。Activit 就是使用 BPMN 进行流程建模、流程执行管理的。
BPMN2.0 是业务流程建模符号 2.0 的缩写,它由 Business Process Management Initiative 这个非营利协会创建并不断发展。BPMN2.0 是使用一些符号来明确业务流程设计流程图的一套符号规范,能增进业务建模时的沟通效率。目前 BPMN2.0 是最新的版本,它用于在 BPM 上下文中进行布局和可视化的沟通。
BPMN2.0 的基本符号主要包含:
-
事件 Event
开始:表示一个流程的开始
中间:发生的开始和结束事件之间,影响处理的流程
结束:表示该过程结束

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

-
网关 GateWay
用于表示流程的分支与合并,有几种常用网关需要了解:
排他网关: 只有一条路径会被选择
并(平)行网关: 所有路径会被同时选择。
包容网关: 可以同时执行多条线路,也可以在网关上设置条件。
事件网关: 专门为中间捕获事件设置的,允许设置多个输出流指向多个不同的中间捕获事件。当流程执行到事件网关后,流程处于等待状态,需要等待抛出事件才能将等待状态转换为活动状态。

-
流向 Flow
流是连接两个流程节点的连线,常见的流向包含以下几种:
顺序流:用一个带实心箭头的实心线表示,用于指定活动执行的顺序
信息流:用一条带箭头的虚线表示,用于描述两个独立的业务参与者(业务实体/业务角色)之间发送和接受的消息流动
关联:用一根带有线箭头的点线表示,用于将相关的数据、文本和其他人工信息与流对象联系起来。用于展示活动的输入和输出

流程示例:

1.4.3 Activiti使用流程
第一步: 引入依赖并初始化数据库
既然activiti是一个框架,那么我们肯定是需要引入对应的jar包坐标的,具体参考代码中的。
第二步: 通过工具绘画流程图
使用 activiti 流程建模工具(activity-designer)定义业务流程(.bpmn 文件)
.bpmn 文件就是业务流程定义文件,通过 xml 定义业务流程。
第三步:流程定义部署;
向 activiti 部署业务流程定义(.bpmn 文件),使用 activiti 提供的 api 向 activiti 中部署.bpmn 文件,
通俗来讲,就是让activiti认识要使用的流程
第四步: 启动一个流程实例(Process Instance)
启动一个流程实例表示开始一次业务流程的运行,比如员工请假流程部署完成,如果张三要请假就可以启动一个流程实例,如果李四要请假也启动一个流程实例,两个流程的执行互相不影响,就好比定义一个java类,实例化两个对象一样,部署的流程就好比java类,启动一个流程实例就好比new一个java对象
第五步: 用户查询待办任务(Task)
因为现在系统的业务流程已经交给 activiti 管理,通过 activiti 就可以查询当前流程执行到哪了,当前用户需要办理什么任务了,这些 activiti帮我们管理了。实际上我们学习activiti也只是学习它的API怎么使用,因为很多功能activiti都已经封装好了,我们会调用就行了
第六步: 用户办理任务
用户查询待办任务后,就可以办理某个任务,如果这个任务办理完成还需要其它用户办理,比如请假单创建后由部门经理审核,这个过程也是由 activiti 帮我们完成了,不需要我们在代码中硬编码指定下一个任务办理人了
第七步: 流程结束
当任务办理完成没有下一个任务节点了,这个流程实例就完成了。
1.4.4 流程设计工具
actiBPM

BPMN-JS

IDEA版本小于等于2019,可使用Activiti插件actiBPM,大于该版本的IDEA可使用Activiti BPMN visualizer插件绘制流程设计。现使用:Activiti Modeler
Activiti Modeler 是 Activiti 官方提供的一款在线流程设计的前端插件,开发人员可以方便在线进行流程设计,保存流程模型,部署至流程定义等等,后续我们的项目也是集成Activiti Modeler绘制流程定义。
1.4.5 下载activiti-explorer
官网下载:https://www.activiti.org/get-started

1.4.6 解压获取部署包
解压activiti-5.22.0.zip,在activiti-5.22.0\wars目录下获取 activiti-explorer.war


1.4.7 部署 activiti-explorer.war
将activiti-explorer.war放到 tomcat部署目录(Tomcat目录下\webapps),启动 tomcat:双击 apache-tomcat的安装目录\bin\startup.bat

1.4.8 访问activiti-explorer
http://localhost:8080/activiti-explorer
默认登录账号:kermit kermit


上面有很多功能,我们关注流程设计即可,如下图:
点击上图:流程 --> 新建模型 --> 输入模型名称(请假)–> 创建
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jjgZDKBx-1686055679452)(./doc/photo/15后.PNG)]
1.5 Activiti 7
Activiti项目是基于Apache License许可的开源项目,项目前主管是 Tom Baeyens。Activiti项目前身是JBPM,因此Activiti初始版本是5,Activiti项目支持BPMN标准。
1.5.1 Activiti7 新特性介绍
-
与Spring Boot更好的原生支持,旧版需要配置
activiti.cfg.xml文件,还需要手动加载,新版本只需配置yml文件即可 -
引入SpringSecurity作为默认用户与角色的默认安全机制(相比旧版自己配置有点强硬)
-
核心API进行了封装,封装了不少如
none值等判断,让开发者更关注业务流程即可, -
对云发布,分布式支持等,去掉了用户和表单两个接口类,需要自己手动添加。
-
取消了
FormService和IdentityService模块,将原先RepositorySertvice,RuntimeService,ManagementService,TaskService,HistoryService五个模块合并成ProcessRuntime,TaskRuntime两个模块

二、Activiti 流程操作
2.1 流程定义
我们定义一个请假流程
2.1.1 新建模型



2.1.2 开始节点

2.1.3 任务节点

2.1.4 结束节点

2.1.5 设置节点属性
指定标签名称:张三审批,节点任务负责人:zhangsan

指定标签名称:李四审批,节点任务负责人:lisi

2.1.6 设置流程定义key

2.1.7保存流程定义模型


2.1.8 下载流程定义文件

下载文件为:qingjia.bpmn20.xml

<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/processdef"><process id="qingjia" isExecutable="true"><startEvent id="sid-1AB654AE-D3A2-4240-82A7-F8D959B29A9A">startEvent><userTask id="sid-0DAD9B06-6F38-4616-BC16-156989CB12BA" name="张三审批" activiti:assignee="zhangsan">userTask><sequenceFlow id="sid-581CCDA2-D9E0-4C5B-83AE-93F409832ED4" sourceRef="sid-1AB654AE-D3A2-4240-82A7-F8D959B29A9A" targetRef="sid-0DAD9B06-6F38-4616-BC16-156989CB12BA">sequenceFlow><endEvent id="sid-E210AB99-58D8-43D0-B24A-CFF3E85A9512">endEvent><userTask id="sid-E9C4E51D-9F58-4C99-B1A8-E258AD244426" name="李四" activiti:assignee="lisi">userTask><sequenceFlow id="sid-9978CDC0-34F4-40ED-8A7C-80E907514297" sourceRef="sid-0DAD9B06-6F38-4616-BC16-156989CB12BA" targetRef="sid-E9C4E51D-9F58-4C99-B1A8-E258AD244426">sequenceFlow><sequenceFlow id="sid-DE57A74C-09D5-42FE-BE85-A0EAE8CC89EF" sourceRef="sid-E9C4E51D-9F58-4C99-B1A8-E258AD244426" targetRef="sid-E210AB99-58D8-43D0-B24A-CFF3E85A9512">sequenceFlow>process><bpmndi:BPMNDiagram id="BPMNDiagram_qingjia"><bpmndi:BPMNPlane bpmnElement="qingjia" id="BPMNPlane_qingjia"><bpmndi:BPMNShape bpmnElement="sid-1AB654AE-D3A2-4240-82A7-F8D959B29A9A" id="BPMNShape_sid-1AB654AE-D3A2-4240-82A7-F8D959B29A9A"><omgdc:Bounds height="30.0" width="30.0" x="136.19997740686003" y="107.99999655783185">omgdc:Bounds>bpmndi:BPMNShape><bpmndi:BPMNShape bpmnElement="sid-0DAD9B06-6F38-4616-BC16-156989CB12BA" id="BPMNShape_sid-0DAD9B06-6F38-4616-BC16-156989CB12BA"><omgdc:Bounds height="79.99999999999999" width="100.0" x="224.99999329447763" y="82.99999532103554">omgdc:Bounds>bpmndi:BPMNShape><bpmndi:BPMNShape bpmnElement="sid-E210AB99-58D8-43D0-B24A-CFF3E85A9512" id="BPMNShape_sid-E210AB99-58D8-43D0-B24A-CFF3E85A9512"><omgdc:Bounds height="28.00000000000007" width="28.0" x="539.9999919533731" y="108.99999493360532">omgdc:Bounds>bpmndi:BPMNShape><bpmndi:BPMNShape bpmnElement="sid-E9C4E51D-9F58-4C99-B1A8-E258AD244426" id="BPMNShape_sid-E9C4E51D-9F58-4C99-B1A8-E258AD244426"><omgdc:Bounds height="80.00000000000001" width="100.0" x="374.9999944120646" y="82.99999369680909">omgdc:Bounds>bpmndi:BPMNShape><bpmndi:BPMNEdge bpmnElement="sid-581CCDA2-D9E0-4C5B-83AE-93F409832ED4" id="BPMNEdge_sid-581CCDA2-D9E0-4C5B-83AE-93F409832ED4"><omgdi:waypoint x="166.19997740686003" y="122.9999964079777">omgdi:waypoint><omgdi:waypoint x="224.99999329447763" y="122.99999582054932">omgdi:waypoint>bpmndi:BPMNEdge><bpmndi:BPMNEdge bpmnElement="sid-9978CDC0-34F4-40ED-8A7C-80E907514297" id="BPMNEdge_sid-9978CDC0-34F4-40ED-8A7C-80E907514297"><omgdi:waypoint x="324.99999329447763" y="122.99999477962673">omgdi:waypoint><omgdi:waypoint x="374.9999944120646" y="122.99999423821791">omgdi:waypoint>bpmndi:BPMNEdge><bpmndi:BPMNEdge bpmnElement="sid-DE57A74C-09D5-42FE-BE85-A0EAE8CC89EF" id="BPMNEdge_sid-DE57A74C-09D5-42FE-BE85-A0EAE8CC89EF"><omgdi:waypoint x="474.9999944120646" y="122.9999941761875">omgdi:waypoint><omgdi:waypoint x="539.9999919533731" y="122.99999479937941">omgdi:waypoint>bpmndi:BPMNEdge>bpmndi:BPMNPlane>bpmndi:BPMNDiagram>
definitions>
2.1.9 下载流程定义图片
单击右键上图图片,图片另存为:qingjia.PNG

2.1.10 将资源文件放入项目
一般我们将在项目resources下新建BPMN资源文件夹,将qingjia.bpmn20.xml与qingjia.PNG放入BPMN文件夹中:

三、实践项目
3.1 创建 Spring Boot 项目:
配置项目相关信息:

选择Spring相关插件:
- Spring Web
- JDBC API 连接数据库
- MySQL Driver 使用的是MySQL,因此配置MySQL驱动
- Mybatis framework 使用Mybatis框架作为持久层的框架

选择JDK版本:1.8

Maven配置(同时Maven最好引用阿里镜像)

3.2 配置文件:
server:port: 8080 # 端口号servlet:context-path: / # 访问起始路径
spring:datasource: # 数据库连接username: root # 用户名password: root # 密码url: jdbc:mysql://127.0.0.1:3306/activiti?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC&nullCatalogMeansCurrent=true driver-class-name: com.mysql.cj.jdbc.Driver # 驱动
3.3 引入依赖
<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.0modelVersion><parent><groupId>org.springframework.bootgroupId><artifactId>spring-boot-starter-parentartifactId><version>2.3.2.RELEASEversion><relativePath/> parent><groupId>com.test.activitigroupId><artifactId>activitiartifactId><version>0.0.1-SNAPSHOTversion><name>activitiname><description>Demo project for Spring Bootdescription><properties><maven.compiler.source>8maven.compiler.source><maven.compiler.target>8maven.compiler.target>properties><dependencies><dependency><groupId>org.springframework.bootgroupId><artifactId>spring-boot-starter-jdbcartifactId>dependency><dependency><groupId>org.springframework.bootgroupId><artifactId>spring-boot-starter-webartifactId>dependency><dependency><groupId>org.mybatis.spring.bootgroupId><artifactId>mybatis-spring-boot-starterartifactId><version>2.2.2version>dependency><dependency><groupId>mysqlgroupId><artifactId>mysql-connector-javaartifactId><scope>runtimescope>dependency><dependency><groupId>org.springframework.bootgroupId><artifactId>spring-boot-starter-testartifactId><scope>testscope><exclusions><exclusion><groupId>org.junit.vintagegroupId><artifactId>junit-vintage-engineartifactId>exclusion>exclusions>dependency><dependency><groupId>org.activitigroupId><artifactId>activiti-spring-boot-starterartifactId><version>7.1.0.M4version>dependency><dependency><groupId>org.activiti.dependenciesgroupId><artifactId>activiti-dependenciesartifactId><version>7.1.0.M4version><type>pomtype>dependency><dependency><groupId>org.projectlombokgroupId><artifactId>lombokartifactId><optional>trueoptional>dependency>dependencies>
project>
3.4 新写一个Controller
创建HelloController.java
package com.test.activiti.controller;import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;@RestController
public class HelloController {@RequestMapping(value = "hello",method = RequestMethod.GET) //Get 浏览器传参 Post 提交表单方式传参public String hello(){return "Activiti7 学习";}
}
启动后访问:

3.5 添加Activiti Maven依赖
https://activiti.gitbook.io/activiti-7-developers-guide/getting-started/getting-started-activiti-core

当然也可以阿里云查询:
https://developer.aliyun.com/mvn/search


<dependency><groupId>org.activitigroupId><artifactId>activiti-spring-boot-starterartifactId><version>7.1.0.M4version>
dependency><dependency><groupId>org.activiti.dependenciesgroupId><artifactId>activiti-dependenciesartifactId><version>7.1.0.M4version><type>pomtype>
dependency>
注意:M5版本以上,会出现每次启动数据库后新生成一条默认数据的BUG,M4版本就不会。
# application.yml
server:port: 8080 # 端口号servlet:context-path: / # 访问起始路径
spring:datasource: # 数据库连接username: root # 用户名password: root # 密码url: jdbc:mysql://127.0.0.1:3306/activiti?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC&nullCatalogMeansCurrent=truedriver-class-name: com.mysql.cj.jdbc.Driver # 驱动
# activiti7 历史表创建
spring.activiti.history-level: full # 完整存储
spring.activiti.db-history-used: true # 开启历史表存储
# 自动部署 (关闭)
spring.activiti.check-process-definitions: false
如果是Activiti7以前的版本会需要另写一个activiti.cfg.xml配置文件,启动项目时需要启动。当然activiti 7 也是支持这样的,但是已经能够适配SpringBoot配置文件,这样配置方式是没必要的。
Activiti 7 之前版本:https://www.activiti.org/userguide/

配置完成后,运行即可:生成多个表,User表需要自己创建。

3.5.1 数据库
Activiti 的运行支持必须要有这 25 张表的支持,主要是在业务流程运行过程中,记录参与流程的用户主体,用户组信息,以及流程的定义,流程执行时的信息,和流程的历史信息等等。
1、 表的命名规则和作用
观察创建的表,我们发现 Activiti 的表都以 act_ 开头,紧接着是表示表的用途的两个字母标识,也和 Activiti 所提供的服务的 API 对应:
- ACT_RE:RE 表示 repository,这个前缀的表包含了部署的流程定义和流程静态资源 (图片、规则、等等)
- ACT_RU:RU 表示 runtime,这些表运行时,会包含流程实例、任务、变量、异步任务等流程业务进行中的数据。Activiti 只在流程实例执行过程中保存这些数据,在流程结束时就会删除这些记录。这样表就可以一直保持很小的体积,并且速度很快
- ACT_HI:HI 表示 history,这些表包含一些历史数据,比如历史流程实例、变量、任务等等
- ACT_GE:GE 表示 general,通用数据
2、Activiti 数据表介绍
| 表分类 | 表名 | 解释 |
|---|---|---|
| 一般数据 | ||
| [ACT_EVT_LOG] | 事件处理日志 | |
| [ACT_GE_BYTEARRAY] | 通用的流程定义和流程资源 (二进制表) | |
| [ACT_GE_PROPERTY] | 系统相关属性 (存BPMN的二进制表) | |
| 流程历史记录 | ||
| [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_EVENT_SUBSCR] | 运行时事件 | |
| [ACT_RU_EXECUTION] | 运行时流程执行实例 | |
| [ACT_RU_IDENTITYLINK] | 运行时用户关系信息,存储任务节点与参与者的相关信息 | |
| [ACT_RU_JOB] | 运行时作业 | |
| [ACT_RU_TASK] | 运行时任务 | |
| [ACT_RU_VARIABLE] | 运行时变量表 |
3.6 Activiti 7 经典类
3.6.1 流程部署 Deployment
Deployment:添加资源文件、获取部署信息、部署时间
- 指定流程key
- 制定流程名称
- 任务指定执行人
- 上传bpmn、上传bpmn和图片、上传zip
- 增删改查
- 查询列表和查询xml
package com.test.activiti;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;import java.io.InputStream;
import java.util.List;@SpringBootTest
public class Part1_Deployment {@Autowiredprivate RepositoryService repositoryService;/*** 通过bpmn部署流程*/@Testpublic void initDeploymentBPMN() {String filename = "BPMN/diagram.bpmn"; //resources 目录下//String pngname = "BPMN/bpmn.PNG";Deployment deployment = repositoryService.createDeployment().addClasspathResource(filename)//.addClasspathResource(pngname)//图片 原来需要上传bpmnxml文件的同时上传一张相应行的图片用来展现.name("流程部署测试BPMN") //如果数据库遇到多个相同文件,会在之后追加_V2.deploy();System.out.println(deployment.getName());}
}
注意:deployment 选择org.activiti.engine.repository 包:

运行成功


/*** 通过bpmn部署流程*/
@Test
public void initDeploymentBPMN() {String pngname = "BPMN/bpmn.PNG";//resources 目录下Deployment deployment = repositoryService.createDeployment().addClasspathResource(pngname)//图片 原来需要上传bpmnxml文件的同时上传一张相应行的图片用来展现.name("流程部署测试BPMN") //如果数据库遇到多个相同文件,会在之后追加_V2.deploy();System.out.println(deployment.getName());
}




/*** 通过zip部署流程*/@Testpublic void initDemploymentZIP(){InputStream fileInputStream = this.getClass().getClassLoader().getResourceAsStream("BPMN/bpmn.zip");ZipInputStream zip = new ZipInputStream(fileInputStream);Deployment deployment = repositoryService.createDeployment().addZipInputStream(zip).name("流程部署测试zip")//zip中存在xml和jpg文件,存入数据库会自动解压生成两条数据.deploy();System.out.println(deployment.getName());}

之前:

之后:


可以看到zip文件上传,压缩文件被解压缩了。
/*** 查询流程部署*/@Testpublic void getDeployments(){List<Deployment> list =repositoryService.createDeploymentQuery().list();for (Deployment deployment :list){System.out.println("Id:"+deployment.getId());System.out.println("Name:"+deployment.getName());System.out.println("Time:"+deployment.getDeploymentTime());System.out.println("Key:"+deployment.getKey());}}


全部代码:
package com.test.activiti;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;import java.io.InputStream;
import java.util.List;
import java.util.zip.ZipInputStream;@SpringBootTest
public class Part1_Deployment {@Autowiredprivate RepositoryService repositoryService;/*** 通过bpmn部署流程*/@Testpublic void initDeploymentBPMN() {String filename = "BPMN/diagram.bpmn"; //resources 目录下//String pngname = "BPMN/bpmn.PNG";Deployment deployment = repositoryService.createDeployment().addClasspathResource(filename)//.addClasspathResource(pngname)//图片 原来需要上传bpmnxml文件的同时上传一张相应行的图片用来展现.name("流程部署测试BPMN") //如果数据库遇到多个相同文件,会在之后追加_V2.deploy();System.out.println(deployment.getName());}/*** 通过zip部署流程*/@Testpublic void initDemploymentZIP(){InputStream fileInputStream = this.getClass().getClassLoader().getResourceAsStream("BPMN/bpmn.zip");ZipInputStream zip = new ZipInputStream(fileInputStream);Deployment deployment = repositoryService.createDeployment().addZipInputStream(zip).name("流程部署测试zip")//zip中存在xml和jpg文件,存入数据库会自动解压生成两条数据.deploy();System.out.println(deployment.getName());}/*** 查询流程部署*/@Testpublic void getDeployments(){List list =repositoryService.createDeploymentQuery().list();for (Deployment deployment :list){System.out.println("Id:"+deployment.getId());System.out.println("Name:"+deployment.getName());System.out.println("Time:"+deployment.getDeploymentTime());System.out.println("Key:"+deployment.getKey());}}
}
3.6.2 流程定义 ProcessDefinition
流程部署
Deployment:添加资源文件、获取部署信息、部署时间
流程定义
ProcessDefinition:获取版本号、key、资源名称、部署ID等
key不变 就是同一个流程,多加v2 v3的版本
package com.test.activiti;import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.ProcessDefinition;
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 Part2_ProcessDefinition {@Autowiredprivate RepositoryService repositoryService;/*** 查询流程定义*/@Testpublic void getDefinitions(){// ProcessDefinition 是 org.activiti.engine.repository.ProcessDefinition 这个类List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().list();for (ProcessDefinition pd: list){System.out.println("---------流程定义-----------");System.out.println("Name:"+pd.getName());System.out.println("Key:"+pd.getKey());System.out.println("ResourceName:"+pd.getResourceName());System.out.println("DeploymentId:"+pd.getDeploymentId());System.out.println("Version:"+pd.getVersion());System.out.println("===========================");}}@Testpublic void delDefinition(){String pdID = "af658828-c940-11ed-9559-005056c00008";repositoryService.deleteDeployment(pdID,true); //true 删除下列 所有的历史, false会保留历史任务,一般真实情况下都是false要留痕迹System.out.println("删除流程定义成功!");}
}
查询结果:

要删除的:

删除成功

删除后结果



3.6.3 流程实例 ProcessInstance
流程定义 ProcessDefinition 与 流程实例 ProcessInstance 是一对多关系, 一个流程定义能变成多个流程实例
理解为:行动计划与具体行动的关系。
流程定义 ProcessDefinition 相当于活动方案
流程实例 ProcessInstance 每一次执行这个方案的实际行动
package com.test.activiti;import org.activiti.engine.RuntimeService;
import org.activiti.engine.runtime.ProcessInstance;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest
public class Part3_ProcessInstance {@Autowiredprivate RuntimeService runtimeService;//初始化流程实例列表@Testpublic void initProcessInstance(){//1、获取页面表单填报的内容,请假时间,请假事由,String fromData//2、fromData 写入业务表,返回业务表主键ID==businessKey//3、把业务数据与Acitivi7流程数据关联//4、可以执行多次该方法ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myProcess_claim","claim");System.out.println("流程实例ID:"+processInstance.getProcessInstanceId());}
}
注意:ProcessInstance 使用 org.activiti.engine.runtime.ProcessInstance 包

运行时流程执行实例


数据库 增加
act_ru_execution 两条,因为是两个节点,(不包括结束节点)
act_ru_identitylink 运行市里人员

//获取流程实例列表@Testpublic void getProcessInstance(){List<ProcessInstance> list = runtimeService.createProcessInstanceQuery().list();for (ProcessInstance pi: list) {System.out.println("--------流程实例--------");System.out.println("ProcessInstanceId:"+pi.getProcessInstanceId());//流程实例id uuidSystem.out.println("ProcessDefinitionId:"+pi.getProcessDefinitionId()); //流程定义id:processDefinitionKey + 版本号 + uuidSystem.out.println("执行结束isEnded:"+pi.isEnded());System.out.println("是否挂起流程实例isSuspended:"+pi.isSuspended());}}

//暂停流程实例@Testpublic void stopProcessInstances(){runtimeService.suspendProcessInstanceById("8f229d56-ca09-11ed-8386-005056c00008");//流程实例idSystem.out.println("挂起流程实例");}


//激活流程实例@Testpublic void activitieProcessInstances(){runtimeService.activateProcessInstanceById("8f229d56-ca09-11ed-8386-005056c00008");//流程实例idSystem.out.println("激活流程实例");}


//删除流程实例@Testpublic void delProcessInstances(){runtimeService.deleteProcessInstance("8f229d56-ca09-11ed-8386-005056c00008","删除原因:删除测试");//流程实例id 删除原因deleteReasonSystem.out.println("删除流程实例");}



package com.test.activiti;import org.activiti.engine.RuntimeService;
import org.activiti.engine.runtime.ProcessInstance;
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 Part3_ProcessInstance {@Autowiredprivate RuntimeService runtimeService;//初始化流程实例列表@Testpublic void initProcessInstance(){//1、获取页面表单填报的内容,请假时间,请假事由,String fromData//2、fromData 写入业务表,返回业务表主键ID==businessKey//3、把业务数据与Acitivi7流程数据关联ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myProcess_claim","claim");System.out.println("流程实例ID:"+processInstance.getProcessInstanceId());}//获取流程实例列表@Testpublic void getProcessInstance(){List<ProcessInstance> list = runtimeService.createProcessInstanceQuery().list();for (ProcessInstance pi: list) {System.out.println("--------流程实例--------");System.out.println("ProcessInstanceId:"+pi.getProcessInstanceId());//流程实例id uuidSystem.out.println("ProcessDefinitionId:"+pi.getProcessDefinitionId()); //流程定义id:processDefinitionKey + 版本号 + uuidSystem.out.println("执行结束isEnded:"+pi.isEnded());System.out.println("是否挂起流程实例isSuspended:"+pi.isSuspended());}}//暂停流程实例@Testpublic void stopProcessInstances(){runtimeService.suspendProcessInstanceById("8f229d56-ca09-11ed-8386-005056c00008");//流程实例idSystem.out.println("挂起流程实例");}//激活流程实例@Testpublic void activitieProcessInstances(){runtimeService.activateProcessInstanceById("8f229d56-ca09-11ed-8386-005056c00008");//流程实例idSystem.out.println("激活流程实例");}//删除流程实例@Testpublic void delProcessInstances(){runtimeService.deleteProcessInstance("8f229d56-ca09-11ed-8386-005056c00008","删除原因:删除测试");//流程实例id 删除原因deleteReasonSystem.out.println("删除流程实例");}
}
3.6.4 任务处理Task
用户任务属性面板
Assignee: 执行人、代理人
Candidate Users: 候选人(多个用户)
Candidate Groups: 候选组
Due Date: 任务到期时间
新设计工作流:

涉及的表:
运行时
ACT_RU_TASK运行时任务节点
先部署事件:




package com.test.activiti;import org.activiti.engine.TaskService;
import org.activiti.engine.task.Task;
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 Part4_Task {@Autowiredprivate TaskService taskService;//任务查询@Testpublic void getTasks(){List<Task> list = taskService.createTaskQuery().list();for (Task tk : list){System.out.println("Id:"+tk.getId());System.out.println("Name:"+tk.getName());System.out.println("执行人Assignee:"+tk.getAssignee());}}
}


//查询我的待办任务@Testpublic void getTasksByAssignee(){List<Task> list = taskService.createTaskQuery().taskAssignee("bajie") //或者填wukong.list();for (Task tk : list){System.out.println("Id:"+tk.getId());System.out.println("Name:"+tk.getName());System.out.println("执行人Assignee:"+tk.getAssignee());}}



//执行任务@Testpublic void completeTask(){ //848df66b-ca1a-11ed-b383-005056c00008 候选人任务taskService.complete("848df66b-ca1a-11ed-b383-005056c00008");//d74d1e61-ca16-11ed-9636-005056c00008System.out.println("完成任务");}



















//拾取任务@Testpublic void claimTask(){
// //查询候选人
// List list = taskService.createTaskQuery()
// .taskCandidateUser("bajie") //Activiti6 之前可以 Activiti7不行
// .list();Task task = taskService.createTaskQuery().taskId("848df66b-ca1a-11ed-b383-005056c00008").singleResult();taskService.claim("848df66b-ca1a-11ed-b383-005056c00008","bajie");//任务id taskId 用户id userId}
//归还任务@Testpublic void setTaskAssignee(){Task task = taskService.createTaskQuery().taskId("848df66b-ca1a-11ed-b383-005056c00008").singleResult();//taskService.setAssignee("848df66b-ca1a-11ed-b383-005056c00008","null"); //归还后候选任务taskService.setAssignee("848df66b-ca1a-11ed-b383-005056c00008","wukong");//交办 交给已知执行人}
全部代码:
package com.test.activiti;import org.activiti.engine.TaskService;
import org.activiti.engine.task.Task;
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 Part4_Task {@Autowiredprivate TaskService taskService;//任务查询@Testpublic void getTasks(){List<Task> list = taskService.createTaskQuery().list();for (Task tk : list){System.out.println("Id:"+tk.getId());System.out.println("Name:"+tk.getName());System.out.println("执行人Assignee:"+tk.getAssignee());}}//查询我的待办任务@Testpublic void getTasksByAssignee(){List<Task> list = taskService.createTaskQuery().taskAssignee("wukong").list();for (Task tk : list){System.out.println("Id:"+tk.getId());System.out.println("Name:"+tk.getName());System.out.println("执行人Assignee:"+tk.getAssignee());}}//执行任务@Testpublic void completeTask(){ //848df66b-ca1a-11ed-b383-005056c00008 候选人任务taskService.complete("848df66b-ca1a-11ed-b383-005056c00008");//d74d1e61-ca16-11ed-9636-005056c00008System.out.println("完成任务");}//拾取任务@Testpublic void claimTask(){
// //查询候选人
// List list = taskService.createTaskQuery()
// .taskCandidateUser("bajie") //Activiti6 之前可以 Activiti7不行
// .list();Task task = taskService.createTaskQuery().taskId("848df66b-ca1a-11ed-b383-005056c00008").singleResult();taskService.claim("848df66b-ca1a-11ed-b383-005056c00008","bajie");}//归还任务@Testpublic void setTaskAssignee(){Task task = taskService.createTaskQuery().taskId("848df66b-ca1a-11ed-b383-005056c00008").singleResult();//taskService.setAssignee("848df66b-ca1a-11ed-b383-005056c00008","null"); //归还后候选任务taskService.setAssignee("848df66b-ca1a-11ed-b383-005056c00008","wukong");//交办 交给已知执行人}
}
3.6.5 历史任务 HistoricTaskInstance
- 历史综合信息:HistoricTaskInstance
- 历史变量:HistoriVariableInstance
A. 历史综合信息 HistoricTaskInstance
package com.test.activiti;import org.activiti.engine.HistoryService;
import org.activiti.engine.history.HistoricTaskInstance;
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 Part5_HistoricTaskInstance {@Autowiredprivate HistoryService historyService;//根据用户名查询历史记录@Testpublic void HistoricTaskInstanceByUser(){List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery().orderByHistoricTaskInstanceEndTime().asc()//正序 desc 倒叙.taskAssignee("bajie").list();for (HistoricTaskInstance hi : list){System.out.println("Id:"+hi.getId());System.out.println("ProcessInstanceId:"+hi.getProcessInstanceId());System.out.println("Name:"+hi.getName());}}
}

B. 历史变量:HistoriVariableInstance
//根据流程实例ID查询历史@Testpublic void HistoricTaskInstanceByProcessInstance(){List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery().orderByHistoricTaskInstanceEndTime().asc()//正序 desc 倒叙.processInstanceId("84891467-ca1a-11ed-b383-005056c00008") //审核人任务 这里所有的id在真实场景下都应该是变量.list();for (HistoricTaskInstance hi : list){System.out.println("Id:"+hi.getId());System.out.println("ProcessInstanceId:"+hi.getProcessInstanceId());System.out.println("Name:"+hi.getName());}}

历史节点

历史流程实例

历史人物实例

历史流程人员:每一个人物环节操作,执行人谁?


3.7 UEL表达式
EL表达式语言(Expression Language)
UEL统一表达式语言(Unified Expression Languange) 使用activiti时,动态赋值用的
- 表达式以
${开始,以}结束,例如${day > 100} - 支持逻辑运算
${userName == "bajie" and pwd == "123 } - 支持变量与实体类赋值
- **使用场景:**对执行流程变量赋值,但不要反复赋值(可能存在BUG)。
- 流程实例启动环节可以赋值,任务完成环节。
对应activiti数据表
act_ru_variable 运行时参数表
act_hi_varinst 历史参数表 varinst参数实例缩写
| UEL表达式的保留字 | ||
|---|---|---|
| and | eq | gt |
| instanceof | div | or |
| le | false | empty |
| not | Lt | ge |
| UEL表达式的运算符 | |||
|---|---|---|---|
| 运算符 | 功能 | 示例 | 结果 |
| + | 加 | ${1 + 1 } | 2 |
| - | div | ${1 - 1 } | 0 |
| * | 乘 | ${2 * 2 } | 4 |
| /或div | 除 | 2/1或{2 div 1} | 2 |
| /或div | 除 | 2/0或{2 div 0} | Infinity |
| % 或 mod | 求余 | {3%2}或{3mod2} | 1 |
| % 或 mod | 求余 | {3%0}或{3mod0} | 异常 java.lang.ArithmenticException:/by zero |




package com.test.activiti;import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.runtime.ProcessInstance;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.util.HashMap;
import java.util.Map;@SpringBootTest
public class Part6_UEL {@Autowiredprivate RuntimeService runtimeService;@Autowiredprivate TaskService taskService;//任务 流程实例启动环节 任务完成 任意时刻//启动流程实例带参数,执行执行人@Testpublic void initProcessInstanceWithArgs() {//1、获取页面表单填报的内容,请假时间,请假事由,String fromData//2、fromData 写入业务表,返回业务表主键ID==businessKey//3、把业务数据与Acitivi7流程数据关联//4、流程变量Map<String,Object> variables = new HashMap<String,Object>();variables.put("ZhiXingRen","wukong");ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myProcess_claim","claim",variables);System.out.println("流程实例ID:"+processInstance.getProcessInstanceId());}//完成任务带参数@Testpublic void completeTaskWItchArgs(){Map<String,Object> variables = new HashMap<String,Object>();variables.put("pay","101");taskService.complete("",null);System.out.println("完成任务");}//启动流程实例带参数,使用实体类 //npmn 必须是小写@Testpublic void initProcessInstanceWitchClassArgs(){UEL_POJO uel_pojo = new UEL_POJO();uel_pojo.setZhixingren("bajie");Map<String,Object> variables = new HashMap<String,Object>();variables.put("uelpojo",uel_pojo);ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myProcess_claim","bKey002",variables);System.out.println("流程实例ID:"+processInstance.getProcessInstanceId());}//任务完成环节带参数,指定多个候选人@Testpublic void initProcessInstanceWithCandiDateArgs(){Map<String,Object> variables = new HashMap<String,Object>();variables.put("houxuanren","wukong,tangseng");taskService.complete("",null);System.out.println("完成任务");}//直接指定流程变量@Testpublic void otherArgs(){runtimeService.setVariable("","pay","101");
// runtimeService.setVariables();
// runtimeService.setVariable();
// runtimeService.setVariables();}//局部变量@Testpublic void otherLocalArgs(){runtimeService.setVariableLocal("","pay","101");// runtimeService.setVariables();
// runtimeService.setVariable();
// runtimeService.setVariables();}
}




流程部署:
/*** 通过bpmn部署流程*/@Testpublic void initDeploymentBPMN2() {String filename = "BPMN/Part6_UEL_V2.bpmn"; //resources 目录下Part6_UEL_V2.bpmn 文件Deployment deployment = repositoryService.createDeployment().addClasspathResource(filename).name("流程Part6_UEL_V2.bpmn部署") //如果数据库遇到多个相同文件,会在之后追加_V2.deploy();System.out.println(deployment.getName());}

初始化实例:
//初始化流程实例列表@Testpublic void initProcessInstance(){//1、获取页面表单填报的内容,请假时间,请假事由,String fromData//2、fromData 写入业务表,返回业务表主键ID==businessKey//3、把业务数据与Acitivi7流程数据关联ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myProcess_1uel_V2","claim"); // 流程实例的key processDefinitionKey, 业务表单关联 businessKey (暂时没用)System.out.println("流程实例ID:"+processInstance.getProcessInstanceId());}

bajie存在任务:
//查询我的待办任务@Testpublic void getTasksByAssignee(){List<Task> list = taskService.createTaskQuery().taskAssignee("bajie").list();for (Task tk : list){System.out.println("Id:"+tk.getId());System.out.println("Name:"+tk.getName());System.out.println("执行人Assignee:"+tk.getAssignee());}}

没有tangseng的任务:
//查询我的待办任务@Testpublic void getTasksByAssignee(){List<Task> list = taskService.createTaskQuery().taskAssignee("tangseng").list();for (Task tk : list){System.out.println("Id:"+tk.getId());System.out.println("Name:"+tk.getName());System.out.println("执行人Assignee:"+tk.getAssignee());}}

完成任务,并将pay赋值 101:
//完成任务带参数@Testpublic void completeTaskWItchArgs(){Map<String,Object> variables = new HashMap<String,Object>();variables.put("pay","101");taskService.complete("e8e412fa-ebe6-11ed-be61-005056c00008",variables);System.out.println("完成任务");}

此时唐僧存在任务了:
//查询我的待办任务@Testpublic void getTasksByAssignee(){List<Task> list = taskService.createTaskQuery().taskAssignee("tangseng").list();for (Task tk : list){System.out.println("Id:"+tk.getId());System.out.println("Name:"+tk.getName());System.out.println("执行人Assignee:"+tk.getAssignee());}}

声明实体类:
package com.test.activiti;import lombok.Data;
import java.io.Serializable;@Data //lombok
public class UEL_POJO implements Serializable {private String zhixingren;private String pay;}
//直接指定流程变量 全局变量@Testpublic void otherArgs(){runtimeService.setVariable("","pay","101");//流程实例id,参数,参数值
// runtimeService.setVariables();//可设置多个参数
// taskService.setVariable();
// runtimeService.setVariables();// 可赋值多个}//局部变量 当前环节设置有用@Testpublic void otherLocalArgs(){runtimeService.setVariableLocal("","pay","101");//流程实例id,参数,参数值// runtimeService.setVariables();
// runtimeService.setVariable();
// runtimeService.setVariables();}
3.8 BPMN 2.0 网关

事件,事件大于等于两个
并行网关

/*** 通过bpmn部署流程*/@Testpublic void initDeploymentBPMN2() {String filename = "BPMN/Part7_1_Parallel.bpmn"; //resources 目录下Deployment deployment = repositoryService.createDeployment().addClasspathResource(filename).name("流程Part7_1_Parallel.bpmn部署") //如果数据库遇到多个相同文件,会在之后追加_V2.deploy();System.out.println(deployment.getName());}

//启动流程实例@Testpublic void initProcessInstanceWitchArgs(){ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myProcess_Parallel");System.out.println("流程示例ID"+processInstance.getProcessDefinitionId());}6b7330e6-ed49-11ed-a19e-005056c0000839887e29-ed4b-11ed-8e48-005056c00008


/*** 通过bpmn部署流程*/@Testpublic void initDeploymentBPMN2() {String filename = "BPMN/Part7_2_Exclusive.bpmn"; //resources 目录下Deployment deployment = repositoryService.createDeployment().addClasspathResource(filename).name("流程Part7_2_Exclusive.bpmn部署") //如果数据库遇到多个相同文件,会在之后追加_V2.deploy();System.out.println(deployment.getName());}
//启动流程实例@Testpublic void initProcessInstanceWitchArgs(){ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myProcess_Exclusive");System.out.println("流程示例ID"+processInstance.getProcessDefinitionId());}8e0074f2-ed4e-11ed-b766-005056c00008

包含网关:多个结果

/*** 通过bpmn部署流程*/@Testpublic void initDeploymentBPMN2() {String filename = "BPMN/Part7_4_Inclusive.bpmn"; //resources 目录下Deployment deployment = repositoryService.createDeployment().addClasspathResource(filename).name("流程Part7_4_Inclusive.bpmn部署") //如果数据库遇到多个相同文件,会在之后追加_V2.deploy();System.out.println(deployment.getName());}

//启动流程实例@Testpublic void initProcessInstanceWitchArgs(){ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myProcess_Inclusive");System.out.println("流程示例ID"+processInstance.getProcessDefinitionId());}633cd72a-ed63-11ed-8848-005056c0000897a4f9d6-ed63-11ed-8d87-005056c0000897a8f17a-ed63-11ed-8d87-005056c00008

//查询我的待办任务@Testpublic void getTasksByAssignee(){List<Task> list = taskService.createTaskQuery().taskAssignee("bajie").list();for (Task tk : list){System.out.println("Id:"+tk.getId());System.out.println("Name:"+tk.getName());System.out.println("执行人Assignee:"+tk.getAssignee());}}

//完成任务带参数@Testpublic void completeTaskWItchArgs(){Map<String,Object> variables = new HashMap<String,Object>();variables.put("day","1");taskService.complete("97a8f17a-ed63-11ed-8d87-005056c00008",variables);System.out.println("完成任务");}

//任务查询@Testpublic void getTasks(){List<Task> list = taskService.createTaskQuery().list();for (Task tk : list){System.out.println("Id:"+tk.getId());System.out.println("Name:"+tk.getName());System.out.println("执行人Assignee:"+tk.getAssignee());}}

@Testpublic void completeTask2(){//沙僧包含任务:a7958645-ed64-11ed-9b87-005056c00008//悟空包含任务:a7958647-ed64-11ed-9b87-005056c00008taskService.complete("a7958645-ed64-11ed-9b87-005056c00008");taskService.complete("a7958647-ed64-11ed-9b87-005056c00008");System.out.println("完成任务");}


3.9 Activiti 新特性
/** Copyright 2010-2020 Alfresco Software, Ltd.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at** http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/
package com.test.activiti;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;import java.util.Collection;@Component
public class SecurityUtil {//登陆类 借助spring securityprivate Logger logger = LoggerFactory.getLogger(SecurityUtil.class);@Autowiredprivate UserDetailsService userDetailsService;public void logInAs(String username) { //用username去登录UserDetails user = userDetailsService.loadUserByUsername(username);if (user == null) {throw new IllegalStateException("User " + username + " doesn't exist, please provide a valid user");}logger.info("> Logged in as: " + username);SecurityContextHolder.setContext(new SecurityContextImpl(new Authentication() {@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return user.getAuthorities();}@Overridepublic Object getCredentials() {return user.getPassword();}@Overridepublic Object getDetails() {return user;}@Overridepublic Object getPrincipal() {return user;}@Overridepublic boolean isAuthenticated() {return true;}@Overridepublic void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {}@Overridepublic String getName() {return user.getUsername();}}));org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(username);}
}
用户创建:(仅用于测试)
/** Copyright 2010-2020 Alfresco Software, Ltd.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at** http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/
package com.test.activiti;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
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 java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;import static java.util.Arrays.asList;//@Configuration
public class DemoApplicationConfiguration {private Logger logger = LoggerFactory.getLogger(DemoApplicationConfiguration.class);//@Beanpublic UserDetailsService myUserDetailsService() {InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();String[][] usersGroupsAndRoles = {{"bob", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},{"bajie", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},//john{"wukong", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},//hannah{"other", "password", "ROLE_ACTIVITI_USER", "GROUP_otherTeam"},{"system", "password", "ROLE_ACTIVITI_USER"},{"admin", "password", "ROLE_ACTIVITI_ADMIN"},};for (String[] user : usersGroupsAndRoles) {List<String> authoritiesStrings = asList(Arrays.copyOfRange(user, 2, user.length));logger.info("> Registering new user: " + user[0] + " with the following Authorities[" + authoritiesStrings + "]");inMemoryUserDetailsManager.createUser(new User(user[0], passwordEncoder().encode(user[1]), //创建用户密码 创建用户 创建项目 创建用户authoritiesStrings.stream().map(s -> new SimpleGrantedAuthority(s)).collect(Collectors.toList())));}return inMemoryUserDetailsManager;}@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}}
写代码:
package com.test.activiti;import org.activiti.api.model.shared.model.VariableInstance;
import org.activiti.api.process.model.ProcessInstance;
import org.activiti.api.process.model.builders.ProcessPayloadBuilder;
import org.activiti.api.process.runtime.ProcessRuntime;
import org.activiti.api.runtime.shared.query.Page;
import org.activiti.api.runtime.shared.query.Pageable;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.core.authority.AuthorityUtils;import java.util.List;@SpringBootTest
public class Part8_ProcessRuntime {@Autowiredprivate ProcessRuntime processRuntime;@Autowiredprivate SecurityUtil securityUtil;}

//获取流程实例@Testpublic void getProcessInstance() {securityUtil.logInAs("bajie");Page<ProcessInstance> processInstancePage = processRuntime.processInstances(Pageable.of(0, 100));//分页查询System.out.println("流程实例数量:" + processInstancePage.getTotalItems());List<ProcessInstance> list = processInstancePage.getContent();for (ProcessInstance pi : list) {System.out.println("Id:" + pi.getId());System.out.println("Name:" + pi.getName());System.out.println("StartDate:" + pi.getStartDate());System.out.println("Status:" + pi.getStatus());System.out.println("ProcessDefinitionId:" + pi.getProcessDefinitionId()); //流程定义IdSystem.out.println("ProcessDefinitionKey:" + pi.getProcessDefinitionKey());//流程定义key}}
7 engine下,5和6都是api下




//启动流程实例@Testpublic void startProcessInstance() {securityUtil.logInAs("bajie");ProcessInstance processInstance = processRuntime.start(ProcessPayloadBuilder.start().withProcessDefinitionKey("myProcess_ProcessRuntime").withName("第一个流程实例名称")//.withVariable("","").withBusinessKey("自定义Key").build());}

//获取流程实例@Testpublic void getProcessInstance() {securityUtil.logInAs("bajie");Page<ProcessInstance> processInstancePage = processRuntime.processInstances(Pageable.of(0, 100));//分页查询System.out.println("流程实例数量:" + processInstancePage.getTotalItems());List<ProcessInstance> list = processInstancePage.getContent();for (ProcessInstance pi : list) {System.out.println("Id:" + pi.getId());System.out.println("Name:" + pi.getName());System.out.println("StartDate:" + pi.getStartDate());System.out.println("Status:" + pi.getStatus());System.out.println("ProcessDefinitionId:" + pi.getProcessDefinitionId()); //流程定义IdSystem.out.println("ProcessDefinitionKey:" + pi.getProcessDefinitionKey());//流程定义key}}

//删除流程实例@Testpublic void delProcessINstance() {securityUtil.logInAs("bajie");ProcessInstance processInstance = processRuntime.delete(ProcessPayloadBuilder.delete().withProcessInstanceId("31a677d2-ed6b-11ed-b88c-005056c00008").build());}


//挂起流程示例@Testpublic void suspendProcessInstance() {securityUtil.logInAs("bajie");ProcessInstance processInstance = processRuntime.suspend(ProcessPayloadBuilder.suspend().withProcessInstanceId("129663bb-ebe4-11ed-afeb-005056c00008").build());}
之前



//激活流程实例@Testpublic void resumeProcessInstance() {securityUtil.logInAs("bajie");ProcessInstance processInstance = processRuntime.resume(ProcessPayloadBuilder.resume().withProcessInstanceId("129663bb-ebe4-11ed-afeb-005056c00008").build());}


//流程实例参数@Testpublic void getVariable(){securityUtil.logInAs("bajie");List<VariableInstance> list = processRuntime.variables(ProcessPayloadBuilder.variables().withProcessInstanceId("a").build());for (VariableInstance vi : list){System.out.println("-------------");System.out.println("Name:"+vi.getName());System.out.println("Value:"+vi.getValue());System.out.println("TaskId:"+vi.getTaskId());System.out.println("ProcessInstance:"+vi.getProcessInstanceId());}}
排他网关:


TaskRuntime
package com.test.activiti;import org.activiti.api.runtime.shared.query.Page;
import org.activiti.api.runtime.shared.query.Pageable;
import org.activiti.api.task.model.Task;
import org.activiti.api.task.model.builders.TaskPayloadBuilder;
import org.activiti.api.task.runtime.TaskRuntime;
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 Part9_TaskRuntime {@Autowiredprivate TaskRuntime taskRuntime;@Autowiredprivate SecurityUtil securityUtil;//获取当前登录用户任务@Testpublic void getTask() {securityUtil.logInAs("bajie"); //真实情况下没有Page<Task> tasks = taskRuntime.tasks(Pageable.of(0, 100));List<Task> list = tasks.getContent();for (Task tk : list) {System.out.println("Id:" + tk.getId());System.out.println("Name:" + tk.getName());System.out.println("Status:" + tk.getStatus());System.out.println("CreatedDate:" + tk.getCreatedDate());if (tk.getAssignee() == null) {//候选人为当前登录用户,null的时候需要前端拾取System.out.println("Assignee: 待拾取任务");} else {System.out.println("Assignee:" + tk.getAssignee());}}}
}/*
* taskRuntime.tasks()源码
*/public Page<Task> tasks(Pageable pageable) {String authenticatedUserId = this.securityManager.getAuthenticatedUserId(); //用到了spring securityif (authenticatedUserId != null && !authenticatedUserId.isEmpty()) {List<String> userGroups = this.userGroupManager.getUserGroups(authenticatedUserId);return this.tasks(pageable, TaskPayloadBuilder.tasks().withAssignee(authenticatedUserId).withGroups(userGroups).build());} else {throw new IllegalStateException("You need an authenticated user to perform a task query");}}


//完成任务@Testpublic void completeTask() {securityUtil.logInAs("bajie"); //真实情况下没有Task task = taskRuntime.task("3afd8512-ebe3-11ed-92b6-005056c00008");if (task.getAssignee() == null) {taskRuntime.claim(TaskPayloadBuilder.claim().withTaskId(task.getId()).build());}taskRuntime.complete(TaskPayloadBuilder.complete().withTaskId(task.getId()).build());System.out.println("任务执行完成");}


3.10 Spring Security
- 认证
- 鉴权、授权
用户三种来源
- application.properties 配置用户
- 代码中配置内存用户
- 从数据库中加载用户
知识点
- 内存登陆改为数据库登陆
- SpringSecurity 配置文件详解
- 登录相应处理方案
package com.test.activiti.security;import com.test.activiti.mapper.UserInfoBeanMapper;
import com.test.activiti.pojo.UserInfoBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;@Component //变成一个组件
public class MyUserDetailsService implements UserDetailsService {@Autowiredprivate UserInfoBeanMapper userInfoBeanMapper;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// //写死
// String password = passwordEncoder().encode("111");
//
// return new User(username
// ,password
// ,AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_ACTIVITI_USER")); //给用户增加角色
////读取数据库判断用户//如果用户是null抛出异常//返回用户信息UserInfoBean userInfoBean = userInfoBeanMapper.selectByUserName(username);if (userInfoBean == null) {throw new UsernameNotFoundException("数据库中无此用户");}return userInfoBean;}@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}
}
创建实体类:
package com.test.activiti.pojo;import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;import java.util.Arrays;
import java.util.Collection;
import java.util.stream.Collectors;@Component
public class UserInfoBean implements UserDetails {private Long id;private String name;private String username;private String password;private String address;private String roles;public String getAddress(){return address;}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return Arrays.stream(roles.split(",")).map(s -> new SimpleGrantedAuthority(s)).collect(Collectors.toList());}@Overridepublic String getPassword() {return password;}@Overridepublic String getUsername() {return username;}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}
}
mapper:
package com.test.activiti.mapper;import com.test.activiti.pojo.UserInfoBean;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Component;@Mapper
@Component
public interface UserInfoBeanMapper {@Select("select * from user where username = #{username}")UserInfoBean selectByUserName(@Param("username")String username);
}
这里因为之前tomcat用了80端口,因此此项目启动用8081端口启用:

package com.test.activiti.security;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;@Configuration
public class ActivitiSecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate LoginSuccessHandler loginSuccessHandler;@Autowiredprivate LoginFailHandler loginFailHandler;@Overrideprotected void configure(HttpSecurity httpSecurity) throws Exception{httpSecurity.formLogin()//登陆方法.loginPage("/login").loginProcessingUrl("/login") //登陆验证之后的处理//登陆后.successHandler(loginSuccessHandler).failureHandler(loginFailHandler).and().authorizeRequests().anyRequest().permitAll().and().logout().permitAll().and().csrf().disable().headers().frameOptions().disable();}
}
登陆成功处理类:
package com.test.activiti.security;import com.fasterxml.jackson.databind.ObjectMapper;
import com.test.activiti.util.AjaxResponse;
import com.test.activiti.util.GlobalConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;@Component("loginSuccessHandler")
public class LoginSuccessHandler implements AuthenticationSuccessHandler {@Autowiredprivate ObjectMapper objectMapper;@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException {AuthenticationSuccessHandler.super.onAuthenticationSuccess(request, response, chain, authentication);}@Override //后台从页面拿到什么值 HttpServletRequest 后台向页面输出什么值 HttpServletResponsepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {response.setContentType("application/json;charset=UTF-8");response.getWriter().write("登陆成功LoginSuccessHandler");
// response.getWriter().write("获取登录名:"+authentication.getName());// response.getWriter().write(objectMapper.writeValueAsString(AjaxResponse.AjaxData(0,"成功",authentication.getName()))
// );response.getWriter().write(objectMapper.writeValueAsString(AjaxResponse.AjaxData(GlobalConfig.ResponseCode.SUCCESS.getCode(),GlobalConfig.ResponseCode.SUCCESS.getDesc(),authentication.getName())));}
}
这里需要使用接口测试工具来进行测试:


测试登录失败:
package com.test.activiti.security;import com.fasterxml.jackson.databind.ObjectMapper;
import com.test.activiti.util.AjaxResponse;
import com.test.activiti.util.GlobalConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;@Component
public class LoginFailHandler implements AuthenticationFailureHandler {@Autowiredprivate ObjectMapper objectMapper;@Overridepublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {response.setStatus(500);response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());response.setContentType("application/json;charset=UTF-8");
// response.getWriter().write("登陆失败,原因是:"+ exception.getMessage());response.getWriter().write(objectMapper.writeValueAsString(AjaxResponse.AjaxData(GlobalConfig.ResponseCode.ERROR.getCode(),GlobalConfig.ResponseCode.ERROR.getDesc(),"登陆失败,原因是:"+ exception.getMessage())));}
}

3.11 BPMN.js
a. 下载BPMN.js
bpmn:https://bpmn.io/


bpmn download:https://bpmn.io/toolkit/bpmn-js/download/
点击 Explore Examples


i18n可以根据需要修改语言配置
properties-panel可以创建BPMN.js的控制面板

b. 安装Node.js
- 下载地址https://nodejs.org/en/download/
- Node.js安装指导
- 运行:
npm installnpm run dev





可自定义配置

本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
