SpringBoot+Flowable快速实现工流_动态选择审批人员

前言

OA系统中的工作流不仅是企业日常运营的重要组成部分,也是实现企业数字化转型、提高工作效率和执行力的重要工具。 在国内大部分的工作流系统使用Activiti框架实现。 其实flowable也可以轻松实现工作流业务。在线体验JeecgFlow

flowable简介

Flowable是一个使用Java编写的轻量级业务流程引擎。它主要用于部署BPMN 2.0流程定义(一种用于定义流程的行业XML标准),创建这些流程定义的流程实例,进行查询,访问运行中或历史的流程实例与相关数据等。Flowable流程引擎的特点包括灵活性和可扩展性,提供了图形化的流程设计器,可以与规则引擎集成,支持多种执行环境,并且可以轻易地加入任何Java环境。

版本信息

Flowable 6.7.2

SpringBoot 2.7.12

SpringBoot整合flowable

maven依赖

<flowable.spring-boot.version>6.7.2</flowable.spring-boot.version<dependency><groupId>org.flowable</groupId><artifactId>flowable-spring-boot-starter</artifactId><version>${flowable.spring-boot.version}</version><exclusions><exclusion><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId></exclusion></exclusions>
</dependency>

配置

flowable:#关闭定时任务JOBasync-executor-activate: false#将databaseSchemaUpdate设置为true。当flowable发现库与数据库表结构不一致时,会自动将数据库表结构升级至新版本。database-schema-update: true# 自动部署验证设置:true-开启(默认)、false-关闭check-process-definitions: false#保存历史数据级别设置为full最高级别,便于历史数据的追溯history-level: full

简单两步,就可以启动boot应用。初次运行时flowable会将自动执行flowable中的初始化脚本完成工作流所需要的数据表的建立,如果指定的数据库中还未创建过flowable的相关数据表的话。

流程定义

假设现在有一个请假需求,用户提交申请表单后, 直属领导进行审批操作。 可以是通过或拒绝。并且要求不同部门的人员绑定所属的领导。 Bpmn在线建模

在这里插入图片描述

申请节点,定义用户提交数据的字段

在这里插入图片描述

申请节点, 设置了任务完成后的监听器。用于动态设置审批领导的名称。

在这里插入图片描述

直属领导申请的节点属性设置。

xml内容

<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn="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:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn"><bpmn:process id="Process_choose_leader" name="动态审批" isExecutable="true"><bpmn:startEvent id="StartEvent_1"><bpmn:outgoing>Flow_09w75kg</bpmn:outgoing></bpmn:startEvent><bpmn:sequenceFlow id="Flow_09w75kg" sourceRef="StartEvent_1" targetRef="Activity_1xn21s1" /><bpmn:userTask id="Activity_1xn21s1" name="申请" camunda:assignee="${general}"><bpmn:extensionElements><camunda:formData><camunda:formField id="days" label="请假天数" type="long" defaultValue="1" /><camunda:formField id="reason" label="请假理由" type="string" /><camunda:formField id="type" label="请假类型" type="string" /></camunda:formData><camunda:taskListener delegateExpression="${chooseLeaderListener}" event="complete" /></bpmn:extensionElements><bpmn:incoming>Flow_09w75kg</bpmn:incoming><bpmn:outgoing>Flow_1n7z9g2</bpmn:outgoing></bpmn:userTask><bpmn:sequenceFlow id="Flow_1n7z9g2" sourceRef="Activity_1xn21s1" targetRef="Activity_0474tsq" /><bpmn:userTask id="Activity_0474tsq" name="直属领导" camunda:assignee="${leader}"><bpmn:extensionElements><camunda:formData><camunda:formField id="comment" label="评论" type="string" /><camunda:formField id="imageUrl" label="图片" type="string" /></camunda:formData></bpmn:extensionElements><bpmn:incoming>Flow_1n7z9g2</bpmn:incoming><bpmn:outgoing>Flow_1lyuw6k</bpmn:outgoing></bpmn:userTask><bpmn:endEvent id="Event_10twye6"><bpmn:incoming>Flow_1lyuw6k</bpmn:incoming></bpmn:endEvent><bpmn:sequenceFlow id="Flow_1lyuw6k" sourceRef="Activity_0474tsq" targetRef="Event_10twye6" /></bpmn:process></bpmn:definitions>

代码编写

开始流程

@ApiOperation(value = "发起请假", notes = "提交请假的数据")
@PostMapping("start")
public Result<?> start(@RequestBody LeaveStartReq leaveStartReq) {log.info("start.leaveStartReq:{}", leaveStartReq);ValidatorUtils.validateEntity(leaveStartReq);//查询登陆用户String id = SecurityContextHolder.getContext().getAuthentication().getName();User loginUser = userService.getById(id);//发起流程并初始化各个用户任务节点的指定人Authentication.setAuthenticatedUserId(Optional.ofNullable(loginUser).map(m -> m.getUsername()).orElse("xxx"));Map<String, Object> variables = new HashMap<>(4);variables.put("general", Optional.ofNullable(loginUser).map(m -> m.getUsername()).orElse("mark"));ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(FlowConstant.MODEL_KEY_LEAVE, variables);//执行一个申请节点的动作userTaskService.completeApplyUserTask(processInstance.getId(), loginUser, leaveStartReq);return Results.newSuccessResult(processInstance.getId());
}

