BPMN2.0,flowable工作流指向多节点,并且只能选择其中一个节点的处理方式

目录

1、环境

2、流程信息

3、需求

4、思路

5、代码实现

6、流程xml文件


1、环境

前端:BPMN2.0.js

后端:flowable:6.8.0

2、流程信息

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

各节点信息:

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

3、需求

【领导审批】节点要根据实际情况,选择【提交】到【结束】,还是【退回】到【员工】

4、思路

【排他网关】:

如果要达到这种指向单一节点的,可以使用【排他网关】,我这边理解【排他网关】往往是用于特定的业务逻辑的时候使用的,但是我这里可能会有很多节点会有退回到上一节点的指向,为了可以做得更加通用,这里先否掉【排他网关】。

【条件顺序流】(最后确定使用):

在两个几点连线上使用【条件顺序流】。前端获取当前节点所有的指向,由用户进行下一节点的单选选择,他节点id传到指定变量中(我这里定义了变量nextNodeId),条件顺序流的表示式为:${nextNodeId=="yg"}(yg为【员工】节点的id值),通过这种方式来确定需要走到哪个节点。

5、代码实现

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

以下代码可能需要对flowable工作流有一些基础的才比较好理解

获取下一节点的所有节点接口:

controller:

java 复制代码
    /**
     * 获取下一步骤的节点
     **/
    @PostMapping(value = "/nextNodeList")
    public R<NextUserTaskVo> nextUserTaskList(@RequestBody WfTaskBo taskBo) {
        NextUserTaskVo nextUserTaskVo = flowTaskService.nextUserTaskList(taskBo);
        return R.ok(nextUserTaskVo);
    }

nextUserTaskList:

java 复制代码
public NextUserTaskVo nextUserTaskList(WfTaskBo taskBo) {
        NextUserTaskVo nextUserTaskVo = new NextUserTaskVo();
        Task task = taskService.createTaskQuery().taskId(taskBo.getTaskId()).singleResult();
        List<UserTask> taskList = FindNextNodeUtil.getNextUserTasks(repositoryService, task, taskBo.getVariables());
        nextUserTaskVo.setUserTaskList(taskList);
        nextUserTaskVo.setTaskDefinitionKey(task.getTaskDefinitionKey());
        return nextUserTaskVo;
    }
复制代码
getNextUserTasks:
java 复制代码
    public static List<UserTask> getNextUserTasks(RepositoryService repositoryService, org.flowable.task.api.Task task, Map<String, Object> map) {
        List<UserTask> data = new ArrayList<>();
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult();
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
        Process mainProcess = bpmnModel.getMainProcess();
        Collection<FlowElement> flowElements = mainProcess.getFlowElements();
        String key = task.getTaskDefinitionKey();
        FlowElement flowElement = bpmnModel.getFlowElement(key);
        next(flowElements, flowElement, map, data);
        return data;
    }
复制代码
next:
java 复制代码
    public static void next(Collection<FlowElement> flowElements, FlowElement flowElement, Map<String, Object> map, List<UserTask> nextUser) {
        //如果是结束节点
        if (flowElement instanceof EndEvent) {
            //如果是子任务的结束节点
            if (getSubProcess(flowElements, flowElement) != null) {
                flowElement = getSubProcess(flowElements, flowElement);
            }
        }
        //获取Task的出线信息--可以拥有多个
        List<SequenceFlow> outGoingFlows = null;
        if (flowElement instanceof Task) {
            outGoingFlows = ((Task) flowElement).getOutgoingFlows();
        } else if (flowElement instanceof Gateway) {
            outGoingFlows = ((Gateway) flowElement).getOutgoingFlows();
        } else if (flowElement instanceof StartEvent) {
            outGoingFlows = ((StartEvent) flowElement).getOutgoingFlows();
        } else if (flowElement instanceof SubProcess) {
            outGoingFlows = ((SubProcess) flowElement).getOutgoingFlows();
        } else if (flowElement instanceof CallActivity) {
            outGoingFlows = ((CallActivity) flowElement).getOutgoingFlows();
        }
        if (outGoingFlows != null && outGoingFlows.size() > 0) {
            //遍历所有的出线--找到可以正确执行的那一条
            for (SequenceFlow sequenceFlow : outGoingFlows) {
                //1.有表达式,且为true
                //2.无表达式
                // 不做表达式的判断
//                String expression = sequenceFlow.getConditionExpression();
//                if (expression == null || expressionResult(map, expression.substring(expression.lastIndexOf("{") + 1, expression.lastIndexOf("}")))) {
                    //出线的下一节点
                    String nextFlowElementID = sequenceFlow.getTargetRef();
                    if (checkSubProcess(nextFlowElementID, flowElements, nextUser)) {
                        continue;
                    }

                    //查询下一节点的信息
                    FlowElement nextFlowElement = getFlowElementById(nextFlowElementID, flowElements);
                    //调用流程
                    if (nextFlowElement instanceof CallActivity) {
                        CallActivity ca = (CallActivity) nextFlowElement;
                        if (ca.getLoopCharacteristics() != null) {
                            UserTask userTask = new UserTask();
                            userTask.setId(ca.getId());

                            userTask.setId(ca.getId());
                            userTask.setLoopCharacteristics(ca.getLoopCharacteristics());
                            userTask.setName(ca.getName());
                            nextUser.add(userTask);
                        }
                        next(flowElements, nextFlowElement, map, nextUser);
                    }
                    //用户任务
                    if (nextFlowElement instanceof UserTask) {
                        nextUser.add((UserTask) nextFlowElement);
                    }
                    //排他网关
                    else if (nextFlowElement instanceof ExclusiveGateway) {
                        next(flowElements, nextFlowElement, map, nextUser);
                    }
                    //并行网关
                    else if (nextFlowElement instanceof ParallelGateway) {
                        next(flowElements, nextFlowElement, map, nextUser);
                    }
                    //接收任务
                    else if (nextFlowElement instanceof ReceiveTask) {
                        next(flowElements, nextFlowElement, map, nextUser);
                    }
                    //服务任务
                    else if (nextFlowElement instanceof ServiceTask) {
                        next(flowElements, nextFlowElement, map, nextUser);
                    }
                    //子任务的起点
                    else if (nextFlowElement instanceof StartEvent) {
                        next(flowElements, nextFlowElement, map, nextUser);
                    }
                    //结束节点
                    else if (nextFlowElement instanceof EndEvent) {
                        nextUser.add(BeanCopyUtils.copy((EndEvent) nextFlowElement, UserTask.class));
                        next(flowElements, nextFlowElement, map, nextUser);
                    }
                    //信号抛出
                    else if(nextFlowElement instanceof ThrowEvent){
                        nextUser.add(BeanCopyUtils.copy((ThrowEvent) nextFlowElement, UserTask.class));
                    }
//                }
            }
        }
    }

