news 2026/4/23 14:10:40

BPMN2.0,flowable工作流,多实例【用户任务】的实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
BPMN2.0,flowable工作流,多实例【用户任务】的实现

目录

1、环境

2、流程信息

3、需求

4、思路

5、【领导审批】节点配置

6、代码实现


1、环境

前端:BPMN2.0.js

后端:flowable:6.8.0

2、流程信息

流程图(7、流程文件在文章最后):

各节点信息:

节点名称节点id
开始ks
登记dj
员工yg
领导审批ldsp
结束js

3、需求

  1. 【领导审批】节点需要多个审批人顺序审批或需要多个审批人都进行审批。
  2. 【领导审批】节点的多个审批人需要动态设置。

4、思路

  1. 针对第一个需求,使用的是【用户任务】的多实例。
  2. 针对第二个需求,在使用【用户任务】的多实例情况下使用【执行监听器】。

5、【领导审批】节点配置

【领导审批】节点xml配置

<bpmn2:userTask id="ldsp" name="领导审批" flowable:dataType="USERS" flowable:assignee="${assignee}" flowable:candidateUsers="分管领导001,部门领导001" flowable:text="分管领导001,部门领导001"> <bpmn2:extensionElements> <flowable:executionListener class="com.cn.workflow.flowable.listener.FlowExecutionListener" event="start" /> </bpmn2:extensionElements> <bpmn2:incoming>sq</bpmn2:incoming> <bpmn2:outgoing>Flow_13elc6g</bpmn2:outgoing> <bpmn2:outgoing>th</bpmn2:outgoing> <bpmn2:multiInstanceLoopCharacteristics isSequential="false" flowable:collection="${multiInstanceHandler.getUserNames(execution)}" flowable:elementVariable="assignee"> <bpmn2:completionCondition xsi:type="bpmn2:tFormalExpression">${nrOfCompletedInstances &gt;= nrOfInstances}</bpmn2:completionCondition> </bpmn2:multiInstanceLoopCharacteristics> </bpmn2:userTask>

说明:

满足需求1的配置:

  • flowable:assignee="${assignee}":${assignee}里面的名称要跟flowable:elementVariable="assignee"的值一样。
  • isSequential="true":true表示顺序执行,false表示并行执行。
  • ${nrOfCompletedInstances &gt;= nrOfInstances}:表示完成条件为全部实例完成。
  • flowable:candidateUsers="分管领导001,部门领导001":表示预设处理人为分管领导001,部门领导001。
  • flowable:collection="${multiInstanceHandler.getUserNames(execution)}":表示处理人的集合,这里调用了java代码(multiInstanceHandler.getUserNames方法)和结合flowable:candidateUsers预设的值返回具体的处理人。下面会贴上代码。

满足需求2的配置:

在上面的配置基础上,再加上【执行监听器】,配置如下

<bpmn2:extensionElements> <flowable:executionListener class="com.cn.workflow.flowable.listener.FlowExecutionListener" event="start" /> </bpmn2:extensionElements>

6、代码实现

代码由开源项目做二次修改:https://gitee.com/nbacheng/ruoyi-nbcio/tree/master/

multiInstanceHandler.getUserNames代码:

@Component("multiInstanceHandler") public class MultiInstanceHandler { @Autowired private RemoteUserService remoteUserService; @Autowired private RemoteDeptService remoteDeptService; public Set<String> getUserNames(DelegateExecution execution) { Set<String> candidateUserNames = new LinkedHashSet<>(); FlowElement flowElement = execution.getCurrentFlowElement(); if (ObjectUtil.isNotEmpty(flowElement) && flowElement instanceof UserTask) { UserTask userTask = (UserTask) flowElement; String dataType = userTask.getAttributeValue( "http://flowable.org/bpmn", "dataType"); if ("USERS".equals(dataType) && CollUtil.isNotEmpty(userTask.getCandidateUsers())) { // 添加候选用户 candidateUserNames.addAll(userTask.getCandidateUsers()); } else if (CollUtil.isNotEmpty(userTask.getCandidateGroups())) { // 以下功能可以参考,具体逻辑按各自的来处理 // 获取组的ID,角色ID集合或部门ID集合 List<Long> groups = userTask.getCandidateGroups().stream() .map(item -> Long.parseLong(item.substring(4))) .collect(Collectors.toList()); List<Long> userIds = new ArrayList<>(); if ("ROLES".equals(dataType)) { // 通过角色id,获取所有用户id集合 R<List<Long>> listR = remoteUserService.selectUserIdsByRoleIds(groups, SecurityConstants.INNER); userIds = listR.getCode() == R.SUCCESS ? listR.getData() : new ArrayList<>(); } else if ("DEPTS".equals(dataType)) { // 通过部门id,获取所有用户id集合 R<List<Long>> listR = remoteDeptService.selectUserIdsByDeptIds(groups, SecurityConstants.INNER); userIds = listR.getCode() == R.SUCCESS ? listR.getData() : new ArrayList<>(); } // 添加候选用户 R<List<String>> listR = remoteUserService.selectUserNames(userIds, SecurityConstants.INNER); if(listR.getCode() == R.SUCCESS){ List<String> userNames = listR.getData(); userNames.forEach(userName -> candidateUserNames.add(userName)); }else{ throw new ServiceException("候选用户不能为空!"); } } } return candidateUserNames; } }

FlowExecutionListener代码:

如果配置了【执行监听器】,那么程序会先执行FlowExecutionListener,再执行multiInstanceHandler.getUserNames

@Component(value = "flowExecutionListener") public class FlowExecutionListener implements ExecutionListener { @Override public void notify(DelegateExecution execution) { FlowElement flowElement = execution.getCurrentFlowElement(); if(ObjectUtil.isNotEmpty(flowElement) && flowElement instanceof UserTask){ UserTask userTask = (UserTask) execution.getCurrentFlowElement(); //这里只做简单的设置办理人 //还可以设置办理组、改变变量dataType的值等 userTask.setCandidateUsers(Arrays.asList("技术部分管领导001","技术部分管领导002","技术部主管领导001")); } System.out.println("执行监听器:{}"+ execution); } }

7、流程文件

flowable.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:flowable="http://flowable.org/bpmn" id="diagram_Process_1761616908502" targetNamespace="http://flowable.org/bpmn" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd"> <bpmn2:process id="Process_1761616908502" name="审批流程" isExecutable="true"> <bpmn2:startEvent id="ks" name="开始"> <bpmn2:outgoing>Flow_0zttbj2</bpmn2:outgoing> </bpmn2:startEvent> <bpmn2:userTask id="yg" name="员工" flowable:dataType="INITIATOR" flowable:assignee="${initiator}" flowable:text="流程发起人"> <bpmn2:incoming>fq</bpmn2:incoming> <bpmn2:incoming>th</bpmn2:incoming> <bpmn2:outgoing>sq</bpmn2:outgoing> </bpmn2:userTask> <bpmn2:userTask id="ldsp" name="领导审批" flowable:dataType="USERS" flowable:assignee="${assignee}" flowable:candidateUsers="分管领导001,部门领导001" flowable:text="分管领导001,部门领导001"> <bpmn2:extensionElements> <flowable:executionListener class="com.cn.workflow.flowable.listener.FlowExecutionListener" event="start" /> </bpmn2:extensionElements> <bpmn2:incoming>sq</bpmn2:incoming> <bpmn2:outgoing>Flow_13elc6g</bpmn2:outgoing> <bpmn2:outgoing>th</bpmn2:outgoing> <bpmn2:multiInstanceLoopCharacteristics flowable:collection="${multiInstanceHandler.getUserNames(execution)}" flowable:elementVariable="assignee"> <bpmn2:completionCondition xsi:type="bpmn2:tFormalExpression">${nrOfCompletedInstances &gt;= nrOfInstances}</bpmn2:completionCondition> </bpmn2:multiInstanceLoopCharacteristics> </bpmn2:userTask> <bpmn2:endEvent id="js" name="结束"> <bpmn2:incoming>Flow_13elc6g</bpmn2:incoming> </bpmn2:endEvent> <bpmn2:sequenceFlow id="Flow_13elc6g" name="提交" sourceRef="ldsp" targetRef="js"> <bpmn2:conditionExpression xsi:type="bpmn2:tFormalExpression">${nextNodeId=="js"}</bpmn2:conditionExpression> </bpmn2:sequenceFlow> <bpmn2:sequenceFlow id="fq" name="发起" sourceRef="dj" targetRef="yg" /> <bpmn2:sequenceFlow id="sq" name="申请" sourceRef="yg" targetRef="ldsp" /> <bpmn2:sequenceFlow id="th" name="退回" sourceRef="ldsp" targetRef="yg"> <bpmn2:conditionExpression xsi:type="bpmn2:tFormalExpression">${nextNodeId=="yg"}</bpmn2:conditionExpression> </bpmn2:sequenceFlow> <bpmn2:userTask id="dj" name="登记" flowable:dataType="INITIATOR" flowable:assignee="${initiator}" flowable:text="流程发起人"> <bpmn2:incoming>Flow_0zttbj2</bpmn2:incoming> <bpmn2:outgoing>fq</bpmn2:outgoing> </bpmn2:userTask> <bpmn2:sequenceFlow id="Flow_0zttbj2" sourceRef="ks" targetRef="dj" /> </bpmn2:process> <bpmndi:BPMNDiagram id="BPMNDiagram_1"> <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1761616908502"> <bpmndi:BPMNEdge id="Flow_0zttbj2_di" bpmnElement="Flow_0zttbj2"> <di:waypoint x="38" y="239" /> <di:waypoint x="110" y="239" /> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge id="th_di" bpmnElement="th"> <di:waypoint x="550" y="199" /> <di:waypoint x="550" y="170" /> <di:waypoint x="336" y="170" /> <di:waypoint x="336" y="199" /> <bpmndi:BPMNLabel> <dc:Bounds x="432" y="152" width="22" height="14" /> </bpmndi:BPMNLabel> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge id="sq_di" bpmnElement="sq"> <di:waypoint x="386" y="239" /> <di:waypoint x="500" y="239" /> <bpmndi:BPMNLabel> <dc:Bounds x="432" y="221" width="22" height="14" /> </bpmndi:BPMNLabel> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge id="fq_di" bpmnElement="fq"> <di:waypoint x="210" y="239" /> <di:waypoint x="286" y="239" /> <bpmndi:BPMNLabel> <dc:Bounds x="235" y="221" width="22" height="14" /> </bpmndi:BPMNLabel> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge id="Flow_13elc6g_di" bpmnElement="Flow_13elc6g"> <di:waypoint x="600" y="239" /> <di:waypoint x="682" y="239" /> <bpmndi:BPMNLabel> <dc:Bounds x="635" y="217" width="22" height="14" /> </bpmndi:BPMNLabel> </bpmndi:BPMNEdge> <bpmndi:BPMNShape id="ks_di" bpmnElement="ks"> <dc:Bounds x="2" y="221" width="36" height="36" /> <bpmndi:BPMNLabel> <dc:Bounds x="10" y="264" width="22" height="14" /> </bpmndi:BPMNLabel> </bpmndi:BPMNShape> <bpmndi:BPMNShape id="yg_di" bpmnElement="yg"> <dc:Bounds x="286" y="199" width="100" height="80" /> </bpmndi:BPMNShape> <bpmndi:BPMNShape id="ldsp_di" bpmnElement="ldsp"> <dc:Bounds x="500" y="199" width="100" height="80" /> </bpmndi:BPMNShape> <bpmndi:BPMNShape id="js_di" bpmnElement="js"> <dc:Bounds x="682" y="221" width="36" height="36" /> <bpmndi:BPMNLabel> <dc:Bounds x="691" y="264" width="22" height="14" /> </bpmndi:BPMNLabel> </bpmndi:BPMNShape> <bpmndi:BPMNShape id="dj_di" bpmnElement="dj"> <dc:Bounds x="110" y="199" width="100" height="80" /> </bpmndi:BPMNShape> </bpmndi:BPMNPlane> </bpmndi:BPMNDiagram> </bpmn2:definitions>
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/23 12:47:04

Wan2.2-T2V-A14B安装与多GPU推理指南

Wan2.2-T2V-A14B安装与多GPU推理实战指南 在AI视频生成领域&#xff0c;720P高清输出早已不再是“炫技”指标&#xff0c;而是商业落地的硬性门槛。当模型参数逼近140亿量级&#xff0c;传统单卡部署方式几乎寸步难行——显存瞬间爆满、推理耗时动辄数分钟&#xff0c;根本无法…

作者头像 李华
网站建设 2026/4/23 12:55:25

宠物用品行业智能客服:痛点破解与发展路径

一、传统服务模式下宠物用品行业的核心痛点 宠物用品行业超 60% 的中小商家仍依赖纯人工客服&#xff0c;服务体系难以匹配行业特性需求&#xff1a;品类覆盖宠物食品、洗护用品、窝具、智能玩具等&#xff0c;涉及宠物年龄适配、成分安全性、使用场景等专业问题&#xff0c;新…

作者头像 李华
网站建设 2026/4/19 18:37:08

传统开发vsAI生成:扫雷游戏开发效率对比

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 生成一个完整的扫雷游戏项目&#xff0c;包含以下功能&#xff1a;1. 基础扫雷游戏功能&#xff1b;2. 难度选择&#xff08;简单、中等、困难&#xff09;&#xff1b;3. 游戏记录…

作者头像 李华
网站建设 2026/4/18 16:39:32

3分钟完成Java环境配置:效率提升10倍的秘诀

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 创建一个极简的Java环境配置效率对比工具&#xff1a;1. 实现传统手动配置流程模拟&#xff08;包含所有步骤的动画演示&#xff09;2. 提供自动化配置功能 3. 记录并对比两种方式耗…

作者头像 李华
网站建设 2026/4/18 13:00:02

linux安装kkFileView和libreOffice

文章目录一、安装包1、安装jdk2、解压3、配置环境变量二、安装libreoffice1、看服务器架构2、下载安装包及中文包3、安装4、验证安装是否成功三、安装kkFileView1、下载kkfileView源码文件来打包2、打包3、解压4、启动总结一、安装包 1、安装jdk 官网下载安装包https://www.o…

作者头像 李华
网站建设 2026/4/23 13:52:54

电商大促期间K8s部署可用性保障实战

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 开发一个电商场景的Kubernetes部署监控看板&#xff0c;功能包括&#xff1a;1. 实时检测deployment可用状态 2. 大促期间自动扩容逻辑 3. 历史故障案例库查询 4. 压力测试模拟模块…

作者头像 李华