Activiti7 开发快速入门【2024版】

记录开发最核心的部分,理论结合业务实操减少废话,从未接触工作流快速带入开发。假设你是后端的同学学过JAVA和流程图,则可以继续向后看,否则先把基础课程书准备好先翻翻。

为什么要工作流

比起直接使用状态字段,工作流有以下几个好处:

  1. 直观管理:可以直接通过维护流程图设计来实现状态流转。流程变化可直接维护BMP流程图发布来实现变更
  2. 复杂流程:会签、序签、指定时间半数以上通过等等
  3. 低代码:基于表单的审批数据模式性很强,可以比较方便的实现低代码,高效开发

其它理由不用多说,光这3项的场景就有充分使用的理由。如果模式非常简单,还是状态字段来的直接。

从流程图说起

我们知道,一个流程图包括开始状态、结束状态、任务节点、流程条件。嗯,看起来像这样

对应Activiti的概念:

实例:开始一个任务后,则产生一个流程实例,即ProcessInstance

任务:当走到一个任务节点时,则产生任务,任务有单个和多个两种模式,多个任务有顺序和并行模式 

执行:通常一个任务节点会产生一个执行,但是并行节点或分支任务会产生多个执行,一个进行中的任务对应一个执行,每个执行可以有多个子执行(多个任务节点时)。这里的执行,相当于有多少个“待办任务”在进行。当对应的任务完成时,执行表的对应记录也会被删除

变量:流程的数据容器,在整个流转过程中。变量分为两种,一种是实例级别的变量,另一种是执行级别的变量。对应task.setVariable和execution.setVariable,区别是execution variable只在当前的执行节点有效,而task variable是针对任务的变量。

变量生命周期:对于task和execution,都有setVariable和setLocalVariable两种。变量的setVariable设置的值针对整个流程实例生命周期有效,而setLocalVariable设置的值针对当前任务节点有效。可以理解为始终使用还是一次性使用(针对网关使用一次后作废)。

边界事件:定时、信号等事件,在事件网关时,可以根据事件来定义。实现时间或信号特性的约束,常见的场景例如客服1小时处理不完投诉则交给主管处理。

约束条件与网关

以前面的流程图为例,超过3天和不超过3天为根据约束条件产生的两种不同的操作任务。在Activiti7里用网关(gateway)来表示。网关有表达式(SPEL)、脚本和Java类3中处理。常用的为表达式模式。当前一个任务完成(complete)时,根据表达式不同走向不同的节点

网关有4种类型:

排他网关(Exclusive Gateway)

会按照给定的条件,产生一个任务实例

蛋疼理论:如果多个分支条件都满足,或者一个分支都不满足,怎么办?
如果都满足,流程会按照id值升序,处理第一条分支(先定义的分支id通常值较小,但不绝对)
如果都不满足,则会抛出异常

并行网关(ParallelGateway)

会按照给定的条件,产生多个任务实例。并行网关可以并行作业,提高业务流程速度。

包容网关(Inclusive Gateway)

相当于排他和并行的综合体 ,同时执行时可以指定条件,例如某些需要补充材料的情况

事件网关(Event Gateway)

用于根据事件的触发选择分支路径。当指定的事件触发时,流程会选择对应的分支执行。

多实例与子执行

有那么一种业务,流程图可能表示为多个人参与同一个活动。例如你可能想到的流程图:

像这种活动,在BPM里,可以作为单个任务节点来实现。叫做多实例任务。

我们可以通过节点的MultiInstance来指定多实例,当指定多实例时,需要设置Collection的参数来决定进入任务时产生多少个子执行。Collection是一组java.util.Collection接口的数据。进入任务时,将产生一个对应的主执行,和多个子执行,这些子任务将分别指派给Collection的每一个人员。

对应的业务场景通常有会签和序签。序签用的较少,这里主要讲会签。

会签模式

会员的模式主要有:

按数量通过:达到一定数量的通过表决后,会签通过。
按比例通过:达到一定比例的通过表决后,会签通过。
一票否决:只要有一个表决是否定的,会签否决。
一票通过:只要有一个表决通过的,会签通过。

我们可以在Task的MultiInstance配置多实例的信息。可以配置的有:

模式参数

