Application development platform integrated workflow - process modeling function model transformation technical solution

background

For the problem of unfriendly process settings, domestic DingTalk designed and implemented a set of process modeling mode separately, which has nothing to do with the bpmn specification. Someone imitated it and made it open source ( https://github.com/StavinLi/Workflow -Vue3 ), the effect diagram is as follows:

the general principle of implementation is based on infinitely nested child nodes, output json data, and pass it to the backend. After the backend parses, it calls the api of the Camunda engine, converts it into a process model, and then persists it.

The previous article introduced the front-end integration. Today we will focus on how to parse the json data from the front-end, call the API of the Camunda engine, and convert it into a process model.

Camunda API

For the core problem, the custom json data is converted into a process model, using the Camunda Model API, the official document address: https://docs.camunda.org/manual/7.19/user-guide/model-api/bpmn- model-api/

It should be noted that there are two sets of APIs provided by the official, one is a well-regulated standard API; the other is a fluent API (fluent builder API). The former can complete all operations and is more cumbersome to use; the latter can complete most functions, but it is easy to use.

Standard API

Here is an example from the official document to briefly look at the usage method, and later we will combine the conversion work for practical combat.

Create a minimal process that only includes one user task link as shown in the figure above, and the related API calls are as follows:

// 创建一个空白模型
BpmnModelInstance modelInstance = Bpmn.createEmptyModel();
Definitions definitions = modelInstance.newInstance(Definitions.class);
definitions.setTargetNamespace("http://camunda.org/examples");
modelInstance.setDefinitions(definitions);

// 创建流程元素
Process process = createElement(definitions, "process-with-one-task", Process.class);

// 创建开始事件,用户任务,结束事件
StartEvent startEvent = createElement(process, "start", StartEvent.class);
UserTask task1 = createElement(process, "task1", UserTask.class);
task1.setName("User Task");
EndEvent endEvent = createElement(process, "end", EndEvent.class);

// 创建各个元素之间的连接
createSequenceFlow(process, startEvent, task1);
createSequenceFlow(process, task1, endEvent);

// 验证模型并写入到文件中
Bpmn.validateModel(modelInstance);
File file = File.createTempFile("bpmn-model-api-", ".bpmn");
Bpmn.writeModelToFile(file, modelInstance);


To create a simple process with two parallel tasks, the relevant API calls are as follows:

// create an empty model
BpmnModelInstance modelInstance = Bpmn.createEmptyModel();
Definitions definitions = modelInstance.newInstance(Definitions.class);
definitions.setTargetNamespace("http://camunda.org/examples");
modelInstance.setDefinitions(definitions);

// create elements
StartEvent startEvent = createElement(process, "start", StartEvent.class);
ParallelGateway fork = createElement(process, "fork", ParallelGateway.class);
ServiceTask task1 = createElement(process, "task1", ServiceTask.class);
task1.setName("Service Task");
UserTask task2 = createElement(process, "task2", UserTask.class);
task2.setName("User Task");
ParallelGateway join = createElement(process, "join", ParallelGateway.class);
EndEvent endEvent = createElement(process, "end", EndEvent.class);

// create flows
createSequenceFlow(process, startEvent, fork);
createSequenceFlow(process, fork, task1);
createSequenceFlow(process, fork, task2);
createSequenceFlow(process, task1, join);
createSequenceFlow(process, task2, join);
createSequenceFlow(process, join, endEvent);

// validate and write model to file
Bpmn.validateModel(modelInstance);
File file = File.createTempFile("bpmn-model-api-", ".bpmn");
Bpmn.writeModelToFile(file, modelInstance);

Several tool class methods are involved in the middle, as follows:

// 创建子元素
protected <T extends BpmnModelElementInstance> T createElement(BpmnModelElementInstance parentElement, String id, Class<T> elementClass) {
    
    
  T element = parentElement.getModelInstance().newInstance(elementClass);
  element.setAttributeValue("id", id, true);
  parentElement.addChildElement(element);
  return element;
}

// 创建顺序边
public SequenceFlow createSequenceFlow(Process process, FlowNode from, FlowNode to) {
    
    
  String identifier = from.getId() + "-" + to.getId();
  SequenceFlow sequenceFlow = createElement(process, identifier, SequenceFlow.class);
  process.addChildElement(sequenceFlow);
  sequenceFlow.setSource(from);
  from.getOutgoing().add(sequenceFlow);
  sequenceFlow.setTarget(to);
  to.getIncoming().add(sequenceFlow);
  return sequenceFlow;
}

Fluent API

As can be seen from the above, it is relatively cumbersome to operate with the standard API. Let's take a look at the smooth API of the official secondary package.

The simplest process, 5 lines of code to get it done.

BpmnModelInstance modelInstance = Bpmn.createProcess()
  .startEvent()
  .userTask()
  .endEvent()
  .done();


The process with branches is only a few 10 lines of code, which is greatly simplified compared with the standard API.

BpmnModelInstance modelInstance = Bpmn.createProcess()
  .startEvent()
  .userTask()
  .parallelGateway()
    .scriptTask()
    .endEvent()
  .moveToLastGateway()
    .serviceTask()
    .endEvent()
  .done();

Relatively complex processes can also be handled.

BpmnModelInstance modelInstance = Bpmn.createProcess()
  .startEvent()
  .userTask()
  .exclusiveGateway()
  .name("What to do next?")
    .condition("Call an agent", "#{action = 'call'}")
    .scriptTask()
    .endEvent()
  .moveToLastGateway()
    .condition("Create a task", "#{action = 'task'}")
    .serviceTask()
    .endEvent()
  .done();

It should be noted that the official statement clearly stated that the Fluent API is close to completion and not capable of realizing all functions. Currently, the following basic elements can be supported:

  • process
  • start event
  • exclusive gateway
  • parallel gateway
  • script task
  • service task
  • user task
  • signal event definition
  • end event
  • subprocess

test verification

A simple test was done using the fluent API, as follows:

    String name = entity.getName();

    // 向模型新增流程
    BpmnModelInstance modelInstance = Bpmn.createProcess()
            .name(name)
            .executable()
            .startEvent()
            .userTask()
            .name("Some work to do")
            .endEvent()
            .done();

    // 发布
    repositoryService.createDeployment().name(name)
            .addModelInstance(name,modelInstance).deploy();

Looking at the library table, the process has indeed been successfully released.
image.png
There is a small problem, that is, the process name in the generated xml has become garbled in Chinese, this piece is put first, and it will be solved later when necessary.

Technical solutions

Implementation ideas

Based on the above technology pre-research and test verification, the simple and smooth API is preferred to convert json, and when encountering functions that cannot be realized by the smooth API, the standard API is used to assist in the implementation. First take a relatively simple process for function development, and gradually enrich and improve the functions.

Next, we start from the Camunda model and API to process the json data from the front end. During the processing, the json data structure will be optimized and reconstructed synchronously. It is expected that the open source projects will be very different in the end.

Realize the use case

The front-end modeling is shown in the figure below. In addition to the start link and the end link, there are three links, two user tasks, and one service task.
image.png
The JSON data format generated by the front end is as follows:

{
    
    
    "nodeName": "发起人",
    "type": 0,
    "priorityLevel": "",
    "settype": "",
    "selectMode": "",
    "selectRange": "",
    "directorLevel": "",
    "examineMode": "",
    "noHanderAction": "",
    "examineEndDirectorLevel": "",
    "ccSelfSelectFlag": "",
    "conditionList": [
        
    ],
    "nodeUserList": [
        
    ],
    "childNode": {
    
    
        "nodeName": "审核人",
        "error": false,
        "type": 1,
        "settype": 2,
        "selectMode": 0,
        "selectRange": 0,
        "directorLevel": 1,
        "examineMode": 1,
        "noHanderAction": 2,
        "examineEndDirectorLevel": 0,
        "childNode": {
    
    
            "nodeName": "抄送人",
            "type": 2,
            "ccSelfSelectFlag": 1,
            "childNode": null,
            "nodeUserList": [
                
            ],
            "error": false
        },
        "nodeUserList": [
            
        ]
    },
    "conditionNodes": [
        
    ]
}

The meanings of the fields are as follows:

"nodeName": "发起人",//节点名称
"type": 0,// 0 发起人 1审批 2抄送 3条件 4路由
"priorityLevel": "",// 条件优先级
"settype": "",// 审批人设置 1指定成员 2主管 4发起人自选 5发起人自己 7连续多级主管
"selectMode": "", //审批人数 1选一个人 2选多个人
"selectRange": "", //选择范围 1.全公司 2指定成员 2指定角色
"directorLevel": "", //审批终点  最高层主管数
"examineMode": "", //多人审批时采用的审批方式 1依次审批 2会签
"noHanderAction": "",//审批人为空时 1自动审批通过/不允许发起 2转交给审核管理员
"examineEndDirectorLevel": "", //审批终点 第n层主管
"ccSelfSelectFlag": "", //允许发起人自选抄送人
"conditionList": [], //当审批单同时满足以下条件时进入此流程
"nodeUserList": [], //操作人
"childNode":{
    
    } //子节点

When analyzing the data structure, it is found that the child node is an object, not an array. I feel that it cannot meet the actual needs. If there is a branch, there will be one node, and if there are multiple subsequent nodes, then a process with branches will be built for comparison. .
image.png
The generated json is as follows:

{
    
    
    "nodeName": "发起人",
    "type": 0,
    "priorityLevel": "",
    "settype": "",
    "selectMode": "",
    "selectRange": "",
    "directorLevel": "",
    "examineMode": "",
    "noHanderAction": "",
    "examineEndDirectorLevel": "",
    "ccSelfSelectFlag": "",
    "conditionList": [
        
    ],
    "nodeUserList": [
        
    ],
    "childNode": {
    
    
        "nodeName": "审核人",
        "error": false,
        "type": 1,
        "settype": 2,
        "selectMode": 0,
        "selectRange": 0,
        "directorLevel": 1,
        "examineMode": 1,
        "noHanderAction": 2,
        "examineEndDirectorLevel": 0,
        "childNode": {
    
    
            "nodeName": "路由",
            "type": 4,
            "childNode": {
    
    
                "nodeName": "抄送人",
                "type": 2,
                "ccSelfSelectFlag": 1,
                "childNode": null,
                "nodeUserList": [
                    
                ],
                "error": false
            },
            "conditionNodes": [
                {
    
    
                    "nodeName": "条件1",
                    "error": true,
                    "type": 3,
                    "priorityLevel": 1,
                    "conditionList": [
                        
                    ],
                    "nodeUserList": [
                        
                    ],
                    "childNode": null
                },
                {
    
    
                    "nodeName": "条件2",
                    "type": 3,
                    "priorityLevel": 2,
                    "conditionList": [
                        
                    ],
                    "nodeUserList": [
                        
                    ],
                    "childNode": null
                }
            ]
        },
        "nodeUserList": [
            
        ]
    },
    "conditionNodes": [
        
    ]
}

Comparing the data, it can be seen that the json data structure design in the front-end open source project regards the branch node, each branch and the aggregation node as one node, and the branches are put into the conditionNodes attribute as an array.

Development platform information

Platform name: One Two Three Development Platform
Introduction: Enterprise-level general development platform
Design information: csdn column
Open source address: Gitee
open source protocol: MIT
open source is not easy, welcome to favorite, like, comment.

Guess you like

Origin blog.csdn.net/seawaving/article/details/131846518