flowable modeler自定义属性,并且在流程中获取属性值

因为自己系统中业务的需求,flowable节点自带的属性是有限的,所以需要在节点上自定义属性以便于流程流转。
一、修改stencilset_bpmn.json

    {
      "name" : "approvalPathPackage",
      "properties" : [ {
        "id" : "approvalPath",
        "type" : "Text",
        "title" : "审批接口路径",
        "value" : "",
        "description" : "The descriptive name of the BPMN element.",
        "popular" : true
      } ]
    },
    {
      "name" : "rejectPathPackage",
      "properties" : [ {
        "id" : "rejectPath",
        "type" : "Text",
        "title" : "驳回接口路径",
        "value" : "",
        "description" : "The descriptive name of the BPMN element.",
        "popular" : true
      } ]
    },

在文件下面添加上面配置的name


4.png
3.png

但是只是这样配好之后我们去下载xml的时候会发现没有新增的属性,这个时候在流程实例流转的时候也是获取不到新增属性的值的。
是因为flowable并没有支持自定义属性的存储,所以这个时候就要自己对自定义属性进行解析了。

二、现在就要修改下载xml文件那个接口,路径前的custom是我自己加进去的,重写这个接口


2.png
  1. 新建ModelController 继承 AbstractModelBpmnResource:
package com.chinairi.workflow.modeler.controller;

import com.chinairi.workflow.modeler.service.CustomModelServiceImpl;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.ui.common.service.exception.BadRequestException;
import org.flowable.ui.common.service.exception.BaseModelerRestException;
import org.flowable.ui.common.service.exception.InternalServerErrorException;
import org.flowable.ui.modeler.domain.AbstractModel;
import org.flowable.ui.modeler.domain.Model;
import org.flowable.ui.modeler.rest.app.AbstractModelBpmnResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.URLEncoder;

/**
 * @author PYJ
 * @date 2019/12/13
 */
@RestController
@RequestMapping("/custom/app")
public class ModelController extends AbstractModelBpmnResource {
    private static final Logger LOGGER = LoggerFactory.getLogger(ModelController.class);
    @Autowired
    protected CustomModelServiceImpl xxModelService;

    /**
     * GET /rest/models/{modelId}/bpmn20 -> Get BPMN 2.0 xml
     */
    @RequestMapping(value = "/rest/models/{processModelId}/bpmn20", method = RequestMethod.GET)
    public void getProcessModelBpmn20Xml(HttpServletResponse response, @PathVariable String processModelId) throws IOException {
        LOGGER.info("开始下载xml文件1");
        if (processModelId == null) {
            throw new BadRequestException("No process model id provided");
        }
        Model model = xxModelService.getModel(processModelId);
        generateBpmn20Xml(response, model);
    }

    protected void generateBpmn20Xml(HttpServletResponse response, AbstractModel model) {
        String name = model.getName().replaceAll(" ", "_") + ".bpmn20.xml";
        String encodedName = null;
        try {
            encodedName = "UTF-8''" + URLEncoder.encode(name, "UTF-8");
        } catch (Exception e) {
            LOGGER.warn("Failed to encode name " + name);
        }

        String contentDispositionValue = "attachment; filename=" + name;
        if (encodedName != null) {
            contentDispositionValue += "; filename*=" + encodedName;
        }
        response.setHeader("Content-Disposition", contentDispositionValue);
        if (model.getModelEditorJson() != null) {
            try {
                ServletOutputStream servletOutputStream = response.getOutputStream();
                response.setContentType("application/xml");

                BpmnModel bpmnModel = xxModelService.getBpmnModel(model);
                byte[] xmlBytes = xxModelService.getBpmnXML(bpmnModel);
                BufferedInputStream in = new BufferedInputStream(new ByteArrayInputStream(xmlBytes));
                byte[] buffer = new byte[8096];
                while (true) {
                    int count = in.read(buffer);
                    if (count == -1) {
                        break;
                    }
                    servletOutputStream.write(buffer, 0, count);
                }
                // Flush and close stream
                servletOutputStream.flush();
                servletOutputStream.close();
            } catch (BaseModelerRestException e) {
                throw e;
            } catch (Exception e) {
                LOGGER.error("Could not generate BPMN 2.0 XML", e);
                throw new InternalServerErrorException("Could not generate BPMN 2.0 xml");
            }
        }
    }
}

  1. 新建CustomModelServiceImpl继承 ModelServiceImpl
package com.chinairi.workflow.modeler.service;

import com.chinairi.workflow.modeler.convert.CustomBpmnJsonConverter;
import org.flowable.editor.language.json.converter.BpmnJsonConverter;
import org.flowable.ui.modeler.service.ModelServiceImpl;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;

/**
 * @author PYJ
 * @date 2019/12/13
 */
@Service("CustomModelServiceImpl")
@Primary
public class CustomModelServiceImpl extends ModelServiceImpl {

    protected BpmnJsonConverter  bpmnJsonConverter = new CustomBpmnJsonConverter();
}
  1. 新建CustomBpmnJsonConverter继承 BpmnJsonConverter
package com.chinairi.workflow.modeler.convert;

import org.flowable.editor.language.json.converter.BpmnJsonConverter;

/**
 * @author PYJ
 * @date 2019/12/13
 */
public class CustomBpmnJsonConverter extends BpmnJsonConverter {


