前面讲到的条件流程履行是经过调整决策节点的履行逻辑来完结的,那分支与兼并流程呢?咱们又应该怎么去处理其履行逻辑呢?下面咱们来看一条存在有分支与兼并的简单流程。如下图: 请假请求->部分领导批阅(多领导并行)->公司领导批阅

流程界说

工作流引擎设计与实现分支与合并流程执行
src/test/resources/leave_06.json 注:以下json并非悉数,短少位置信息。

{
  "name": "leave",
  "displayName": "请假",
  "instanceUrl": "leaveForm",
  "nodes": [
    {
      "id": "start",
      "type": "snaker:start",
      "properties": {
        "width": "120",
        "height": "80"
      },
      "text": {
        "value": "开端"
      }
    },
    {
      "id": "apply",
      "type": "snaker:task",
      "properties": {
        "assignee": "approve.operator",
        "taskType": "Major",
        "performType": "ANY",
        "autoExecute": "N",
        "width": "120",
        "height": "80",
        "field": {
          "userKey": "1"
        }
      },
      "text": {
        "value": "请假请求"
      }
    },
    {
      "id": "approveDept_a1",
      "type": "snaker:task",
      "properties": {
        "assignmentHandler": "com.mldong.config.FlowAssignmentHandler",
        "taskType": "Major",
        "performType": "ANY",
        "autoExecute": "N",
        "width": 120,
        "height": 80,
        "field": {}
      },
      "text": {
        "value": "部分领导批阅A1"
      }
    },
    {
      "id": "approveBoss",
      "type": "snaker:task",
      "x": 1400,
      "y": 340,
      "properties": {
        "assignmentHandler": "com.mldong.config.FlowAssignmentHandler",
        "taskType": "Major",
        "performType": "ANY",
        "autoExecute": "N",
        "width": "120",
        "height": "80"
      },
      "text": {
        "value": "公司领导批阅"
      }
    },
    {
      "id": "end",
      "type": "snaker:end",
      "properties": {
        "width": "120",
        "height": "80"
      },
      "text": {
        "value": "完毕"
      }
    },
    {
      "id": "be5429b2-b9f6-4ae7-ae3c-dbc2c4dae947",
      "type": "snaker:fork",
      "properties": {}
    },
    {
      "id": "approveDept_a2",
      "type": "snaker:task",
      "properties": {
        "height": 80,
        "width": 120,
        "field": {}
      },
      "text": {
        "value": "部分领导批阅A2"
      }
    },
    {
      "id": "approveDept_b1",
      "type": "snaker:task",
      "x": 1020,
      "y": 160,
      "properties": {
        "height": 80,
        "width": 120,
        "field": {},
        "taskType": "Major",
        "performType": "ANY"
      },
      "text": {
        "value": "部分领导批阅B1"
      }
    },
    {
      "id": "approveDept_b2",
      "type": "snaker:task",
      "x": 1020,
      "y": 500,
      "properties": {
        "height": 80,
        "width": 120,
        "field": {}
      },
      "text": {
        "value": "部分领导批阅B2"
      }
    },
    {
      "id": "a4fccb9c-0146-4b20-929f-fed681a37173",
      "type": "snaker:join",
      "properties": {}
    }
  ],
  "edges": [
    {
      "id": "3037be41-5682-4344-b94a-9faf5c3e62ba",
      "type": "snaker:transition",
      "sourceNodeId": "start",
      "targetNodeId": "apply",
      "properties": {}
    },
    {
      "id": "0ea009ea-d48e-4d69-95bf-de64fc0fc84b",
      "type": "snaker:transition",
      "sourceNodeId": "apply",
      "targetNodeId": "be5429b2-b9f6-4ae7-ae3c-dbc2c4dae947",
      "properties": {}
    },
    {
      "id": "88854f27-0781-4dd0-8dfa-d44f27550519",
      "type": "snaker:transition",
      "sourceNodeId": "be5429b2-b9f6-4ae7-ae3c-dbc2c4dae947",
      "targetNodeId": "approveDept_a1",
      "properties": {}
    },
    {
      "id": "4aa89dd3-90b5-415d-9853-2b25507e7e3f",
      "type": "snaker:transition",
      "sourceNodeId": "be5429b2-b9f6-4ae7-ae3c-dbc2c4dae947",
      "targetNodeId": "approveDept_a2",
      "properties": {}
    },
    {
      "id": "7620a4e0-d147-4ca4-a140-a14003c0d9cd",
      "type": "snaker:transition",
      "sourceNodeId": "approveDept_a1",
      "targetNodeId": "approveDept_b1",
      "properties": {}
    },
    {
      "id": "4f1b1351-e9fb-42f2-a2a5-e820a71d41c6",
      "type": "snaker:transition",
      "sourceNodeId": "approveDept_a2",
      "targetNodeId": "approveDept_b2",
      "properties": {}
    },
    {
      "id": "a1dee2c8-c505-4a8d-abae-c1f0710dd70a",
      "type": "snaker:transition",
      "sourceNodeId": "approveDept_b1",
      "targetNodeId": "a4fccb9c-0146-4b20-929f-fed681a37173",
      "properties": {}
    },
    {
      "id": "f132dfe3-fd3a-4c88-b8f7-7c89fba602fe",
      "type": "snaker:transition",
      "sourceNodeId": "approveDept_b2",
      "targetNodeId": "a4fccb9c-0146-4b20-929f-fed681a37173",
      "properties": {}
    },
    {
      "id": "11e10610-576b-4965-a16b-23712ddb9d94",
      "type": "snaker:transition",
      "sourceNodeId": "a4fccb9c-0146-4b20-929f-fed681a37173",
      "targetNodeId": "approveBoss",
      "properties": {}
    },
    {
      "id": "1575853c-878b-49e6-bfd8-db21c986bd0b",
      "type": "snaker:transition",
      "sourceNodeId": "approveBoss",
      "targetNodeId": "end",
      "properties": {}
    }
  ]
}

