业务流程。
举个例子:
假设有一个在线博客平台,我们要让一篇新的文章从作者的头脑里发表出来。整个过程可以分为以下几个步骤:
创建文章草稿
:作者登录博客平台,点击“写新文章”的按钮,开始写文章。这篇文章的状态是“草稿”,还没有发布。
编辑和审阅
:作者写完后,可以选择把文章给编辑团队看。编辑们会读文章,提供建议和修改意见。文章还是“草稿”状态。
修改文章
:作者按照编辑的意见进行修改,并再次提交给编辑。这个过程可能需要多轮修改,直到编辑满意为止。
审稿
:编辑确认文章没有问题后,会将文章交给审稿人员。审稿人员会仔细检查文章内容,确保没有错误或问题。
发布文章
:经过审稿人员的确认,文章会被标记为“已发布”,并在博客平台上公开展示给读者们。
这样,一篇文章就从作者的构思,经过编辑和审稿,最终发布到博客平台上了。这个过程就是一个简单的工作流。
在没有专门的工作流引擎之前,我们之前为了实现流程控制,通常的做法就是采用状态字段的值来跟踪流程的变化情况。这样不用角色的用户,通过状态字段的取值来决定记录是否显示。
这是一种最为原始的方式。通过状态字段虽然做到了流程控制,但是当我们的流程发生变更的时候,这种方式所编写的代码也要进行调整。比如:提交一个审批,一个小组长跟一个小职员的流程是不一样的。那逻辑就复杂了。
Activiti是一个工作流引擎, activiti可以将业务系统中复杂的业务流程抽取出来,使用专门的建模语言BPMN2.0进行定义,业务流程按照预先定义的流程进行执行,实现了系统的流程由activiti进行管理,减少业务系统由于流程变更进行系统升级改造的工作量,从而提高系统的健壮性,同时也减少了系统开发维护成本。
官方网站:
Open Source Business Automation | Activiti
BPM(Business Process Management),即业务流程管理,是一种规范化的构造端到端的业务流程,以持续的提高组织业务效率。
BPMN是一种用来描述业务流程的图形化表示方法。它可以帮助企业和组织更好地理解、分析和改进自己的业务流程。
BPMN 是目前被各 BPM 厂商广泛接受的 BPM 标准。Activiti 就是使用 BPMN 2.0 进行流程建模、流程执行管理,它包括很多的建模符号,比如:
Event
用一个圆圈表示、它是流程在运行中发生的事件
活动用圆角矩形表示,一个流程由一个活动或多个活动组成
Activiti是一个工作流引擎(其实就是一堆jar包API),业务系统访问(操作)activiti的接口,就可以方便的操作流程相关数据,这样就可以把工作流环境与业务系统的环境集成在一起。
使用activiti流程建模工具(activity-designer)定义业务流程(.bpmn文件) 。
.bpmn文件就是业务流程定义文件,通过xml定义业务流程。
activiti部署业务流程定义(.bpmn文件)。
使用activiti提供的api把流程定义内容存储起来,在Activiti执行过程中可以查询定义的内容
Activiti执行把流程定义内容存储在数据库中
流程实例也叫:ProcessInstance
启动一个流程实例表示开始一次业务流程的运行。
在员工请假流程定义部署完成后,如果张三要请假就可以启动一个流程实例,如果李四要请假也启动一个流程实例,两个流程的执行互相不影响。
因为现在系统的业务流程已经交给activiti管理,通过activiti就可以查询当前流程执行到哪了,当前用户需要办理什么任务了,这些activiti帮我们管理了,而不需要开发人员自己编写在sql语句查询。
用户查询待办任务后,就可以办理某个任务,如果这个任务办理完成还需要其它用户办理,比如采购单创建后由部门经理审核,这个过程也是由activiti帮我们完成了。
当任务办理完成没有下一个任务结点了,这个流程实例就完成了。
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<slf4j.version>1.6.6</slf4j.version>
<log4j.version>1.2.12</log4j.version>
<activiti.version>7.0.0.Beta1</activiti.version>
</properties>
<dependencies>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-engine</artifactId>
<version>${activiti.version}</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring</artifactId>
<version>${activiti.version}</version>
</dependency>
<!-- bpmn 模型处理 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-model</artifactId>
<version>${activiti.version}</version>
</dependency>
<!-- bpmn 转换 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-converter</artifactId>
<version>${activiti.version}</version>
</dependency>
<!-- bpmn json数据转换 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-json-converter</artifactId>
<version>${activiti.version}</version>
</dependency>
<!-- bpmn 布局 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-layout</artifactId>
<version>${activiti.version}</version>
</dependency>
<!-- activiti 云支持 -->
<dependency>
<groupId>org.activiti.cloud</groupId>
<artifactId>activiti-cloud-services-api</artifactId>
<version>${activiti.version}</version>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.40</version>
</dependency>
<!-- mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<!-- 链接池 -->
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
</dependencies>
spring:
datasource:
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/contex
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 这里可以使用 链接池 dbcp-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql:///activiti-demo" />
<property name="username" value="root" />
<property name="password" value="root" />
<property name="maxActive" value="3" />
<property name="maxIdle" value="1" />
</bean>
<bean id="processEngineConfiguration"
class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<!-- 引用数据源 上面已经设置好了-->
<property name="dataSource" ref="dataSource" />
<!-- activiti数据库表处理策略 -->
<property name="databaseSchemaUpdate" value="true"/>
</bean>
</beans>
package cn.swj.activiti;
import cn.swj.activity.ActivitiDemoApplication;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
/**
* @Author swj
* @Date 2023/7/20 11:03
* @Description: TODO
* @Version 1.0
*/
@SpringBootTest(classes = ActivitiDemoApplication.class)
@RunWith(SpringRunner.class)
public class ActivitiDemoTest {
/**
* 生成 activiti的数据库表
*/
@Test
public void testCreateDbTable() {
//使用classpath下的activiti.cfg.xml中的配置创建processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
System.out.println(processEngine);
}
}
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] | 运行时变量表 |
@Autowired
private RepositoryService repositoryService;
仓储服务可以用来部署我们的流程图,还可以创建我们的流程部署查询对象,用于查询刚刚部署的流程列表,便于我们的管理流程,方法如下。
主要操作:流程部署、流程定义
@Autowired
private RepositoryService repositoryService;
/**
* 部署流程部署*
*/
@Test
public void createDeployment() {
//1、使用RepositoryService进行部署
Deployment deployment = repositoryService.createDeployment()
.addClasspathResource("bpmn/test.bpmn20.xml") // 添加bpmn资源
.name("出差申请流程")
.deploy();
log.info("DeploymentId ---------- {}",deployment.getId());
log.info("DeploymentName ---------- {}",deployment.getName());
}
/**
* 查询所有流程部署*
*/
@Test
public void list() {
DeploymentQuery deploymentQuery = repositoryService.createDeploymentQuery();
List<Deployment> list = deploymentQuery.list();
list.forEach(System.out::println);
}
/**
* 根据名字查询流程部署*
*/
@Test
public void listByName() {
DeploymentQuery deploymentQuery = repositoryService.createDeploymentQuery();
deploymentQuery.deploymentNameLike("%出差%");
List<Deployment> list = deploymentQuery.list();
list.forEach(System.out::println);
}
/**
* 删除流程部署*
*/
@Test
public void delete() {
//repositoryService.deleteDeployment("15001");
repositoryService.deleteDeployment("15001",true);
}
/**
* 查询所有流程定义*
*/
@Test
public void queryProcessDefinition() {
List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().list();
list.forEach(System.out::println);
}
/**
* 根据流程部署id查询所有流程定义*
*/
@Test
public void queryProcessDefinitionByDeploymentId() {
List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().deploymentId("15001").list();
list.forEach(System.out::println);
}
运?时服务主要?来开启流程实例,?个流程实例对应多个任务,也就是多个流程节点,?如请假审批是?个流程实例,部?主管,部?经理,总经理都是节点,我们开启服务是通过流程定义key或者流程定义id来开启的,?法如下:
主要操作:开启流程实例
@Autowired
private RuntimeService runtimeService;
@Autowired
private RepositoryService repositoryService;
/**
* 启动流程实例,只能根据流程定义id或者流程定义key去启动实例*
*/
@Test
public void startProcessInstance() {
ProcessDefinition def = repositoryService.createProcessDefinitionQuery().deploymentId("15001").singleResult();
ProcessInstance processInstance = runtimeService.startProcessInstanceById(def.getId());
log.info("processInstanceId ------- {}",processInstance.getId());
log.info("deploymentId ------- {}",processInstance.getProcessDefinitionId());
log.info("activityId ------- {}",processInstance.getActivityId());
}
/**
* 查询所有流程实例*
*/
@Test
public void listProcessInstance() {
List<ProcessInstance> processInstanceList = runtimeService.createProcessInstanceQuery().processDefinitionId("test:1:15003").list();
processInstanceList.forEach(System.out::println);
}
/**
* 开启流程实例的时候添加我们自己的业务id*
*/
@Test
public void startWithSetBusinessKey() {
//获取流程定义id
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId("22501").singleResult();
//启动实例
ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinition.getId(), "businessId");
System.out.println(processInstance.getBusinessKey());
}
当我们?仓储服务部署了流程图之后,就会产??个流程部署id,?个流程部署id对应?个流程定义,?个流程定义对应多个流程实例,流程定义和流程实例之间的关系就好?是类和对象的关系。?个流程实例对应多个任务节点。
主要操作: 任务
任务服务是?来可以?来领取,完成,查询任务列表功能的,使??法分别如下:
/**
* //根据任务id和?户领取任务
* taskService.claim(String taskId, String userId)
* //根据任务id完成??节点的任务
* taskService.complete(String taskId)
* //创建任务查询对象之后根据候选?也就是任务处理?查询??的任务列表
* taskService.createTaskQuery().taskAssignee(String assignee)
*/
@Autowired
private TaskService taskService;
/**
* 查询流程实例的所有*
*/
@Test
public void listTask() {
List<Task> taskList = taskService.createTaskQuery().processDefinitionId("test:1:15003").taskAssignee("zhansan").list();
taskList.forEach(System.out::println);
}
/**
* 完成自己的任务*
*/
@Test
public void complete() {
Task task = taskService.createTaskQuery().taskAssignee("zhansan").processDefinitionId("test:1:15003").singleResult();
taskService.complete(task.getId());
}
主要操作:历史信息
历史服务可以查看审批?曾经审批完成了哪些项?,审批项?总共花了多少时间,以及在哪个环节?较耗费时间等等,便于审批?查看历史信息,?法如下。
@Autowired
private HistoryService historyService;
@Test
public void listTaskByAssignee() {
List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery().taskAssignee("zhansan").finished().list();
for (HistoricTaskInstance taskInstance : list) {
System.out.println(taskInstance.getId());
System.out.println(taskInstance.getName());
System.out.println(taskInstance.getProcessDefinitionId());
System.out.println(taskInstance.getProcessInstanceId());
System.out.println(taskInstance.getWorkTimeInMillis());
System.out.println("<==========================>");
}
}
在Activiti中,DynamicBpmnService 是一个用于动态修改和更新流程定义(BPMN模型)的服务。它允许您在流程部署后,通过编程方式对已部署的流程定义进行修改,添加或删除流程元素,而无需重新部署整个流程。
使用 DynamicBpmnService 可以实现以下功能:
修改流程元素:
您可以在运行时通过 DynamicBpmnService 修改已部署的流程的各种元素,如任务、连接线、条件等。这对于需要根据运行时条件调整流程逻辑的场景非常有用。
动态添加任务:
您可以通过 DynamicBpmnService 动态地向流程中添加任务、网关、事件等元素,从而在不停机的情况下扩展或调整流程的功能。
删除任务:
如果需要,您也可以使用 DynamicBpmnService 从已部署的流程定义中删除任务或其他元素,以适应业务变化。
流程优化:
可以使用该服务来进行流程优化,以实现更高效的业务流程。
总之,DynamicBpmnService 提供了一种在流程运行时进行动态流程定义修改的机制,使您能够根据实际需求对已部署的流程进行灵活调整,而无需重新部署整个流程。这对于灵活性和快速响应业务变化是非常有帮助的。需要注意的是,虽然这种动态修改流程的能力非常强大,但也需要小心使用,以确保不会引入意外的问题。
ManagementService 管理和监控引擎运行时状态的服务
在Activiti中,ManagementService 是一个用于管理和监控引擎运行时状态的服务。它提供了一些用于管理和监控流程引擎的操作,使您能够以编程方式执行一些管理任务,而无需直接访问底层的数据库或进行复杂的配置。
ManagementService 提供了一系列方法,用于执行以下操作:
作业管理:
ManagementService 允许您管理作业(jobs),这些作业用于异步执行一些任务,如定时器触发的事件。您可以查询、挂起、恢复、删除和重新执行作业。
流程实例删除:
您可以使用 ManagementService 删除特定的流程实例,不管其当前的状态。
数据库操作:
ManagementService 提供了一些方法来执行数据库操作,如执行原生的SQL查询。
清理历史数据:
您可以使用 ManagementService 执行一些历史数据的清理操作,以便维护数据库性能。
引擎配置:
您可以获取和修改引擎的配置信息,如数据库表前缀等。
执行异步操作:
ManagementService 提供了一些方法来执行一些耗时的操作,如数据的导入和导出。
需要注意的是,ManagementService 提供的操作通常是对引擎运行时状态进行管理和维护的,这些操作可能具有潜在的风险,因此需要小心谨慎地使用。
总之,ManagementService 是用于管理和监控Activiti引擎运行时状态的服务,提供了一些管理操作和功能,使您能够在运行时对引擎进行维护和管理。
创建Activiti工作流主要包含以下几步:
1、定义流程,按照BPMN的规范,使用流程定义工具,用流程符号把整个流程描述出来
2、部署流程,把画好的流程定义文件,加载到数据库中,生成表的数据
3、启动流程,使用java代码来操作数据库表中的内容
活动是工作或任务的一个通用术语。一个活动可以是一个任务,还可以是一个当前流程的子处理流程; 其次,你还可以为活动指定不同的类型。常见活动如下:
网关用来处理决策,有几种常用网关需要了解:
——只有一条路径会被选择。流程执行到该网关时,按照输出流的顺序逐个计算,当条件的计算结果为true时,继续执行当前网关的输出流;
如果多条线路计算结果都是 true,则会执行第一个值为 true 的线路。如果所有网关计算结果没有true,则引擎会抛出异常。
排他网关需要和条件顺序流结合使用,default 属性指定默认顺序流,当所有的条件不满足时会执行默认顺序流。
并行网关 (+)
——所有路径会被同时选择
拆分 —— 并行执行所有输出顺序流,为每一条顺序流创建一个并行执行线路。
合并 —— 所有从并行网关拆分并执行完成的线路均在此等候,直到所有的线路都执行完成才继续向下执行。
包容网关 (+)
—— 可以同时执行多条线路,也可以在网关上设置条件
拆分 —— 计算每条线路上的表达式,当表达式计算结果为true时,创建一个并行线路并继续执行
合并 —— 所有从并行网关拆分并执行完成的线路均在此等候,直到所有的线路都执行完成才继续向下执行。
事件网关 (+)
—— 专门为中间捕获事件设置的,允许设置多个输出流指向多个不同的中间捕获事件。当流程执行到事件网关后,流程处于等待状态,需要等待抛出事件才能将等待状态转换为活动状态。
流向 Flow
流是连接两个流程节点的连线。常见的流向包含以下几种:
安装activiti BPM visualizer
先画一个流程文件
画出来这个
//部署流程定义
//1、创建ProcessEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2、得到RepositoryService实例
RepositoryService repositoryService = processEngine.getRepositoryService();
//3、使用RepositoryService进行部署
Deployment deployment = repositoryService.createDeployment()
.addClasspathResource("bpmn/test.bpmn20.xml") // 添加bpmn资源
.name("出差申请流程")
.deploy();
//4、输出部署信息
System.out.println("流程部署id:" + deployment.getId());
System.out.println("流程部署名称:" + deployment.getName());
流程定义部署在activiti后就可以通过工作流管理业务流程了,也就是说上边部署的出差申请流程可以使用了。
针对该流程,启动一个流程表示发起一个新的出差申请单,这就相当于java类与java对象的关系,类定义好后需要new创建一个对象使用,当然可以new多个对象。对于请出差申请流程,张三发起一个出差申请单需要启动一个流程实例,出差申请单发起一个出差单也需要启动一个流程实例。
代码如下:
/**
* 启动流程实例
*/
@Test
public void testStartProcess(){
//1、创建ProcessEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2、获取RunTimeService
RuntimeService runtimeService = processEngine.getRuntimeService();
//3、根据流程定义Id启动流程
ProcessInstance processInstance = runtimeService
.startProcessInstanceByKey("test");
//输出内容
System.out.println("流程定义id:" + processInstance.getProcessDefinitionId());
System.out.println("流程实例id:" + processInstance.getId());
System.out.println("当前活动Id:" + processInstance.getActivityId());
}
流程启动后,任务的负责人就可以查询自己当前需要处理的任务,查询出来的任务都是该用户的待办任务。
/**
* 查询当前个人待执行的任务
*/
@Test
public void testFindPersonalTaskList() {
// 任务负责人
String assignee = "zhangsan";
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 创建TaskService
TaskService taskService = processEngine.getTaskService();
// 根据流程key 和 任务负责人 查询任务
List<Task> list = taskService.createTaskQuery()
.processDefinitionKey("test") //流程Key
.taskAssignee(assignee)//只查询该任务负责人的任务
.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 completTask(){
// 获取引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取taskService
TaskService taskService = processEngine.getTaskService();
// 根据流程key 和 任务的负责人 查询任务
// 返回一个任务对象
Task task = taskService.createTaskQuery()
.processDefinitionKey("test") //流程Key
.taskAssignee("lishi") //要查询的负责人
.singleResult();
// 完成任务,参数:任务id
taskService.complete(task.getId());
}
查询流程相关信息,包含流程定义,流程部署,流程定义版本
/**
* 查询流程定义
*/
@Test
public void queryProcessDefinition(){
// 获取引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// repositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
// 得到ProcessDefinitionQuery 对象
ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
// 查询出当前所有的流程定义
// 条件:processDefinitionKey =evection
// orderByProcessDefinitionVersion 按照版本排序
// desc倒叙
// list 返回集合
List<ProcessDefinition> definitionList = processDefinitionQuery.processDefinitionKey("test")
.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 = "1";
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 通过流程引擎获取repositoryService
RepositoryService repositoryService = processEngine
.getRepositoryService();
//删除流程定义,如果该流程定义已有流程实例启动则删除时出错
repositoryService.deleteDeployment(deploymentId,true);
//设置true 级联删除流程定义,即使该流程有流程实例启动也可以删除,设置为false非级别删除方式,如果流程
//repositoryService.deleteDeployment(deploymentId, true);
}
说明:
1) 使用repositoryService删除流程定义,历史表信息不会被删除
2) 如果该流程定义下没有正在运行的流程,则可以用普通删除。
如果该流程定义下存在已经运行的流程,使用普通删除报错,可用级联删除方法将流程及相关记录全部删除。
先删除没有完成流程节点,最后就可以完全删除流程定义信息
项目开发中级联删除操作一般只开放给超级管理员使用.
//下载文件
@Test
public void queryBpmnFile() throws IOException {
// 1、得到引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2、获取repositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
// 3、得到查询器:ProcessDefinitionQuery,设置查询条件,得到想要的流程定义
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.processDefinitionKey("myEvection")
.singleResult();
// 4、通过流程定义信息,得到部署ID
String deploymentId = processDefinition.getDeploymentId();
// 5、通过repositoryService的方法,实现读取图片信息和bpmn信息
// png图片的流
InputStream pngInput = repositoryService.getResourceAsStream(deploymentId, processDefinition.getDiagramResourceName());
// bpmn文件的流
InputStream bpmnInput = repositoryService.getResourceAsStream(deploymentId, processDefinition.getResourceName());
// 6、构造OutputStream流
File file_png = new File("d:/evectionflow01.png");
File file_bpmn = new File("d:/evectionflow01.bpmn");
FileOutputStream bpmnOut = new FileOutputStream(file_bpmn);
FileOutputStream pngOut = new FileOutputStream(file_png);
// 7、输入流,输出流的转换
IOUtils.copy(pngInput,pngOut);
IOUtils.copy(bpmnInput,bpmnOut);
// 8、关闭流
pngOut.close();
bpmnOut.close();
pngInput.close();
bpmnInput.close();
}
即使流程定义已经删除了,流程执行的历史信息通过前面的分析,依然保存在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表,条件:根据 查询
instanceQuery.processDefinitionId("test:1:3");
// 增加排序操作,orderByHistoricActivityInstanceStartTime 根据开始时间排序 asc 升序
instanceQuery.orderByHistoricActivityInstanceStartTime().asc();
// 查询所有内容
List<HistoricActivityInstance> 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提供的数据是针对于该框架所需要的流程控制维护的数据,也就是数据库25张表存放的数据,但是在业务系统中,业务数据如何与Activiti框架进?关联?通过流程实例数据的BusinessKey字段来实现关联。在创建流程实例时,指明BusinessKey即可。
示例代码:
/**
* 开启流程实例的时候添加我们自己的业务id*
*/
@Test
public void startWithSetBusinessKey() {
//获取流程定义id
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId("22501").singleResult();
//启动实例
ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinition.getId(), "businessId");
System.out.println(processInstance.getBusinessKey());
}
当因为业务需要,将执?的流程挂起,或者将已被挂起的流程激活,可以通过Activiti框架,对流程进?挂起和激活。
@Test
public void processInstanceIsSuspended() {
//获取流程定义id
ProcessDefinition def = repositoryService.createProcessDefinitionQuery().deploymentId("22501").singleResult();
//1、获取流程实例
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processDefinitionId(def.getId()).singleResult();
boolean suspended = processInstance.isSuspended();
//如果激活
if(!suspended) {
//挂起实例
runtimeService.suspendProcessInstanceById(processInstance.getId());
}
//如果挂起
if(suspended) {
//激活实例
runtimeService.activateProcessInstanceById(processInstance.getId());
}
}
1、在画流程图的时候需要在assignee那边写一个插值表达式
${assginee0}
2、在创建实例的时候new一个Map在值存到里面并将map作为值传入创建实例的方法中
示例代码:
@Test
public void createProcessInstanceWithCustomAssignee() {
//1、获取流程定义id
ProcessDefinition def = repositoryService.createProcessDefinitionQuery().deploymentId("30001").singleResult();
//2、新建一个流程实例
Map<String, Object> map = new HashMap<>();
map.put("assignee0","zhangsan");
map.put("assignee1","lishi");
map.put("assignee2","wangwu");
ProcessInstance processInstance = runtimeService.startProcessInstanceById(def.getId(), map);
System.out.println(processInstance.getId());
}
一样使用插值表达式
@Test
public void createProcessInstanceWithCustomAssignee() {
//1、获取流程定义id
ProcessDefinition def = repositoryService.createProcessDefinitionQuery().deploymentId("30001").singleResult();
//2、新建一个流程实例
Map<String, Object> map = new HashMap<>();
map.put("assignee0","zhangsan");
map.put("assignee1","lishi");
map.put("assignee2","wangwu");
map.put("days","3");
ProcessInstance processInstance = runtimeService.startProcessInstanceById(def.getId(), map);
System.out.println(processInstance.getId());
}
有的变量只是在某个节点会使用到这样子就可以直接使用局部变量
@Test
public void localVariable() {
taskService.setVariableLocal("123","day",3);
taskService.complete("123");
}
如果任务负责?是单?存在的,且任务负责?因为默写原因没有办法完成任务,那么流程就没办法执?下去了。此时可以为任务设置候选?,通过候选?拾取、执?、归还等操作,完成组任务相关的操作
任务候选?
创建任务候选?,允许?个任务可以被多个负责?领取。通过流程图或者程序来指明任务的候选?。查询候选?查询任务的代码如下:
List<Task> list = taskService.createTaskQuery()
.processDefinitionId("defId")
.taskCandidateOrAssigned("候选人或者责任人")
.list();
任务候选?先拾取任务,才能执?任务
Task task = taskService.createTaskQuery()
.processDefinitionId("defId")
.taskCandidateUser("候选人")
.singleResult();
taskService.claim(task.getId(),"候选人");
候选?拾取任务后,在没执?任务之前,可以归还任务
Task task = taskService.createTaskQuery()
.processDefinitionId("defId")
.taskCandidateUser("候选人")
.singleResult();
taskService.setAssignee(task.getId(),null);
任务候选?拾取完任务后,可以选择归还任务,也可以选择把任务转交给其他候选?。
Task task = taskService.createTaskQuery()
.processDefinitionId("defId")
.taskCandidateUser("候选人")
.singleResult();
taskService.setAssignee(task.getId(),"otherUser");
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.3.9.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.14</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<activiti.version>7.0.0.Beta1</activiti.version>
</properties>
<dependencies>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
<version>${activiti.version}</version>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.40</version>
</dependency>
<!-- mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<!-- 链接池 -->
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
</dependencies>
数据库连接信息根据自己的信息进行修改
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/contex
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 这里可以使用 链接池 dbcp-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql:///activiti-demo" />
<property name="username" value="root" />
<property name="password" value="root" />
<property name="maxActive" value="3" />
<property name="maxIdle" value="1" />
</bean>
<bean id="processEngineConfiguration"
class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<!-- 引用数据源 上面已经设置好了-->
<property name="dataSource" ref="dataSource" />
<!-- activiti数据库表处理策略 -->
<property name="databaseSchemaUpdate" value="true"/>
</bean>
</beans>
spring:
datasource:
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
activiti:
database-schema-update: true
db-history-used: true
history-level: full
package cn.swj.activity.config;
import org.activiti.engine.*;
import org.activiti.engine.test.ActivitiRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Author swj
* @Date 2023/8/5 16:22
* @Description: TODO
* @Version 1.0
*/
@Configuration
public class ActivitiConfig {
//创建ProcessEngine
private ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
@Bean
public RepositoryService repositoryService() {
//1、得到RepositoryService实例
RepositoryService repositoryService = processEngine.getRepositoryService();
return repositoryService;
}
@Bean
public RuntimeService runtimeService() {
//1、得到 RuntimeService 实例
RuntimeService runtimeService = processEngine.getRuntimeService();
return runtimeService;
}
@Bean
public TaskService taskService() {
//1、得到 TaskService 实例
TaskService taskService = processEngine.getTaskService();
return taskService;
}
@Bean
public HistoryService historyService() {
//1、得到 TaskService 实例
HistoryService historyService = processEngine.getHistoryService();
return historyService;
}
@Bean
public DynamicBpmnService dynamicBpmnService() {
//1、得到 DynamicBpmnService 实例
DynamicBpmnService dynamicBpmnService = processEngine.getDynamicBpmnService();
return dynamicBpmnService;
}
@Bean
public ManagementService managementService() {
//1、得到 ManagementService 实例
ManagementService managementService = processEngine.getManagementService();
return managementService;
}
}
在resource\processes下新建一个流程图
<?xml version="1.0" encoding="UTF-8"?>
<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="leave" name="leave" isExecutable="true">
<startEvent id="sid-b2417e62-b70b-4f06-90b4-4a9c453e7ebc"/>
<endEvent id="sid-a25bc48b-1191-413d-a0ab-263c2d27793b"/>
<userTask id="sid-37861cb6-80d2-45dc-9b50-375ad320a01f" name="员工提交请假申请" activiti:assignee="${assignee0}"/>
<userTask id="sid-b6df96ad-44df-4295-bbe6-c74ab4cf7f7e" name="上级领导审批" activiti:assignee="${assignee1}"/>
<userTask id="sid-204ae414-37c9-47e2-a84b-bd34dcb7463a" name="人事记录" activiti:assignee="${assignee2}"/>
<sequenceFlow id="sid-effa22e4-daaa-4508-9628-7906e9bfd274" sourceRef="sid-b2417e62-b70b-4f06-90b4-4a9c453e7ebc" targetRef="sid-37861cb6-80d2-45dc-9b50-375ad320a01f"/>
<sequenceFlow id="sid-2a2c5aa6-2f52-4963-9ca4-f63412cd8b67" sourceRef="sid-37861cb6-80d2-45dc-9b50-375ad320a01f" targetRef="sid-b6df96ad-44df-4295-bbe6-c74ab4cf7f7e"/>
<sequenceFlow id="sid-24db9d32-2541-41a3-84e4-1228f1b1e806" sourceRef="sid-b6df96ad-44df-4295-bbe6-c74ab4cf7f7e" targetRef="sid-204ae414-37c9-47e2-a84b-bd34dcb7463a"/>
<sequenceFlow id="sid-fda019e5-a786-441a-b226-22993b36c82a" sourceRef="sid-204ae414-37c9-47e2-a84b-bd34dcb7463a" targetRef="sid-a25bc48b-1191-413d-a0ab-263c2d27793b"/>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_leave">
<bpmndi:BPMNPlane bpmnElement="leave" id="BPMNPlane_leave">
<bpmndi:BPMNShape id="shape-27b24655-a3d8-4cb6-b1e2-d94f9382dfc9" bpmnElement="sid-b2417e62-b70b-4f06-90b4-4a9c453e7ebc">
<omgdc:Bounds x="-70.0" y="-145.0" width="30.0" height="30.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="shape-f86c2c60-9f01-4430-943e-ea5232d724df" bpmnElement="sid-a25bc48b-1191-413d-a0ab-263c2d27793b">
<omgdc:Bounds x="-75.0" y="200.0" width="30.0" height="30.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="shape-ac9635c3-16cb-42e3-a5dc-ed54f96a2d47" bpmnElement="sid-37861cb6-80d2-45dc-9b50-375ad320a01f">
<omgdc:Bounds x="-110.0" y="-95.0" width="100.0" height="80.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="sid-b8b9d748-c04f-4624-b13b-5ad6367d5f84" bpmnElement="sid-b6df96ad-44df-4295-bbe6-c74ab4cf7f7e">
<omgdc:Bounds x="-110.0" y="0.95951843" width="100.0" height="80.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="sid-9a68376e-da4c-464a-be4e-c2d48feaf397" bpmnElement="sid-204ae414-37c9-47e2-a84b-bd34dcb7463a">
<omgdc:Bounds x="-110.0" y="94.34161" width="100.0" height="80.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="edge-3dc5ee67-24c6-475f-9fb9-dbffa94892f7" bpmnElement="sid-effa22e4-daaa-4508-9628-7906e9bfd274">
<omgdi:waypoint x="-62.5" y="-115.0"/>
<omgdi:waypoint x="-60.0" y="-95.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="edge-ddc9236c-49bd-410b-b40b-a99899d08eef" bpmnElement="sid-2a2c5aa6-2f52-4963-9ca4-f63412cd8b67">
<omgdi:waypoint x="-60.0" y="-15.0"/>
<omgdi:waypoint x="-60.0" y="0.95951843"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="edge-002614a7-62e7-4307-b60e-855b2863b828" bpmnElement="sid-24db9d32-2541-41a3-84e4-1228f1b1e806">
<omgdi:waypoint x="-60.0" y="80.95952"/>
<omgdi:waypoint x="-60.0" y="94.34161"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="edge-afc8140f-3ffc-403e-9bbb-b62819df498b" bpmnElement="sid-fda019e5-a786-441a-b226-22993b36c82a">
<omgdi:waypoint x="-60.0" y="174.34161"/>
<omgdi:waypoint x="-60.0" y="200.0"/>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
package cn.swj.activiti;
import cn.hutool.core.util.ObjectUtil;
import cn.swj.activity.ActivitiDemoApplication;
import lombok.extern.slf4j.Slf4j;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.HashMap;
/**
* @Author swj
* @Date 2023/8/6 16:12
* @Description: TODO
* @Version 1.0
*/
@Slf4j
@SpringBootTest(classes = ActivitiDemoApplication.class)
@RunWith(SpringRunner.class)
public class SubmitLeaveTest {
@Autowired
private RepositoryService repositoryService;
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
/**
* 1、员工提交请假申请
* 2、上级领导审批
* 3、人事记录
*/
/**
* 员工提交请假申请*
*/
@Test
public void submitLeave() {
//1、创建流程实例
//初始化流程里面的负责人信息
HashMap<String, Object> map = new HashMap<>();
map.put("assignee0", "zhangsan");
map.put("assignee1", "lishi");
map.put("assignee2", "wangwu");
//创建实例
ProcessDefinition def = repositoryService.createProcessDefinitionQuery()
.deploymentId("d0d0f7c2-3439-11ee-83a8-cec84ba31bc7")
.singleResult();
ProcessInstance processInstance = runtimeService.startProcessInstanceById(def.getId(), map);
//2、完成张三任务
Task task = taskService.createTaskQuery().deploymentId("d0d0f7c2-3439-11ee-83a8-cec84ba31bc7").taskAssignee("zhangsan").singleResult();
if (ObjectUtil.isEmpty(task)) {
return;
}
taskService.complete(task.getId());
log.info("张三请假申请已经提交~");
//3、完成李四的任务
//查询李四的任务
Task lishiTask = taskService.createTaskQuery().deploymentId("d0d0f7c2-3439-11ee-83a8-cec84ba31bc7").taskAssignee("lishi").singleResult();
if(ObjectUtil.isEmpty(lishiTask)) {
return;
}
log.info("查询李四的任务: {}", lishiTask.getName());
//完成
taskService.complete(lishiTask.getId());
//4、完成王五的任务
//查询王五的任务
Task wangwuTask = taskService.createTaskQuery().deploymentId("d0d0f7c2-3439-11ee-83a8-cec84ba31bc7").taskAssignee("wangwu").singleResult();
//完成
if(ObjectUtil.isEmpty(wangwuTask)) {
return;
}
log.info("查询王五的任务: {}", wangwuTask.getName());
taskService.complete(wangwuTask.getId());
log.info("流程结束~");
}
}