    static {
        convertersToBpmnMap.put(STENCIL_TASK_USER,CustomizeUserTaskJsonConverter.class);
    }
}
  1. 新建自定义userTaskjson解析器CustomizeUserTaskJsonConverter继承UserTaskJsonConverter
package com.chinairi.workflow.modeler.convert;

import com.chinairi.workflow.modeler.util.ExtensionAttributeUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.flowable.bpmn.model.BaseElement;
import org.flowable.bpmn.model.ExtensionAttribute;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.UserTask;
import org.flowable.editor.language.json.converter.BaseBpmnJsonConverter;
import org.flowable.editor.language.json.converter.UserTaskJsonConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

/**
 * @author PYJ
 * @date 2019/12/13
 */
public class CustomizeUserTaskJsonConverter extends UserTaskJsonConverter {

    private static final Logger LOGGER = LoggerFactory.getLogger(CustomizeUserTaskJsonConverter.class);
    //审批接口路径配置
    private static final String TASK_APPROVAL_PATH="approvalpath";
    private static final String TASK_APPROVAL_PATH_KEY="approvalPath";
    //驳回接口路径设置
    private static final String TASK_REJECT_PATH="rejectpath";
    private static final String TASK_REJECT_PATH_KEY="rejectPath";

    public static void fillBpmnTypes(
            Map<Class<? extends BaseElement>, Class<? extends BaseBpmnJsonConverter>> convertersToJsonMap) {
        convertersToJsonMap.put(UserTask.class, CustomizeUserTaskJsonConverter.class);
    }

    @Override
    protected FlowElement convertJsonToElement(JsonNode elementNode, JsonNode modelNode,
                                               Map<String, JsonNode> shapeMap) {
        FlowElement flowElement = super.convertJsonToElement(elementNode, modelNode, shapeMap);
        LOGGER.info("进入自定义属性解析");
        if(flowElement instanceof UserTask){
            ObjectMapper objectMapper = new ObjectMapper();
            UserTask userTask = (UserTask) flowElement;
            try {
                LOGGER.info("节点:" + objectMapper.writeValueAsString(userTask));
            }catch (JsonProcessingException e) {
                LOGGER.error("节点序列化异常.");
            }
            Map<String,List<ExtensionAttribute>> atts = new HashMap<String,List<ExtensionAttribute>>();

            String approvalPath = getPropertyValueAsString(TASK_APPROVAL_PATH,elementNode);
            LOGGER.info("新增自定义审批接口路径;路径属性["+TASK_APPROVAL_PATH+"]="+approvalPath);
            ExtensionAttribute ea1 =  ExtensionAttributeUtils.generate(TASK_APPROVAL_PATH_KEY,approvalPath);

            String rejectPath = getPropertyValueAsString(TASK_REJECT_PATH,elementNode);
            LOGGER.info("新增自定义驳回接口路径;路径属性["+TASK_REJECT_PATH+"]="+rejectPath);
            ExtensionAttribute ea2 =  ExtensionAttributeUtils.generate(TASK_REJECT_PATH_KEY,rejectPath);

            List<ExtensionAttribute> list = new ArrayList<>();
            if (ea1 != null){
                list.add(ea1);
            }
            if (ea2 != null){
                list.add(ea2);
            }
            atts.put("CUSTOM-FLOWABLE-EXT",list);
            if (atts.size()>0){
                flowElement.setAttributes(atts);
            }
        }
        return flowElement;
    }
}

  1. 创建ExtensionAttributeUtils 对 拓展属性进行操作
package com.chinairi.workflow.modeler.util;

import org.flowable.bpmn.model.ExtensionAttribute;

/**
 * @author PYJ
 * @date 2019/12/13
 */
public class ExtensionAttributeUtils {

    public static ExtensionAttribute generate(String key, String val){
        ExtensionAttribute ea = new ExtensionAttribute();
        ea.setNamespace("http://xx.com.cn");
        ea.setName(key);
        ea.setNamespacePrefix("custom");
        ea.setValue(val);
        return ea;
    }

}

重新部署之后再下载xml 可以看到已经有了我们自定义的属性


5.png

三、接下来就是在流程中获取自定义属性值

public Map<String, List<ExtensionAttribute>> getZidiyingParam(String taskId){
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (task == null){
            return null;
        }
        Map<String, List<ExtensionAttribute>> extensionAttrs = null;
        String processDefId = task.getProcessDefinitionId();
        FlowNode flowNode = findFlowNodeByActivityId(processDefId,task.getTaskDefinitionKey());
        if (flowNode != null && flowNode instanceof UserTask) {
            UserTask userTask = (UserTask) flowNode;
            extensionAttrs = userTask.getAttributes();

        }
        for (String key : extensionAttrs.keySet()){
            System.out.println("====");
            System.out.println("key:"+key);
            System.out.println(extensionAttrs.get(key));
            System.out.println(extensionAttrs.get(key).get(0).getValue());
        }
        return extensionAttrs;
    }
  public FlowNode findFlowNodeByActivityId(String processDefId, String activityId) {
        FlowNode activity = null;
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefId);
        List<Process> processes = bpmnModel.getProcesses();
        for (Process process : processes) {
            FlowElement flowElement = process.getFlowElementMap().get(activityId);
            if (flowElement != null) {
                activity = (FlowNode) flowElement;
                break;
            }
        }
        return activity;
    }

接下来可以看到输出的结果,和流程设计的时候输入的自定义属性值一样的


6.png

到这里我们已经成功获取到了,希望对大家有帮助。不对的地方还请指出。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351