news 2026/6/10 9:47:22

Spring Boot3.x集成Flowable7.x(一)Spring Boot集成与设计、部署、发起、完成简单流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring Boot3.x集成Flowable7.x(一)Spring Boot集成与设计、部署、发起、完成简单流程

一、Flowable简介

Flowable 是一个轻量级、开源的业务流程管理(BPM)和工作流引擎,旨在帮助开发者和企业实现业务流程的自动化。它支持 BPMN 2.0 标准,适用于各种规模的企业和项目。Flowable 的核心功能包括流程定义、流程执行、任务管理、历史记录查询等,广泛应用于企业级应用中。

官网:https://www.flowable.com/open-source/docs/

二、Spring Boot3整合

环境:JDK21、Spring Boot3.4.1、Flowable7.1.0

1.引入依赖

<!-- Flowable启动引擎 --> <dependency> <groupId>org.flowable</groupId> <artifactId>flowable-spring-boot-starter</artifactId> <version>7.1.0</version> </dependency>

2.yml配置

注意本文数据源、数据库是MySQL和Druid,各位码友请根据实际情况调整

spring: #配置数据源 datasource: #MySQL driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/foleable?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true username: root password: 123456 type: com.alibaba.druid.pool.DruidDataSource flowable: # 开启定时任务JOB async-executor-activate: true # 在引擎启动时,会自动更新数据库架构 database-schema-update: true

3.线程池配置

1.提供一个全局的默认线程池

2.提供一个folwable依赖的线程池,注意Bean的名称一定要是applicationTaskExecutor

import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.util.concurrent.ThreadPoolExecutor; /** * 统一线程池管理 * * @author xie **/ @Configuration public class ThreadPoolTaskConfig { @Bean("applicationTaskExecutor") public ThreadPoolTaskExecutor applicationTaskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); //此方法返回可用处理器的虚拟机的最大数量; 不小于1 int core = Runtime.getRuntime().availableProcessors(); executor.setCorePoolSize(core);//设置核心线程数 executor.setMaxPoolSize(core * 2 + 1);//设置最大线程数 executor.setKeepAliveSeconds(120);//除核心线程外的线程存活时间 executor.setQueueCapacity(120);//如果传入值大于0,底层队列使用的是LinkedBlockingQueue,否则默认使用SynchronousQueue executor.setThreadNamePrefix("thread-default-execute");//线程名称前缀 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());//设置拒绝策略,抛出 RejectedExecutionException来拒绝新任务的处理。 // executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());//设置拒绝策略,使用主线程 // executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());//设置拒绝策略,直接丢弃掉 // executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());//设置拒绝策略,丢弃最早的未处理的任务请求。 return executor; } }

不配置线程池可能出现以下错误

三、流程定义

官方流程设计器

docker run -p 9096:8080 -d --name flow flowable/flowable-ui:6.8.0

访问IP加启动容器的端口

默认账户/密码:admin/test

进入设计器

创建流程并设计

创建测试流程
这里先简单画个流程 后期写个详细流程图的绘画

确定后进入设计界面

添加用活动后设置名称

点击用户任务设置用户任务的执行人

这里先简单设置一个固定的人,分配用户ID为:user1

再重复添加一个用户任务,添加一下结束事件

最后就依次连线就行了,选中开始节点然后连接到用户一

最后的效果图


点击保存

选择查看刚刚创建的流程

导出XML

四、部署流程

1.把导出的xml放入我们工程的resources目录

2.部署流程(这里先把我们后面用到的查询服务都注入)

