基于若依的ruoyi-nbcio流程管理系统仿钉钉流程初步完成转bpmn设计(还有bug,以后再修改)

简介: 基于若依的ruoyi-nbcio流程管理系统仿钉钉流程初步完成转bpmn设计(还有bug,以后再修改)

更多ruoyi-nbcio功能请看演示系统

gitee源代码地址

前后端代码: https://gitee.com/nbacheng/ruoyi-nbcio

演示地址:RuoYi-Nbcio后台管理系统

    今天初步完成仿钉钉流程转bpmn设计的工作,当然还有不少bug,以后有需要或者网友也帮忙改进。

1、前端

  前端主要部分如下:

previewJson() {
      const getCmpData = name => this.$refs[name].getData()
      // processDesign 返回的是Promise 因为要做校验
      console.log("publish getCmpData",getCmpData)
      const p3 = getCmpData('processDesign')
      console.log("publish p3",p3)
      Promise.all([p3])
      .then(res => {
        const param = {
          processData: res[0].formData
        }
        this.previewResult = JSON.stringify(param, null, 2);//json格式化
        this.previewType = "json";
        this.previewModelVisible = true;
      })
      .catch(err => {
        err.target && (this.activeStep = err.target)
        err.msg && this.$message.warning(err.msg)
      })
    },
    previewXml() {
      const getCmpData = name => this.$refs[name].getData()
      // processDesign 返回的是Promise 因为要做校验
      console.log("publish getCmpData",getCmpData)
      const p3 = getCmpData('processDesign')
      console.log("publish p3",p3)
      Promise.all([p3])
      .then(res => {
        const param = {
          processData: res[0].formData
        }
        var convert = require('xml-js');
        var json = JSON.stringify(param, null, 2);//json格式化
        // 启动流程并将表单数据加入流程变量
        dingdingToBpmn(json).then(res => {
          console.log("dingdingToBpmn res",res)
          if(res.code == 200) {
            this.previewResult = res.msg;
            this.previewType = "xml";
            this.previewModelVisible = true
          }
        })
      })
      .catch(err => {
        err.target && (this.activeStep = err.target)
        err.msg && this.$message.warning(err.msg)
      })
    },
    previewBpmn() {
      const getCmpData = name => this.$refs[name].getData()
      // processDesign 返回的是Promise 因为要做校验
      console.log("publish getCmpData",getCmpData)
      const p3 = getCmpData('processDesign')
      console.log("publish p3",p3)
      Promise.all([p3])
      .then(res => {
        const param = {
          processData: res[0].formData
        }
        var convert = require('xml-js');
        var json = JSON.stringify(param, null, 2);//json格式化
        // 钉钉流程转为bpmn格式
        dingdingToBpmn(json).then(reponse => {
          console.log("dingdingToBpmn reponse",reponse)
          if(reponse.code == 200) {
            this.processView.title = "Bpmn流程图预览";
            this.processView.xmlData = reponse.msg;
          }
        })
        this.processView.open = true;
      })
      .catch(err => {
        err.target && (this.activeStep = err.target)
        err.msg && this.$message.warning(err.msg)
      })
    },

2、后端主要部分如下:

