工作流引擎之activiti-modeler在线流程设计器流程节点校验

2018-12-24 15:52:26 浏览数 (1)

前言

Activiti Modeler是一款基于angular的web流程在线设计器,主要用于保存BPMN规范相关的对象,例如将模型转换为相应的流程图对象。该组件可以方便业务人员进行在线工作流程图设计。 Activiti Modeler它本身是不提供流程节点合法性校验,而为了保证流程能够顺利走通,因此我们需要进行流程节点校验。

流程节点校验的方式

1、前端保存前校验,通过扩展流程设计器的校验功能 2、后端保存校验,主要通过异常导致事务回滚机制进行校验

由于项目其前端框架主要用iview,项目组缺乏有angular.js开发经验的伙伴,又因为工期原因,没法空出多余时间进行angular研究,因此后面采用的后端保存校验的方式,本文主要介绍以后端保存校验

需要校验流程节点的哪些环节

其实就是校验流程节点的完整性

1、流程节点是否存在开始节点、步骤节点、结束节点 2、流程节点是否有设置节点名称 3、流程节点与节点之间是否有设置流程连接线 4、当流程节点出口存在多个分支时,是否有出口规则条件判断 5、。。。

后端校验的方式

1、通过activiti自带的API进行校验

核心代码片段

代码语言:javascript复制
List<ValidationError> validationErrorList = repositoryService.validateProcess(bpmnModel);

上面的校验方式,其自带的文案可能过于技术性,可能会给业务人员带来一定的阅读困难,因此项目组采用下面的校验方式

2、通过校验FlowElement合法性

核心代码