@Resource private RepositoryService repositoryService; @Resource private RuntimeService runtimeService; @Autowired private TaskService taskService; @Resource private HistoryService historyService; @Resource IdentityService identityService; /** * 初始化流程 * * @method: initFlow * @return: * @author: xie * @date: 2024/12/24 上午9:26 **/ @GetMapping("initFlow") @Transactional(rollbackFor = Exception.class) public void initFlow() { // 获取bpmn文件夹的所有.bpmn20.xml文件 ClassPathResource bpmnFolder = new ClassPathResource("bpmn/"); var files = bpmnFolder.getFile().listFiles((dir, name) -> name.endsWith(".bpmn20.xml")); if (files != null && files.length > 0) { // 创建部署对象 var deploymentBuilder = repositoryService.createDeployment(); for (var file : files) { // 添加BPMN文件到部署 deploymentBuilder.addInputStream(file.getName(), file.toURI().toURL().openStream()); } // 执行部署 Deployment deployment = deploymentBuilder.deploy(); } }

五、查询部署的全部流程

当然这里也可以进行分页查询已经写在下面注释了 这里为了测试 我就简单写了

/*** * 查询所有的流程实例 * @method: queryAllDeployedProcesses * @return: * @author: xie * @date: 2024/12/20 下午1:12 **/ @GetMapping("/queryAllDeployedProcesses") public List<JSONObject> queryAllDeployedProcesses() { List<JSONObject> jsonObjects = new ArrayList<>(); // 查询所有流程定义 List<ProcessDefinition> processDefinitions = repositoryService.createProcessDefinitionQuery() .orderByProcessDefinitionKey().asc() // 按流程定义的 Key 排序 .latestVersion() // 只查询每个流程定义的最新版本 .list(); // 打印所有已部署的流程的 key 和 name for (ProcessDefinition processDefinition : processDefinitions) { System.out.println("Process ID: " + processDefinition.getId()); System.out.println("Process Key: " + processDefinition.getKey()); System.out.println("Process Name: " + processDefinition.getName()); System.out.println("Process Version: " + processDefinition.getVersion()); JSONObject object = new JSONObject(); object.put("id", processDefinition.getId()); object.put("key", processDefinition.getKey()); object.put("name", processDefinition.getName()); object.put("version", processDefinition.getVersion()); jsonObjects.add(object); } //分页查询 // 创建查询对象 // ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery() // .latestVersion() // 只查询最新版本的流程定义 // .orderByProcessDefinitionKey().asc(); // 按流程定义的 Key 升序排序 // // // 获取总条数 // long totalCount = query.count(); // // // 分页查询流程定义 // List<ProcessDefinition> processDefinitions = query.listPage((pageNum - 1) * pageSize, pageSize); return jsonObjects; }

看看界面的效果吧,这个简单的前端页面展示方便查询

六、发起审批

/** * 是否被拒绝标记 */ public static final String REFUSEFLAG = "refuseFlag"; /** * 发起流程 * * @param key : 流程信息 * @method: startFlow * @return: * @author: xie * @date: 2024/12/20 下午1:43 **/ @GetMapping("/startFlow") @Transactional(rollbackFor = Exception.class) public String startFlow(@RequestParam("key") String key) { Map<String, Object> map = Map.of( "businessType", "业务类型(业务审批、请假、出差等)", "day", 1, REFUSEFLAG, false ); //发起人用户ID String userId = SysConstan.USER_ID; //订单号 String businessKey = "PO00001"; //参数一 流程key //参数二 页面单据类型(比如请假流水号、订单号等) //参数三 运行时变量信息比如请假天数 //设置发起人 identityService.setAuthenticatedUserId(userId); //启动流程变量 ProcessInstance processInstanceByKey = runtimeService.startProcessInstanceByKey(key, businessKey, map); log.info("流程实例id-{}", processInstanceByKey.getId()); // 清除当前用户 identityService.setAuthenticatedUserId(null); return processInstanceByKey.getId(); }

注意:

参数key:指的是我们上一步部署流程后得到的key也就是页面查询的key

map对象是设置的变量集,比如我们请假的时候可能要根据不同的天数走不同的分支,这里的变量集后期可以自己封装一个对象指定一些必须传入的变量信息

拒绝标记:由于Flowable并不会记录流程是否是被拒绝而结束的流程实例

userId:Flowable默认是无状态的可以通过identityService临时设置发起人

businessKey:业务订单号,可以把它看做是审批的单据号后面审批通过了需要通知业务系统的时候需要用到

七、查询所有发起的流程及展示状态

这里包含了:我的发起、我参与审批的流程、根据业务单号查询审批流程等查询功能

/** * 查询所有流程实例 * * @method: queryAllprocess * @return: * @author: xie * @date: 2024/12/20 下午3:35 **/ @GetMapping("queryAllprocess") public List<JSONObject> queryAllprocess() { // 获取所有活跃的流程实例(包括已结束的) List<HistoricProcessInstance> allProcessInstances = historyService.createHistoricProcessInstanceQuery() //查询用户参与过的流程实例 //.involvedUser("1") //查询发起人用户 //.startedBy(SysConstan.USER_ID) //根据变量查询(自己设的变量) //.variableValueEquals("businessType", "查询的业务类型") //根据订单号模糊查询 //.processInstanceBusinessKeyLikeIgnoreCase("orderCode") .orderByProcessInstanceStartTime().asc() // 可以按ID排序,便于调试 .list(); // 查询所有的流程实例,包括历史的和活跃的 List<JSONObject> jsonObjects = new ArrayList<>(); for (HistoricProcessInstance processInstance : allProcessInstances) { String processInstanceId = processInstance.getId(); JSONObject json = new JSONObject(); if (processInstance.getEndTime() == null) { json.put("status", "审批中"); } else { json.put("status", "审批完成"); } json.put("id", processInstance.getProcessDefinitionId()); json.put("processInstanceId", processInstanceId); json.put("startUser", processInstance.getStartUserId()); json.put("key", processInstance.getProcessDefinitionKey()); json.put("businessKey", processInstance.getBusinessKey()); json.put("name", processInstance.getProcessDefinitionName()); json.put("deleteReason", processInstance.getDeleteReason()); json.put("startTime", DateUtil.format(processInstance.getStartTime(), "yyyy-MM-dd HH:mm:ss")); json.put("endTime", processInstance.getEndTime() != null ? DateUtil.format(processInstance.getEndTime(), "yyyy-MM-dd HH:mm:ss") : ""); // 获取与任务相关的所有变量 // 获取该流程实例的变量 List<HistoricVariableInstance> variables = historyService.createHistoricVariableInstanceQuery() .processInstanceId(processInstanceId) .list(); if(CollUtil.isNotEmpty(variables)){ for (HistoricVariableInstance variable : variables) { json.put(variable.getVariableName(), variable.getValue()); if (REFUSEFLAG.equals(variable.getVariableName()) && variable.getValue() != null && variable.getValue().toString().equalsIgnoreCase("true")) { json.put("status", "审批驳回"); } } } jsonObjects.add(json); } return jsonObjects; }

这里就用到了发起流程时是否驳回这个变量,判断当前业务单据是否被驳回

查询流程时可以有多种条件查询比如查询我发起的审批流程上面代码已经列出查询条件

对应的页面简单展示,发起后进入到用户一审批

八、我的代办任务

这里注释了用户ID条件查询,实际业务系统应该是要设置当前用户的ID查询

/** * 获取代办列表 (这里暂时查看所有的) * * @method: getTasks * @return: * @author: xie * @date: 2024/12/20 下午1:44 **/ @GetMapping("/allTasks") public List<JSONObject> getTasks() { List<Task> taskList = taskService .createTaskQuery() //查询业务类型为指定的任务 //.processInstanceBusinessKey("LEAVE") //查询所有zhangsan用户代办的任务 //.taskAssignee(SysConstan.USER_ID) .list(); List<JSONObject> jsonObjects = new ArrayList<>(); for (Task task : taskList) { JSONObject json = new JSONObject(); json.put("id", task.getId()); json.put("name", task.getName()); json.put("user", task.getAssignee()); json.put("processDefinitionId", task.getProcessDefinitionId()); json.put("processInstanceId", task.getProcessInstanceId()); // 获取与任务相关的所有变量 Map<String, Object> taskVariables = taskService.getVariables(task.getId()); // 打印出任务的变量 json.putAll(taskVariables); jsonObjects.add(json); } return jsonObjects; }

九、完成任务

这里的任务ID为上面我的代办任务接口查询出来Task的ID

/** * 完成任务 * * @method: testComplete * @return: * @author: xie * @date: 2024/12/20 下午1:44 **/ @GetMapping("/testComplete") @Transactional(rollbackFor = Exception.class) public boolean testComplete(@RequestParam("id") String taskId) { // 获取任务对应的流程实例ID Task task = taskService.createTaskQuery().taskId(taskId).singleResult(); // 检查任务是否为空 if (task == null) { System.out.println("任务不存在"); return false; } String processInstanceId = task.getProcessInstanceId(); String orderCode = (String) runtimeService.getVariable(processInstanceId, "orderCode"); log.info("业务单据:{}", orderCode); // 1. 设置新执行者 //taskService.setAssignee(taskId, newAssigneeId); // 任务的执行者被替换为新的用户 taskService.addComment(taskId, processInstanceId, "备注信息test"); // 完成任务 taskService.complete(taskId); // 查询当前流程实例的所有活动任务 boolean isFinish = processInstanceFinished(processInstanceId); log.debug("流程是否完成L:{}", isFinish); return isFinish; } /** * 校验当前流程是否结束了 * @method: isProcessInstanceFinished * @param processInstanceId : * @return: * @author: xie * @date: 2024/12/23 下午1:57 **/ public boolean processInstanceFinished(String processInstanceId) { // 获取当前流程实例 ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() .processInstanceId(processInstanceId) .singleResult(); // 如果 processInstance 为空,表示该流程实例已结束 return processInstance == null; }

十、驳回任务

processInstanceId指的是发起流程的实例ID

/** * 驳回流程 * @method: stopFlow * @param processInstanceId : * @return: * @author: xie * @date: 2024/12/24 下午4:38 **/ @GetMapping("stopFlow") @Transactional(rollbackFor = Exception.class) public void stopFlow(@RequestParam("id") String processInstanceId) { // 在删除前设置拒绝变量 runtimeService.setVariable(processInstanceId, "refuseFlag", true); //拒绝 后一个参数是拒绝的原因 runtimeService.deleteProcessInstance(processInstanceId, "驳回任务备注原因"); }

十一、 完成效果展示

1.BPMN-JS插件渲染的连接线不起效果

2.Flowable引擎自带构建的图片没有经过的节点


版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/10 11:14:19

数据安全公司Cyera融资4亿美元 估值90亿美元

雷递网 乐天 1月10日数据安全公司Cyera日前宣布&#xff0c;公司已完成4亿美元融资&#xff0c;估值达到90亿美元Cyera在2024年11月的一轮融资中估值为30亿美元&#xff0c;并在2025年6月的上一轮融资中估值飙升至60亿美元&#xff0c;当时融资额为5.4亿美元。Cyera此轮融资由黑…

作者头像 李华
网站建设 2026/6/10 11:11:32

远信储能冲刺港股:9个月营收8.8亿,利润7089万 粤财是股东

雷递网 雷建平 1月10日深圳市远信储能技术股份有限公司&#xff08;简称&#xff1a;“远信储能”&#xff09;日前递交招股书&#xff0c;准备在港交所上市。9个月营收8.8亿&#xff0c;利润7089万远信储能成立于2019年&#xff0c;是一家集成储能系统(ESS)解决方案提供商&…

作者头像 李华
网站建设 2026/6/9 20:03:31

克拉泼振荡电路Multisim仿真:新手入门必看指南

克拉泼振荡电路Multisim仿真&#xff1a;从零开始的高频正弦波设计实战你是否曾为一个简单的LC振荡电路在面包板上“死活不起振”而抓耳挠腮&#xff1f;是否在示波器前等了十几秒&#xff0c;只看到一片噪声或一条直线&#xff1f;又或者&#xff0c;面对复杂的晶体管寄生参数…

作者头像 李华
网站建设 2026/6/10 11:12:13

HY-MT1.5-1.8B性能优化:实时翻译延迟降低方案

HY-MT1.5-1.8B性能优化&#xff1a;实时翻译延迟降低方案 随着多语言交流需求的不断增长&#xff0c;高质量、低延迟的实时翻译技术成为智能设备、跨语言沟通和全球化服务的核心支撑。腾讯开源的混元翻译大模型HY-MT1.5系列&#xff0c;凭借其在翻译质量与推理效率之间的出色平…

作者头像 李华
网站建设 2026/6/9 15:21:46

多语言客服系统搭建:HY-MT1.5企业级部署实战指南

多语言客服系统搭建&#xff1a;HY-MT1.5企业级部署实战指南 随着全球化业务的不断扩展&#xff0c;企业对多语言客服系统的需求日益增长。传统翻译服务在响应速度、术语一致性与上下文理解方面存在明显短板&#xff0c;难以满足高并发、低延迟、强专业性的客服场景需求。腾讯…

作者头像 李华
网站建设 2026/6/9 17:55:47

HY-MT1.5术语干预API:专业翻译质量控制

HY-MT1.5术语干预API&#xff1a;专业翻译质量控制 随着全球化进程的加速&#xff0c;高质量、可定制化的机器翻译需求日益增长。传统翻译模型在面对专业术语、多语言混合文本以及上下文依赖场景时&#xff0c;往往难以保证输出的一致性与准确性。为解决这一问题&#xff0c;腾…

作者头像 李华