专栏首页Linyb极客之路工作流引擎之activiti-modeler在线流程设计器流程节点校验

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

前言

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

流程节点校验的方式

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

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

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

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

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

后端校验的方式

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

核心代码片段

List<ValidationError> validationErrorList = repositoryService.validateProcess(bpmnModel);

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

2、通过校验FlowElement合法性

核心代码

/**
     * 流程校验,因流程判断比较复杂,取巧借用事务回滚机制,如果校验失败,则回滚
     * @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弹窗拖拽和遮罩层禁用的方法如下

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

本文分享自微信公众号 - Linyb极客之路(gh_c420b2cf6b47)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-12-02

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 微服务的360度运行状况检查的评审模板

    lyb-geek
  • SpringCloud和微服务

    马丁福勒微服务论文:https://martinfowler.com/articles/microservices.html

    lyb-geek
  • Nginx配置对同一IP限速限流

     Nginx通过limit_conn_zone和limit_req_zone对同一个IP地址进行限速限流,可防止DDOS/CC和flood攻击,这类文章网上比较...

    lyb-geek
  • 企业与个人是如何跨越战略转折点 - 读《只有偏执狂才能生存》

    对于任何一个企业而言,战略转折点都极为重要。如果放任自流,战略转折点会将企业置于死地。可以想象当年的雅虎是如何错失搜索而拱手让Google崛起,诺基亚如何错失智...

    歪脖贰点零
  • Spring Boot是如何通过自动装配来简化开发的?

    最近用了一些spring-boot-starter-data的组件(redis,mongodb,jpa,elasticsearch等),才意识到Spring B...

    Java识堂
  • 真实经历,互联网大厂升职加薪那些事

    我的第一份工作,在三星电子北京通信研究院。11年8月份,也就是研二暑假开始在这里实习,12年3月底硕士毕业,正式入职,月薪9980元。

    纯洁的微笑
  • 一文掌握inbreeding coefficient近交系数的计算

    inbreeding coefficient,中文翻译为近交系数,近婚系数,近亲交配系数等等,用大写字母F表示。要理解这个概念,首先要搞清楚”近亲”的定义。

    生信修炼手册
  • Django: 路由与视图

    Django框架是美国World Company(世界公司?NB)的工程师Adrian Holovaty和Simon Willison在开发其公司运行的新闻网站...

    笔阁
  • 数学救命:决斗中的概率问题!

    大数据文摘
  • 一文彻底搞懂抽象类和接口!

    最近一些刚入门JAVA的读者朋友让我来写一篇有关于抽象类和接口的文章,我当即就答应了,因为抽象类和接口确实是两个很重要但是一时半会又无法完全理解的...

    Python进击者

扫码关注云+社区

领取腾讯云代金券