bpmn转换器

willian fu 2022 ~

bpmn转换器

本章节为开源版用户自行适配后端提供思路以及为pro用户介绍原理

wflow 流程设计器的数据格式是嵌套结构的json,后端使用了基于bpmn2.0 的流程引擎 flowable,数据格式不一致,所以我们需要对流程设计器的json进行转换成flowable可以接受的格式,这个转换器实现类为 WFlowToBpmnCreator ,内容细节大家可以参考pro实现。

实现原理

首先我们来看一个bpmn.xml 的数据

<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn="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" id="Definitions_Process_1686014563526" targetNamespace="http://bpmn.io/schema/bpmn">
  <bpmn:process id="Process_1686014563526" name="业务流程_1686014563526" isExecutable="true">
    <bpmn:startEvent id="Event_1jquky6" name="开始">
      <bpmn:outgoing>Flow_1sjxic8</bpmn:outgoing>
    </bpmn:startEvent>
    <bpmn:userTask id="Activity_1eko0k5" name="审批人1">
      <bpmn:incoming>Flow_1sjxic8</bpmn:incoming>
      <bpmn:outgoing>Flow_0z7rg7c</bpmn:outgoing>
    </bpmn:userTask>
    <bpmn:userTask id="Activity_1gkys96" name="审批人2">
      <bpmn:incoming>Flow_0z7rg7c</bpmn:incoming>
      <bpmn:outgoing>Flow_1yoq9zl</bpmn:outgoing>
    </bpmn:userTask>
    <bpmn:endEvent id="Event_1mun3j2" name="流程结束">
      <bpmn:incoming>Flow_1yoq9zl</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:sequenceFlow id="Flow_1sjxic8" sourceRef="Event_1jquky6" targetRef="Activity_1eko0k5" />
    <bpmn:sequenceFlow id="Flow_0z7rg7c" sourceRef="Activity_1eko0k5" targetRef="Activity_1gkys96" />
    <bpmn:sequenceFlow id="Flow_1yoq9zl" sourceRef="Activity_1gkys96" targetRef="Event_1mun3j2" />
  </bpmn:process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_1">
      流程节点布局
  </bpmndi:BPMNDiagram>
</bpmn:definitions>

对应上方xml的流程图与wflow流程图为:

image-20230606093746320

对应wflow的json为:

{
    "id": "root",
    "desc": "任何人",
    "name": "发起人",
    "type": "ROOT",
    "props": {},
    "children": {
        "id": "node_151977794862",
        "parentId": "root",
        "props": {},
        "type": "APPROVAL",
        "name": "审批人1",
        "children": {
            "id": "node_152038799603",
            "parentId": "node_151977794862",
            "props": {},
            "type": "APPROVAL",
            "name": "审批人2",
            "children": {}
        }
    },
    "parentId": null
}

那么如何去建立一个对应关系这是最重要的,我们的目的是通过json将其构造成xml,flowable 流程引擎是由java实现的,那么每个xml节点在flowable中都应该有一个java类与之对应,我们就是根据构造出这些节点,去构造xml流程图。

案例教程

我们可以在先后端引入 flowable 的maven依赖,再在IDEA里面尝试打出如下名称:

image-20230606101346646

image-20230606101503379

可以发现,在 org.flowable.bpmn.model 包下都有对应的类与bpmn里面的节点一一对应,那么好办了,我们梳理下刚才用到的这几个节点。

  • StartEvent:开始事件
  • EndEvent:结束事件
  • SequenceFlow:流程连接线
  • UserTask:用户任务节点

暂且以这几个节点为例,我们来根据上述分析尝试构造一个流程图对应的 model

//开始节点
StartEvent start = new StartEvent();
start.setId("start");

//发起人
UserTask task1 = new UserTask();
task1.setId("root");

//审批1
UserTask task1 = new UserTask();
task1.setId("node_151977794862");

//审批2
UserTask task2 = new UserTask();
task2.setId("node_152038799603");

//结束节点
EndEvent end = new EndEvent();
end.setId("end");

//构造连接线连接各个节点
SequenceFlow line1 = new SequenceFlow();
line1.setId("start_root");
line1.setSourceRef("start"); //从哪个节点
line1.setTargetRef("root"); //连接到哪个节点

// ....后续的几个节点连接都以这种方式做,不做赘述了

//构建process节点
Process process = new Process();
process.setId("process_id");
process.setName("流程图");
//把之前new出来的节点和连接线都添加到process节点内
process.addFlowElement(start);
process.addFlowElement(task1);
// ...... 其他节点

//构建Bpmn模型
BpmnModel bpmnModel = new BpmnModel();
bpmnModel.addProcess(process);
// Bpmn xml自动生成布局及布局节点位置
new BpmnAutoLayout(bpmnModel).execute();


//转换成bpmn.xml格式字符串并打印
System.out.println(new BpmnXMLConverter().convertToXML(bpmnModel));

为了将上述的节点找出来,我们肯定是要和 wflow 的 json对应,json是嵌套的结构,也就是说下级节点是上级节点内部 children 字段的子项,那么我们就需要遍历所有的节点,使用递归即可,简单示例如下:

public static void main(String[] args) {
    String json = "流程json";
    forEachNode(JSONObject.parseObject(json), node -> {
        //在这里进行每个节点的处理,去new出那些节点,然后连线
    });
}

private static void forEachNode(JSONObject node, Consumer<? super JSONObject> action){
    JSONObject childNode = node.getJSONObject("children");
    action.accept(childNode);
    forEachNode(childNode, action);
}

进阶

按照上述方法我们生成了一个bpmn的流程图xml,但是仅仅是xml,无法创建用户任务,因为没有给节点设置审批人等属性,那么我们可以进行设置,这里演示一个静态的设置

//审批1
UserTask task = new UserTask();
task.setName("审批人1");
task.setId("node_151977794862");
task.setAssignee("审批人ID"); //指定审批人ID

这样就构造好了,为适配节点指定了一个审批人,当然,这里还可以指定为UEL表达式,参考: flowable文档-用户任务open in new window

这样我们就完成了一个简单的串行无分支流程的转换,当然实际流程是比较复杂的,但是原理过程就是这么简单,我们需要去由简入深,不要一上来就去构造带分支的

带分支的构造方式也是递归,主要是并行网关和条件网关,带分支的话连线就比较复杂了,大家自行琢磨😘

调试技巧

我们再调试的时候,将生成的流程 xml 输出到一个文件内,将该xml导入到 bpmn.js 的 设计器内,看下回显出来的流程图对不对,还缺少哪些东西。

自己找一个在线的那种或者自己部署一个,这里有个在线的Vite Vue Process Designer (miyuesc.github.io)open in new window

在生成流程图 new BpmnAutoLayout(bpmnModel).execute(); 这一步时会执行一个基本校验,如果流程图构造连接关系不对,就会抛异常,这时候要检查下各个节点是不是正确进行连接了,debug 查看 process对象里面添加的节点关系,如果节点都有,那么就是查看连接关系。