Flowable用代码自定义流程

项目集成flowable有段时间了,发现行业的流程业务全是一条直线加3,4个人工审核的节点,并没有分支网关等其它节点。因此原来用的model设计器组件多参数复杂,客户现场的体验不是很好。所以根据现场的实际业务自己调api的方式实现个简单的流程设计器。

最终的实现的效果是由开始节点[StartEvent]和结束结点[EndEvent]及用户任务节点[UserTask]组成的直线审核流程,并兼容用原生的流程设计器打开编辑。最终的效果如下图所示:


最终的效果

前端界面设计介绍,用户界面可以增加节点,删除节点。对节点命名,定义节点的审核用户[普通用户或角色],具体效果图如下所示:


用户界面

定义收集前端参数的对象,具体代码如下所示

@Data
public class ProcessParam {
    //流程ID
    private String modelId;
    //流程定义key
    private String key;
    //流程名字
    private String name;
    //流程节点
    private List<TaskParam> tasks;
    //开始结点名称
    private String startNodeName;
    //结束节点名称
    private String endNodeName;
}

@Data
public class TaskParam {

    public TaskParam(){

    }

    public TaskParam(String name,String no,String userType,List<String> userOrRoleIds,String userOrRoleName){
        this.name = name;
        this.no = no;
        this.userType = userType;
        this.userOrRoleIds = userOrRoleIds;
        this.userOrRoleName = userOrRoleName;
    }

    //节点名称
    private String name;
    //节点编号
    private String no;
    //审核用户类型 用户(user)或角色(role)
    private String userType;
    //审核用户或角色id
    private List<String> userOrRoleIds;

    //用户或角色名称
    private String userOrRoleName;

}

保存流程设计器的数据,具体的代妈如下:
大体思路是,构建一个BpmnModel对象,构建出各节点对象并设置好相关的参数,如名称,编号,审核人等,用SequenceFlow对象按顺序连接各节点对象,特别注意要设置各节点对象的outgoing属性网上很多教程没有提到这个,不设置会导致连接不正确。最后用BpmnAutoLayout对象自动设置好节点的坐标信息,不然也会导致流程在原生设计器显示不正常。

public void saveModelData(ProcessParam processParam){

        Model model = modelService.getModel(processParam.getModelId());

        Process process = new Process();
        process.setId(model.getKey());
        process.setName(model.getName());

        BpmnModel bpmnModelNew = new BpmnModel();
        bpmnModelNew.addProcess(process);

        List<TaskParam> nodeList = processParam.getTasks();

        //开始事件
        StartEvent startEvent = new StartEvent();
        startEvent.setId("start");
        startEvent.setName(processParam.getStartNodeName());

        process.addFlowElement(startEvent);
        FlowElement waitNode = startEvent;
        int i = 0;
        for ( ;i < nodeList.size(); i++) {
            TaskParam taskParam = nodeList.get(i);
            UserTask userTask = new UserTask();
            userTask.setId((i+1)+"");
            userTask.setName(taskParam.getName());
            if("user".equals(taskParam.getUserType())){
                userTask.setCandidateUsers(taskParam.getUserOrRoleIds());
            }
            if("role".equals(taskParam.getUserType())){
                userTask.setCandidateGroups(taskParam.getUserOrRoleIds());
            }
            process.addFlowElement(userTask);
            SequenceFlow sequenceFlow = new SequenceFlow(waitNode.getId(), userTask.getId());
            process.addFlowElement(sequenceFlow);
                    //设置当前节点的出口这个很关键,不设置这个的话生的流程图会错乱,网上很多教程也没有提到
            if(i==0){
                ((StartEvent) waitNode).setOutgoingFlows(ListUtil.toList(sequenceFlow));
            }else {
                ((UserTask) waitNode).setOutgoingFlows(ListUtil.toList(sequenceFlow));
            }

            waitNode = userTask;
        }

        //结束事件
        EndEvent completeEvent = new EndEvent();
        completeEvent.setId("complete");
        completeEvent.setName(processParam.getEndNodeName());
        process.addFlowElement(completeEvent);
        SequenceFlow endSequenceFlow = new SequenceFlow(waitNode.getId(), completeEvent.getId());
        ((UserTask) waitNode).setOutgoingFlows(ListUtil.toList(endSequenceFlow));
        process.addFlowElement(endSequenceFlow);
            //这句很关键让程序自动部局各节点的位置,网上很多教程没有提到
        new BpmnAutoLayout(bpmnModelNew).execute();

        //把BpmnModel转成json格式进行保存
        BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
        ObjectNode jsonNode = jsonConverter.convertToJson(bpmnModelNew);
        modelService.saveModel(model.getId(),model.getName(),model.getKey(),model.getDescription(),jsonNode.toString(),true,"",new UserWf());

    }