顺序(sequential ):执行顺序,必选项。true:多实例顺序执行。false:多实例并行。
并行(parallel):多个实例会同时并行发放给处理人

loop cardinality:循环基数(实例数量),可选项。可填整数,表示会签的人数。
Collection:集合,可选项。会签人数的集合list,与loop cardinality二选一。
Element variable:元素变量。选择Collection时必选,为collection集合每次遍历的元素。
Completion condition:完成条件,可选项。

完成条件

会签配置关键就是Completion condition,可以填写一个UEL(类似SPEL)。在流程流转时,对于多实例节点,会内置下面几个参数:

nrOfInstances:创建的实例总数,在进入任务节点时则设置好,一般等于Collection的数量
nrOfActiveInstances:当前活动的实例数,针对顺序类型的多实例,该变量值等于1;对于并行的类型,进入任务时为Collection数量,每完成一个执行,则活动任务数减1。
nrOfCompletedInstances:已执行实例数。每完成一个执行,则加1。
loopCounter:表示多实例流程循环的下标,顺序执行时记录执行顺序。

条件实现

理解上面的内容这样实现的方式就很明确了:

假设我们为会签设置了2个投票池(说白了就是变量)approveCount和denyCount。操作时,每点击"同意"或"拒绝"(先不考虑弃权情况)则变量+1

1)按数量通过:2人通过则通过#{approveCount > 2}
2)按比例通过:常见的过半数同意则通过 #{agreeCount/nrOfInstance > 0.5}
3)一票否决:#{denyCount== 1}
4)一票通过:#{approveCount== 1}

序签:较少使用。需要每个人员逐一完成任务。

抢单与代办

接下来讲两个常见业务场景,第一个是抢单。抢单的需求为我们可以将任务指定给一个处理小组,处理小组某个人点击“开始处理”后,其它人将无法再点击处理按钮或点击处理按钮提示已被抢单。

核心业务实现:

1)任务的CandidateUser属性可以指定一组处理人员,Task的Candidata Users属性可以通过SPEL指定一个列表。当指派列表后,任务流转到节点时所有的Candidata Users都会收到一个代办任务。

2)操作员点击开始处理时,通过taskService.setAssignee(taskId, assignee);指派给某个人员,指派后,其它操作员将再无法看到这个任务

3)当其他操作员点击操作时,需要检查操作列表,如果已经被指派给非当前操作员 ,需要返回前端提醒用户该任务已指派。否则直接使用Activity引擎来处理指派,将会导致任务重新分配(和后面讲的代办逻辑一样)

代办的模式和抢单的一样,当某任务已经指定给某个操作员后。可以通过setAssignee实现操作的重新指派。从而实现代办指派。当然这只是流程级别的操作,对于业务的具体流转记录,我们还时得借助日志的实现。

Activiti之历史记录

对于工作流业务,仅有流程状态支撑显然不够。我们还需要对历史数据进行查询和展示。Activiti提供了一组历史记录表,以ACT_HI_开头,记录了流程信息、流程任务信息、以及流程的变量信息。能够满足一些常见的业务场景需求,例如:

审批历史

在审批的过程,我们通常需要添加批示信息。一个流程的审批历史类似:

张三:提交请假申请
组长:同意请假 
经理:同意请假
人事:请假申请处理完毕

如果不借助额外的数据表,我们可以使用流程变量。定义为一个String数组或者JSON,每经过一个处理节点添加一行记录。流程进行或完成时,将该记录拿出来展示,该信息记录在表act_hi_taskinst

注意如果使用流程实例变量记录审批过程JSON,需要留意String变量大小的限制(4000字符),以免在审批过程中超过大小。以每个节点审批信息256字符为例,我们可以推算最少大概15个节点审批信息填满会导致审批记录满。

流转节点

在任务记录表act_hi_taskinst里,记录了流程产生的任务以及经过的节点,以及对于流程图文件的任务节点的KEY。这样我们可以通过act_hi_taskinst,在BPMN流程定义图上绘制详细的节点流转标记进行展示

理论了很多,下一步来点干货实操

环境搭建

以springboot+Activiti7为例,开始环境搭建。