代码语言:javascript复制
/**
     * 流程校验,因流程判断比较复杂,取巧借用事务回滚机制,如果校验失败,则回滚
     * @param bpmnModel
     * @param handlerPersonMap 流程办理人map
     */
    private void validateFlowModel(BpmnModel bpmnModel,Map<String, List<String>> handlerPersonMap) {

        if(bpmnModel != null){
            Collection<FlowElement> flowElements = bpmnModel.getMainProcess().getFlowElements();
            List<StartEvent> startEvents = new ArrayList<>();
            List<UserTask> userTasks = new ArrayList<>();
            List<EndEvent> endEvents = new ArrayList<>();
            List<SequenceFlow> sequenceFlows = new ArrayList<>();
            StringBuilder nodeError = new StringBuilder();
            boolean isFoundNodeNameNotSet = false;
            for (FlowElement e : flowElements) {
                if (e instanceof StartEvent) {
                    StartEvent startEvent = (StartEvent)e;
                    startEvents.add(startEvent);
                } else if (e instanceof SequenceFlow) {
                    SequenceFlow sequenceFlow = (SequenceFlow)e;
                    sequenceFlows.add(sequenceFlow);
                } else if (e instanceof UserTask) {
                    UserTask userTask = (UserTask) e;
                    userTasks.add(userTask);
                    if(StringUtil.isBlank(userTask.getName())){
                        isFoundNodeNameNotSet = true;
                    }
                } else if (e instanceof EndEvent) {
                    EndEvent endEvent = (EndEvent) e;
                    endEvents.add(endEvent);
                }
            }

            if(CollectionUtils.isEmpty(startEvents)){
                nodeError.append(Constants.PRCOESS_STARTEVENT_NOT_FOUND).append(Constants.SEMICOLON).append(Constants.HTML_NEWLINE);
            }

            if(CollectionUtils.isEmpty(userTasks)){
                nodeError.append(Constants.PRCOESS_USERTASK_NOT_FOUND).append(Constants.SEMICOLON).append(Constants.HTML_NEWLINE);
            }

            if (CollectionUtils.isEmpty(endEvents)){
                nodeError.append(Constants.PRCOESS_ENDEVENT_NOT_FOUND).append(Constants.SEMICOLON).append(Constants.HTML_NEWLINE);
            }

            if(StringUtil.isNotBlank(nodeError.toString())){
                throw new MuseException(ErrorCode.PROCESS_SAVE_FAIL,nodeError.toString());
            }

            if(isFoundNodeNameNotSet){
                throw new MuseException(ErrorCode.PROCESS_SAVE_FAIL,Constants.PRCOESS_NODE_NAME_NOT_SET);
            }


            if(!CollectionUtils.isEmpty(sequenceFlows)){
                for(SequenceFlow sequenceFlow : sequenceFlows){
                    if(StringUtil.isBlank(sequenceFlow.getSourceRef()) && StringUtil.isBlank(sequenceFlow.getTargetRef())){
                        nodeError.append(Constants.PRCOESS_SEQUENCEFLOW_RELATE_NODE_NOT_FOUND).append(Constants.SEMICOLON).append(Constants.HTML_NEWLINE);
                        break;
                    }
                }
            }


            StringBuilder handlerPersonNotFound = new StringBuilder();

            for(StartEvent startEvent : startEvents){
                List<SequenceFlow> outGoingFlows = startEvent.getOutgoingFlows();
                if(CollectionUtils.isEmpty(outGoingFlows)){
                   nodeError.append("[").append(startEvent.getName()).append("]").append(Constants.PRCOESS_OUTGOING_FLOWS_NOT_FOUND).append(Constants.SEMICOLON).append(Constants.HTML_NEWLINE);
                }
                //节点之间连线校验
                validateSequenceFlows(nodeError, startEvent.getName(), outGoingFlows,SequenceFlowType.TARGET);

                List<String> handlerPersonList = handlerPersonMap.get(startEvent.getId());
                if(CollectionUtils.isEmpty(handlerPersonList)){
                    handlerPersonNotFound.append("[").append(startEvent.getName()).append("]").append(Constants.PRCOESS_HANDLER_PERSON_NOT_FOUND).append(Constants.SEMICOLON).append(Constants.HTML_NEWLINE);
                }

            }


            for(UserTask userTask : userTasks){
                List<SequenceFlow> incomingFlows = userTask.getIncomingFlows();
                if(CollectionUtils.isEmpty(incomingFlows)){
                    nodeError.append("[").append(StringUtil.isBlank(userTask.getName())? Constants.PRCOESS_WIHTOUT_NAME : userTask.getName()).append("]").append(Constants.PRCOESS_INCOMING_FLOWS_NOT_FOUND).append(Constants.SEMICOLON).append(Constants.HTML_NEWLINE);
                }
                //节点之间连线校验
                validateSequenceFlows(nodeError,StringUtil.isBlank(userTask.getName())? Constants.PRCOESS_WIHTOUT_NAME : userTask.getName(),incomingFlows,SequenceFlowType.SOURCE);

                List<SequenceFlow> outGoingFlows = userTask.getOutgoingFlows();
                if(CollectionUtils.isEmpty(outGoingFlows)){
                    nodeError.append("[").append(StringUtil.isBlank(userTask.getName())? Constants.PRCOESS_WIHTOUT_NAME : userTask.getName()).append("]").append(Constants.PRCOESS_OUTGOING_FLOWS_NOT_FOUND).append(Constants.SEMICOLON).append(Constants.HTML_NEWLINE);
                }
                //节点之间连线校验
                validateSequenceFlows(nodeError, StringUtil.isBlank(userTask.getName())? Constants.PRCOESS_WIHTOUT_NAME : userTask.getName(), outGoingFlows,SequenceFlowType.TARGET);

                List<String> handlerPersonList = handlerPersonMap.get(userTask.getId());
                if(CollectionUtils.isEmpty(handlerPersonList)){
                    handlerPersonNotFound.append("[").append(userTask.getName()).append("]").append(Constants.PRCOESS_HANDLER_PERSON_NOT_FOUND).append(Constants.SEMICOLON).append(Constants.HTML_NEWLINE);
                }

            }

            for(EndEvent endEvent : endEvents){
                List<SequenceFlow> incomingFlows = endEvent.getIncomingFlows();
                if(CollectionUtils.isEmpty(incomingFlows)){
                    nodeError.append("[").append(StringUtil.isBlank(endEvent.getName()) ? Constants.ACT_TYPE_END_CN : endEvent.getName()).append("]").append(Constants.PRCOESS_INCOMING_FLOWS_NOT_FOUND).append(Constants.SEMICOLON).append(Constants.HTML_NEWLINE);
                }
                //节点之间连线校验
                validateSequenceFlows(nodeError,StringUtil.isBlank(endEvent.getName()) ? Constants.ACT_TYPE_END_CN : endEvent.getName(),incomingFlows,SequenceFlowType.SOURCE);
            }

            if(StringUtil.isNotBlank(handlerPersonNotFound.toString())){
                nodeError.append(handlerPersonNotFound.toString());
            }



            if(StringUtil.isNotBlank(nodeError.toString())){
                throw new MuseException(ErrorCode.PROCESS_SAVE_FAIL,nodeError.toString());
            }



        }
    }

    /**
     * 节点之间连接线校验
     * @param nodeError
     * @param nodeName
     * @param sequenceFlows
     */
    private void validateSequenceFlows(StringBuilder nodeError, String nodeName, List<SequenceFlow> sequenceFlows,SequenceFlowType sequenceFlowType) {
        if(CollectionUtils.isEmpty(sequenceFlows)){
            return;
        }
        String tip = null;
        String seqRef = null;
        int  conditionCount = sequenceFlows.size();
        for(SequenceFlow sequenceFlow : sequenceFlows){
            switch (sequenceFlowType){
                case SOURCE:
                    seqRef = sequenceFlow.getSourceRef();
                    tip = Constants.PRCOESS_INCOMING_FLOWS_NOT_FOUND;
                    break;
                case TARGET:
                    seqRef = sequenceFlow.getTargetRef();
                    tip = Constants.PRCOESS_OUTGOING_FLOWS_NOT_FOUND;
                    break;
                    default:
                        break;
            }

            if(StringUtil.isBlank(seqRef)){
                nodeError.append("[").append(nodeName).append("]").append(tip).append(Constants.SEMICOLON).append(Constants.HTML_NEWLINE);
                break;
            }

            //存在多个分支时,则进行出口条件校验
            if(sequenceFlows.size() > 1){
               String conditionExpression = sequenceFlow.getConditionExpression();
               if(StringUtil.isBlank(conditionExpression)){
                   conditionCount = conditionCount - 1;
               }

            }

        }

        if(conditionCount == 0){
            nodeError.append("[").append(nodeName).append("]").append(Constants.PRCOESS_EXPORT_RULE_NOT_FOUND).append(Constants.SEMICOLON).append(Constants.HTML_NEWLINE);
        }
    }

效果图展示

其他

项目组有用iview的伙伴们,且要实现modal弹窗的拖拽、遮罩层的禁用的需求的话,如果你们项目是采用iview3.x版本以上,则modal加上draggable=true,和mask=false这两个属性就可以实现效果。如果目前采用的是iview2.x版本,这个版本没有这两个属性,可以通过引入jquery-ui.min.js,调用该js提供的 draggable()实现拖拽

附录

view2.x 版本实现modal弹窗拖拽和遮罩层禁用的方法如下

代码语言:javascript复制
draggableWithDisAbledMask:function(){
            $("div.ivu-modal").draggable();
            $("div.ivu-modal-mask").remove();
            $("div.ivu-modal-content").css("background","#F7F7F7");
            setTimeout(function(){
                $(".ivu-modal-wrap").removeClass("ivu-modal-wrap");
            }, 1500);
        }

0 人点赞