Springboot整合Camunda工作流引擎实现审批流程实例
环境:Spingboot2.6.14 +
camunda-spring-boot-starter7.18.0
环境配置
依赖配置
<camunda.version>7.18.0</camunda.version>
<dependency><groupId>org.camunda.bpm.springboot</groupId><artifactId>camunda-bpm-spring-boot-starter-webapp</artifactId><version>${camunda.version}</version>
</dependency>
<dependency><groupId>org.camunda.bpm.springboot</groupId><artifactId>camunda-bpm-spring-boot-starter-rest</artifactId><version>${camunda.version}</version>
</dependency>
应用程序配置
camunda.bpm:webapp:# 设置管理控制台的访问上下文application-path: /workflowauto-deployment-enabled: trueadmin-user:# 配置登录管理控制台的用户id: adminpassword: adminfirstName: adminfilter:create: All tasksdatabase:#数据库类型type: mysql #是否自动更新表信息schema-update: true
logging:level:#配置日志,这样在开发过程中就能看到每步执行的SQL语句了'[org.camunda.bpm.engine.impl.persistence.entity]': debug
---
spring:jersey:application-path: /api-flowtype: servletservlet:load-on-startup: 0
通过上面的配置后访问控制台:
http://localhost:8100/workflow/

默认是没有上面的tasks中的内容,这里是我之前测试数据
环境准备好后,接下来就可以设计工作流程。
上面的
camunda-bpm-spring-boot-starter-rest依赖中定义了一系列操作camunda的 rest api 这api的实现是通过jersey实现,我们可以通过/api-flow前缀来访问这些接口,具体有哪些接口,我们可以通过官方提供的
camunda-bpm-run-7.18.0.zip 解压后运行访问如下地址就能查看所有的api接口:
http://localhost:8080/swaggerui/#/
设计流程
这里设计两个节点的审批流程,经理审批---》人事审批 流程。

经理审批节点