@Override
  public R<Void> dingdingToBpmn(String ddjson) {  
    try {
            JSONObject object = JSON.parseObject(ddjson, JSONObject.class);
            //JSONObject workflow = object.getJSONObject("process");
            //ddBpmnModel.addProcess(ddProcess);
            //ddProcess.setName (workflow.getString("name"));
            //ddProcess.setId(workflow.getString("processId"));
            ddProcess = new Process();
            ddBpmnModel = new BpmnModel();
            ddSequenceFlows = Lists.newArrayList();
            ddBpmnModel.addProcess(ddProcess);
            ddProcess.setId("Process_"+UUID.randomUUID());
            ddProcess.setName ("dingding演示流程");
            JSONObject flowNode = object.getJSONObject("processData");
            StartEvent startEvent = createStartEvent(flowNode);
            ddProcess.addFlowElement(startEvent);
            String lastNode = create(startEvent.getId(), flowNode);
            EndEvent endEvent = createEndEvent();
            ddProcess.addFlowElement(endEvent);
            ddProcess.addFlowElement(connect(lastNode, endEvent.getId()));
            new BpmnAutoLayout(ddBpmnModel).execute();
            return R.ok(new String(new BpmnXMLConverter().convertToXML(ddBpmnModel)));
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("创建失败: e=" + e.getMessage());
        }
  }
  
  String id(String prefix) {
        return prefix + "_" + UUID.randomUUID().toString().replace("-", "").toLowerCase();
    }
    ServiceTask serviceTask(String name) {
        ServiceTask serviceTask = new ServiceTask();
        serviceTask.setName(name);
        return serviceTask;
    }
    SequenceFlow connect(String from, String to) {
        SequenceFlow flow = new SequenceFlow();
        flow.setId(id("sequenceFlow"));
        flow.setSourceRef(from);
        flow.setTargetRef(to);
        ddSequenceFlows.add(flow);
        return flow;
    }
    StartEvent createStartEvent(JSONObject flowNode) {
      String nodeType = flowNode.getString("type");
        StartEvent startEvent = new StartEvent();
        startEvent.setId(id("start"));
        if (Type.INITIATOR_TASK.isEqual(nodeType)) {
          JSONObject properties = flowNode.getJSONObject("properties");
          if(StringUtils.isNotEmpty(properties.getString("formKey"))) {
            startEvent.setFormKey(properties.getString("formKey"));
          }
        }
        return startEvent;
    }
    EndEvent createEndEvent() {
        EndEvent endEvent = new EndEvent();
        endEvent.setId(id("end"));
        return endEvent;
    }
    String create(String fromId, JSONObject flowNode) throws InvocationTargetException, IllegalAccessException {
        String nodeType = flowNode.getString("type");
       
        if (Type.INITIATOR_TASK.isEqual(nodeType)) {
            flowNode.put("incoming", Collections.singletonList(fromId));
            String id = createUserTask(flowNode,nodeType);
            if(flowNode.containsKey("concurrentNodes")) { //并行网关
                return createConcurrentGatewayBuilder(id, flowNode);   
            }
            
            if(flowNode.containsKey("conditionNodes")) { //排它网关或叫条件网关
                return createExclusiveGatewayBuilder(id, flowNode);
            }    
            
            // 如果当前任务还有后续任务,则遍历创建后续任务
            JSONObject nextNode = flowNode.getJSONObject("childNode");
            if (Objects.nonNull(nextNode)) {
                FlowElement flowElement = ddBpmnModel.getFlowElement(id);
                return create(id, nextNode);
            } else {
                return id;
            }
        } else if (Type.USER_TASK.isEqual(nodeType) || Type.APPROVER_TASK.isEqual(nodeType)) {
          flowNode.put("incoming", Collections.singletonList(fromId));     
            String id = createUserTask(flowNode,nodeType);
            if(flowNode.containsKey("concurrentNodes")) { //并行网关
                return createConcurrentGatewayBuilder(id, flowNode);   
            }
            
            if(flowNode.containsKey("conditionNodes")) { //排它网关或叫条件网关
                return createExclusiveGatewayBuilder(id, flowNode);
            }  
            
            // 如果当前任务还有后续任务,则遍历创建后续任务
            JSONObject nextNode = flowNode.getJSONObject("childNode");
            if (Objects.nonNull(nextNode)) {
                FlowElement flowElement = ddBpmnModel.getFlowElement(id);
                return create(id, nextNode);          
            } else {
              return id;  
            }
        } else if (Type.SERVICE_TASK.isEqual(nodeType)) {
            flowNode.put("incoming", Collections.singletonList(fromId));
            String id = createServiceTask(flowNode);
            if(flowNode.containsKey("concurrentNodes")) { //并行网关
                return createConcurrentGatewayBuilder(id, flowNode);   
            }
            
            if(flowNode.containsKey("conditionNodes")) { //排它网关或叫条件网关
                return createExclusiveGatewayBuilder(id, flowNode);
            } 
            
            // 如果当前任务还有后续任务,则遍历创建后续任务
            JSONObject nextNode = flowNode.getJSONObject("childNode");
            if (Objects.nonNull(nextNode)) {
                FlowElement flowElement = ddBpmnModel.getFlowElement(id);
                return create(id, nextNode);
            } else {
                return id;
            }
        }  
        else {
            throw new RuntimeException("未知节点类型: nodeType=" + nodeType);
        }
    }
    String createExclusiveGatewayBuilder(String formId, JSONObject flowNode) throws InvocationTargetException, IllegalAccessException {
        //String name = flowNode.getString("nodeName");
        String exclusiveGatewayId = id("exclusiveGateway");
        ExclusiveGateway exclusiveGateway = new ExclusiveGateway();
        exclusiveGateway.setId(exclusiveGatewayId);
        exclusiveGateway.setName("排它条件网关");
        ddProcess.addFlowElement(exclusiveGateway);
        ddProcess.addFlowElement(connect(formId, exclusiveGatewayId));
        if (Objects.isNull(flowNode.getJSONArray("conditionNodes")) && Objects.isNull(flowNode.getJSONObject("childNode"))) {
            return exclusiveGatewayId;
        }
        List<JSONObject> flowNodes = Optional.ofNullable(flowNode.getJSONArray("conditionNodes")).map(e -> e.toJavaList(JSONObject.class)).orElse(Collections.emptyList());
        List<String> incoming = Lists.newArrayListWithCapacity(flowNodes.size());
        List<JSONObject> conditions = Lists.newCopyOnWriteArrayList();
        for (JSONObject element : flowNodes) {
            JSONObject childNode = element.getJSONObject("childNode");
            JSONObject properties = element.getJSONObject("properties");
            String nodeName = properties.getString("title");
            String expression = properties.getString("conditions");
            if (Objects.isNull(childNode)) {
                incoming.add(exclusiveGatewayId);
                JSONObject condition = new JSONObject();
                condition.fluentPut("nodeName", nodeName)
                        .fluentPut("expression", expression);
                conditions.add(condition);
                continue;
            }
            // 只生成一个任务,同时设置当前任务的条件
            childNode.put("incoming", Collections.singletonList(exclusiveGatewayId));
            String identifier = create(exclusiveGatewayId, childNode);
            List<SequenceFlow> flows = ddSequenceFlows.stream().filter(flow -> StringUtils.equals(exclusiveGatewayId, flow.getSourceRef()))
                    .collect(Collectors.toList());
            
            flows.stream().forEach(
                    e -> {
                        if (StringUtils.isBlank(e.getName()) && StringUtils.isNotBlank(nodeName)) {
                            e.setName(nodeName);
                        }
                        // 设置条件表达式
                        if (Objects.isNull(e.getConditionExpression()) && StringUtils.isNotBlank(expression)) {
                            e.setConditionExpression(expression);
                        }
                    }
            );
            if (Objects.nonNull(identifier)) {
                incoming.add(identifier);
            }
        }
        JSONObject childNode = flowNode.getJSONObject("childNode");
        if (Objects.nonNull(childNode)) {
            if (incoming == null || incoming.isEmpty()) {
                return create(exclusiveGatewayId, childNode);
            } else {
                // 所有 service task 连接 end exclusive gateway
                childNode.put("incoming", incoming);
                FlowElement flowElement = ddBpmnModel.getFlowElement(incoming.get(0));
                // 1.0 先进行边连接, 暂存 nextNode
                JSONObject nextNode = childNode.getJSONObject("childNode");
                childNode.put("childNode", null);
                String identifier = create(flowElement.getId(), childNode);
                for (int i = 1; i < incoming.size(); i++) {
                  ddProcess.addFlowElement(connect(incoming.get(i), identifier));
                }
                //  针对 gateway 空任务分支 添加条件表达式
                if (!conditions.isEmpty()) {
                    FlowElement flowElement1 = ddBpmnModel.getFlowElement(identifier);
                    // 获取从 gateway 到目标节点 未设置条件表达式的节点
                    List<SequenceFlow> flows = ddSequenceFlows.stream().filter(flow -> StringUtils.equals(flowElement1.getId(), flow.getTargetRef()))
                            .filter(flow -> StringUtils.equals(flow.getSourceRef(), exclusiveGatewayId))
                            .collect(Collectors.toList());
                    flows.stream().forEach(sequenceFlow -> {
                        if (!conditions.isEmpty()) {
                            JSONObject condition = conditions.get(0);
                            String nodeName = condition.getString("content");
                            String expression = condition.getString("expression");
                            if (StringUtils.isBlank(sequenceFlow.getName()) && StringUtils.isNotBlank(nodeName)) {
                                sequenceFlow.setName(nodeName);
                            }
                            // 设置条件表达式
                            if (Objects.isNull(sequenceFlow.getConditionExpression()) && StringUtils.isNotBlank(expression)) {
                                sequenceFlow.setConditionExpression(expression);
                            }
                            conditions.remove(0);
                        }
                    });
                }
                // 1.1 边连接完成后,在进行 nextNode 创建
                if (Objects.nonNull(nextNode)) {
                    return create(identifier, nextNode);
                } else {
                    return identifier;
                }
            }
        }
        return exclusiveGatewayId;
    }
    String createConcurrentGatewayBuilder(String fromId, JSONObject flowNode) throws InvocationTargetException, IllegalAccessException {
        //String name = flowNode.getString("nodeName");
        //下面创建并行网关并进行连线
      ParallelGateway parallelGateway = new ParallelGateway();
        String parallelGatewayId = id("parallelGateway");
        parallelGateway.setId(parallelGatewayId);
        parallelGateway.setName("并行网关");
        ddProcess.addFlowElement(parallelGateway);
        ddProcess.addFlowElement(connect(fromId, parallelGatewayId));
        if (Objects.isNull(flowNode.getJSONArray("concurrentNodes"))
                && Objects.isNull(flowNode.getJSONObject("childNode"))) {
            return parallelGatewayId;
        }
        //获取并行列表数据
        List<JSONObject> flowNodes = Optional.ofNullable(flowNode.getJSONArray("concurrentNodes")).map(e -> e.toJavaList(JSONObject.class)).orElse(Collections.emptyList());
        List<String> incoming = Lists.newArrayListWithCapacity(flowNodes.size());
        for (JSONObject element : flowNodes) {
            JSONObject childNode = element.getJSONObject("childNode");
            if (Objects.isNull(childNode)) {//没子节点,就把并行id加入入口队列
                incoming.add(parallelGatewayId);
                continue;
            }
            String identifier = create(parallelGatewayId, childNode);
            if (Objects.nonNull(identifier)) {//否则加入有子节点的用户id
                incoming.add(identifier);
            }
        }
        JSONObject childNode = flowNode.getJSONObject("childNode");
       
        if (Objects.nonNull(childNode)) {
            // 普通结束网关
            if (CollectionUtils.isEmpty(incoming)) {
                return create(parallelGatewayId, childNode);
            } else {
                // 所有 user task 连接 end parallel gateway
                childNode.put("incoming", incoming);
                FlowElement flowElement = ddBpmnModel.getFlowElement(incoming.get(0));
                // 1.0 先进行边连接, 暂存 nextNode
                JSONObject nextNode = childNode.getJSONObject("childNode");
                childNode.put("childNode", null); //不加这个,下面创建子节点会进入递归了
                String identifier = create(incoming.get(0), childNode);
                for (int i = 1; i < incoming.size(); i++) {//其中0之前创建的时候已经连接过了,所以从1开始补另外一条
                    FlowElement flowElementIncoming = ddBpmnModel.getFlowElement(incoming.get(i));
                    ddProcess.addFlowElement(connect(flowElementIncoming.getId(), identifier));
                }
                // 1.1 边连接完成后,在进行 nextNode 创建
                if (Objects.nonNull(nextNode)) {
                    return create(identifier, nextNode);
                } else {
                    return identifier;
                }
            }
        }
        if(incoming.size()>0) {
          return incoming.get(1);
        }
        else {
          return parallelGatewayId;   
        }
        
    }
    String createUserTask(JSONObject flowNode, String nodeType) {
        List<String> incoming = flowNode.getJSONArray("incoming").toJavaList(String.class);
        // 自动生成id
        String id = id("userTask");
        if (incoming != null && !incoming.isEmpty()) {
          UserTask userTask = new UserTask();
          JSONObject properties = flowNode.getJSONObject("properties");
          userTask.setName(properties.getString("title"));
          userTask.setId(id);
          List<ExtensionAttribute> attributes = new  ArrayList<ExtensionAttribute>();
          if (Type.INITIATOR_TASK.isEqual(nodeType)) {
            ExtensionAttribute extAttribute =  new ExtensionAttribute();
            extAttribute.setNamespace(ProcessConstants.NAMASPASE);
            extAttribute.setName("dataType");
            extAttribute.setValue("INITIATOR");
            attributes.add(extAttribute);
            userTask.addAttribute(extAttribute);
            userTask.setAssignee("${initiator}");
          } else if (Type.USER_TASK.isEqual(nodeType) || Type.APPROVER_TASK.isEqual(nodeType)) {
            JSONArray approvers = properties.getJSONArray("approvers");
            JSONObject approver = approvers.getJSONObject(0);
            ExtensionAttribute extDataTypeAttribute =  new ExtensionAttribute();
            extDataTypeAttribute.setNamespace(ProcessConstants.NAMASPASE);
            extDataTypeAttribute.setName("dataType");
            extDataTypeAttribute.setValue("USERS");
            userTask.addAttribute(extDataTypeAttribute);
            ExtensionAttribute extTextAttribute =  new ExtensionAttribute();
            extTextAttribute.setNamespace(ProcessConstants.NAMASPASE);
            extTextAttribute.setName("text");
            extTextAttribute.setValue(approver.getString("nickName"));
            userTask.addAttribute(extTextAttribute);
            userTask.setFormKey(properties.getString("formKey"));
            userTask.setAssignee(approver.getString("userName"));
          }
          
            ddProcess.addFlowElement(userTask);
            ddProcess.addFlowElement(connect(incoming.get(0), id));
        }
        return id;
    }
    
    String createServiceTask(JSONObject flowNode) {
        List<String> incoming = flowNode.getJSONArray("incoming").toJavaList(String.class);
        // 自动生成id
        String id = id("serviceTask");
        if (incoming != null && !incoming.isEmpty()) {
            ServiceTask serviceTask = new ServiceTask();
            serviceTask.setName(flowNode.getString("nodeName"));
            serviceTask.setId(id);
            ddProcess.addFlowElement(serviceTask);
            ddProcess.addFlowElement(connect(incoming.get(0), id));
        }
        return id;
    }
    enum Type {
        /**
         * 并行事件
         */
      CONCURRENT("concurrent", ParallelGateway.class),
        /**
         * 排他事件
         */
        EXCLUSIVE("exclusive", ExclusiveGateway.class),
        /**
         * 服务任务
         */
        SERVICE_TASK("serviceTask", ServiceTask.class),
        
        /**
         * 发起人任务
         */
        INITIATOR_TASK("start", ServiceTask.class),
        
        /**
         * 审批任务
         */
        APPROVER_TASK("approver", ServiceTask.class),
      
      /**
         * 用户任务
         */
        USER_TASK("userTask", UserTask.class);
        private String type;
        private Class<?> typeClass;
        Type(String type, Class<?> typeClass) {
            this.type = type;
            this.typeClass = typeClass;
        }
        public final static Map<String, Class<?>> TYPE_MAP = Maps.newHashMap();
        static {
            for (Type element : Type.values()) {
                TYPE_MAP.put(element.type, element.typeClass);
            }
        }
        public boolean isEqual(String type) {
            return this.type.equals(type);
        }
    }