以上逻辑就是发起流程, 核心步骤就是获取登陆用户信息,并且设置流程启动参数。 在流程启动后完成第一个用户任务节点。此时流程便可以往下面流转。

完成申请任务的监听事件

@Component("chooseLeaderListener")
@Slf4j
public class ChooseLeaderListener implements TaskListener {@Autowiredprivate ISysBaseAPI sysBaseAPI;@Overridepublic void notify(DelegateTask delegateTask) {log.info("动态选择下一个用户任务节点审批人员监听器");//查询发起用户所属的部门Id集合String startUser = String.valueOf(delegateTask.getVariable("general"));List<String> myDeptIds = sysBaseAPI.getDepartIdsByUsername(startUser);//根据部门id查询负责人List<String> leaderList = new ArrayList<>();for (String myDeptId : myDeptIds) {leaderList.addAll(sysBaseAPI.getDeptHeadByDepId(myDeptId));}log.info("直属部门领导人员:{}", leaderList);//设置下一个审批节点的用户名称,注意如何是有多个领导需要同时审批,要把审批节点设置为多实例。if(CollectionUtil.isNotEmpty(leaderList)){delegateTask.setVariable("leader", leaderList.get(0));}}
}

在上一步申请节点完成后,将执行任务监听起逻辑,查询到下一个节点的审批用户名称。并且绑定到leader流程变量。此时便可以实现不同部门有不同领导。

审批通过

public void pass(PassReq passReq, User loginUser) {Task task = taskService.createTaskQuery().processInstanceId(passReq.getProcessInstanceId()).taskAssignee(loginUser.getUsername()).singleResult();//完成用户任务Map<String, Object> map = new HashMap<>(12);map.put("comment", passReq.getContent());map.put("imageUrl", imageUrl);taskService.complete(task.getId(), map);
}

审批通过操作的核心逻辑。 驳回就留给大家自己研究了。

运行效果的图

在这里插入图片描述

通过Springboot+flowable就是这么简单, 在线体验JeecgFlow

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

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

相关文章

【ONE·基础算法 || 动态规划(三)】

总言 主要内容&#xff1a;编程题举例&#xff0c;熟悉理解动态规划类题型&#xff08;回文串问题、两个数组的 dp问题&#xff09;。                文章目录 总言7、回文串问题7.1、 回文子串&#xff08;medium&#xff09;7.1.1、题解 7.2、 最长回文子串&#…

Python 3 教程第33篇(MySQL - mysql-connector 驱动)

Python MySQL - mysql-connector 驱动 MySQL 是最流行的关系型数据库管理系统&#xff0c;如果你不熟悉 MySQL&#xff0c;可以阅读我们的 MySQL 教程。 本章节我们为大家介绍使用 mysql-connector 来连接使用 MySQL&#xff0c; mysql-connector 是 MySQL 官方提供的驱动器。…

LLM*:路径规划的大型语言模型增强增量启发式搜索

路径规划是机器人技术和自主导航中的一个基本科学问题&#xff0c;需要从起点到目的地推导出有效的路线&#xff0c;同时避开障碍物。A* 及其变体等传统算法能够确保路径有效性&#xff0c;但随着状态空间的增长&#xff0c;计算和内存效率会严重降低。相反&#xff0c;大型语言…

【Db First】.NET开源 ORM 框架 SqlSugar 系列

.NET开源 ORM 框架 SqlSugar 系列 【开篇】.NET开源 ORM 框架 SqlSugar 系列【入门必看】.NET开源 ORM 框架 SqlSugar 系列【实体配置】.NET开源 ORM 框架 SqlSugar 系列【Db First】.NET开源 ORM 框架 SqlSugar 系列【Code First】.NET开源 ORM 框架 SqlSugar 系列【数据事务…

企业品牌曝光的新策略:短视频矩阵系统

企业品牌曝光的新策略&#xff1a;短视频矩阵系统 在当今数字化时代&#xff0c;短视频已经渗透到我们的日常生活之中&#xff0c;成为连接品牌与消费者的关键渠道。然而&#xff0c;随着平台于7月20日全面下线了短视频矩阵的官方接口&#xff0c;许多依赖于此接口的小公司和内…

006 MATLAB编程基础

01 M文件 MATLAB输入命令有两种方法&#xff1a; 一是在MATLAB主窗口逐行输入命令&#xff0c;每个命令之间用分号或逗号分隔&#xff0c;每行可包含多个命令。 二是将命令组织成一个命令语句文集&#xff0c;使用扩展名“.m”&#xff0c;称为M文件。它由一系列的命令和语句…

Java基于SpringBoot+Vue的IT技术交流和分享平台(附源码+lw+部署)

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝30W、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

【计算机网络】实验3:集线器和交换器的区别及交换器的自学习算法

实验 3&#xff1a;集线器和交换器的区别及交换器的自学习算法 一、 实验目的 加深对集线器和交换器的区别的理解。 了解交换器的自学习算法。 二、 实验环境 • Cisco Packet Tracer 模拟器 三、 实验内容 1、熟悉集线器和交换器的区别 (1) 第一步&#xff1a;构建网络…

UICollectionView在xcode16编译闪退问题

使用xcode15运行工程&#xff0c;控制台会出现如下提示&#xff1a; Expected dequeued view to be returned to the collection view in preparation for display. When the collection views data source is asked to provide a view for a given index path, ensure that a …

Proteus8.17下载安装教程

Proteus是一款嵌入式系统仿真开发软件&#xff0c;实现了从原理图设计、单片机编程、系统仿真到PCB设计&#xff0c;真正实现了从概念到产品的完整设计&#xff0c;其处理器模型支持8051、HC11、PIC10/12/16/18/24/30/DsPIC33、AVR、ARM、8086和MSP430等&#xff0c;能够帮助用…

Vue教程|搭建vue项目|Vue-CLI2.x 模板脚手架

一、项目构建环境准备 在构建Vue项目之前&#xff0c;需要搭建Node环境以及Vue-CLI脚手架&#xff0c;由于本篇文章为上一篇文章的补充&#xff0c;也是为了给大家分享更为完整的搭建vue项目方式&#xff0c;所以环境准备部分采用Vue教程&#xff5c;搭建vue项目&#xff5c;V…

一款支持80+语言,包括:拉丁文、中文、阿拉伯文、梵文等开源OCR库

大家好&#xff0c;今天给大家分享一个基于PyTorch的OCR库EasyOCR&#xff0c;它允许开发者通过简单的API调用来读取图片中的文本&#xff0c;无需复杂的模型训练过程。 项目介绍 EasyOCR 是一个基于Python的开源项目&#xff0c;它提供了一个简单易用的光学字符识别&#xff…

cocotb pytest

打印python中的print &#xff0c; 应该使用 pytest -s

【C++】STL——map和set

目录 1、序列式容器和关联式容器前 2、set 2.1 set类的介绍 2.2 set的构造和迭代器 2.3 set的增删查 set 的插入 set的查找 set的删除 2.4 multiset和set的差异 3、map 3 .1 pair类型 3.2 map的构造 3.3 map的增删查 map的构造遍历 map的插入 map的删除 map的查…

java基础概念46-数据结构1

一、引入 List集合的三种实现类使用了不同的数据结构&#xff01; 二、数据结构的定义 三、常见的数据结构 3-1、栈 特点&#xff1a;先进后出&#xff0c;后进先出。 java内存容器&#xff1a; 3-2、队列 特点&#xff1a;先进先出、后进后出。 栈VS队列-小结 3-3、数组 3-…

Docker:在 ubuntu 系统上生成和加载 Docker 镜像

本文将介绍在 ubuntu系统上进行 Docker 镜像的生成和加载方法和代码。 文章目录 一、下载和安装 docker二、加载 docker 文件三、保存你的镜像四、将镜像上传到云端并通过连接下载和加载 Docker 镜像五、Docker 容器和本地的文件交互5.1 从容器复制文件到本地宿主机5.1.1 单个文…

《数据挖掘:概念、模型、方法与算法(第三版)》

嘿&#xff0c;数据挖掘的小伙伴们&#xff01;今天我要给你们介绍一本超级实用的书——《数据挖掘&#xff1a;概念、模型、方法与算法》第三版。这本书是数据挖掘领域的经典之作&#xff0c;由该领域的知名专家编写&#xff0c;系统性地介绍了在高维数据空间中分析和提取大量…

做异端中的异端 -- Emacs裸奔之路4: 你不需要IDE

确切地说&#xff0c;你不需要在IDE里面编写或者阅读代码。 IDE用于Render资源文件比较合适&#xff0c;但处理文本&#xff0c;并不划算。 这的文本文件&#xff0c;包括源代码&#xff0c;配置文件&#xff0c;文档等非二进制文件。 先说说IDE带的便利: 函数或者变量的自动…

【C++】编程题目分析与实现回顾:从浮点数运算到整型转换的全面解读

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;题目一&#xff1a;计算成绩问题分析与优化实现优化后的实现优势 &#x1f4af;题目二&#xff1a;浮点数向零舍入不同实现方式的比较1. 使用强制类型转换 (int)2. 使用标准…

时间表格Java

输入&#xff1a;XXX XXX 小时 分钟 输出&#xff1a; XXX&#xff1a;XXX ~ XXX: XXX XXX&#xff1a;XXX ~ XXX: XXX XXX&#xff1a;XXX ~ XXX: XXX 处理&#xff1a;间隔五分钟、区间45分钟 14:15 ~ 15:0 15:5 ~ 15:50 15:55 ~ 16:40 16:45 ~ 17:30 17:35 ~ 18:20…