Flowable7.x学习笔记(九)部署 BPMN XML 流程

前言

        到本篇为止,我们已经完成了流程定义以及其 BPMN XML 本身的查询和新增功能,那我们有有了XML之后就可以开始着手研究实现 Flowable7对流程的各种操作了,比如部署,挂起,发起等等。

        首先第一步,我们本篇文章先来探讨一下 BPMN XML 的部署(deploy)的知识点以及基于当前框架的实践操作。

一、部署(deploy)是什么?

        部署(deploy)是 Flowable7 中将流程定义、表单、决策规则等资源打包、解析、校验并持久化到引擎数据库的关键环节。

二、部署(deploy)发挥的作用

① 资源解析与合法性校验

        Flowable 在部署阶段将 BPMN XML 转为内存模型(BpmnModel),并对流程结构(如 StartEvent、连线正确性等)进行基础校验,以避免运行时异常​。

② 持久化部署数据

        部署操作会写入多张数据库表:

  • ACT_RE_DEPLOYMENT:部署元信息;

  • ACT_GE_BYTEARRAY:流程定义、图像等二进制资源;

  • ACT_RE_PROCDEF:流程定义记录,包含 key、version、deploymentId 等字段​。

③ 流程定义注册

        只有部署后的流程定义才能被 RuntimeService.startProcessInstanceByKey() 识别并实例化,部署是流程从“设计”到“执行”的必要桥梁​。 

④ 版本管理

        每次部署同一流程 key 时,Flowable 会在 ACT_RE_PROCDEF.version 上递增版本号,旧版可继续服务已运行实例,新版可用于新实例,实现灰度发布和快速回滚​。

⑤ 集群高可用

        集群模式下,各节点共享同一数据库的部署信息,确保任意节点都能加载相同流程定义,实现负载均衡与故障切换​。

⑥ 审计与追踪

        部署记录与资源持久化后,可通过 API 或管理界面查询历史部署、导出流程图等,对合规审计和故障排查至关重要​。

三、执行部署(deploy)的必要性

① 流程实例启动前提

        未部署的流程定义对引擎“不可见”,无法创建实例,也无法执行任何任务或事件​。

② 提前发现设计问题

        部署时的解析和校验能在 CI/CD 流程中及早捕获模型缺陷,降低生产环境风险​。

③ 支持多版本并存

        通过部署版控,可平滑升级流程、验证新版行为,并在出现问题时迅速回滚到旧版,提高系统可靠性​。

④ 资源统一管理

        将流程、表单与决策规则集中存储并分类(如设置 namecategorytenantId),便于检索与运维管理​。

⑤ 程序化与自动化

        无论是通过 Java API、Spring Boot 自动部署还是 REST 接口,部署都可集成至 DevOps 管道,确保每次版本发布都能稳定、可控。

四、后端:完成部署功能

① 创建一个常量类

package com.ceair.constant;/*** @author wangbaohai* @ClassName Flowable7Constants* @description: Flowable7 常量类* @date 2025年04月18日* @version: 1.0.0*/
public class Flowable7Constants {/*** 流程 xml 文件后缀*/public static final String BPMN_XML_SUFFIX = ".bpmn20.xml";}

② 创建一个流程状态枚举类

package com.ceair.enums;import lombok.Getter;/*** @author wangbaohai* @ClassName ProcessStatus* @description: 流程状态* @date 2025年04月18日* @version: 1.0.0*/
@Getter
public enum ProcessStatus {/*** 0:草稿*/DRAFT(0, "草稿"),/*** 0:发布*/PUBLISH(1, "发布"),/*** 0:草稿*/DEACTIVATE(2, "停用");/*** 状态码*/private final Integer code;/*** 状态描述*/private final String value;ProcessStatus(Integer code, String value) {this.code = code;this.value = value;}}

③ 创建一个流程引擎配置类

主要是为了让Flowable支持中文,具体什么字体可以自由指定,我使用【宋体】