旧的代码逻辑

JoinModel.javaForkModel.java调用输入变迁办法runOutTransition(...)

package com.mldong.flow.engine.model;
import com.mldong.flow.engine.core.Execution;
/**
 *
 * 分支模型
 * @author mldong
 * @date 2023/4/25
 */
public class ForkModel extends NodeModel {
    @Override
    public void exec(Execution execution) {
        // 履行分支节点自界说履行逻辑
        runOutTransition(execution);
    }
}
package com.mldong.flow.engine.model;
import com.mldong.flow.engine.core.Execution;
import lombok.Data;
/**
 *
 * 兼并模型
 * @author mldong
 * @date 2023/4/25
 */
@Data
public class JoinModel extends NodeModel {
    @Override
    public void exec(Execution execution) {
        // 履行兼并节点自界说履行逻辑
        runOutTransition(execution);
    }
}

修正src/test/java/com/mldong/flow/ExecuteTest.java 办法executeLeave_06,履行逻辑和之前的相同,就是解析的流程界说文件修正为leave_06.json。

  • 加载配置
  • 解析流程界说文件
  • 履行流程
package com.mldong.flow;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Dict;
import com.mldong.flow.engine.MockExecuteTask;
import com.mldong.flow.engine.cfg.Configuration;
import com.mldong.flow.engine.core.Execution;
import com.mldong.flow.engine.model.ProcessModel;
import com.mldong.flow.engine.parser.ModelParser;
import org.junit.Test;
/**
 *
 * 履行测验
 * @author mldong
 * @date 2023/5/1
 */
public class ExecuteTest {
    @Test
    public void executeLeave_06() {
        new Configuration();
        // 将流程界说文件解析成流程模型
        ProcessModel processModel = ModelParser.parse(IoUtil.readBytes(this.getClass().getResourceAsStream("/leave_06.json")));
        // 结构履行参数
        Execution execution = new Execution();
        // 设置当时流程模型
        execution.setProcessModel(processModel);
        // 设置扩展特点
        execution.setArgs(Dict.create());
        // 拿到开端节点调用履行办法
        processModel.getStart().execute(execution);
        // 履行模仿履行使命办法
        new MockExecuteTask(execution).run();
    }
}

履行成果如下:

调用模型节点履行办法:model:StartModel,name:start,displayName:开端
创立使命:apply,请假请求
设置使命状况为已完结:apply,请假请求
调用模型节点履行办法:model:TaskModel,name:apply,displayName:请假请求
创立使命:approveDept_a1,部分领导批阅A1
创立使命:approveDept_a2,部分领导批阅A2
设置使命状况为已完结:approveDept_a1,部分领导批阅A1
调用模型节点履行办法:model:TaskModel,name:approveDept_a1,displayName:部分领导批阅A1
创立使命:approveDept_b1,部分领导批阅B1
设置使命状况为已完结:approveDept_a2,部分领导批阅A2
调用模型节点履行办法:model:TaskModel,name:approveDept_a2,displayName:部分领导批阅A2
创立使命:approveDept_b2,部分领导批阅B2
设置使命状况为已完结:approveDept_b1,部分领导批阅B1
调用模型节点履行办法:model:TaskModel,name:approveDept_b1,displayName:部分领导批阅B1
创立使命:approveBoss,公司领导批阅
设置使命状况为已完结:approveDept_b2,部分领导批阅B2
调用模型节点履行办法:model:TaskModel,name:approveDept_b2,displayName:部分领导批阅B2
创立使命:approveBoss,公司领导批阅
设置使命状况为已完结:approveBoss,公司领导批阅
调用模型节点履行办法:model:TaskModel,name:approveBoss,displayName:公司领导批阅
调用模型节点履行办法:model:EndModel,name:end,displayName:完毕
设置使命状况为已完结:approveBoss,公司领导批阅
调用模型节点履行办法:model:TaskModel,name:approveBoss,displayName:公司领导批阅
调用模型节点履行办法:model:EndModel,name:end,displayName:完毕

咱们会看到,executeLeave_06的履行也呈现两个公司领导批阅和完毕,由于咱们对分支节点和兼并节点进行处理是默认的处理办法,即直接调用runOutTransition(...)办法。下面咱们要重新调整节点的处理办法,使其完好且正确的从开端节点走向完毕节点。

分支与兼并节点分析

分支节点

分支节点和开端节点相同,没有什么特别履行过程,只需求调用输出边履行办法,驱动流程往下一个节点履行。

public class ForkModel extends NodeModel {
    @Override
    public void exec(Execution execution) {
        // 履行分支节点自界说履行逻辑
        runOutTransition(execution);
    }
}

兼并节点

兼并节点需求判别是否到达兼并条件,假如到达兼并条件,则能够继续调用输出边履行办法,驱动流程往下一个节点履行。

履行流程图

工作流引擎设计与实现分支与合并流程执行

履行流程说明

  • 是否到达兼并条件,首要判别前面节点产生的使命是否完结
  • 假如到达兼并条件,则调用输出边履行办法
  • 假如未到达兼并条件,则继续阻塞,不往前进行

如下,咱们能够经过给Execution目标添加是否可兼并特点来判别是否调用输出边履行办法

public class JoinModel extends NodeModel {
    @Override
    public void exec(Execution execution) {
        // 履行兼并节点自界说履行逻辑
        if(execution.isMerged()) {
            runOutTransition(execution);
        }
    }
}

当然,仅上面一步仍是不足以完结触发的,由于isMerged永远为false,下面咱们画类图重新安排代码。

类图

尽管判别是否到达兼并条件可直接在JoinModel的exec中完结,可是为了通用性,咱们能够按如下办法来安排代码。即在上文的基础上添加MergeBranchHandler完结类,经过完结类去修正Execution 的isMerged特点。

工作流引擎设计与实现分支与合并流程执行

代码完结

流程履行参数core/Execution.java添加isMerged特点

package com.mldong.flow.engine.core;
import cn.hutool.core.lang.Dict;
import com.mldong.flow.engine.entity.ProcessTask;
import com.mldong.flow.engine.enums.TaskStateEnum;
import com.mldong.flow.engine.model.ProcessModel;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
 *
 * 履行目标参数
 * @author mldong
 * @date 2023/4/25
 */
@Data
public class Execution {
    // 流程实例ID
    private String processInstanceId;
    // 当时流程使命ID
    private String processTaskId;
    // 履行目标扩展参数
    private Dict args;
    // 当时流程模型
    private ProcessModel processModel;
    // 当时使命
    private ProcessTask processTask;
    // 一切使命调集
    private List<ProcessTask> processTaskList = new ArrayList<>();
    // 是否可兼并
    private boolean isMerged;
    /**
     * 添加使命到使命调集
     * @param processTask
     */
    public void addTask(ProcessTask processTask) {
        this.processTaskList.add(processTask);
    }
    /**
     * 获取正在进行中的使命列表
     * @return
     */
    public List<ProcessTask> getDoingTaskList() {
        return this.processTaskList.stream().filter(item->{
            return TaskStateEnum.DOING.getCode().equals(item.getTaskState());
        }).collect(Collectors.toList());
    }
 }

