学习笔记 | Activiti7

发布时间:2024年01月09日

什么是工作流?

业务流程。

举个例子:

假设有一个在线博客平台,我们要让一篇新的文章从作者的头脑里发表出来。整个过程可以分为以下几个步骤:

创建文章草稿

:作者登录博客平台,点击“写新文章”的按钮,开始写文章。这篇文章的状态是“草稿”,还没有发布。

编辑和审阅

:作者写完后,可以选择把文章给编辑团队看。编辑们会读文章,提供建议和修改意见。文章还是“草稿”状态。

修改文章

:作者按照编辑的意见进行修改,并再次提交给编辑。这个过程可能需要多轮修改,直到编辑满意为止。

审稿

:编辑确认文章没有问题后,会将文章交给审稿人员。审稿人员会仔细检查文章内容,确保没有错误或问题。

发布文章

:经过审稿人员的确认,文章会被标记为“已发布”,并在博客平台上公开展示给读者们。

这样,一篇文章就从作者的构思,经过编辑和审稿,最终发布到博客平台上了。这个过程就是一个简单的工作流。

工作流的实现演变

在没有专门的工作流引擎之前,我们之前为了实现流程控制,通常的做法就是采用状态字段的值来跟踪流程的变化情况。这样不用角色的用户,通过状态字段的取值来决定记录是否显示。

这是一种最为原始的方式。通过状态字段虽然做到了流程控制,但是当我们的流程发生变更的时候,这种方式所编写的代码也要进行调整。比如:提交一个审批,一个小组长跟一个小职员的流程是不一样的。那逻辑就复杂了。

Activiti7概述

Activiti是一个工作流引擎, activiti可以将业务系统中复杂的业务流程抽取出来,使用专门的建模语言BPMN2.0进行定义,业务流程按照预先定义的流程进行执行,实现了系统的流程由activiti进行管理,减少业务系统由于流程变更进行系统升级改造的工作量,从而提高系统的健壮性,同时也减少了系统开发维护成本。

官方网站:

Open Source Business Automation | Activiti

BPM

BPM(Business Process Management),即业务流程管理,是一种规范化的构造端到端的业务流程,以持续的提高组织业务效率。

BPMN

BPMN是一种用来描述业务流程的图形化表示方法。它可以帮助企业和组织更好地理解、分析和改进自己的业务流程。

BPMN 是目前被各 BPM 厂商广泛接受的 BPM 标准。Activiti 就是使用 BPMN 2.0 进行流程建模、流程执行管理,它包括很多的建模符号,比如:

Event

用一个圆圈表示、它是流程在运行中发生的事件

活动用圆角矩形表示,一个流程由一个活动或多个活动组成

Actviti的使用步骤

1、集成Activiti

Activiti是一个工作流引擎(其实就是一堆jar包API),业务系统访问(操作)activiti的接口,就可以方便的操作流程相关数据,这样就可以把工作流环境与业务系统的环境集成在一起。

2、流程定义

使用activiti流程建模工具(activity-designer)定义业务流程(.bpmn文件) 。

.bpmn文件就是业务流程定义文件,通过xml定义业务流程。

3、流程定义部署

activiti部署业务流程定义(.bpmn文件)。

使用activiti提供的api把流程定义内容存储起来,在Activiti执行过程中可以查询定义的内容

Activiti执行把流程定义内容存储在数据库中

4、启动一个流程实例

流程实例也叫:ProcessInstance

启动一个流程实例表示开始一次业务流程的运行。

在员工请假流程定义部署完成后,如果张三要请假就可以启动一个流程实例,如果李四要请假也启动一个流程实例,两个流程的执行互相不影响。

5、用户查询待办任务(Task)

因为现在系统的业务流程已经交给activiti管理,通过activiti就可以查询当前流程执行到哪了,当前用户需要办理什么任务了,这些activiti帮我们管理了,而不需要开发人员自己编写在sql语句查询。

6、用户办理任务

用户查询待办任务后,就可以办理某个任务,如果这个任务办理完成还需要其它用户办理,比如采购单创建后由部门经理审核,这个过程也是由activiti帮我们完成了。

7、流程结束

当任务办理完成没有下一个任务结点了,这个流程实例就完成了。

创建Activiti需要的表

1 创建一个springboot的工程。

2 导入依赖

 <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>

3 新建application.yml

spring:
  datasource:
    username: root
    password: root
    driver-class-name: com.mysql.jdbc.Driver

4 新建activiti.cfg.xml

<?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>

5 创建测试类执行方法

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]

运行时变量表

Activiti的六大Service服务

RepositoryService 仓储服务

@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);
}

RunTimeServce 运行时服务

