Flowable定时器与实时流程图江南一点雨

news2024/11/23 12:02:13

1. 定时器

1.1. 流程定义定时激活

在之前松哥给小伙伴们介绍流程定义的时候,流程都是定义好之后立马就激活了,其实在流程定义的这个过程中,我们还可以设置一个激活时间,也就是流程定义好之后,并不会立马激活(不激活就不能据此流程定义创建新流程),而是在延迟某一个固定时间之后,才会激活,代码如下:

@RestController
public class ProcessDeployController {

    @Autowired
    RepositoryService repositoryService;

    @PostMapping("/deploy")
    public RespBean deploy(MultipartFile file,String tenantId) throws IOException {
        System.out.println(new Date());
        DeploymentBuilder deploymentBuilder = repositoryService.createDeployment()
                .category("javaboy的工作流分类")
                .name("javaboy的工作流名称")
                .addInputStream("fff.bpmn", file.getInputStream())
                .tenantId(tenantId)
                .activateProcessDefinitionsOn(new Date(System.currentTimeMillis() + 1000 * 60))
                .key("javaboy的工作流key666");
        Deployment deployment = deploymentBuilder
                .deploy();
        return RespBean.ok("部署成功",deployment.getId());
    }
}
复制代码

.activateProcessDefinitionsOn(new Date(System.currentTimeMillis() + 1000 * 60)) 表示流程在延迟一分钟之后,才激活。

此时,我们启动项目,然后调用该接口部署一个流程,部署完成之后,如果立马调用流程启动方法去启动流程,就会抛出如下异常:

可以看到,这里也说的很明确了,这个流程定义目前是一个挂起的状态,无法启动。

这个时候,我们去查看 ACT_RU_TIMER_JOB 表,就会发现该表中多了一条定时任务执行计划:

该表有一个 DUEDATE_ 字段,这个字段描述了这个定时任务执行的具体时间,在到达时间后,定时任务会自动执行,将 ACT_RE_PROCDEF 表中,流程的状态字段 SUSPENSION_STATE_ 由 2 改为 1。

1.2. 流程实例定时挂起

除了流程定义可以定时挂起外,流程实例也可以定时挂起。方式如下:

@Autowired
RepositoryService repositoryService;
@Test
void test23() {
    repositoryService.suspendProcessDefinitionByKey("UserTaskDemo", true, new Date(System.currentTimeMillis() + 120 * 1000));
}
复制代码

这个执行完成后,也会在 ACT_RU_TIMER_JOB 表中添加一条定时任务,在两分钟之后,会自动挂起这个流程定义以及与之相对应的流程。实际上就是将对应表中的 SUSPENSION_STATE_ 字段值由 1 改为 2。

1.3. 定时任务执行过程

前面两个小节,松哥都和大家提到,ACT_RU_TIMER_JOB 表中会保存定时任务信息,时间到了就会自动执行。

但是小伙伴们注意,定时任务每次执行的时候,其实并不是去 ACT_RU_TIMER_JOB 表中查询数据,而是去 ACT_RU_JOB 表中查询数据并执行。

当定时的时间到了后,Flowable 会自动将数据从 ACT_RU_TIMER_JOB 表中移动到 ACT_RU_JOB 表中,然后定时器查询到 ACT_RU_JOB 表中的数据之后,就立马自动执行了。大致上就是这样一个流程。

我给大家手动演示一个。

我现在的流程定义和流程实例都挂起了,我想要在 4 分钟之后,将之全部启动,代码如下:

@Test
void test24() {
    repositoryService.activateProcessDefinitionByKey("UserTaskDemo", true, new Date(System.currentTimeMillis() + 240 * 1000));
}
复制代码

当这行代码执行之后,4 分钟之后,流程定义和流程实例就全部都启动了。但是我现在忽然就不想等四分钟了,我想立马执行,那么我们可以去 ACT_RU_TIMER_JOB 表中找到这个定时任务的 ID,然后执行如下代码:

@Autowired
ManagementService managementService;
@Test
void test25() {
    managementService.moveTimerToExecutableJob("b7e9501d-5075-11ed-9706-acde48001122");
}
复制代码

