目录
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>