从数据库获取流程设计数据给到前端,具体代码如下:
大体思路是:调用api获取model对象,从model对象获取流程的json字符串然后用工具类转成BpmnModel对象,然后根据他们的关系生成ProcessParam对象传到前端去。

public ProcessParam getModelData(String modelId){

        ProcessParam processParam = new ProcessParam();
        List<TaskParam> tasks = new ArrayList<>();
        processParam.setTasks(tasks);
        Model model = modelService.getModel(modelId);
        processParam.setModelId(model.getId());
        processParam.setKey(model.getKey());
        processParam.setName(model.getName());
        //获取流程设计器的json数据
        String modelEditorJson = model.getModelEditorJson();
        ObjectNode editorJsonNode = null;
        try {
            editorJsonNode = (ObjectNode) objectMapper.readTree(modelEditorJson);
        } catch (IOException e) {
            e.printStackTrace();
            return processParam;
        }
        BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
        //把json转成bpmnModel对象
        BpmnModel bpmnModel = jsonConverter.convertToBpmnModel(editorJsonNode);
        Process process = bpmnModel.getMainProcess();
        Map<String,FlowElement> allNode = process.getFlowElementMap();
        if(allNode==null||allNode.size()<1){//没有节点信息直接返回
            return processParam;
        }
        List<StartEvent> startEventList = process.findFlowElementsOfType(StartEvent.class);
        StartEvent startEvent = startEventList.get(0);
        processParam.setStartNodeName(startEvent.getName());
        String nextNodeId = startEvent.getOutgoingFlows().get(0).getTargetRef();
        while (allNode.get(nextNodeId)!=null){
            FlowElement fe = allNode.get(nextNodeId);
            if(fe instanceof UserTask){
                UserTask userTask = (UserTask) fe;
                List<String> userOrRoleIds = null;
                String userOrRoleInfo = "";
                String userType = null;
                if(StrUtil.isNotBlank(userTask.getAssignee())){
                    userOrRoleIds = ListUtil.toList(userTask.getAssignee());
                    userType = "user";
                }
                if(userTask.getCandidateUsers()!=null&&userTask.getCandidateUsers().size()>0){
                    userOrRoleIds = userTask.getCandidateUsers();
                    userType = "user";
                }
                if(userTask.getCandidateGroups()!=null&&userTask.getCandidateGroups().size()>0){
                    userOrRoleIds = userTask.getCandidateGroups();
                    userType = "role";
                }
                //查询用户或角色名称
                if ("user".equals(userType)) {
                    userOrRoleInfo = this.getUserInfo(userOrRoleIds);
                }
                if ("role".equals(userType)) {
                    userOrRoleInfo = this.getRoleInfo(userOrRoleIds);
                }
                if(userTask.getOutgoingFlows().size()>1){
                    throw new RuntimeException("不支持多个分支流程数据,请使用高级流程设计器");
                }
                tasks.add(new TaskParam(userTask.getName(),userTask.getId(),userType,userOrRoleIds,userOrRoleInfo));
                nextNodeId = userTask.getOutgoingFlows().get(0).getTargetRef();
            }else if(fe instanceof EndEvent){
                EndEvent endEvent = (EndEvent) fe;
                processParam.setEndNodeName(endEvent.getName());
                nextNodeId = "";
                break;
            }else {
                throw new RuntimeException("不支持当前流程数据,请使用高级流程设计器");
            }
        }

        return processParam;

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

推荐阅读更多精彩内容