这个代码表示将 ID 为 b7e9501d-5075-11ed-9706-acde48001122 的记录由 ACT_RU_TIMER_JOB 表移动到 ACT_RU_JOB 表中,移动完成后,这个任务就会被立马执行。

当一个定时任务开启了,还能不能取消呢?当然可以!我们将这个定时任务放到私信队列表即可,私信队列表是 ACT_RU_DEADLETTER_JOB,具体操作方式如下:

@Test
void test27() {
    managementService.moveJobToDeadLetterJob("6b95dc62-5081-11ed-a00f-acde48001122");
}
复制代码

上面这个方法执行的参数是 ACT_RU_TIMER_JOB 表中的任务 ID,执行完成后,ACT_RU_TIMER_JOB 表中对应的记录就会被移动到 ACT_RU_DEADLETTER_JOB 表中,所以定时任务就不会被执行了。

对于已经移动到私信队列的定时任务,也可以再通过如下方法移动回 ACT_RU_JOB 表中被立马执行(即使时间没到也会立马执行),如下:

@Test
void test26() {
    managementService.moveDeadLetterJobToExecutableJob("6b95dc62-5081-11ed-a00f-acde48001122", 10);
}
复制代码

参数就是任务 ID。

好啦,几个简单的例子和小伙伴们分享了下 Flowable 中定时器的玩法。

2. 实时流程图

在之前的文章中,松哥和大家展示过 Flowable 中的一个功能,就是我们可以绘制一张图片,来实时展示某一个流程走到哪一步了。不过当时没有跟大家详细介绍这个图片到底是如何绘制出来的,现在我们就来聊一聊这个话题。

2.1. 效果图

首先我们先来看一下绘制出来的效果图,如下:

已经执行的节点和连线用红色标记出来。大致上就是这么一个效果,今天就跟大家聊一聊这里的实现细节。

2.2. 实践

将一个流程图绘制成图片,相关的 API 在 flowable 中其实都是有提供的,流程图片的绘制,是根据流程的定义来绘制的,所以只需要提供一个流程定义的 ID 即可,如下:

ProcessDefinition pd = repositoryService.createProcessDefinitionQuery().processDefinitionKey("ExclusiveGatewayDemo01").latestVersion().singleResult();
BpmnModel bpmnModel = repositoryService.getBpmnModel(pd.getId());
DefaultProcessDiagramGenerator generator = new DefaultProcessDiagramGenerator();
InputStream inputStream = generator.generatePngDiagram(bpmnModel, 1.0, true);
FileUtils.copyInputStreamToFile(inputStream, new File("/Users/sang/Downloads/1.png"));
复制代码

这五行代码应该都好理解:

  1. 查找到流程定义对象。
  2. 根据流程定义对象,获取到一个 BpmnModel 对象。
  3. 创建一个图片生成器对象 DefaultProcessDiagramGenerator。
  4. 调用 generatePngDiagram 方法生成这个流程定义所对应的图片,参数有三个,分别是:前面查询到的 bpmnModel 对象;缩放因子以及是否在绘制流程图的时候,在连线上加上描述文字,generatePngDiagram 方法的返回值则是一个输入流。
  5. 最后将这个输入流打印出来,就是一张图片了。

上面这个方法执行结果如下:

可以看到,这就是普通的流程图,没有高亮。

如果希望已经执行的节点变成高亮的,那么可以使用如下方法:

这个方法参数比较多,我们挨个来说下:

  1. 流程的 bpmnModel 对象。
  2. 生成的图片类型。
  3. 高亮的活动节点。
  4. 高亮的连线。
  5. 缩放因子。
  6. 是否在绘制连线的时候添加文字描述。

这里的关键就是第三个和第四个参数。这个流程图中哪些节点哪些连线需要高亮,我们将之列出来即可。

根据我们之前文章中的介绍,小伙伴们知道,一个流程在执行过程中的活动信息,都是保存在 ACT_RU_ACTINST 表中,所以我们只需要根据流程实例的 ID 在 ACT_RU_ACTINST 表中查询到即可,如下:

@Test
void test01() throws IOException {
    ProcessDefinition pd = repositoryService.createProcessDefinitionQuery().processDefinitionKey("ExclusiveGatewayDemo01").latestVersion().singleResult();
    BpmnModel bpmnModel = repositoryService.getBpmnModel(pd.getId());
    ProcessInstance pi = runtimeService.createProcessInstanceQuery().singleResult();
    List<String> highLightedActivities = new ArrayList<>();
    List<String> hightLightedFlows = new ArrayList<>();
    double scaleFactor = 1.0;
    boolean drawSqquenceFlowNameWithNoLabelDI = true;
    if (pi == null) {
        return;
    }
    List<ActivityInstance> list = runtimeService.createActivityInstanceQuery().list();
    for (ActivityInstance ai : list) {
        if (ai.getActivityType().equals("sequenceFlow")) {
            hightLightedFlows.add(ai.getActivityId());
        } else {
            highLightedActivities.add(ai.getActivityId());
        }
    }
    DefaultProcessDiagramGenerator generator = new DefaultProcessDiagramGenerator();
    InputStream inputStream = generator.generateDiagram(bpmnModel, "PNG", highLightedActivities, hightLightedFlows, scaleFactor, drawSqquenceFlowNameWithNoLabelDI);
    FileUtils.copyInputStreamToFile(inputStream, new File("/Users/sang/Downloads/1.png"));
}
复制代码

小伙伴们看到,我们这里使用 highLightedActivitieshightLightedFlows 两个集合,分别存一个流程已经执行的活动 ID 和连线的 ID。

通过 runtimeService.createActivityInstanceQuery().list(); 来查询到所有已经执行的活动,然后遍历,遍历的时候注意区分是不是 sequenceFlow,如果是 sequenceFlow 则将之添加到 hightLightedFlows 集合中,否则将之添加到 highLightedActivities 结合中。

最终,执行生成的图片就是本文已开始大家看到的图片。

按照上面这种方式,对于一个已经执行完毕的流程来说,似乎就画不出来流程图了,因为当一个流程执行完毕之后,ACT_RU_ACTINST 表中的数据就会自动清空。

不过。。。根据我们前面文章的介绍,执行完毕的活动信息还可以去 ACT_HI_ACTINST 表中进行查询,因此,我们这个流程图还可以这样画:

@Test
void test05() throws IOException {
    ProcessDefinition pd = repositoryService.createProcessDefinitionQuery().processDefinitionKey("ExclusiveGatewayDemo01").latestVersion().singleResult();
    BpmnModel bpmnModel = repositoryService.getBpmnModel(pd.getId());
    HistoricProcessInstance hpi = historyService.createHistoricProcessInstanceQuery().singleResult();
    if (hpi == null) {
        return;
    }
    List<String> highLightedActivities = new ArrayList<>();
    List<String> hightLightedFlows = new ArrayList<>();
    double scaleFactor = 1.0;
    boolean drawSqquenceFlowNameWithNoLabelDI = true;
    List<HistoricActivityInstance> list = historyService.createHistoricActivityInstanceQuery().processInstanceId(hpi.getId()).list();
    for (HistoricActivityInstance hai : list) {
        if (hai.getActivityType().equals("sequenceFlow")) {
            hightLightedFlows.add(hai.getActivityId());
        } else {
            highLightedActivities.add(hai.getActivityId());
        }
    }
    DefaultProcessDiagramGenerator generator = new DefaultProcessDiagramGenerator();
    InputStream inputStream = generator.generateDiagram(bpmnModel, "PNG", highLightedActivities, hightLightedFlows, scaleFactor, drawSqquenceFlowNameWithNoLabelDI);
    FileUtils.copyInputStreamToFile(inputStream, new File("/Users/sang/Downloads/1.png"));
}
复制代码

可以看到,当一个流程实例执行完毕的时候,我们可以去历史表中查询这个流程实例,同时也去历史表中查询这个流程实例所执行过的活动 ID,找到之后,还是按照之前的办法,填充给 hightLightedFlowshighLightedActivities 两个变量。最终绘制出来的结果如下图:

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据存储——存储视频