3、效果图如下:  


相关文章
|
3月前
|
存储 安全 API
"解锁企业级黑科技!用阿里云视觉智能打造钉钉级人脸打卡系统,安全高效,让考勤管理秒变智能范儿!"
【8月更文挑战第14天】随着数字化办公的发展,人脸打卡成为企业考勤的新标准。利用阿里云视觉智能开放平台构建类似钉钉的人脸打卡系统,其关键在于:高精度人脸识别API支持复杂场景下的快速检测与比对;活体检测技术防止非生物特征欺骗,确保安全性;云端存储与计算能力满足大数据处理需求;丰富的SDK与API简化集成过程,实现高效、安全的考勤管理。
91 2
|
3月前
|
XML 算法 API
访问者模式问题之钉钉审批流程配置为什么适合使用访问者模式
访问者模式问题之钉钉审批流程配置为什么适合使用访问者模式
|
5月前
|
人工智能 移动开发 IDE
安利几款与钉钉平台无缝集成打通账号认证的企业文档管理系统
钉钉是很多中小企业都爱用的产品,开通账号就能直接使用了,应用生态非常丰富,尤其是AI技术的应用,走在行业前列。但仍有很多企业对于全面拥抱SaaS服务充满了顾虑,尤其在内部资料的管理这块,即使钉钉在线文档已经提供了非常优秀的协作体验,不少客户仍更偏爱私有部署在局域网里面的企业文档管理系统。那么能将企业内部部署的文档管理系统集成到钉钉平台上面,和钉钉文档并行使用呢?市面上又有哪些企业文档管理系统软件支持与钉钉的集成呢?这也是很多企业客户的疑问。
安利几款与钉钉平台无缝集成打通账号认证的企业文档管理系统
|
6月前
|
XML 移动开发 前端开发
基于若依的ruoyi-nbcio流程管理系统里修正仿钉钉流程部门主管与多实例转xml的bug
基于若依的ruoyi-nbcio流程管理系统里修正仿钉钉流程部门主管与多实例转xml的bug
45 1
|
存储 弹性计算 安全
成功案例-钉钉 | 学习笔记
快速学习 成功案例-钉钉
400 0
|
存储 弹性计算 安全
案例分享——钉钉|学习笔记
快速学习 案例分享——钉钉
373 0
|
存储 弹性计算 安全
案例分享——钉钉
rds数据库 阿里云为钉钉提供了安全性和整体服务的稳定性
案例分享——钉钉
|
存储 弹性计算 监控
|
存储 弹性计算 安全
了解成功案例-钉钉(进阶班-Day6)
钉钉使用阿里云而成功的案例主要介绍了: 1、钉钉背后的技术架构 2、使用阿里云(云计算)后为钉钉解决的问题(主要是安全可靠性) 3、钉钉
639 0

热门文章

最新文章