Activiti依赖的支持有:

  • spring:提供了一组starter,启动后可以通过对应的Bean完成流程处理
  • spring-security:activiti7.X默认整合了springsecurity,使用sa-token或Shiro等其他权限控制需要剥离对spring-security的依赖,否则会报错。对springsecurity的依赖主要包含群组权限的部分,我们不需要完全可以剥离它
  • mybatis:数据存储层使用的是mybatis来进行控制

Activiti最新的版本

如果从sonatype公共的仓库去拿,仅能拿到4年前的版本7.1.0.M6。你可能满脑袋的问号:Activiti开源死掉了吗?不再支持了吗?

如果你去Activiti官网,会发现Activiti仍旧有维护,甚至发展到8.0版本。无法获得是因为我们没有配置Activiti官网仓库。以gradle为例,我们只需要添加这个仓库即可

    maven { url 'https://artifacts.alfresco.com/nexus/content/repositories/activiti-releases' } // activiti最新版仓库

然后我们可以使用最新版本了,为了避免表结构出现不兼容,还是选择7.X的最新版本:

implementation("org.activiti:activiti-spring-boot-starter:7.11.0")
implementation("org.activiti:activiti-core-dependencies:7.11.0")

升级注意

JDK版本依赖:Activiti自身仓库的版本都是使用JDK11编译,如果是使用JDK1.8编译会出现报错。解决办法1是升级JDK到11,2是重新使用JDK1.8编译整个Activity源(比较麻烦)。我选择的是升级到JDK11

剥离springsecurity依赖

剥离springsecurity依赖主要是方式是禁用Activiti里与springsecurity相关的starter

YAML配置的方式:
 

spring:
  autoconfigure:
    exclude:
      - org.activiti.spring.boot.ActivitiMethodSecurityAutoConfiguration
      - org.activiti.core.common.spring.identity.config.ActivitiSpringIdentityAutoConfiguration

代码配置方式:

@SpringBootApplication(
	exclude = {
		ActivitiMethodSecurityAutoConfiguration.class, ActivitiSpringIdentityAutoConfiguration.class
	}
)

然后启动你会发现,还会有Bean UserGroupManager报错,这个Bean是依赖spring security的群组功能。不使用群组的话,我们可以直接new个匿名类让spring装配不报错。

我的ActivitiConfiguration如下

package org.ccframe.app;

import org.activiti.api.runtime.shared.identity.UserGroupManager;
import org.activiti.spring.SpringProcessEngineConfiguration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

@Configuration
public class ActivitiConfiguration {

    @Autowired
    private DataSource dataSource;

    @Autowired
    private PlatformTransactionManager transactionManager;

    //通过@Bean注解将SpringProcessEngineConfiguration实例声明为Spring Bean,使其可供其他组件注入和使用
    @Bean
    public SpringProcessEngineConfiguration springProcessEngineConfiguration() {
        SpringProcessEngineConfiguration spec = new SpringProcessEngineConfiguration();
        //设置数据源,将注入的数据源设置到SpringProcessEngineConfiguration实例中
        spec.setDataSource(this.dataSource);
        //设置事务管理器将注入的事务管理器设置到SpringProcessEngineConfiguration实例中
        spec.setTransactionManager(this.transactionManager);
        //设置数据库模式更新策略 true表示在启动时自动创建或更新Activiti引擎所需的数据库表结构
        spec.setDatabaseSchemaUpdate("true");
        Resource[] resources = null;
        //配置流程部署资源
        //使用PathMatchingResourcePatternResolver从classpath中的bpmn目录下加载所有以.bpmn为扩展名的文件作为流程定义资源,
        // 并将它们设置到SpringProcessEngineConfiguration实例中。
        try {
            resources = (new PathMatchingResourcePatternResolver()).getResources("classpath*:bpmn/*.bpmn");
        } catch (IOException var4) {
            var4.printStackTrace();
        }

        spec.setDeploymentResources(resources);
        return spec;
    }

    @Bean
    public UserGroupManager userGroupManager(){
        return new UserGroupManager(){

            @Override
            public List<String> getUserGroups(String username) {
                return new ArrayList<>();
            }

            @Override
            public List<String> getUserRoles(String username) {
                return new ArrayList<>();
            }

            @Override
            public List<String> getGroups() {
                return new ArrayList<>();
            }

            @Override
            public List<String> getUsers() {
                return new ArrayList<>();
            }
        };
    }
}