数据存储——存储视频视频的数字化一、视频采样二、视频量化总结&#xff1a;视频数字化的过程视频的数字化 1.视频是图像&#xff08;帧&#xff09;在时间上的表示 图象是离散的视频&#xff0c;视频是连续的图像 2.视频储存 每一帧图像或帧被转化为位模式并加以储存 一、视…

(3)点云数据处理学习——KD树近邻搜索

1、主要参考资料 &#xff08;1&#xff09;kd树原理 数组索引的kdtree建立及简明快速的k近邻搜索方法 &#xff08;2&#xff09;open3d 爆肝5万字 Open3D 点云数据处理基础&#xff08;Python版&#xff09;&#xff09;-技术圈 (3)视频参考 【PythonOpen3D处理点云数据…

12【MyBatis注解开发】

文章目录三、Mybatis注解开发3.1 快速入门3.1.1 常用注解说明3.1.2 注解实现开发3.2 注解实现一对一映射3.2.1 接口3.2.2 测试类3.3 注解实现一对多映射3.2.1 接口3.2.2 测试类三、Mybatis注解开发 3.1 快速入门 3.1.1 常用注解说明 注解功能Insert新增Update更新Delete删除…

创新指南|如何以STEPPS模型6招打造病毒式传播产品

从爆款产品到网络流行语&#xff0c;这种流行绝对不是依赖于运气&#xff0c;更不是神话。让人们喜欢读某些文章&#xff0c;让人们尝试某项新服务&#xff0c;甚至是投票竞选&#xff0c;这些事情的背后都有STEPPS模型的驱动&#xff0c;遵循或者仅仅应用STEPPS中的某几条&…

【毕业设计】21-基于单片机的智能恒温箱_温度报警装置设计(原理图+仿真+源代码+答辩论文+答辩PPT)

【毕业设计】21-基于单片机的智能恒温箱/温度报警装置设计&#xff08;原理图仿真源代码答辩论文答辩PPT&#xff09; 文章目录【毕业设计】21-基于单片机的智能恒温箱/温度报警装置设计&#xff08;原理图仿真源代码答辩论文答辩PPT&#xff09;任务书设计说明书摘要设计框架架…

Tomcat:servlet与servlet容器

前言 在介绍Tomcat之前&#xff0c;我们首先需要了解它的作用&#xff0c;简单的说&#xff0c;tomcat就是一个servlet容器。因此&#xff0c;本文首先从Web 应用程序开始讲起&#xff0c;逐步介绍servlet。 Web即表示网页的意思&#xff0c;它用于表示 Internet 主机上供外界访…

【Android App】GPS获取定位经纬度和根据经纬度获取详细地址讲解及实战(附源码和演示 超详细)

需要全部代码请点赞关注收藏后评论区留言私信~~~ 一、获取定位信息 开启定位相关功能只是将定位的前提条件准备好&#xff0c;若想获得手机当前所处的位置信息&#xff0c;还要依靠下列的3种定位工具。 &#xff08;1&#xff09;定位条件器Criteria 定位条件器用于设置定位的…

如何从报表控件FastReport .NET中连接到 PostgreSQL 数据库?

FastReport.NET官方版下载 Fastreport是目前世界上主流的图表控件&#xff0c;具有超高性价比&#xff0c;以更具成本优势的价格&#xff0c;便能提供功能齐全的报表解决方案&#xff0c;连续三年蝉联全球文档创建组件和库的“ Top 50 Publishers”奖。 慧都科技是Fast Repor…

Nacos注册中心和服务消费方式(服务治理)

目录 一、服务治理介绍 什么是服务治理&#xff1f; 二、nacos简介 三、nacos实战入门 1.搭建nacos环境 2.将商品、订单、微服务注册到nacos 四、实现服务调用的负载均衡 1.什么是负载均衡 2. DiscoveryClient实现负载均衡 3.Ribbon实现负载均衡 Ribbon支持的负载…

Nacos作为配置中心详解

前言 在单体架构的时候我们可以将配置写在配置文件中&#xff0c;但有⼀个缺点就是每次修改配置都需要重启服务才能生效。 当应用程序实例比较少的时候还可以维护。如果转向微服务架构有成百上千个实例&#xff0c;每修改⼀次配置要将全部实例重启&#xff0c;不仅增加了系统的…