前端调用接口后拼接成单选:(选择后提交时,把id值赋值到变量nextNodeId中)

html 复制代码
<el-form-item label="选择节点:" prop="jumpType" :rules="[{ required: true, message: '请选择节点', trigger: 'blur' }]">
              <el-radio-group v-model="selectedRouteNodeRows" class="ml-4">
                <template v-for="nodeData in routeNodeData.userTaskList" :key="nodeData.id">
                  <el-radio
                    v-if="(nodeData.incomingFlows && nodeData.incomingFlows.length > 0 && nodeData.incomingFlows.filter(item => item.targetRef == nodeData.id && item.sourceRef == routeNodeData.taskDefinitionKey)[0]) || (nodeData.incomingFlows && nodeData.incomingFlows.length == 0 && nodeData.loopCharacteristics)"
                    :label="nodeData.id"
                  >{{ 
                  `${(nodeData.incomingFlows && nodeData.incomingFlows.length > 0 && nodeData.incomingFlows.filter(item => item.targetRef == nodeData.id && item.sourceRef == routeNodeData.taskDefinitionKey)[0]?.name) || ''} -> ${nodeData.name}`
                  }}
                  </el-radio>
                  </template>
              </el-radio-group>
              
            </el-form-item>

6、流程xml文件

flowable.xml:

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="ROLES" flowable:candidateGroups="ROLE123456" flowable:text="领导">
            <bpmn2:incoming>sq</bpmn2:incoming>
            <bpmn2:outgoing>Flow_13elc6g</bpmn2:outgoing>
            <bpmn2:outgoing>th</bpmn2:outgoing>
        </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="dj_di" bpmnElement="dj">
                <dc:Bounds x="110" 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:BPMNPlane>
    </bpmndi:BPMNDiagram>
</bpmn2:definitions>

bpmn.xml

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="ROLES" flowable:candidateGroups="ROLE123456" flowable:text="领导">
            <bpmn2:incoming>sq</bpmn2:incoming>
            <bpmn2:outgoing>Flow_13elc6g</bpmn2:outgoing>
            <bpmn2:outgoing>th</bpmn2:outgoing>
        </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="dj_di" bpmnElement="dj">
                <dc:Bounds x="110" 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:BPMNPlane>
    </bpmndi:BPMNDiagram>
</bpmn2:definitions>
相关推荐
a程序小傲2 小时前
蚂蚁Java面试被问:注解的工作原理及如何自定义注解
java·开发语言·python·面试
幽络源小助理2 小时前
SpringBoot+Vue摄影师分享社区源码 – Java项目免费下载 | 幽络源
java·vue.js·spring boot
0和1的舞者2 小时前
《软件测试分类指南:8 大维度 + 核心要点梳理》
java·软件测试·单元测试·测试·黑盒测试·白盒测试·测试分类
TAEHENGV2 小时前
创建目标模块 Cordova 与 OpenHarmony 混合开发实战
android·java·开发语言
是一个Bug2 小时前
如何阅读JDK源码?
java·开发语言
Ledison72 小时前
Springboot 3.5.7 + Springcloud 2025 升级记录
java
没有bug.的程序员3 小时前
熔断、降级、限流:高可用架构的三道防线
java·网络·jvm·微服务·架构·熔断·服务注册
派大鑫wink3 小时前
【Day15】集合框架(三):Map 接口(HashMap 底层原理 + 实战)
java·开发语言
派大鑫wink3 小时前
【Day14】集合框架(二):Set 接口(HashSet、TreeSet)去重与排序
java·开发语言
weixin_515069663 小时前
BeanToMapUtil-对象转Map
java·工具类·java常用api