org.activiti
activiti-dependencies
7.0.0.Beta1
import
pom
activiti运行需要有数据库的支持,支持的数据库有:h2, mysql, oracle, postgres, mssql, db2。
注意新版idea已经不支持,自己百度下载
搜索到actiBPM插件,它就是Activiti Designer的IDEA版本,我们点击Install安装
首先需要在 java 工程中加入 ProcessEngine 所需要的 jar 包,包括:
1) activiti-engine-7.0.0.beta1.jar2) activiti 依赖的 jar 包: mybatis、 alf4j、 log4j 等
3) activiti 依赖的 spring 包
4) mysql数据库驱动
5) 第三方数据连接池 dbcp6) 单元测试 Junit-4.12.jar
1.6.6
1.2.12
7.0.0.Beta1
org.activiti
activiti-engine
${activiti.version}
org.activiti
activiti-spring
${activiti.version}
org.activiti
activiti-bpmn-model
${activiti.version}
org.activiti
activiti-bpmn-converter
${activiti.version}
org.activiti
activiti-json-converter
${activiti.version}
org.activiti
activiti-bpmn-layout
${activiti.version}
org.activiti.cloud
activiti-cloud-services-api
${activiti.version}
mysql
mysql-connector-java
5.1.40
org.mybatis
mybatis
3.4.5
commons-dbcp
commons-dbcp
1.4
junit
junit
4.12
log4j
log4j
${log4j.version}
org.slf4j
slf4j-api
${slf4j.version}
org.slf4j
slf4j-log4j12
${slf4j.version}
3) 添加log4j日志配置
我们使用log4j日志包,可以对日志进行配置
在resources 下创建log4j.properties
# Set root category priority to INFO and its only appender to CONSOLE.
#log4j.rootCategory=INFO, CONSOLE debug info warn error fatal
log4j.rootCategory=debug, CONSOLE, LOGFILE
# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r[%15.15t] %-5p %30.30c %x - %m\n
# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=f:\act\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
我们使用activiti提供的默认方式来创建mysql的表。
默认方式的要求是在 resources 下创建 activiti.cfg.xml 文件,
注意:默认方式目录和文件名不能修改,因为activiti的源码中已经设置,到固定的目录读取固定文件名的文件。
默认方式要在在activiti.cfg.xml中bean的名字叫processEngineConfiguration,名字不可修改
在这里有2中配置方式:一种是单独配置数据源,一种是不单独配置数据源
1、直接配置processEngineConfiguration
processEngineConfiguration 用来创建 ProcessEngine,在创建 ProcessEngine 时会执行数据库的操作
/// 表示本地localhost
2、配置数据源后,在processEngineConfiguration 引用
首先配置数据源
创建一个测试类,调用activiti的工具类,生成acitivti需要的数据库表。
直接使用activiti提供的工具类ProcessEngines,会默认读取classpath下的activiti.cfg.xml文件,读取其中的数据库配置,创建 ProcessEngine,在创建ProcessEngine 时会自动创建表。
代码如下:
package com.itheima.activiti01.test;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngineConfiguration;
import org.junit.Test;
public class TestDemo {
/**
* 生成 activiti的数据库表
*/
@Test
public void testCreateDbTable() {
//使用classpath下的activiti.cfg.xml中的配置创建processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
System.out.println(processEngine);
}
}
说明:1、运行以上程序段即可完成 activiti 表创建,通过改变 activiti.cfg.xml 中databaseSchemaUpdate 参数的值执行不同的数据表处理策略。2 、 上 边 的 方法 getDefaultProcessEngine方法在执行时,从activiti.cfg.xml 中找固定的名称 processEngineConfiguration 。
在测试程序执行过程中,idea的控制台会输出日志,说明程序正在创建数据表,类似如下,注意红线内容:
执行完成后我们查看数据库, 创建了 25 张表,结果如下:
到这,我们就完成activiti运行需要的数据库和表的创建。
看到刚才创建的表,我们发现Activiti 的表都以 ACT_ 开头。
第二部分是表示表的用途的两个字母标识。 用途也和服务的 API 对应。
ACT_RE :'RE'表示 repository。 这个前缀的表包含了流程定义和流程静态资源 (图片,规则,等等)。
ACT_RU:'RU'表示 runtime。 这些运行时的表,包含流程实例,任务,变量,异步任务,等运行中的数据。 Activiti 只在流程实例执行过程中保存这些数据, 在流程结束时就会删除这些记录。 这样运行时表可以一直很小速度很快
ACT_HI:'HI'表示 history。 这些表包含历史数据,比如历史流程实例, 变量,任务等等。
ACT_GE : GE 表示 general。 通用数据, 用于不同场景下
表分类 | 表名 | 解释 |
---|---|---|
一般数据 | ||
[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_EVENT_SUBSCR] | 运行时事件 | |
[ACT_RU_EXECUTION] | 运行时流程执行实例 | |
[ACT_RU_IDENTITYLINK] | 运行时用户关系信息,存储任务节点与参与者的相关信息 | |
[ACT_RU_JOB] | 运行时作业 | |
[ACT_RU_TASK] | 运行时任务 | |
[ACT_RU_VARIABLE] |
流程定义部署后操作activiti的3张表如下:
act_re_deployment 流程定义部署表,每部署一次增加一条记录
act_re_procdef 流程定义表,部署每个新的流程定义都会在这张表中增加一条记录
act_re_procdef 表中的 KEY 这个字段是用来唯一识别不同流程的关键字
act_ge_bytearray 流程资源表
注意:
act_re_deployment和act_re_procdef一对多关系,一次部署在流程部署表生成一条记录,但一次部署可以部署多个流程定义,每个流程定义在流程定义表生成一条记录。每一个流程定义在act_ge_bytearray会存在两个资源记录,bpmn和png。
发起一个请假流程
/**
* 启动流程实例
*/
@Test
public void testStartProcess(){
// 1、创建ProcessEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2、获取RunTimeService
RuntimeService runtimeService = processEngine.getRuntimeService();
// 3、根据流程定义Id启动流程
ProcessInstance processInstance = runtimeService
.startProcessInstanceByKey("evection");
// 输出内容
System.out.println("流程定义id:" + processInstance.getProcessDefinitionId());
System.out.println("流程实例id:" + processInstance.getId());
System.out.println("当前活动Id:" + processInstance.getActivityId());
操作数据表
act_hi_actinst 流程实例执行历史(流程节点信息,可以看到流程走到哪里)
act_hi_identitylink 流程的参与用户历史信息
act_hi_procinst 流程实例历史信息
act_hi_taskinst 流程任务历史信息
act_ru_execution 流程执行信息
act_ru_identitylink 流程运行中的参与用户信息
字段:userid 是审批人
act_ru_task 流程运行中的任务信息
任务负责人查询待办任务,选择任务进行处理审批,完成任务。
// 完成任务
@Test
public void completTask(){
// 获取引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取taskService
TaskService taskService = processEngine.getTaskService();
// 根据流程key 和 任务的负责人 查询任务
// 返回一个任务对象
Task task = taskService.createTaskQuery()
.processDefinitionKey("evection") //流程Key
.taskAssignee("审核人") //要查询的负责人
.singleResult();
// 完成任务,参数:任务id
taskService.complete(task.getId());
}
即使流程定义已经删除了,流程执行的历史信息通过前面的分析,依然保存在activiti的act_hi_*相关的表中。所以我们还是可以查询流程执行的历史信息,可以通过HistoryService来查看相关的历史记录。
/**
* 查看历史信息
*/
@Test
public void findHistoryInfo(){
// 获取引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取HistoryService
HistoryService historyService = processEngine.getHistoryService();
// 获取 actinst表的查询对象
HistoricActivityInstanceQuery instanceQuery = historyService.createHistoricActivityInstanceQuery();
// 查询 actinst表,条件:根据 InstanceId 查询
// instanceQuery.processInstanceId("2501");
// 查询 actinst表,条件:根据 DefinitionId 查询
instanceQuery.processDefinitionId("myEvection:1:4");
// 增加排序操作,orderByHistoricActivityInstanceStartTime 根据开始时间排序 asc 升序
instanceQuery.orderByHistoricActivityInstanceStartTime().asc();
// 查询所有内容
List activityInstanceList = instanceQuery.list();
// 输出
for (HistoricActivityInstance hi : activityInstanceList) {
System.out.println(hi.getActivityId());
System.out.println(hi.getActivityName());
System.out.println(hi.getProcessDefinitionId());
System.out.println(hi.getProcessInstanceId());
System.out.println("<==========================>");
}
}
引入Activiti依赖
org.activiti
activiti-spring-boot-starter
7.1.0.M6
mybatis
org.mybatis
添加配置
在application-dev.yml
中添加如下配置
spring:
activiti:
# false:默认,数据库表不变,但是如果版本不对或者缺失表会抛出异常(生产使用)
# true:表不存在,自动创建(开发使用)
# create_drop: 启动时创建,关闭时删除表(测试使用)
# drop_create: 启动时删除表,在创建表 (不需要手动关闭引擎)
database-schema-update: true
#监测历史表是否存在,activities7默认不开启历史表
db-history-used: true
#none:不保存任何历史数据,流程中这是最高效的
#activity:只保存流程实例和流程行为
#audit:除了activity,还保存全部的流程任务以及其属性,audit为history默认值
#full:除了audit、还保存其他全部流程相关的细节数据,包括一些流程参数
history-level: full
#校验流程文件,默认校验resources下的process 文件夹的流程文件
check-process-definitions: true
会自己创建数据表
下载activiti-explorer
官网下载:Get started | Activiti
解压部署
把解压出来的activiti-explorer.war
放在Tomcat的webapps
下
启动Tomcat服务器
访问activiti-explorer
http://localhost:8080/activiti-explorer
默认登录账号: kermit
kermit
请假流程审批绘制
新建
绘制
导出
下载文件
qingjia.bpmn20.xml
下载流程定义图片
单击右键上图图片,图片另存为:qingjia.png
将资源文件放入项目
在resources下新建process资源文件夹
将qingjia.bpmn20.xml与qingjia.png放入process目录
package com.jerry.auth.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;
/**
* ClassName: ProcessTest
* Package: com.jerry.activiti
* Description:
*
* @Author: jerry_jy
* @Create: 2023-03-05 10:51
* @Version: 1.0
*/
@SpringBootTest
public class ProcessTest {
@Autowired
private RepositoryService repositoryService;
// 单个文件的部署
@Test
public void deployProcess() {
Deployment deploy = repositoryService.createDeployment()
.addClasspathResource("process/qingjia.bpmn20.xml")
.addClasspathResource("process/qingjia.png")
.name("请假申请流程")
.deploy();
System.out.println("deploy.getId() = " + deploy.getId());
System.out.println("deploy.getName() = " + deploy.getName());
}
}
package com.jerry.auth.activiti;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
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 ProcessTest1 {
@Autowired
private RepositoryService repositoryService;
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
@Autowired
private HistoryService historyService;
/**
* 启动流程实例,添加businessKey 业务标识
*/
@Test
public void startUpProcessAddBusinessKey(){
// 启动流程实例,指定业务标识businessKey,也就是请假申请单id
ProcessInstance processInstance = runtimeService.
startProcessInstanceByKey("qingjia","1001");
// 输出
System.out.println("业务id:"+processInstance.getBusinessKey()); //1001
System.out.println("processInstance.getId() = " + processInstance.getId()); // 71f6803b-bb19-11ed-a845-005056c00001
}
/**
* 查询流程定义
*/
@Test
public void findProcessDefinitionList(){
List definitionList = repositoryService.createProcessDefinitionQuery()
.orderByProcessDefinitionVersion()
.desc()
.list();
//输出流程定义信息
for (ProcessDefinition processDefinition : definitionList) {
System.out.println("流程定义 id="+processDefinition.getId());
System.out.println("流程定义 name="+processDefinition.getName());
System.out.println("流程定义 key="+processDefinition.getKey());
System.out.println("流程定义 Version="+processDefinition.getVersion());
System.out.println("流程部署ID ="+processDefinition.getDeploymentId());
}
}
/**
* 删除流程定义
*/
@Test
public void deleteDeployment() {
//部署id
String deploymentId = "qingjia:1:c493c327-bb02-11ed-8360-005056c00001";
// //删除流程定义,如果该流程定义已有流程实例启动则删除时出错
// repositoryService.deleteDeployment(deploymentId);
//设置true 级联删除流程定义,即使该流程有流程实例启动也可以删除,设置为false非级别删除方式
repositoryService.deleteDeployment(deploymentId, true);
}
// 查询已经处理的任务
@Test
public void findCompleteTaskList(){
List list = historyService.createHistoricTaskInstanceQuery()
.taskAssignee("zhangsan")
.finished().list();
for (HistoricTaskInstance historicTaskInstance : list) {
System.out.println("流程实例id:" + historicTaskInstance.getProcessInstanceId());
System.out.println("任务id:" + historicTaskInstance.getId());
System.out.println("任务负责人:" + historicTaskInstance.getAssignee());
System.out.println("任务名称:" + historicTaskInstance.getName());
}
}
// 处理当前任务
@Test
public void completeTask(){
// 查询负责人需要处理的任务,返回一条
Task task = taskService.createTaskQuery().taskAssignee("zhangsan").singleResult();
// 完成任务
taskService.complete(task.getId());
}
// 查询个人的代办任务--zhangsan
@Test
public void findTaskList(){
String assign = "zhangsan";
List list = taskService.createTaskQuery()
.taskAssignee(assign).list();
for (Task task : list) {
System.out.println("task.getProcessInstanceId() = " + task.getProcessInstanceId());
System.out.println("任务id:" + task.getId());
System.out.println("任务负责人:" + task.getAssignee());
System.out.println("任务名称:" + task.getName());
}
}
// 启动流程实例
@Test
public void startProcess(){
ProcessInstance processInstance = runtimeService.startProcessInstanceById("qingjia");
System.out.println("processInstance.getProcessDefinitionId() = " + processInstance.getProcessDefinitionId());
System.out.println("processInstance.getId() = " + processInstance.getId());
System.out.println("processInstance.getActivityId() = " + processInstance.getActivityId());
}
//部署
// 单个文件的部署
@Test
public void deployProcess() {
Deployment deploy = repositoryService.createDeployment()
.addClasspathResource("process/qingjia.bpmn20.xml")
.addClasspathResource("process/qingjia.png")
.name("请假申请流程")
.deploy();
System.out.println("deploy.getId() = " + deploy.getId());
System.out.println("deploy.getName() = " + deploy.getName());
}
}
@SpringBootTest
public class ProcessTest1 {
@Autowired
private RepositoryService repositoryService;
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
@Autowired
private HistoryService historyService;
// 单个流程实例挂起
@Test
public void SingleSuspendProcessInstance() {
//流程实例id
String processInstanceId = "71f6803b-bb19-11ed-a845-005056c00001";
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
//获取到当前流程定义是否为暂停状态 suspended方法为true代表为暂停 false就是运行的
boolean suspended = processInstance.isSuspended();
if (suspended) {
runtimeService.activateProcessInstanceById(processInstanceId);
System.out.println("流程实例:" + processInstanceId + "激活");
} else {
runtimeService.suspendProcessInstanceById(processInstanceId);
System.out.println("流程实例:" + processInstanceId + "挂起");
}
}
// 全部流程实例挂起
@Test
public void suspendProcessInstance() {
// 1、获取流程定义对象
ProcessDefinition qingjia = repositoryService.createProcessDefinitionQuery().processDefinitionKey("qingjia").singleResult();
// 2、调用流程定义对象的方法判断当前状态:挂起 激活
boolean suspended = qingjia.isSuspended();
if (suspended) {
// 暂定,那就可以激活
// 参数1:流程定义的id 参数2:是否激活 参数3:时间点
repositoryService.activateProcessDefinitionById(qingjia.getId(), true, null);
System.out.println("流程定义:" + qingjia.getId() + "激活");
} else {
repositoryService.suspendProcessDefinitionById(qingjia.getId(), true, null);
System.out.println("流程定义:" + qingjia.getId() + "挂起");
}
}
}
表达式分配
package com.jerry.auth.activiti;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.ProcessInstance;
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.HashMap;
import java.util.List;
import java.util.Map;
@SpringBootTest
public class ProcessTest2 {
@Autowired
private RepositoryService repositoryService;
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
@Autowired
private HistoryService historyService;
// uel-value
// 部署流程定义
@Test
public void deployProcess() {
Deployment deploy = repositoryService.createDeployment().addClasspathResource("process/jiaban.bpmn20.xml").name("加班申请流程").deploy();
System.out.println("deploy.getId() = " + deploy.getId()); // 5c5519ad-bb1d-11ed-b5c8-005056c00001
System.out.println("deploy.getName() = " + deploy.getName()); // 加班申请流程
}
// 启动流程实例
@Test
public void startProcessInstance() {
Map map = new HashMap<>();
// 设置任务人
map.put("assignee1","tom");
map.put("assignee2","jerry");
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("jiaban", map);
System.out.println("processInstance.getProcessDefinitionId() = " + processInstance.getProcessDefinitionId()); // jiaban:1:5c60d97f-bb1d-11ed-b5c8-005056c00001
System.out.println("processInstance.getId() = " + processInstance.getId()); // 7f720dd9-bb1d-11ed-b6e9-005056c00001
}
// 查询个人的代办任务--tom
@Test
public void findTaskList(){
String assign = "tom";
List list = taskService.createTaskQuery()
.taskAssignee(assign).list();
for (Task task : list) {
System.out.println("task.getProcessInstanceId() = " + task.getProcessInstanceId()); //7f720dd9-bb1d-11ed-b6e9-005056c00001
System.out.println("任务id:" + task.getId()); // 7f759051-bb1d-11ed-b6e9-005056c00001
System.out.println("任务负责人:" + task.getAssignee()); // tom
System.out.println("任务名称:" + task.getName()); // 经理审批
}
}
}
package com.jerry.auth.activiti;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.ProcessInstance;
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.HashMap;
import java.util.List;
import java.util.Map;
@SpringBootTest
public class ProcessTest2 {
@Autowired
private RepositoryService repositoryService;
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
@Autowired
private HistoryService historyService;
// uel-method
// 部署流程定义
@Test
public void deployProcess01() {
Deployment deploy = repositoryService.createDeployment().addClasspathResource("process/jiaban01.bpmn20.xml").name("加班申请流程01").deploy();
System.out.println("deploy.getId() = " + deploy.getId()); // 8c4ac05e-bb20-11ed-8d65-005056c00001
System.out.println("deploy.getName() = " + deploy.getName()); // 加班申请流程01
}
//创建流程实例,会自动调用userBean.getUserName()方法
@Test
public void startProcessInstance01(){
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("jiaban01");
System.out.println("processInstance.getProcessDefinitionId() = " + processInstance.getProcessDefinitionId()); // jiaban01:1:8c56a740-bb20-11ed-8d65-005056c00001
System.out.println("processInstance.getId() = " + processInstance.getId()); // abb9c7c4-bb20-11ed-b608-005056c00001
}
// 查询个人的代办任务--zhangsan
@Test
public void findTaskList01(){
String assign = "zhangsan";
List list = taskService.createTaskQuery()
.taskAssignee(assign).list();
for (Task task : list) {
System.out.println("task.getProcessInstanceId() = " + task.getProcessInstanceId()); // abb9c7c4-bb20-11ed-b608-005056c00001
System.out.println("任务id:" + task.getId()); // abbd4a38-bb20-11ed-b608-005056c00001
System.out.println("任务负责人:" + task.getAssignee()); // LiLei
System.out.println("任务名称:" + task.getName()); // 经理审批
}
}
}
配置监听器
package com.jerry.auth.activiti;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.ProcessInstance;
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.HashMap;
import java.util.List;
import java.util.Map;
@SpringBootTest
public class ProcessTest2 {
@Autowired
private RepositoryService repositoryService;
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
@Autowired
private HistoryService historyService;
///
// 监听器分配任务
// 部署流程定义
@Test
public void deployProcess02() {
Deployment deploy = repositoryService.createDeployment().addClasspathResource("process/jiaban02.bpmn20.xml").name("加班申请流程02").deploy();
System.out.println("deploy.getId() = " + deploy.getId()); // ed080f00-bb41-11ed-a6f2-005056c00001
System.out.println("deploy.getName() = " + deploy.getName()); // 加班申请流程02
}
@Test
public void startProcessInstance02(){
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("jiaban02");
System.out.println("processInstance.getProcessDefinitionId() = " + processInstance.getProcessDefinitionId()); // jiaban02:1:ed150752-bb41-11ed-a6f2-005056c00001
System.out.println("processInstance.getId() = " + processInstance.getId()); // 06eca124-bb42-11ed-9bbc-005056c00001
}
// 查询个人的代办任务--Tim
@Test
public void findTaskList02(){
String assign = "Tim";
List list = taskService.createTaskQuery()
.taskAssignee(assign).list();
for (Task task : list) {
System.out.println("task.getProcessInstanceId() = " + task.getProcessInstanceId()); // 06eca124-bb42-11ed-9bbc-005056c00001
System.out.println("任务id:" + task.getId()); // 06f071b8-bb42-11ed-9bbc-005056c00001
System.out.println("任务负责人:" + task.getAssignee()); // Tim
System.out.println("任务名称:" + task.getName()); // 经理审批
}
}
}
package com.jerry.auth.activiti;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.ProcessInstance;
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.HashMap;
import java.util.List;
import java.util.Map;
/**
* ClassName: ProcessTest3
* Package: com.jerry.auth.activiti
* Description:
*
* @Author: jerry_jy
* @Create: 2023-03-05 19:18
* @Version: 1.0
*/
@SpringBootTest
public class ProcessTest3 {
@Autowired
private RepositoryService repositoryService;
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
// 1、部署流程定义
@Test
public void deployProcess() {
Deployment deploy = repositoryService.createDeployment().addClasspathResource("process/jiaban04.bpmn20.xml").name("加班申请流程04").deploy();
System.out.println("deploy.getId() = " + deploy.getId()); // f204be8a-bb48-11ed-950e-005056c00001
System.out.println("deploy.getName() = " + deploy.getName()); // 加班申请流程04
}
// 1.5、启动流程实例
@Test
public void startProcessInstance() {
// Map map = new HashMap<>();
// 设置任务人
// map.put("assignee1","tom");
// map.put("assignee2","jerry");
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("jiaban04");
System.out.println("processInstance.getProcessDefinitionId() = " + processInstance.getProcessDefinitionId()); // jiaban04:1:f210f38c-bb48-11ed-950e-005056c00001
System.out.println("processInstance.getId() = " + processInstance.getId()); // 428d0c0f-bb49-11ed-83a5-005056c00001
}
// 2、查询组任务
@Test
public void findGroupTaskList(){
List list = taskService.createTaskQuery()
.taskCandidateUser("tom01")
.list();
for (Task task : list) {
System.out.println("----------------------------");
System.out.println("流程实例id:" + task.getProcessInstanceId());
System.out.println("任务id:" + task.getId());
System.out.println("任务负责人:" + task.getAssignee());
System.out.println("任务名称:" + task.getName());
}
}
// 3、分配组任务,拾取任务,让某人去做这个事情
@Test
public void claimTask(){
Task task = taskService.createTaskQuery()
.taskCandidateUser("tom01")
.singleResult();
if (task!=null){
taskService.claim(task.getId(),"tom");
System.out.println("分配任务完成/任务拾取完成");
}
}
// 4、查询个人的代办任务--tom01
@Test
public void findTaskList(){
String assign = "tom01";
List list = taskService.createTaskQuery()
.taskAssignee(assign).list();
for (Task task : list) {
System.out.println("task.getProcessInstanceId() = " + task.getProcessInstanceId()); //7f720dd9-bb1d-11ed-b6e9-005056c00001
System.out.println("任务id:" + task.getId()); // 7f759051-bb1d-11ed-b6e9-005056c00001
System.out.println("任务负责人:" + task.getAssignee()); // tom
System.out.println("任务名称:" + task.getName()); // 经理审批
}
}
// 5、办理个人任务
@Test
public void completeGroupTask() {
Task task = taskService.createTaskQuery()
.taskAssignee("tom01") //要查询的负责人
.singleResult();//返回一条
taskService.complete(task.getId());
}
}
网关用来控制流程的流向,通常会和流程变量一起使用。
排他网关
当你的流程出现这样的场景:请假申请,两天以内,部门经理审批流程就结束了,两天以上需要总经理直接审批,这个时候就需要排他网关
并行网关
当出现这样的场景:请假申请开始,需要部门经理和总经理都审批,两者没有前后需要两个人全部审批才能进入下个节点人事审批。这个时候就需要并行网关
与排他网关的主要区别是,并行网关不会解析条件。 即使顺序流中定义了条件,也会被忽略。
包含网关
包容网关:可以同时执行多条线路,也可以在网关上设置条件,可以看做是排他网关和并行网关的结合体。
当出现这样的场景:请假申请大于等于2天需要由部门总经理审批,小于2天由部门经理审批,请假申请必须经过人事经理审批。这个时候就需要包含网关
package com.jerry.auth.activiti;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.ProcessInstance;
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.HashMap;
import java.util.List;
import java.util.Map;
@SpringBootTest
public class ProcessTestGateway {
@Autowired
private RepositoryService repositoryService;
//注入RuntimeService
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
@Autowired
private HistoryService historyService;
//1 部署流程定义
@Test
public void deployProcess() {
Deployment deployment = repositoryService.createDeployment()
.addClasspathResource("process/qingjia003.bpmn20.xml")
.name("请假申请流程003")
.deploy();
System.out.println(deployment.getId()); // af9242f0-bb4c-11ed-85bf-005056c00001
System.out.println(deployment.getName()); // 请假申请流程002
}
//2 启动流程实例
@Test
public void startProcessInstance() {
Map map = new HashMap<>();
//设置请假天数
map.put("day", "3");
ProcessInstance processInstance =
// runtimeService.startProcessInstanceByKey("qingjia002", map);
runtimeService.startProcessInstanceByKey("qingjia003");
System.out.println(processInstance.getProcessDefinitionId()); // qingjia002:1:afac0c82-bb4c-11ed-85bf-005056c00001
System.out.println(processInstance.getId()); // 90d46e2c-bb4d-11ed-9b92-005056c00001
}
//3 查询个人的代办任务--zhao6
@Test
public void findTaskList() {
// String assign = "zhao6";
// String assign = "gousheng";
// String assign = "xiaocui";
// String assign = "wang5";
// String assign = "gouwa";
String assign = "xiaoli";
List list = taskService.createTaskQuery()
.taskAssignee(assign).list();
for (Task task : list) {
System.out.println("流程实例id:" + task.getProcessInstanceId());
System.out.println("任务id:" + task.getId());
System.out.println("任务负责人:" + task.getAssignee());
System.out.println("任务名称:" + task.getName());
}
}
//完成任务
@Test
public void completeTask() {
Task task = taskService.createTaskQuery()
// .taskAssignee("zhao6") //要查询的负责人
// .taskAssignee("xiaocui") //要查询的负责人
// .taskAssignee("gousheng")
// .taskAssignee("wang5")
.taskAssignee("gouwa")
.singleResult();//返回一条
//完成任务,参数:任务id
taskService.complete(task.getId());
}
}
11、审批管理
11.1、审批设置–CRUD
package com.jerry.process.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.jerry.common.result.Result;
import com.jerry.model.process.ProcessType;
import com.jerry.process.service.OaProcessTypeService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
/**
*
* 审批类型 前端控制器
*
*
* @author jerry
* @since 2023-03-05
*/
@Api(value = "审批类型", tags = "审批类型")
@RestController
@RequestMapping(value = "/admin/process/processType")
public class OaProcessTypeController {
@Autowired
private OaProcessTypeService processTypeService;
@ApiOperation(value = "获取分页列表")
@GetMapping("{page}/{pageSize}")
public Result index(@PathVariable Long page, @PathVariable Long pageSize) {
Page pageInfo = new Page<>(page, pageSize);
Page pageModel = processTypeService.page(pageInfo);
return Result.ok(pageModel);
}
@PreAuthorize("hasAuthority('bnt.processType.list')")
@ApiOperation(value = "获取")
@GetMapping("get/{id}")
public Result get(@PathVariable Long id) {
ProcessType processType = processTypeService.getById(id);
return Result.ok(processType);
}
@PreAuthorize("hasAuthority('bnt.processType.add')")
@ApiOperation(value = "新增")
@PostMapping("save")
public Result save(@RequestBody ProcessType processType) {
processTypeService.save(processType);
return Result.ok();
}
@PreAuthorize("hasAuthority('bnt.processType.update')")
@ApiOperation(value = "修改")
@PutMapping("update")
public Result updateById(@RequestBody ProcessType processType) {
processTypeService.updateById(processType);
return Result.ok();
}
@ApiOperation(value = "删除")
@DeleteMapping("remove/{id}")
public Result remove(@PathVariable Long id) {
processTypeService.removeById(id);
return Result.ok();
}
}
11.2、模板审批–CRUD
package com.jerry.process.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.jerry.common.result.Result;
import com.jerry.model.process.ProcessTemplate;
import com.jerry.process.service.OaProcessTemplateService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
*
* 审批模板 前端控制器
*
*
* @author jerry
* @since 2023-03-05
*/
@Api(value = "审批模板管理", tags = "审批模板管理")
@RestController
@RequestMapping(value = "/admin/process/processTemplate")
public class OaProcessTemplateController {
@Autowired
private OaProcessTemplateService processTemplateService;
// 分页查询审批模板
@ApiOperation("获取分页查询审批模板数据")
@GetMapping("{page}/{pageSize}")
public Result index(@PathVariable Long page, @PathVariable Long pageSize){
Page pageInfo = new Page<>(page, pageSize);
//分页查询审批模板,把审批类型对应名称查询
IPage pageModel =
processTemplateService.selectPageProcessTemplate(pageInfo);
return Result.ok(pageModel);
}
//@PreAuthorize("hasAuthority('bnt.processTemplate.list')")
@ApiOperation(value = "获取")
@GetMapping("get/{id}")
public Result get(@PathVariable Long id) {
ProcessTemplate processTemplate = processTemplateService.getById(id);
return Result.ok(processTemplate);
}
//@PreAuthorize("hasAuthority('bnt.processTemplate.templateSet')")
@ApiOperation(value = "新增")
@PostMapping("save")
public Result save(@RequestBody ProcessTemplate processTemplate) {
processTemplateService.save(processTemplate);
return Result.ok();
}
//@PreAuthorize("hasAuthority('bnt.processTemplate.templateSet')")
@ApiOperation(value = "修改")
@PutMapping("update")
public Result updateById(@RequestBody ProcessTemplate processTemplate) {
processTemplateService.updateById(processTemplate);
return Result.ok();
}
//@PreAuthorize("hasAuthority('bnt.processTemplate.remove')")
@ApiOperation(value = "删除")
@DeleteMapping("remove/{id}")
public Result remove(@PathVariable Long id) {
processTemplateService.removeById(id);
return Result.ok();
}
}
OaProcessTypeController
@ApiOperation(value = "获取全部审批分类")
@GetMapping("findAll")
public Result findAll() {
return Result.ok(processTypeService.list());
}
OaProcessTemplateController
@ApiOperation(value = "上传流程定义")
@PostMapping("/uploadProcessDefinition")
public Result uploadProcessDefinition(MultipartFile file) throws FileNotFoundException {
// 获取classes目录位置
String path = new File(ResourceUtils.getURL("classpath:").getPath()).getAbsolutePath();
// 设置上传文件夹
File tempFile = new File(path + "/processes/");
if (!tempFile.exists()) {
tempFile.mkdirs();
}
// 创建空文件,实现文件写入
String filename = file.getOriginalFilename();
File zipFile = new File(path + "/processes/" + filename);
// 保存文件
try {
file.transferTo(zipFile);
} catch (IOException e) {
return Result.fail();
}
Map map = new HashMap<>();
//根据上传地址后续部署流程定义,文件名称为流程定义的默认key
map.put("processDefinitionPath", "processes/" + filename);
map.put("processDefinitionKey", filename.substring(0, filename.lastIndexOf(".")));
return Result.ok(map);
}
public static void main(String[] args) {
try {
String path = new File(ResourceUtils.getURL("classpath:").getPath()).getAbsolutePath();
System.out.println("path = " + path); //E:\CodeLife\IdeaProject\guigu-oa\guigu-oa-parent\service-oa\target\classes
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
}
整合前端,无后台接口
11.5、审批列表
OaProcessController
package com.jerry.process.controller;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.jerry.common.result.Result;
import com.jerry.model.process.Process;
import com.jerry.process.service.OaProcessService;
import com.jerry.vo.process.ProcessQueryVo;
import com.jerry.vo.process.ProcessVo;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
*
* 审批类型 前端控制器
*
*
* @author jerry
* @since 2023-03-06
*/
@RestController
@RequestMapping(value = "/admin/process")
public class OaProcessController {
@Autowired
private OaProcessService processService;
//审批管理列表
@ApiOperation(value = "获取分页列表")
@GetMapping("{page}/{limit}")
public Result index(@PathVariable Long page,
@PathVariable Long limit,
ProcessQueryVo processQueryVo) {
Page pageInfo = new Page<>(page, limit);
IPage pageModel = processService.selectPage(pageInfo,processQueryVo);
return Result.ok();
}
}
OaProcessService
public interface OaProcessService extends IService {
//审批管理列表
IPage selectPage(Page pageInfo, ProcessQueryVo processQueryVo);
}
OaProcessServiceImpl
//审批管理列表
@Override
public IPage selectPage(Page pageInfo, ProcessQueryVo processQueryVo) {
IPage pageModel = baseMapper.selectPage(pageInfo,processQueryVo);
return pageModel;
}
OaProcessMapper
//审批管理列表
IPage selectPage(Page pageInfo, @Param("vo") ProcessQueryVo processQueryVo);
涉及到4张表的多表查询,自己编写SQL语句
OaProcessMapper.xml
修改mapper的映射路径
页面展示
OaProcessTemplateServiceImpl
// 修改模板的发布状态 status==1 代表已发布
// 流程定义部署
@Override
public void publish(Long id) {
// 修改模板的发布状态 status==1 代表已发布
ProcessTemplate processTemplate = baseMapper.selectById(id);
processTemplate.setStatus(1);
baseMapper.updateById(processTemplate);
// 流程定义部署
if (StringUtils.isEmpty(processTemplate.getProcessDefinitionPath())){
processService.deployByZip(processTemplate.getProcessDefinitionPath());
}
}
}
OaProcessService
// 流程定义部署
void deployByZip(String deployPath);
OaProcessServiceImpl
// 流程定义部署
@Override
public void deployByZip(String deployPath) {
InputStream inputStream= this.getClass().getClassLoader().getResourceAsStream(deployPath);
ZipInputStream zipInputStream = new ZipInputStream(inputStream);
// 部署
Deployment deployment = repositoryService.createDeployment().addZipInputStream(zipInputStream).deploy();
System.out.println("deployment.getId() = " + deployment.getId());
System.out.println("deployment.getName() = " + deployment.getName());
}
Springboot2.0与activiti-explorer5.23.0整合,从0开始构建自己的工作流编辑平台(附件 ***) - 灰信网(软件开发博客聚合)
解压,如图:
在Springboot的项目的resource目录的static文件夹下新建一个activiti-explorer文件夹。(此处根据自己要求是否新建文件夹)
stencilset.json在activiti-webapp-explorer2-5.23.0.war的文件夹下路径:
于是手动编写一个初始化模型数据类如下:
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Model;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
@Controller
@RequestMapping("/models")
public class TestActivitiController {
private static final Logger LOGGER = LoggerFactory.getLogger(TestActivitiController.class);
@Autowired
private RepositoryService repositoryService;
@Autowired
private ObjectMapper objectMapper;
@RequestMapping("/create")
public void newModel(HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException {
try {
// 初始化一个空模型
Model model = repositoryService.newModel();
// 设置一些默认信息
String name = "new-process";
String description = "";
int revision = 1;
String key = "process";
ObjectNode modelNode = objectMapper.createObjectNode();
modelNode.put(ModelDataJsonConstants.MODEL_NAME, name);
modelNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, description);
modelNode.put(ModelDataJsonConstants.MODEL_REVISION, revision);
model.setName(name);
model.setKey(key);
model.setMetaInfo(modelNode.toString());
repositoryService.saveModel(model);
String id = model.getId();
// 完善ModelEditorSource
ObjectNode editorNode = objectMapper.createObjectNode();
editorNode.put("id", "canvas");
editorNode.put("resourceId", "canvas");
ObjectNode stencilSetNode = objectMapper.createObjectNode();
stencilSetNode.put("namespace", "http://b3mn.org/stencilset/bpmn2.0#");
editorNode.put("stencilset", stencilSetNode);
repositoryService.addModelEditorSource(id, editorNode.toString().getBytes("utf-8"));
response.sendRedirect(request.getContextPath() + "/static/modeler.html?modelId=" + id);
} catch (IOException e) {
e.printStackTrace();
LOGGER.info("模型创建失败!");
}
}
}
同时加上Bean的扫描路径注解:
@SpringBootApplication(scanBasePackages = { "org.activiti.rest", "hu.itget" }, exclude = {
org.activiti.spring.boot.SecurityAutoConfiguration.class,
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class })