人事审批节点
上面配置了2个用户任务节点,并且为每个任务节点都设置了表达式,指定节点的审批人。
最终生成的流程XML内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" id="sample-diagram" targetNamespace="http://pack.org/bpmn" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd"><bpmn2:process id="Process_1" isExecutable="true"><bpmn2:startEvent id="StartEvent_1"><bpmn2:outgoing>Flow_18pxcpx</bpmn2:outgoing></bpmn2:startEvent><bpmn2:sequenceFlow id="Flow_18pxcpx" sourceRef="StartEvent_1" targetRef="Activity_0vs8hu4" /><bpmn2:userTask id="Activity_0vs8hu4" camunda:assignee="${uid}"><bpmn2:incoming>Flow_18pxcpx</bpmn2:incoming><bpmn2:outgoing>Flow_0n014x3</bpmn2:outgoing></bpmn2:userTask><bpmn2:sequenceFlow id="Flow_0n014x3" sourceRef="Activity_0vs8hu4" targetRef="Activity_0bcruuz" /><bpmn2:userTask id="Activity_0bcruuz" camunda:assignee="${mid}"><bpmn2:incoming>Flow_0n014x3</bpmn2:incoming><bpmn2:outgoing>Flow_0dsfy6s</bpmn2:outgoing></bpmn2:userTask><bpmn2:endEvent id="Event_1xosttx"><bpmn2:incoming>Flow_0dsfy6s</bpmn2:incoming></bpmn2:endEvent><bpmn2:sequenceFlow id="Flow_0dsfy6s" sourceRef="Activity_0bcruuz" targetRef="Event_1xosttx" /></bpmn2:process><bpmndi:BPMNDiagram id="BPMNDiagram_1"><bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1"><bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1"><dc:Bounds x="252" y="252" width="36" height="36" /></bpmndi:BPMNShape><bpmndi:BPMNShape id="Activity_1py8b5e_di" bpmnElement="Activity_0vs8hu4"><dc:Bounds x="340" y="230" width="100" height="80" /></bpmndi:BPMNShape><bpmndi:BPMNShape id="Activity_0arbs87_di" bpmnElement="Activity_0bcruuz"><dc:Bounds x="500" y="230" width="100" height="80" /></bpmndi:BPMNShape><bpmndi:BPMNShape id="Event_1xosttx_di" bpmnElement="Event_1xosttx"><dc:Bounds x="662" y="252" width="36" height="36" /></bpmndi:BPMNShape><bpmndi:BPMNEdge id="Flow_18pxcpx_di" bpmnElement="Flow_18pxcpx"><di:waypoint x="288" y="270" /><di:waypoint x="340" y="270" /></bpmndi:BPMNEdge><bpmndi:BPMNEdge id="Flow_0n014x3_di" bpmnElement="Flow_0n014x3"><di:waypoint x="440" y="270" /><di:waypoint x="500" y="270" /></bpmndi:BPMNEdge><bpmndi:BPMNEdge id="Flow_0dsfy6s_di" bpmnElement="Flow_0dsfy6s"><di:waypoint x="600" y="270" /><di:waypoint x="662" y="270" /></bpmndi:BPMNEdge></bpmndi:BPMNPlane></bpmndi:BPMNDiagram>
</bpmn2:definitions>
部署流程
这里我不通过上面的rest api 进行部署,而是通过自定义的接口然后调用camunda的相关api来实现流程部署。
上面的流程设计我是通过vue整合的camunda进行设计,并没有使用官方提供的设计器。设计完成后直接上传到服务端。
接口
@RestController
@RequestMapping("/camunda")
public class BpmnController {// 上传路径@Value("${gx.camunda.upload}")private String path ;// 通用的工作流操作api服务类@Resourceprivate ProcessService processService ;@PostMapping("/bpmn/upload")public AjaxResult uploadFile(MultipartFile file, String fileName, String name) throws Exception {try {// 上传并返回新文件名称InputStream is = file.getInputStream() ;File storageFile = new File(path + File.separator + fileName) ;FileOutputStream fos = new FileOutputStream(new File(path + File.separator + fileName)) ;byte[] buf = new byte[10 * 1024] ;int len = -1 ;while((len = is.read(buf)) > -1) {fos.write(buf, 0, len) ;}fos.close() ;is.close() ;// 创建部署流程processService.createDeploy(fileName, name, new FileSystemResource(storageFile)) ;return AjaxResult.success();} catch (Exception e) {return AjaxResult.error(e.getMessage());}}
}
部署流程Service
// 这个是camunda spring boot starter 自动配置
@Resource
private RepositoryService repositoryService ;public void createDeploy(String resourceName, String name, org.springframework.core.io.Resource resource) {try {Deployment deployment = repositoryService.createDeployment().addInputStream(resourceName, resource.getInputStream()).name(name).deploy();logger.info("流程部署id: {}", deployment.getId());logger.info("流程部署名称: {}", deployment.getName());} catch (IOException e) {throw new RuntimeException(e) ;}
}
执行上面的接口就能将上面设计的流程部署到camunda中(其实就是将流程文件保存到了数据库中,对应的数据表是:act_ge_bytearray)。
启动流程
启动流程还是一样,通过我们自己的接口来实现。
接口
@RestController
@RequestMapping("/process")
public class ProcessController {@Resourceprivate ProcessService processService ;// 根据流程定义id,启动流程;整个流程需要动态传2个参数(审批人),如果不传将会报错@GetMapping("/start/{processDefinitionId}")public AjaxResult startProcess(@PathVariable("processDefinitionId") String processDefinitionId) {Map<String, Object> variables = new HashMap<>() ;variables.put("uid", "1") ;variables.put("mid", "1000") ;processService.startProcessInstanceAssignVariables(processDefinitionId, "AKF", variables) ;return AjaxResult.success("流程启动成功") ;}
}
服务Service接口
@Resource
private RuntimeService runtimeService ;public ProcessInstance startProcessInstanceAssignVariables(String processDefinitionId, String businessKey, Map<String, Object> variables) {ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinitionId, businessKey, variables);logger.info("流程定义ID: {}", processInstance.getProcessDefinitionId());logger.info("流程实例ID: {}", processInstance.getId());logger.info("BussinessKey: {}", processInstance.getBusinessKey()) ;return processInstance ;
}
流程启动后就可以查看当前需要自己审批的所有审批单

