前言
最近項目有用到Activiti工作流,但是需求有點特殊,需要通過參數去生成BPMN圖,查閱了資料後,能實作簡單的工作流生成,那接下來看看如何通過代碼動态生成工作流的吧。
一.設計思路
1.以普通的請假流程為例
(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和不使用的生成的圖對比
四.總結
通過代碼生成的BPMN圖其實很亂,即使加了BpmnAutoLayout方法去調整布局,但是不影響流程正常使用。還有就是看了一遍源碼,沒有找到子流程(SubProcess的子任務)的生成方法,暫時不支援生成子流程,待我再多研究研究。
後話
簡單流程通過代碼生成沒問題,複雜流程還是建議用工具繪圖,通過代碼生成有時候容易出問題。畢竟有一些節點不能連線,而且複雜流程對于一些退回操作不是很友好,實作起來很麻煩。