本文正在参与「金石计划 . 瓜分6万现金大奖」

前语

最近项目有用到Activiti工作流,可是需求有点特别,需求经过参数去生成BPMN图,查阅了资料后,能完成简略的工作流生成,那接下来看看如何经过代码动态生成工作流的吧。

一.规划思路

1.以一般的请假流程为例

Activiti7 通过代码动态生成工作流
(1)生成开端节点加第1个使命和调整请求使命(回绝或者退回操作)以及其排他网关

(2)循环批阅列表,生成第2至第N-1个使命,并每个使命后都有1个排他网关,用于连接调整请求使命

(3)最后1个节点,后面无排他使命,故需求独自处理

二.详细完成代码

    List<String> roles=new ArrayList<>();
        roles.add("1508574");
        roles.add("13765234");
        roles.add("18834222");
        listMap.put("province",roles);
          for (String auditRole : processParamsDto.getAuditRoles()) {
        if (auditRole.equals(processParamsDto.getAuditRoles().get(0))) {
            //开端连线
            process.addFlowElement(createUserTask("task".concat("_").concat(auditRole), "批阅".concat(auditRole), auditRole));
            process.addFlowElement(createSequenceFlow("start", "task".concat("_").concat(auditRole), null));
            //正常的 第一个网关 process.addFlowElement(createExclusiveGateway("exclusiveGateWay".concat("_").concat(auditRole), "exclusiveGateWay".concat("_").concat(auditRole)));
            //第一个排他网关连线 使命->排他网关
            process.addFlowElement(createSequenceFlow("task".concat("_").concat(auditRole), "exclusiveGateWay".concat("_").concat(auditRole), null));
            //从头请求分支  创立从头请求使命
            process.addFlowElement(createUserTask("task".concat("_").concat("从头请求"), "指定人".concat("批阅"), "${startBy}"));
            //请求网关->请求使命
            process.addFlowElement(createSequenceFlow("exclusiveGateWay".concat("_").concat(auditRole), "task".concat("_").concat("从头请求"), "${flag==false}"));
            //请求网关
            process.addFlowElement(createExclusiveGateway("exclusiveGateWay".concat("_").concat("从头请求"), "exclusiveGateWay".concat("_").concat("从头请求")));
            //请求使命->请求网关
            process.addFlowElement(createSequenceFlow("task".concat("_").concat("从头请求"),"exclusiveGateWay".concat("_").concat("从头请求"), null));
        } else if (!auditRole.equals(processParamsDto.getAuditRoles().get(processParamsDto.getAuditRoles().size() - 1))) {
            //创立排他网关  每个使命后都有一个排他网关
            //第二个使命至第size-1个使命
            process.addFlowElement(createUserTask("task".concat("_").concat(auditRole), "批阅".concat(auditRole), auditRole));
            //第一个网关->第二个使命  使命之前的节点 网关->第二个使命....第N个使命 true
            process.addFlowElement(createSequenceFlow("exclusiveGateWay".concat("_").concat(getPreAuditRole(auditRole,processParamsDto.getAuditRoles())), "task".concat("_").concat(auditRole), "${flag==true}"));
            //网关->请求使命 false
            process.addFlowElement(createExclusiveGateway("exclusiveGateWay".concat("_").concat(auditRole), "exclusiveGateWay(当前)".concat("_").concat(auditRole)));
            process.addFlowElement(createSequenceFlow("task".concat("_").concat(auditRole), "exclusiveGateWay".concat("_").concat(auditRole), null));
            //排他网关—>从头请求使命
            process.addFlowElement(createSequenceFlow("exclusiveGateWay".concat("_").concat(auditRole), "task".concat("_").concat("从头请求"),"#{flag==false}"));
        } else if (auditRole.equals(processParamsDto.getAuditRoles().get(processParamsDto.getAuditRoles().size() - 1))) {
            String lastNode=processParamsDto.getAuditRoles().get(processParamsDto.getAuditRoles().size() - 1);
            System.out.println("当前节点"+lastNode);
            process.addFlowElement(createUserTask("task".concat(lastNode), "批阅".concat(auditRole), auditRole));
            process.addFlowElement(createSequenceFlow("exclusiveGateWay".concat("_").concat(getPreAuditRole(auditRole,processParamsDto.getAuditRoles())),"task".concat(lastNode),"#{flag==true}"));
            process.addFlowElement(createEndEvent());
            process.addFlowElement(createSequenceFlow("task".concat(processParamsDto.getAuditRoles().get(processParamsDto.getAuditRoles().size() - 1)), "end", "${flag==true}"));     process.addFlowElement(createSequenceFlow("exclusiveGateWay".concat("_").concat("从头请求"), "end", "${flag==false}"));
  process.addFlowElement(createSequenceFlow("exclusiveGateWay".concat("_").concat("从头请求"), "task".concat("_").concat(processParamsDto.getAuditRoles().get(0)), "${flag==true}"));
        }
    }     
     new BpmnAutoLayout(model).execute();
    //界说并设置流程变量
    Map<String, Object> variables = new HashMap<>();
        variables.put("flag", 1 == 2);
    //deploy
    Deployment deployment = repositoryService.createDeployment().addBpmnModel("process/dynamic-model.bpmn", model).name("Dynamic process deployment").key("test_bpmn").deploy();
processEngine.getRuntimeService().startProcessInstanceByKey(processDefinition.getKey(), variables);
InputStream inputStreamXml = processEngine.getRepositoryService().getResourceAsStream(deployment.getId(), "process/dynamic-model.bpmn");
//保存到本地,便利查看生成后的文件
 FileUtils.copyInputStreamToFile(inputStreamXml, new File("D:\bpmn_data\process.bpmn.xml"));

三.注意事项

1.activiti-bpmn-layout.jar

//BPMN图布局主动调整需求增加
<dependency>
   <groupId>org.activiti</groupId>
   <artifactId>activiti-bpmn-layout</artifactId>
   <version>7.1.0.M4</version>
</dependency>

2.mxgraph-all.jar

//需求手动增加,其自带的mxgraph找不到办法,原因待承认,有时间去提个issue问问
<dependency>
   <groupId>com.mxgraph</groupId>
   <artifactId>mxgraph-all</artifactId>
   <version>4.2.2</version>
   <scope>system</scope>
</dependency>

3.运用了layout和不运用的生成的图对比

Activiti7 通过代码动态生成工作流

四.总结

经过代码生成的BPMN图其实很乱,即便加了BpmnAutoLayout办法去调整布局,可是不影响流程正常运用。还有就是看了一遍源码,没有找到子流程(SubProcess的子使命)的生成办法,暂时不支持生成子流程,待我再多研究研究。

后话

简略流程经过代码生成没问题,复杂流程还是主张用东西绘图,经过代码生成有时候容易出问题。究竟有一些节点不能连线,并且复杂流程对于一些退回操作不是很友爱,完成起来很麻烦。