接口实现
@Resource
private TaskService taskService ;
@Resource
private ManagementService managementService ;
// 根据时间段查询
public List<Task> queryTasksByBusinessAndCreateTime(String assignee, String businessKey, String startTime, String endTime) {NativeTaskQuery nativeQuery = taskService.createNativeTaskQuery() ;nativeQuery.sql("select distinct RES.* from " + managementService.getTableName(TaskEntity.class) + " RES "+ " left join " + managementService.getTableName(IdentityLinkEntity.class) + " I on I.TASK_ID_ = RES.ID_ "+ " WHERE (RES.ASSIGNEE_ = #{assignee} or "+ " (RES.ASSIGNEE_ is null and I.TYPE_ = 'candidate' "+ " and (I.USER_ID_ = #{assignee} or I.GROUP_ID_ IN ( #{assignee} ) ))) "+ " and RES.CREATE_TIME_ between #{startTime} and #{endTime} "+ " order by RES.CREATE_TIME_ asc LIMIT #{size} OFFSET 0") ;nativeQuery.parameter("assignee", assignee) ;nativeQuery.parameter("startTime", startTime) ;nativeQuery.parameter("endTime", endTime) ;nativeQuery.parameter("size", Integer.MAX_VALUE) ;return nativeQuery.list() ;
}
审批流程
流程启动后,接下来就是各个用户任务节点配置的用户进行审批
接口
@GetMapping("/approve/{id}")
public AjaxResult approve(@PathVariable("id") String instanceId) {if (StringUtils.isEmpty(instanceId)) {return AjaxResult.error("未知审批任务") ;}// 下面的参数信息应该自行保存管理(与发起审批设置的指派人要一致)Map<String, Object> variables = new HashMap<>() ;// 第一个节点所要提供的遍历信息(这里就是依次类推,mid等)variables.put("uid", "1") ;processService.executionTask(variables, instanceId, task -> {}, null) ;return AjaxResult.success() ;
}
服务Service接口
@Resource
private TaskService taskService ;
@Resource
private RuntimeService runtimeService ;@Transactional
public void executionTask(Map<String, Object> variables, String instanceId, Consumer<Task> consumer, String type) {Task task = taskService.createTaskQuery().processInstanceId(instanceId).singleResult() ;if (task == null) {logger.error("任务【{}】不存在", instanceId) ;throw new RuntimeException("任务【" + instanceId + "】不存在") ;}taskService.setVariables(task.getId(), variables);taskService.complete(task.getId(), variables) ;long count = runtimeService.createExecutionQuery().processInstanceId(instanceId).count();if (count == 0) {consumer.accept(task) ;}
}
以上就完成了从整个流程的生命周期:
设计流程 ---》部署流程 ---》启动流程 ---》审批流程
完毕!!!




