应用开发平台集成工作流——流程建模功能模型转换技术方案

背景

对于流程设置不友好的问题,国内钉钉另行设计与实现了一套流程建模模式,跟bpmn规范无关,有人仿照实现了下,并做了开源(https://github.com/StavinLi/Workflow-Vue3),效果图如下:

实现大致原理是基于无限嵌套的子节点,输出json数据,传给后端,后端进行解析后,调用Camunda引擎的api,转换成流程模型后持久化。

上篇介绍了前端集成,今天重点来说下如何解析前端传来的json数据,调用Camunda引擎的api,转换成流程模型这部分内容。

Camunda API

对于核心问题,自定义的json数据,转换为流程模型,使用的是Camunda Model API,官方文档地址:https://docs.camunda.org/manual/7.19/user-guide/model-api/bpmn-model-api/

需要注意的是,官方这里提供了两套api,一套是中规中矩的标准API;另一套则是流畅API( fluent builder API)。前者能完成所有操作,使用起来比较繁琐;后者能完成大部分功能,但使用起来简便。

标准API

这边引用官方文档的示例,来简单看下使用方法,后面会结合转换工作来说实战。

创建如上图一个只包含一个用户任务环节的最简流程,相关API调用如下所示:

// 创建一个空白模型
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);


创建有两个并行任务的简单流程,相关API调用如下所示:

// 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);

中间涉及到几个工具类方法,具体如下:

// 创建子元素
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;
}

流畅API

从上面可以看出来,用标准API操作还是相对比较繁琐的。下面来看看官方二次封装的流畅API。

最简流程,5行代码搞定。

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


带分支的流程,也不过几10行代码,相比标准API大幅简化。

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

相对复杂的流程,也能搞定。

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();

需要注意的是,官方明确表态,流畅API是接近完成而不是可实现所有功能,目前能支持以下基本元素:

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

测试验证

使用流畅API做了一个简单测试,如下:

    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();

查看库表,流程确实发布成功了。
image.png
有个小问题,就是生成的xml中流程名称中文变成了乱码,这块先放放,后面必要时再解决。

技术方案

实现思路

基于上述技术预研和测试验证,优先使用简便的流畅API来转换json,遇到流畅API无法实现的功能时,使用标准API来辅助实现。先拿一个比较简单的流程来做功能开发,逐步丰富和完善功能。

接下来,我们从Camunda模型和API出发,处理前端传来的json数据,处理过程中,会同步对json的数据结构进行优化重构,预计最终与会开源项目差异很大。

实现用例

前端建模如下图所示,除开始环节和结束环节外,有三个环节,两个用户任务,一个服务任务。
image.png
前端生成的JSON数据格式如下:

{
    
    
    "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": [
        
    ]
}

字段含义如下:

"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":{
    
    } //子节点

数据结构分析时发现子节点是一个对象,而不是一个数组,感觉满足不了实际需求,有分支的情况,会出现一个节点,有多个后续节点的情况,然后建了一个有分支的流程来对比。
image.png
生成的json如下:

{
    
    
    "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": [
        
    ]
}

对比数据可以看出来,前端开源项目中的json数据结构设计,将分支节点、各分支和汇聚节点视为1个节点,分支作为数组放入conditionNodes属性中。

开发平台资料

平台名称:一二三开发平台
简介: 企业级通用开发平台
设计资料:csdn专栏
开源地址:Gitee
开源协议:MIT
开源不易,欢迎收藏、点赞、评论。

猜你喜欢

转载自blog.csdn.net/seawaving/article/details/131846518
今日推荐