bpmn-js 流程設計器 與 flowable/activiti 拓展的可行性研究

bpmn-js 流程設計器 與 flowable/activiti 拓展的可行性研究

前因

最近在準備開源一款流程引擎項目,主要包含 流程設計器 表單設計器 流程引擎,碰見了一個問題 開發過程中 經常需要拓展節點或節點元素,因爲bpmn規範可能不滿足實際項目需求。記錄一下 解決思路。

先上效果圖

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

涉及技術

前端使用 bpmn.js

  1. 拓展 flowable.json或者 activiti.json,新增我們 拓展的節點及元素。
{
  "name": "Flowable",
  "uri": "http://flowable.org/bpmn",
  "prefix": "flowable",
  "xml": {
    "tagAlias": "lowerCase"
  },
  "associations": [],
  "types": [
    {
       // 拓展節點名稱
      "name": "CustomProperties",
      "superClass": [
        "Element"
      ],
      "meta": {
      	// * 表示所有bpmn節點都可繼承該屬性
        "allowedIn": [
          "*"
        ]
      },
      "properties": [
       // 拓展屬性
         {
          "name": "values",
          "type": "CustomProperty",
          "isMany": true
        },
        {
          "name": "userIdList",
          "isAttr": true,
          "type": "String"
        },
		{
          "name": "userNameList",
          "isAttr": true,
          "type": "String"
        },
		{
          "name": "assigneeField",
          "isAttr": true,
          "type": "String"
        },
        {
          "name": "handlerStrategy",
          "isAttr": true,
          "type": "String"
        },
        {
          "name": "roleGroupCode",
          "isAttr": true,
          "type": "String"
        },
		{
          "name": "roleCode",
          "isAttr": true,
          "type": "String"
        },
        {
          "name": "findUserType",
          "isAttr": true,
          "type": "String"
        },
        {
          "name": "combineType",
          "isAttr": true,
          "type": "String"
        },
        {
          "name": "relationNodeId",
          "isAttr": true,
          "type": "String"
        },
        {
          "name": "actionList",
          "isAttr": true,
          "type": "String"
        },
        {
          "name": "taskType",
          "isAttr": true,
          "type": "String"
        },
        {
          "name": "nodeType",
          "isAttr": true,
          "type": "String"
        },
        {
          "name": "isSequential",
          "isAttr": true,
          "type": "String"
        },
        {
          "name": "proportion",
          "isAttr": true,
          "type": "String"
        },
        {
          "name": "expression",
          "isAttr": true,
          "type": "String"
        },
        {
          "name": "skipExpression",
          "isAttr": true,
          "type": "String"
        },{
          "name": "formName",
          "isAttr": true,
          "type": "String"
        },
        {
          "name": "selectFormKey",
          "isAttr": true,
          "type": "String"
        },
        {
          "name": "selectPath",
          "isAttr": true,
          "type": "String"
        }
        
      ]
    },
    {
      "name": "CustomProperty",
      "superClass": [
        "Element"
      ],
      "properties": [
        {
          "name": "id",
          "type": "String",
          "isAttr": true
        },
        {
          "name": "name",
          "type": "String",
          "isAttr": true
        },
        {
          "name": "value",
          "type": "String",
          "isAttr": true
        }
      ]
    },
  1. 通過bpmn.js 新增或者修改對應節點元素XML
   // 先判斷當前element 是否包含 flowable:CustomProperties ,如果包含 則找出來更新對應屬性,如果不存在,則創建後, 在更新整個 extensionElements 即可
 createOrUpdateCustomProperties(property, value) {
      const that = this
      const bpmnModeler = that.bpmnModeler()
      const bpmnFactory = bpmnModeler.get('bpmnFactory')
      let extensionElements = bpmnHelper.getPropertie(that.element, 'extensionElements')
      if (!extensionElements) {
        extensionElements = elementHelper.createElement('bpmn:ExtensionElements', null, this.element, bpmnFactory)
      }
      const length = extensionElements.get('values').length
      let customProperties
      let customPropertiesIndex = -1
      for (let i = 0; i < length; i++) {
        if (extensionElements.get('values')[i] && extensionElements.get('values')[i].$type === 'flowable:CustomProperties') {
          customProperties = extensionElements.get('values')[i]
          customPropertiesIndex = i
        }
      }
      if (!customProperties) {
        customProperties = elementHelper.createElement('flowable:CustomProperties', null, this.element, bpmnFactory)
      }

      const data = {}
      data[property] = value
      customProperties.$attrs[property] = value

      if (customPropertiesIndex > -1) {
        extensionElements.get('values')[customPropertiesIndex] = customProperties
      } else {
        extensionElements.get('values').push(customProperties)
      }
      const modeling = bpmnModeler.get('modeling')
      // 更新
      modeling.updateProperties(this.element, {
        extensionElements: extensionElements
      })
    }
elementHelper 如下:
'use strict'

var ElementHelper = {}
module.exports = ElementHelper

/**
 * Creates a new element and set the parent to it
 *
 * @method ElementHelper#createElement
 *
 * @param {String} elementType of the new element
 * @param {Object} properties of the new element in key-value pairs
 * @param {moddle.object} parent of the new element
 * @param {BpmnFactory} factory which creates the new element
 *
 * @returns {djs.model.Base} element which is created
 */
ElementHelper.createElement = function(elementType, properties, parent, factory) {
  var element = factory.create(elementType, properties)
  element.$parent = parent
  return element
}

  1. 前端將這個xml 文件 傳遞給後端,後端可以通過如下代碼解析:
  BpmnXMLConverter bpmnXMLConverter = new BpmnXMLConverter();
        byte[] bytes = processXml.getBytes();
        ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
        XMLInputFactory xif = XMLInputFactory.newInstance();
        InputStreamReader in = null;
        try {
            in = new InputStreamReader(inputStream, "UTF-8");
            XMLStreamReader xtr = xif.createXMLStreamReader(in);
            BpmnModel bpmnModel = bpmnXMLConverter.convertToBpmnModel(xtr);
            // 注意 這個 bpmnModel 已經包含了我們剛纔定義的屬性
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (XMLStreamException e) {
          e.printStackTrace()
        }
  1. 後臺解析結果,即可拿到對應拓展屬性與業務對接
    在這裏插入圖片描述
附上後臺讀取Utils 方法
    public static final String CUSTOME_EXTENSIONELEMENT = "customProperties";

    /**
     * 功能描述: 從 flowElement 獲取 指定名稱的 拓展元素
     *
     *
     * @param flowElement 元素
     * @param extensionElementName 拓展元素名稱
     * @return : org.flowable.bpmn.model.ExtensionElement
     * @author : zhoulin.zhu
     * @date : 2020/6/19 18:28
     */
    public static ExtensionElement getExtensionElementFromFlowElementByName(FlowElement flowElement, String extensionElementName) {

        if (flowElement == null) {
            return null;
        }
        if (StringUtils.isEmpty(extensionElementName)) {
            extensionElementName = CUSTOME_EXTENSIONELEMENT;
        }
        Map<String, List<ExtensionElement>> extensionElements = flowElement.getExtensionElements();
        for (Map.Entry<String, List<ExtensionElement>> stringEntry : extensionElements.entrySet()) {
            if (stringEntry.getKey().equals(extensionElementName)) {
                for (ExtensionElement extensionElement : stringEntry.getValue()) {
                    if (extensionElement.getName().equals(extensionElementName)) {
                        return extensionElement;
                    }
                }
            }
        }

        return null;
    }

    /**
     * 功能描述: 從拓展元素 獲取 拓展 屬性值
    /**
     * 功能描述: 從拓展元素 獲取 拓展 屬性值
     *
     *
     * @param extensionElement 拓展元素
     * @param attributesName 屬性名稱
     * @return : java.lang.String
     * @author : zhoulin.zhu
     * @date : 2020/6/19 18:30
     */
    public static  String getAttributesFromExtensionElementByName(ExtensionElement extensionElement, String attributesName) {

        if (extensionElement == null
                || StringUtils.isEmpty(attributesName)) {
            return null;
        }
        Map<String, List<ExtensionAttribute>> stringListMap = extensionElement.getAttributes();
        for (Map.Entry<String, List<ExtensionAttribute>> listEntry : stringListMap.entrySet()) {
            if (listEntry.getKey().equals(attributesName)) {
                return listEntry.getValue() != null && listEntry.getValue().size() > 0 ? listEntry.getValue().get(0).getValue() : null;
            }
        }
        return null;
    }

引用

鏈接: link.
[1]:bpmn.js.
[2]: 表單設計器: 表單設計器.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章