Spring Boot FailureAnalyzer 应用场景

Spring Boot 自定义FailureAnalyzer 今天在学习Spring Boot 源码的过程中&#xff0c;在spring.factories 文件中无意中发现了FailureAnalyzer 这个接口。由于之前没有接触过&#xff0c;今天来学习一下 FailureAnalyzer 接口的作用。 在学习FailureAnalyzer之前, 我们先看以…

Spark 3.0 - 8.ML Pipeline 之决策树原理与实战

目录 一.引言 二.决策树基础-信息熵 三.决策树的算法基础 - ID3 算法 四.ML 中决策树的构建 1.信息增益计算 2.连续属性划分 五.ML 决策树实战 1.Libsvm 数据与加载 2.StringIndexer 3.VectorIndexer 4.构建决策树与 Pipeline 5.测试与评估 6.获取决策树 六.总结…

【学习笔记67】JavaScript中的闭包

一、认识函数的过程 1. 定义 在堆内存中开辟一段内存空间(XF001)把函数体的内容&#xff0c;完全百分百的照抄一份&#xff0c;存放在内存空间中(XF001)把内存空间的地址(XF001) 赋值给函数名2. 调用 根据函数名内存储的地址 (XF001) &#xff0c;去堆内存中找到对应函数会去…

Nginx安装Openresty加载Lua代码

1、下载 VM环境&#xff1a;ubuntu 16 http://openresty.org/cn/download.html 我选择的是截图红框的那个版本&#xff0c;其他高级的版本&#xff0c;我编译的时候都会报错&#xff0c;所以选择了这个版本&#xff0c;大家编译失败的时候不要放弃&#xff0c;继续选择其他版…

【PS-8】选区

目录 矩形选框工具 先选区再按【shift】&#xff0c;正方形选区 选区的同时按【ALT】&#xff0c;从中心点选区 选区时按【shiftalt】&#xff0c;从中心点建立正方形选区 模式1&#xff1a;添加到选区 模式2&#xff1a;新选区 模式3&#xff1a; 从选区减去 模式4&am…

《C++Primer》-1-前序与基础第I部分重点

文章目录第一章 开始1. c之与其他语言的优点&#xff1f;2. c语言的组成3. 标准输入输出cin、cout4. include格式第二章 变量和基本类型1. 无符号数的使用注意2. 初始化注意事项3. 声明与定义4. 标识符的下划线规则5. &引用、取地址&、指针的区别6. 如何理解“因为引用…

Rainbow Brackets的配色修改和使用

修改配色&#xff1a;&#xff09; 敲好看内&#xff01;&#xff01; 记得每个都要改噢&#xff01; 5分别对应的是&#xff1a; 圆括号 方括号 波形括号 尖括号 6分别对应的是&#xff1a; ECB1E9 F6F0A9 78B8EF F3BBA2 A9D57E 使用方式 Ctrl 鼠标右键&#xff1a;高亮{}…

C++ Reference: Standard C++ Library reference: Containers: list: list: end

C官网参考链接&#xff1a;https://cplusplus.com/reference/list/list/end/ 公有成员函数 <list> std::list::end C98 iterator end(); const_iterator end() const; C11 iterator end() noexcept; const_iterator end() const noexcept; 返回结束迭代器 返回一个指向…

企业日常公关如何抵御负面信息的入侵?

如今&#xff0c;互联网时代信息传播速度极快&#xff0c;这使得宣传工作效率倍增&#xff0c;也给企业舆情管理带来一定的挑战。舆情优化搞得好&#xff0c;企业宣传工作事半功倍&#xff0c;网络舆论走向负面的话&#xff0c;则对宣传工作非常不利&#xff0c;会导致推广效果…

Echart 柱状图,X轴斜着展示

option { color: [‘#3398DB’], tooltip: { trigger: ‘axis’, axisPointer: { // 坐标轴指示器&#xff0c;坐标轴触发有效 type: ‘shadow’ // 默认为直线&#xff0c;可选为&#xff1a;‘line’ | ‘shadow’ } }, grid: { left: ‘3%’, right: ‘4%’, bottom: ‘3%’…