运?时服务主要?来开启流程实例,?个流程实例对应多个任务,也就是多个流程节点,?如请假审批是?个流程实例,部?主管,部?经理,总经理都是节点,我们开启服务是通过流程定义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对应?个流程定义,?个流程定义对应多个流程实例,流程定义和流程实例之间的关系就好?是类和对象的关系。?个流程实例对应多个任务节点。

TaskService 任务服务

主要操作: 任务

任务服务是?来可以?来领取,完成,查询任务列表功能的,使??法分别如下:

/**
 * //根据任务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());
}

HistoryService 历史服务

主要操作:历史信息

历史服务可以查看审批?曾经审批完成了哪些项?,审批项?总共花了多少时间,以及在哪个环节?较耗费时间等等,便于审批?查看历史信息,?法如下。

@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("<==========================>");
    }
}

DynamicBpmnService 修改流程定义的服务

在Activiti中,DynamicBpmnService 是一个用于动态修改和更新流程定义(BPMN模型)的服务。它允许您在流程部署后,通过编程方式对已部署的流程定义进行修改,添加或删除流程元素,而无需重新部署整个流程。

使用 DynamicBpmnService 可以实现以下功能:

修改流程元素:

您可以在运行时通过 DynamicBpmnService 修改已部署的流程的各种元素,如任务、连接线、条件等。这对于需要根据运行时条件调整流程逻辑的场景非常有用。

动态添加任务:

您可以通过 DynamicBpmnService 动态地向流程中添加任务、网关、事件等元素,从而在不停机的情况下扩展或调整流程的功能。

删除任务:

如果需要,您也可以使用 DynamicBpmnService 从已部署的流程定义中删除任务或其他元素,以适应业务变化。

流程优化:

可以使用该服务来进行流程优化,以实现更高效的业务流程。

总之,DynamicBpmnService 提供了一种在流程运行时进行动态流程定义修改的机制,使您能够根据实际需求对已部署的流程进行灵活调整,而无需重新部署整个流程。这对于灵活性和快速响应业务变化是非常有帮助的。需要注意的是,虽然这种动态修改流程的能力非常强大,但也需要小心使用,以确保不会引入意外的问题。

ManagementService 管理和监控引擎运行时状态的服务

在Activiti中,ManagementService 是一个用于管理和监控引擎运行时状态的服务。它提供了一些用于管理和监控流程引擎的操作,使您能够以编程方式执行一些管理任务,而无需直接访问底层的数据库或进行复杂的配置。

ManagementService 提供了一系列方法,用于执行以下操作:

作业管理:

ManagementService 允许您管理作业(jobs),这些作业用于异步执行一些任务,如定时器触发的事件。您可以查询、挂起、恢复、删除和重新执行作业。

流程实例删除:

您可以使用 ManagementService 删除特定的流程实例,不管其当前的状态。

数据库操作:

ManagementService 提供了一些方法来执行数据库操作,如执行原生的SQL查询。

清理历史数据:

您可以使用 ManagementService 执行一些历史数据的清理操作,以便维护数据库性能。

引擎配置:

您可以获取和修改引擎的配置信息,如数据库表前缀等。

执行异步操作:

ManagementService 提供了一些方法来执行一些耗时的操作,如数据的导入和导出。

需要注意的是,ManagementService 提供的操作通常是对引擎运行时状态进行管理和维护的,这些操作可能具有潜在的风险,因此需要小心谨慎地使用。

总之,ManagementService 是用于管理和监控Activiti引擎运行时状态的服务,提供了一些管理操作和功能,使您能够在运行时对引擎进行维护和管理。

Activiti入门

创建Activiti工作流主要包含以下几步:

1、定义流程,按照BPMN的规范,使用流程定义工具,用流程符号把整个流程描述出来

2、部署流程,把画好的流程定义文件,加载到数据库中,生成表的数据

3、启动流程,使用java代码来操作数据库表中的内容

流程符号

事件event

活动 Activity

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

网关 GateWay

网关用来处理决策,有几种常用网关需要了解:

排他网关 (x)

——只有一条路径会被选择。流程执行到该网关时,按照输出流的顺序逐个计算,当条件的计算结果为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("<==========================>");
        }
    }

流程实例绑定业务id

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());
    }

}

使?变量设置Assignee

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());

}

使?Local局部变量

有的变量只是在某个节点会使用到这样子就可以直接使用局部变量

@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");

SpringBoot整合Activiti7入门案例

1、创建一个SpringBoot工程

<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>

2、导入依赖

<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>

3、新建俩个配置文件

数据库连接信息根据自己的信息进行修改

<?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

4、在resource下新建一个processes目录

5、初始化bean

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;
    }


}

6、安装插件

7、画一个流程图

在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>

8、启动项目

9、编写测试用例

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("流程结束~");


    }
}

文章来源:https://blog.csdn.net/weixin_65861329/article/details/135469481
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。