这里只是不使用group的功能,当然你可以使用自己的权限系统来实现group的功能,将group信息对应到自己权限系统的用户列表。


可控流转的实现

<编写中>

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【 书生·浦语大模型实战营】作业(六):Lagent AgentLego 智能体应用搭建

【 书生浦语大模型实战营】作业&#xff08;六&#xff09;&#xff1a;Lagent & AgentLego 智能体应用搭建 &#x1f389;AI学习星球推荐&#xff1a; GoAI的学习社区 知识星球是一个致力于提供《机器学习 | 深度学习 | CV | NLP | 大模型 | 多模态 | AIGC 》各个最新AI方…

【机器学习】集成方法---Boosting之AdaBoost

一、Boosting的介绍 1.1 集成学习的概念 1.1.1集成学习的定义 集成学习是一种通过组合多个学习器来完成学习任务的机器学习方法。它通过将多个单一模型&#xff08;也称为“基学习器”或“弱学习器”&#xff09;的输出结果进行集成&#xff0c;以获得比单一模型更好的泛化性…

上海计算机学会2021年1月月赛C++丙组T2康托表

题目背景 康托是一名数学家&#xff0c;他证明了一个重要的定理&#xff0c;需要使用一张表&#xff1a; 这个表的规律是&#xff1a; 从上到下&#xff1a;每一行的分子依次增大&#xff1b;从左到右&#xff1a;每一列的分母依次增大。 康托以一种不重复、不遗漏的方式&am…

【深耕 Python】Quantum Computing 量子计算机(1)图像绘制基础

一、绘制静止图像 使用matplotlib库绘制函数图像y sin(pi * x): import math import matplotlib.pyplot as pltx_min -2.0 x_max 2.0N 1000x1 [] y1 []for i in range(N 1):x x_min (x_max - x_min) * i / Ny math.sin(math.pi * x)x1.append(x)y1.append(y)plt.xl…

关于继承~

继承 动物有猫、狗&#xff0c; 猫又分为加菲猫、布偶猫......&#xff1b;狗又有哈士奇、德国牧羊犬...... 我们发现&#xff0c;下一类除了拥有上一类的共性之外&#xff0c;还拥有自己的特性。 于是我们可以利用继承的方式来减少重复的代码 继承的基本语法 class A:p…

二叉树的直径

题目描述&#xff1a;给你一棵二叉树的根节点&#xff0c;返回该树的 直径 。二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。两节点之间路径的 长度 由它们之间边数表示。 示例 1&#xff1a; 输入&#xff1a;root […

在剪映专业版中新增字体的方法

我一开始以为剪映专业版没有繁体字&#xff0c;结果发现有一个现代繁体&#xff0c;如图所示: 但是我已经下载了字体了&#xff0c;不用就可惜了。 点击汉仪粗黑繁&#xff0c;安装。 安装之后&#xff0c;重启电脑&#xff0c;打开剪映&#xff0c;就可以搜索到这个字体了。 这…

每日OJ题_贪心算法二④_力扣2418. 按身高排序

目录 力扣2418. 按身高排序 解析代码 力扣2418. 按身高排序 2418. 按身高排序 难度 简单 给你一个字符串数组 names &#xff0c;和一个由 互不相同 的正整数组成的数组 heights 。两个数组的长度均为 n 。 对于每个下标 i&#xff0c;names[i] 和 heights[i] 表示第 i 个…

罗宾斯《管理学》第13版/教材讲解/考研真题视频课程/网课

本课程是罗宾斯《管理学》&#xff08;第13版&#xff09;精讲班&#xff0c;为了帮助参加研究生招生考试指定考研参考书目为罗宾斯《管理学》&#xff08;第13版&#xff09;的考生复习专业课&#xff0c;我们根据教材和名校考研真题的命题规律精心讲解教材章节内容。 序号名…

读天才与算法:人脑与AI的数学思维笔记17_歌曲的创作公式

1. 人为何创作音乐 1.1. 音乐一直具有算法性质&#xff0c;这意味着在所有的艺术形式中&#xff0c;它受到人工智能进步的威胁最大 1.1.1. 音乐也是所有艺术形式中最抽象的一种&#xff0c;它利用结构和模式&#xff0c;而正是这种抽象的性质使它与数学紧密相连 1.1.2. 在这…