相关文章:
Springboot整合Camunda工作流引擎实现审批流程实例
环境:Spingboot2.6.14 camunda-spring-boot-starter7.18.0 环境配置 依赖配置 <camunda.version>7.18.0</camunda.version> <dependency><groupId>org.camunda.bpm.springboot</groupId><artifactId>camunda-bpm-spring-boo…...
PHP设计模式21-工厂模式的讲解及应用
文章目录 前言基础知识简单工厂模式工厂方法模式抽象工厂模式 详解工厂模式普通的实现更加优雅的实现 总结 前言 本文已收录于PHP全栈系列专栏:PHP快速入门与实战 学会好设计模式,能够对我们的技术水平得到非常大的提升。同时也会让我们的代码写的非常…...
【玩转Docker小鲸鱼叭】理解Docker的核心概念
Docker核心概念 Docker有三大核心概念:镜像(Image)、容器(Container)、仓库(Repository) 1、镜像(Image) Docker镜像 是我们创建和运行Docker容器的基础,它…...
Eureka 心跳和服务续约源码探秘——图解、源码级解析
🍊 Java学习:社区快速通道 🍊 深入浅出RocketMQ设计思想:深入浅出RocketMQ设计思想 🍊 绝对不一样的职场干货:大厂最佳实践经验指南 📆 最近更新:2023年5月25日 🍊 点赞 👍 收藏 ⭐留言 📝 都是我最大的动力! 文章目录 分布式系统的心跳机制心跳机制的实…...
代码随想录二刷 530 二叉搜索树的最小绝对差 98. 验证二叉搜索树 700. 二叉搜索树中的搜索
530 二叉搜索树的最小绝对差 代码如下 func getMinimumDifference(root *TreeNode) int { var pre *TreeNode res : math.MaxInt var traverse func(root * TreeNode) traverse func(root * TreeNode) { if root nil { return } traverse(root.Left) …...
Docker安装——CentOS7.6(详细版)
ps:docker官网 在 CentOS 上安装 Docker 引擎 |官方文档 () 一、确定版本(必须是7以上版本) cat /etc/redhat-release 二、卸载旧版本(或者之前装过,没有安装过就不用管了) (root用…...
论信息系统项目的整体管理(范文)
论信息系统项目的整体管理(范文) 【摘要】 2016年10月,XX省卫生健康委启动了XX省分级转诊服务平台建设项目,我在项目中担任项目经理,负责项目的全面管理工作。该平台作为全省上下级医院转诊的信息化通道,…...
【音视频处理】音频编码AAC详解,低码率提高音质?
大家好,欢迎来到停止重构的频道。 本期我们介绍音频编码格式AAC。 AAC是音频最常用的编码格式之一,几乎所有的播放器都支持这个编码格式。 其他音频编码格式都是类似的,只是某些细节存在差别,如压缩算法、某些音频参数存在限制…...
逆函数学习
逆函数 给定关系 R ⊆ X Y R\subseteq X\times Y R⊆XY,颠倒 R R R的所有有序偶可以得到 R R R的逆关系 R ~ ⊆ Y X \tilde{R}\subseteq Y\times X R~⊆YX 但是对于函数 f : X → Y f:X\to Y f:X→Y而言,其逆关系 f ~ \tilde{f} f~可能不是 Y Y Y到…...
代码审计——SSRF详解
为方便您的阅读,可点击下方蓝色字体,进行跳转↓↓↓ 01 漏洞描述02 审计要点03 漏洞特征04 漏洞案例05 修复方案 01 漏洞描述 服务端请求伪造攻击(SSRF)也成为跨站点端口攻击,是由于一些应用在向第三方主机请求资源时提…...
搭建Scala开发环境
一、Windows上安装Scala 1、到Scala官网下载Scala Scala2.13.10下载网址:https://www.scala-lang.org/download/2.13.10.html 单击【scala-2.13.10.msi】超链接,将scala安装程序下载到本地 2、安装Scala 双击安装程序图标,进入安装向导&…...
BLIP和BLIP2
文章主要是对BLIP2 (使用冻结图像编码器和大型语言模型的Bootstrapping语言图像预训练)论文的阅读笔记,也对BLIP(用于统一视觉语言理解和生成的Bootstrapping语言图像预训练)算法进行了简单的介绍。 文章:…...
微信小程序开发实战 ⑨(TabBar)
作者 : SYFStrive 博客首页 : HomePage 📜: 微信小程序 📌:个人社区(欢迎大佬们加入) 👉:社区链接🔗 📌:觉得文章不错可以点点关注 Ǵ…...
微前端探秘:初始微前端、现有方案和未来趋势
初识微前端 微前端是什么 概念: 微前端是指存在于浏览器中的微服务。 微前端是一种类似于微服务的架构,它将微服务的理念应用于浏览器端,即将单页面前端应用由单一的单体应用转变为把多个小型前端应用聚合为一体的应用。这就意味着前端应用…...
运维(SRE)成长之路-第2天 文本编辑工具之神VIM
vi和vim简介 在Linux中我们经常编辑修改文本文件,即由ASCII, Unicode 或其它编码的纯文字的文件。之前介绍过nano,实际工作中我们会使用更为专业,功能强大的工具 文本编辑种类: 全屏编辑器:nano(字符工具…...
45从零开始学Java之详解static修饰符、静态变量和静态方法
作者:孙玉昌,昵称【一一哥】,另外【壹壹哥】也是我哦 千锋教育高级教研员、CSDN博客专家、万粉博主、阿里云专家博主、掘金优质作者 前言 在前一篇文章中,壹哥给大家讲解了abstract关键字,从而我们掌握了抽象类与抽象…...
电商超卖,从业务到设计
编辑导语:超卖这一概念的定义可以从不同层面进行阐述,比如平台层面、渠道层面、仓库层面等。而假设因超卖导致订单难以履行,则容易让用户体验“打折”。为什么有时电商超卖的现象会发生?可以从哪些角度来降低超卖导致的风险&#…...
【MySQL】表的约束
表的约束 表的约束1. 空属性2. 默认值3. 列描述4. zerofill(自动补零)5. 主键—primary key5.1 复合主键 6. 自增长—auto_increment7.唯一键 --- unique8. 外键 --- foreign key…reference9. 综合案例 表的约束 真正约束字段的是数据类型,…...
【计算机网络】第一章 概述(下)
文章目录 第一章 概述1.5 计算机网络的性能指标1.5.1 速率1.5.2 带宽1.5.3 吞吐量1.5.4 时延 1.6 计算机网络体系结构1.6.1 常见的体系结构1.6.2 分层的必要性1.6.4 体系结构中的专用术语 1.8 习题 第一章 概述 1.5 计算机网络的性能指标 常用的 计算机网络 的性能指标有以下 …...
化工园区人员全过程轨迹化安全解决方案
1、项目背景 化工园区化工厂是生产安全重点单位,对人员定位管理需求强烈。对人员定位主要需求是:一般区域人数统计、人员轨迹、重点区域人员实时精准定位。 华安联大安全化工园区人员全过程轨迹化安全解决方案通过人员实时定位管理、移动轨迹追溯、险情…...
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...
第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明
AI 领域的快速发展正在催生一个新时代,智能代理(agents)不再是孤立的个体,而是能够像一个数字团队一样协作。然而,当前 AI 生态系统的碎片化阻碍了这一愿景的实现,导致了“AI 巴别塔问题”——不同代理之间…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
Typeerror: cannot read properties of undefined (reading ‘XXX‘)
最近需要在离线机器上运行软件,所以得把软件用docker打包起来,大部分功能都没问题,出了一个奇怪的事情。同样的代码,在本机上用vscode可以运行起来,但是打包之后在docker里出现了问题。使用的是dialog组件,…...
【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)
本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...
计算机基础知识解析:从应用到架构的全面拆解
目录 前言 1、 计算机的应用领域:无处不在的数字助手 2、 计算机的进化史:从算盘到量子计算 3、计算机的分类:不止 “台式机和笔记本” 4、计算机的组件:硬件与软件的协同 4.1 硬件:五大核心部件 4.2 软件&#…...
解决:Android studio 编译后报错\app\src\main\cpp\CMakeLists.txt‘ to exist
现象: android studio报错: [CXX1409] D:\GitLab\xxxxx\app.cxx\Debug\3f3w4y1i\arm64-v8a\android_gradle_build.json : expected buildFiles file ‘D:\GitLab\xxxxx\app\src\main\cpp\CMakeLists.txt’ to exist 解决: 不要动CMakeLists.…...
MyBatis中关于缓存的理解
MyBatis缓存 MyBatis系统当中默认定义两级缓存:一级缓存、二级缓存 默认情况下,只有一级缓存开启(sqlSession级别的缓存)二级缓存需要手动开启配置,需要局域namespace级别的缓存 一级缓存(本地缓存&#…...
Pydantic + Function Calling的结合
1、Pydantic Pydantic 是一个 Python 库,用于数据验证和设置管理,通过 Python 类型注解强制执行数据类型。它广泛用于 API 开发(如 FastAPI)、配置管理和数据解析,核心功能包括: 数据验证:通过…...