package com.ceair.config;import org.flowable.spring.SpringProcessEngineConfiguration;
import org.flowable.spring.boot.EngineConfigurationConfigurer;
import org.springframework.context.annotation.Configuration;/*** @author wangbaohai* @ClassName FlowableConfig* @description: Flowable配置类* @date 2025年04月18日* @version: 1.0.0*/
@Configuration
public class FlowableConfig implements EngineConfigurationConfigurer<SpringProcessEngineConfiguration> {/*** 配置流程引擎的字体设置,以解决流程图中中文显示乱码的问题。** @param engineConfiguration SpringProcessEngineConfiguration 对象,用于配置流程引擎的相关属性。*                            该参数不能为空,且应为已初始化的配置对象。** 此方法通过设置活动字体、标签字体和注解字体为“宋体”,确保生成的流程图中中文能够正确显示。*/@Overridepublic void configure(SpringProcessEngineConfiguration engineConfiguration) {// 设置流程图中活动节点的字体为宋体,避免中文乱码问题engineConfiguration.setActivityFontName("宋体");// 设置流程图中标签的字体为宋体,确保标签中的中文正常显示engineConfiguration.setLabelFontName("宋体");// 设置流程图中注解的字体为宋体,防止注解内容出现中文乱码engineConfiguration.setAnnotationFontName("宋体");}
}

④ 创建一个请求类

package com.ceair.entity.request;import lombok.Data;import java.io.Serial;
import java.io.Serializable;/*** @author wangbaohai* @ClassName DeployProcessDefinitionXmlReq* @description: 部署流程定义XML请求对象* @date 2025年04月18日* @version: 1.0.0*/
@Data
public class DeployProcessDefinitionXmlReq implements Serializable {@Serialprivate static final long serialVersionUID = 1L;/*** 流程唯一标识(业务中使用的 key)*/private String processKey;/*** 流程版本号*/private Integer processVersion;}

⑤ 创建一个部署服务

/*** 部署BPMN XML文件的函数。** 该函数接收一个包含流程定义XML部署请求的对象,并返回部署结果。** @param deployProcessDefinitionXmlReq 包含流程定义XML部署请求的参数对象。*        该对象通常包含BPMN XML文件的内容、部署的相关配置信息等。** @return Boolean 返回部署结果。*         - 如果部署成功,返回true;*         - 如果部署失败,返回false。*/
Boolean deployBpmnXml(DeployProcessDefinitionXmlReq deployProcessDefinitionXmlReq);

⑥ 创建服务的实现

private final RepositoryService repositoryService;/*** 部署 BPMN XML 流程定义。** @param deployProcessDefinitionXmlReq 包含部署流程定义所需信息的请求对象。*                                      必须包含有效的流程定义标识和相关信息。* @return 返回布尔值,表示部署是否成功。如果方法执行完成且未抛出异常,则返回 true。* @throws BusinessException 如果在部署过程中发生业务异常,则抛出此异常。*                           异常信息会记录日志并重新抛出。* @throws Exception         如果在部署过程中发生未知异常,则包装为 BusinessException 抛出。*/
@Override
public Boolean deployBpmnXml(DeployProcessDefinitionXmlReq deployProcessDefinitionXmlReq) {try {// 校验请求参数的合法性,确保输入数据符合业务要求validateRequest(deployProcessDefinitionXmlReq);// 查询流程定义信息,获取与请求参数匹配的流程定义对象ProcessDefinition processDefinition = fetchProcessDefinition(deployProcessDefinitionXmlReq);// 获取流程定义对应的 BPMN XML 数据,用于后续部署操作String bpmnXml = fetchBpmnXml(processDefinition);// 使用 Flowable 的 RepositoryService 部署流程定义repositoryService.createDeployment().addString(processDefinition.getProcessKey() + Flowable7Constants.BPMN_XML_SUFFIX, bpmnXml).name(processDefinition.getProcessName()).key(processDefinition.getProcessKey()).deploy();// 将流程定义的状态变更为 1:发布processDefinition.setProcessStatus(ProcessStatus.PUBLISH.getCode());updateById(processDefinition);} catch (BusinessException e) {// 记录业务异常日志,并重新抛出异常以便调用方处理log.error("部署流程时出现业务异常,具体异常原因: {}", e.getMessage(), e);throw e;} catch (Exception e) {// 记录未知异常日志,并将异常包装为 BusinessException 抛出log.error("部署流程时出现未知异常,具体异常原因: {}", e.getMessage(), e);throw new BusinessException("部署流程时出现未知异常", e);}return true;
}// 参数校验方法
private void validateRequest(DeployProcessDefinitionXmlReq deployProcessDefinitionXmlReq) {if (deployProcessDefinitionXmlReq == null|| StringUtils.isBlank(deployProcessDefinitionXmlReq.getProcessKey())|| deployProcessDefinitionXmlReq.getProcessVersion() == null) {log.error("部署流程时参数校验失败,请重新选择流程定义");throw new BusinessException("部署流程时参数校验失败,请重新选择流程定义");}
}// 查询流程定义信息方法
private ProcessDefinition fetchProcessDefinition(DeployProcessDefinitionXmlReq deployProcessDefinitionXmlReq) {ProcessDefinition processDefinition = lambdaQuery().eq(ProcessDefinition::getProcessKey, deployProcessDefinitionXmlReq.getProcessKey()).eq(ProcessDefinition::getProcessVersion, deployProcessDefinitionXmlReq.getProcessVersion()).one();if (processDefinition == null) {log.error("部署流程时流程定义已不存在");throw new BusinessException("部署流程时流程定义已不存在");}return processDefinition;
}// 获取 BPMN XML 数据方法
private String fetchBpmnXml(ProcessDefinition processDefinition) {Optional<BpmnDefinitions> bpmnDefinitions = bpmnDefinitionRepository.findById(processDefinition.getXmlMongoId());return bpmnDefinitions.map(def -> {String bpmnXml = def.getBpmnXml();if (StringUtils.isBlank(bpmnXml)) {log.error("部署流程时流程定义的 BPMN XML 数据为空");throw new BusinessException("部署流程时流程定义的 BPMN XML 数据为空");}return bpmnXml;}).orElseThrow(() -> {log.error("部署流程时流程定义的 BPMN XML 数据为空");return new BusinessException("部署流程时流程定义的 BPMN XML 数据为空");});
}

⑦ 创建接口

/*** 部署流程定义XML的接口方法。** 该方法通过接收一个部署流程定义XML的请求对象,调用服务层方法完成流程定义的部署。* 如果部署过程中发生异常,会根据异常类型返回具体的错误信息。** @param deployProcessDefinitionXmlReq 部署流程定义XML请求对象,包含部署所需的必要信息(必填)。* @return 返回一个Result对象,包含布尔值表示部署是否成功。*         - 如果成功,返回Result.success(true)。*         - 如果失败,返回Result.error(),并附带具体的错误信息。*/
@PreAuthorize("hasAnyAuthority('/api/v1/definition/deployProcessDefinitionXml')")
@Parameter(name="deployProcessDefinitionXmlReq", description = "部署流程定义XML请求对象", required = true)
@Operation(summary = "部署流程定义XML")
@PostMapping("/deployProcessDefinitionXml")
public Result<Boolean> deployProcessDefinitionXml(@RequestBody DeployProcessDefinitionXmlReq deployProcessDefinitionXmlReq) {try {// 调用服务层方法部署流程定义XML,并返回成功结果return Result.success(processDefinitionService.deployBpmnXml(deployProcessDefinitionXmlReq));} catch (BusinessException e) {// 捕获业务逻辑异常,返回具体的错误信息return Result.error("部署流程定义失败,发生已知业务异常,具体原因:" + e);} catch (Exception e) {// 捕获其他未知异常,返回通用错误信息return Result.error("部署流程定义失败,发生未知业务异常,具体原因:" + e);}
}

五、前端:完成部署界面

① 创建请求对象

// 发布流程 xml 请求对象
export interface PublishProcessDefinitionXmlReq {processKey?: string // 流程唯一标识(业务中使用的 key)processVersion?: number
}

② 创建请求接口

// 发布流程定义 xml
export function publishProcessDefinition(data: PublishProcessDefinitionXmlReq) {return request.post<any>({url: '/pm-process/api/v1/definition/deployProcessDefinitionXml',data,})
}

③ 引入请求接口

④ 创建按钮方法

/*** 发布流程定义的异步函数。** @param {ProcessDefinitionVO} row - 包含流程定义信息的对象,需提供 processKey 和 processVersion 属性。* @returns {Promise<void>} - 无返回值,函数主要通过调用接口和更新状态来完成操作。*/
async function onPublishProcessDefinition(row: ProcessDefinitionVO) {try {// 调用发布流程定义的接口,并传递流程键和版本信息const result: any = await publishProcessDefinition({processKey: row.processKey,processVersion: row.processVersion,})// 根据接口返回的结果判断发布是否成功,并执行相应操作if (result.success && result.code === 200) {// 如果发布成功,提示用户操作成功ElMessage({message: '发布成功',type: 'success',})// 初始化分页参数为默认值:当前页为第一页,每页显示10条数据currentPage.value = 1pageSize.value = 10// 清空流程名称的输入框内容processName.value = ''// 重新获取流程定义数据以刷新页面getProcessDefinitionPageData()}else {// 如果发布失败,提示用户具体的错误信息ElMessage({message: result.message,})}}catch (error) {// 捕获异常并提取错误信息,确保提供有意义的错误提示let errorMessage = '未知错误'if (error instanceof Error) {errorMessage = error.message}// 显示异常错误消息,提示用户发布失败的具体原因ElMessage({message: `发布失败: ${errorMessage || '未知错误'}`,type: 'error',})}
}

⑤ 操作列增加按钮

<!-- 发布流程功能按钮 -->
<el-button type="primary" @click="onPublishProcessDefinition(scope.row)">
  发布
</el-button>

六、添加权限 

① 增加按钮

② 绑定角色权限

七、XML开启允许发布

填写基础信息并且按照图中勾选即可

八、执行发布操作以及验证

验证 【ACT_RE_DEPLOYMENT】表中的NAME和KEY字段都和我们自己的流程定义的对应

验证 【ACT_GE_BYTEARRAY】

验证【ACT_RE_PROCDEF】

后记

本篇文章的前后端仓库地址请查询专栏第一篇文章

本文的后端分支是 process-4

本文的前端分支是 process-6

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

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

相关文章

electron 渲染进程按钮创建新window,报BrowserWindow is not a constructor错误;

在 Electron 中&#xff0c;有主进程和渲染进程 主进程&#xff1a;在Node.js环境中运行—意味着能够使用require模块并使用所有Node.js API 渲染进程&#xff1a;每个electron应用都会为每个打开的BrowserWindow&#xff08;与每个网页嵌入&#xff09;生成一个单独的渲染器进…

深入规划 Elasticsearch 索引:策略与实践

一、Elasticsearch 索引概述 &#xff08;一&#xff09;索引基本概念 Elasticsearch 是一个分布式、高性能的全文搜索引擎&#xff0c;其核心概念之一便是索引。索引本质上是一个存储文档的逻辑容器&#xff0c;它使得数据能够在高效的检索机制下被查询到。当我们对文档进行…

llamafactory的包安装

cuda版本12.1&#xff0c;python版本3.10&#xff0c;torch版本2.4.0&#xff0c;几个关键包版本如下&#xff1a; torch2.4.0cu121 transformers4.48.3 triton3.0.0 flash-attn2.7.1.post4 xformers0.0.27.post2 vllm0.6.3.post1 vllm-flash-attn2.6.1 unsloth2025.3.18 unsl…

Redis专题

前言 Redis的各种思想跟机组Cache和操作系统对进程的管理非常类似&#xff01; 一&#xff1a;看到你的简历上写了你的项目里面用到了redis&#xff0c;为啥用redis&#xff1f; 因为传统的关系型数据库如Mysql,已经不能适用所有的场景&#xff0c;比如秒杀的库存扣减&#xff…

【Rust 精进之路之第7篇-函数之道】定义、调用与参数传递:构建代码的基本单元

系列: Rust 精进之路:构建可靠、高效软件的底层逻辑 作者: 码觉客 发布日期: 2025-04-20 引言:封装逻辑,代码复用的基石 在之前的文章中,我们已经探索了 Rust 如何处理数据(变量、标量类型、复合类型)以及如何控制程序的执行流程(if/else、循环)。这些构成了编写简…

文件有几十个T,需要做rag,用ragFlow能否快速落地呢?

一、RAGFlow的优势 1、RAGFlow处理大规模数据性能&#xff1a; &#xff08;1&#xff09;、RAGFlow支持分布式索引构建&#xff0c;采用分片技术&#xff0c;能够处理TB级数据。 &#xff08;2&#xff09;、它结合向量搜索和关键词搜索&#xff0c;提高检索效率。 &#xf…

安卓的桌面 launcher是什么

安卓的桌面Launcher是一种安卓应用程序&#xff0c;它主要负责管理和展示手机主屏幕的界面以及相关功能&#xff0c;为用户提供与设备交互的主要入口。以下是其详细介绍&#xff1a; 功能 主屏幕管理&#xff1a;用户可以在主屏幕上添加、删除和排列各种应用程序图标、小部件…

【学习笔记】计算机网络(九)—— 无线网络和移动网络

第9章 无线网络和移动网络 文章目录 第9章 无线网络和移动网络9.1 无线局域网WLAN9.1.1 无线局域网的组成9.1.2 802.11局域网的物理层9.1.3 802.11局域网的MAC层协议CSMA 协议CSMA/CD 协议 - 总线型 - 半双工CSMA/CA 协议 9.1.4 802.11局域网的MAC帧 9.2 无线个人区域网WPAN9.3…

无线网络入侵检测系统实战 | 基于React+Python的可视化安全平台开发详解

随着无线网络的普及&#xff0c;网络攻击风险也日益严峻。本项目旨在构建一个实时监测、智能识别、高效防护的无线网络安全平台&#xff0c;通过结合前后端技术与安全算法&#xff0c;实现对常见攻击行为的有效监控和防御。 一、项目简介与功能目的 本系统是一款基于 React 前…

速通FlinkCDC3.0

1.FlinkCDC概述 1.1FlinkCDC是什么&#xff1f; FlinkCDC&#xff08;Flink Change Data Capture&#xff09;是一个用于实时捕获数据库变更日志的工具&#xff0c;它可以将数据库的变更实时同步到ApacheFlink系统中。 1.2 FlinkCDC的三个版本&#xff1f; 1.x 这个版本的Fli…

B+树节点与插入操作

B树节点与插入操作 设计B树节点 在设计B树的数据结构时&#xff0c;我们首先需要定义节点的格式&#xff0c;这将帮助我们理解如何进行插入、删除以及分裂和合并操作。以下是对B树节点设计的详细说明。 节点格式概述 所有的B树节点大小相同&#xff0c;这是为了后续使用自由…

C# 检查字符串是否包含在另一个字符串中

string shopList "我是大浪,你的小狼"; this.ShopId"你的小狼"; bool existsShopId false; if (!string.IsNullOrEmpty(shopList)) {existsShopId shopList.Split(,).Any(part > part.Trim() this.ShopId); }检查 goodsIdSet 中的每个元素是否都在 …

珈和科技遥感赋能农业保险创新 入选省级卫星应用示范标杆

为促进空天信息与数字经济深度融合&#xff0c;拓展卫星数据应用场景价值&#xff0c;提升卫星数据应用效能和用户体验&#xff0c;加速卫星遥感技术向民生领域转化应用&#xff0c;近日&#xff0c;湖北省国防科工办组织开展了2024年湖北省卫星应用示范项目遴选工作。 经多渠…

深入理解 React 组件的生命周期:从创建到销毁的全过程

React 作为当今最流行的前端框架之一&#xff0c;其组件生命周期是每个 React 开发者必须掌握的核心概念。本文将全面剖析 React 组件的生命周期&#xff0c;包括类组件的各个生命周期方法和函数组件如何使用 Hooks 模拟生命周期行为&#xff0c;帮助开发者编写更高效、更健壮的…

缓存 --- Redis性能瓶颈和大Key问题

缓存 --- Redis性能瓶颈和大Key问题 内存瓶颈网络瓶颈CPU 瓶颈持久化瓶颈大key问题优化方案 Redis 是一个高性能的内存数据库&#xff0c;但在实际使用中&#xff0c;可能会在内存、网络、CPU、持久化、大键值对等方面遇到性能瓶颈。下面从这些方面详细分析 Redis 的性能瓶颈&a…

Python爬虫与代理IP:高效抓取数据的实战指南

目录 一、基础概念解析 1.1 爬虫的工作原理 1.2 代理IP的作用 二、环境搭建与工具选择 2.1 Python库准备 2.2 代理IP选择技巧 三、实战步骤分解 3.1 基础版&#xff1a;单线程免费代理 3.2 进阶版&#xff1a;多线程付费代理池 3.3 终极版&#xff1a;Scrapy框架自动…

Nginx HTTP 414 与“大面积”式洪水攻击联合防御实战

一、引言 在大规模分布式应用中&#xff0c;Nginx 常作为前端负载均衡和反向代理服务器。攻击者若结合超长 URI/头部攻击&#xff08;触发 HTTP 414&#xff09;与海量洪水攻击&#xff0c;可在网络层与应用层形成双重打击&#xff1a;一方面耗尽缓冲区和内存&#xff0c;另一…

【上位机——MFC】运行时类信息机制

运行时类信息机制的使用 类必须派生自CObject类内必须添加声明宏DECLARE_DYNAMIC(theClass)3.类外必须添加实现宏 IMPLEMENT_DYNAMIC(theClass,baseClass) 具备上述三个条件后&#xff0c;CObject::IsKindOf函数就可以正确判断对象是否属于某个类。 代码示例 #include <…

Maven插件管理的基本原理

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;精通Java编…

卷积神经网络--手写数字识别

本文我们通过搭建卷积神经网络模型&#xff0c;实现手写数字识别。 pytorch中提供了手写数字的数据集 &#xff0c;我们可以直接从pytorch中下载 MNIST中包含70000张手写数字图像&#xff1a;60000张用于训练&#xff0c;10000张用于测试 图像是灰度的&#xff0c;28x28像素 …