查找算法之二分查找

一、算法介绍 二分查找&#xff0c;也称为折半查找&#xff0c;是一种在有序数组中查找特定元素的高效算法。对于包含 n 个元素的有序数组&#xff0c;二分查找的步骤如下&#xff1a; 确定搜索范围&#xff1a;首先&#xff0c;将要查找的元素与数组中间的元素进行比较。如果…

【C++】学习笔记——string_5

文章目录 六、string类7. string类的模拟实现8. string类的模拟实现的完整代码string.h头文件test.c源文件 9. string收尾写时拷贝 未完待续 六、string类 7. string类的模拟实现 我们之前讲了实现 insert &#xff0c;但是那个插入函数仅仅是在 pos 位置插入一个字符而且&am…

13.1 QQ邮箱

1. 邮箱发送 2. 准备工作 3. 整合SpringBoot 3.1 配置 依赖引入 <!-- 邮件服务--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId></dependency>application.…

发表博客之:transformer 架构 推理时候运算流程详细讲解,以及变长推理支持,小白都可以看得懂,AI推理工程师必备技能!

文章目录 [发表博客之&#xff1a;transformer 架构 推理时候运算流程详细讲解&#xff0c;以及变长推理支持&#xff0c;小白都可以看得懂&#xff0c;AI推理工程师必备技能&#xff01;](https://cyj666.blog.csdn.net/article/details/138439826)总结一下高性能变长推理 发表…

DevicData-P-XXXXXX勒索病毒有什么特点,如何恢复重要数据?

DevicData-P-XXXXXXXX这种新型勒索病毒有什么特点&#xff1f; DevicData-P-XXXXXXXX勒索病毒的特点主要包括以下几点&#xff1a; 文件加密&#xff1a;该病毒会搜索并加密用户计算机上的重要文件&#xff0c;如文档、图片、视频等&#xff0c;使其无法正常打开。这是通过强…

IoTDB 入门教程 问题篇②——RPC远程连接IoTDB服务器失败

文章目录 一、前文二、发现问题三、分析问题四、检查6667端口是否监听所有IP五、检查ECS云服务器的安全组是否允许六、检查Linux防火墙是否允许 一、前文 IoTDB入门教程——导读 二、发现问题 使用本地IP127.0.0.1可以连接IoTDB服务器使用远程IPxx.xx.xx.xx却连接不到。提示你…

抖音小店怎么运营操作,无货源正确做店方式,新手就看这一篇

大家好&#xff0c;我是电商笨笨熊 当前抖店还能做无货源模式吗&#xff1f; 当然可以。 近两年由于平台对于无货源的打击&#xff0c;很多人开始怀疑&#xff0c;无货源模式在抖店还能行得通吗&#xff1f; 抖店这个项目我们做了四年多的时间&#xff0c;无货源模式也是&a…

双fifo流水线操作——verilog练习与设计

文章目录 一、案例分析二、fifo_ctrl模块设计2.1 波形设计&#xff1a;2.2 代码实现2.2.1 fifo_ctrl2.2.2 顶层文件top_fifo_ctrl&#xff08;rx和tx模块省略&#xff09;2.2.3 仿真文件tb_fifo_ctrl 2.3波形仿真 一、案例分析 案例要求&#xff1a;写一个 fifo 控制器&#x…

vivado Aurora 8B/10B IP核(12)- Setp By Step搭建FPGA工程

Step1:任意创建一个新的空的工程&#xff08;创建工程的具体工程如果还不清楚的看我们教程第一季部分&#xff09;&#xff0c; 并且进入IP CORE列表 右击Customize ip Step2:配置 IP CORE-Core options Step3:配置 IP CORE-GT Selections Step4:配置 IP CORE-Shared Logic 为 …

深入解析Python中的`add_argument`用法

深入解析Python中的add_argument用法 在Python编程中&#xff0c;add_argument通常与命令行参数解析库argparse有关。这个库主要用于编写用户友好的命令行接口&#xff0c;其核心功能之一就是通过add_argument方法来指定程序可以接受哪些命令行参数。本篇博客将详细介绍argpar…