添加兼并分支操作的处理器,handlers/impl/MergeBranchHandler.java

package com.mldong.flow.engine.handlers.impl;
import com.mldong.flow.engine.core.Execution;
import com.mldong.flow.engine.handlers.IHandler;
import com.mldong.flow.engine.model.JoinModel;
/**
 * 兼并分支操作的处理器
 * @author mldong
 * @date 2023/5/21
 */
public class MergeBranchHandler implements IHandler {
    private JoinModel joinModel;
    public MergeBranchHandler(JoinModel joinModel) {
        this.joinModel = joinModel;
    }
    @Override
    public void handle(Execution execution) {
        // 判别是否存在正在履行的使命,存在则不答应兼并
        execution.setMerged(execution.getDoingTaskList().isEmpty());
    }
}

model/JoinModel.java兼并节点类添加调用分支处理类办法fire(new MergeBranchHandler(this),execution)

package com.mldong.flow.engine.model;
import com.mldong.flow.engine.core.Execution;
import com.mldong.flow.engine.handlers.impl.MergeBranchHandler;
import lombok.Data;
/**
 *
 * 兼并模型
 * @author mldong
 * @date 2023/4/25
 */
@Data
public class JoinModel extends NodeModel {
    @Override
    public void exec(Execution execution) {
        // 履行兼并节点自界说履行逻辑
        fire(new MergeBranchHandler(this),execution);
        if(execution.isMerged()) {
            runOutTransition(execution);
        }
    }
}

调用测验类,最终履行成果如下:

调用模型节点履行办法:model:StartModel,name:start,displayName:开端
创立使命:apply,请假请求
设置使命状况为已完结:apply,请假请求
调用模型节点履行办法:model:TaskModel,name:apply,displayName:请假请求
创立使命:approveDept_a1,部分领导批阅A1
创立使命:approveDept_a2,部分领导批阅A2
设置使命状况为已完结:approveDept_a1,部分领导批阅A1
调用模型节点履行办法:model:TaskModel,name:approveDept_a1,displayName:部分领导批阅A1
创立使命:approveDept_b1,部分领导批阅B1
设置使命状况为已完结:approveDept_a2,部分领导批阅A2
调用模型节点履行办法:model:TaskModel,name:approveDept_a2,displayName:部分领导批阅A2
创立使命:approveDept_b2,部分领导批阅B2
设置使命状况为已完结:approveDept_b1,部分领导批阅B1
调用模型节点履行办法:model:TaskModel,name:approveDept_b1,displayName:部分领导批阅B1
设置使命状况为已完结:approveDept_b2,部分领导批阅B2
调用模型节点履行办法:model:TaskModel,name:approveDept_b2,displayName:部分领导批阅B2
创立使命:approveBoss,公司领导批阅
设置使命状况为已完结:approveBoss,公司领导批阅
调用模型节点履行办法:model:TaskModel,name:approveBoss,displayName:公司领导批阅
调用模型节点履行办法:model:EndModel,name:end,displayName:完毕

小结

本文经过调整兼并节点的兼并处理逻辑来完结分支与兼并流程,要害点其实就是判别是否能兼并。从履行成果中咱们能够看出,当咱们完结approveDept_b1节点使命时,其会触发一次兼并处理,此时使命approveDept_b2未完结,所以不能兼并。当履行完approveDept_b2时,会再一次触发兼并处理,此时一切使命都已完结,所以答应兼并,即答应流程往下一个节点跋涉。由于这里仅仅简单模仿,真实的场景approveDept_b1和approveDept_b2是不分先后的,可是这并不影响判别。最终只要是最终一个使命完结,才会真正的触发兼并节兼并动作,答应流程往下一个节点跋涉。

参加安排

请在微信中翻开: 《立东和他的朋友们》

注:最近发现有些CSDN账号将作者的工作流系列文章搬运曩昔然后标为原创(猜想是用程序自动抓取的)。为了添加一些限制,作者决议把该系列文章更新至《立东和他的朋友们》这个圈子上,对此感兴趣的小伙伴能够扫描二维码参加,谢谢我们的支持!

工作流引擎设计与实现分支与合并流程执行

相关源码

mldong-flow-demo-06

流程规划器

在线体验