前言: 這些天由於一直在設計新系統的數據庫表,導致了activiti的遲遲更新,原本之前是打算先分享下監聽器的。結果被工作耽擱了,期間正好了解到新系統有這樣的一個功能,流程的動態創建,即用戶在前端界面選擇任務節點,后台生成流程實例。參考了下網上的資料,再改了改,最終也實現了,覺得可用性還是挺大的,所以先來分享一下吧。
先附上參考鏈接吧,畢竟也得尊重下別人的成果:https://my.oschina.net/u/3053883/blog/1628393
其實核心也就兩點,一個是節點任務,即userTask;另一個就是節點任務的流向,即SequenceFlow。網關的話,根據需求決定加不加,一般來說都會用到排他網關,話不多說開始核心代碼演示。
1.先看幾個里面通用的方法
/** * 開始任務節點 * @return */ protected StartEvent createStartEvent() { StartEvent startEvent = new StartEvent(); startEvent.setId("start"); return startEvent; } /** * 結束任務節點 * @return */ protected EndEvent createEndEvent() { EndEvent endEvent = new EndEvent(); endEvent.setId("end"); return endEvent; } /** * * @param id 對應我們畫流程圖中節點任務id * @param name 節點任務名稱 * @param assignee 任務的執行者(這一塊自行決定是否添加每一環節的執行者,若是動態分配的話,可以不用傳值) * @return */ protected UserTask createUserTask(String id, String name, String assignee) { UserTask userTask = new UserTask(); userTask.setName(name); userTask.setId(id); userTask.setAssignee(assignee); return userTask; } /** * * @param id 網關id * @return */ protected static ExclusiveGateway createExclusiveGateway(String id) { ExclusiveGateway exclusiveGateway = new ExclusiveGateway(); exclusiveGateway.setId(id); return exclusiveGateway; } /** * * @param from 連線來源節點 * @param to 連線目標節點 * @param name 連線名稱(可不填) * @param conditionExpression 網關每一種線路走向的條件表達式 * @return */ protected SequenceFlow createSequenceFlow(String from, String to, String name, String conditionExpression) { SequenceFlow flow = new SequenceFlow(); flow.setSourceRef(from); flow.setTargetRef(to); flow.setName(name); if (StringUtils.isNotEmpty(conditionExpression)) { flow.setConditionExpression(conditionExpression); } return flow; }
現在開始搭建每一步
1.實例化BpmnModel 對象
BpmnModel model = new BpmnModel();
2.構造process對象
Process process = new Process(); model.addProcess(process); process.setId("multiple-process3"); // 判斷是否僅為一個節點任務 List<String> taskList = new ArrayList<String>(); taskList.add("報銷申請"); taskList.add("主管審批"); taskList.add("經理審批"); taskList.add("總經理審批 "); //單節點任務 if (taskList.size() == 1) { process.addFlowElement(createStartEvent()); process.addFlowElement(createUserTask("task1", taskList.get(0), null)); process.addFlowElement(createEndEvent()); process.addFlowElement(createSequenceFlow("start", "task1", "", "")); process.addFlowElement(createSequenceFlow("task1", "end", "", "")); } else { // 多節點任務 // 構造開始節點任務 process.addFlowElement(createStartEvent()); // 構造首個節點任務 process.addFlowElement(createUserTask("task1", taskList.get(0), null)); // 構造除去首尾節點的任務 for (int i = 1; i < taskList.size() - 1; i++) { process.addFlowElement(createExclusiveGateway("createExclusiveGateway" + i)); process.addFlowElement(createUserTask("task" + (i + 1), taskList.get(i), null)); } // 構造尾節點任務 process.addFlowElement(createExclusiveGateway("createExclusiveGateway" + (taskList.size() - 1))); process.addFlowElement(createUserTask("task" + taskList.size(), taskList.get(taskList.size() - 1), null)); // 構造結束節點任務 process.addFlowElement(createEndEvent()); // 構造連線(加網關) process.addFlowElement(createSequenceFlow("start", "task1", "", "")); // 第一個節點任務到第二個百分百通過的,因此不存在網關 process.addFlowElement(createSequenceFlow("task1", "task2", "", "")); for (int i = 1; i < taskList.size(); i++) { process.addFlowElement(createSequenceFlow("task" + (i + 1), "createExclusiveGateway" + i, "", "")); // 判斷網關走向(同意則直接到下一節點即可,不同意需要判斷回退層級,決定回退到哪個節點,returnLevel等於0,即回退到task1) // i等於幾,即意味着回退的線路有幾種可能,例如i等於1,即是task2,那么只能回退 到task1 // 如果i等於2,即是task3,那么此時可以回退到task1和task2;returnLevel =1 ,即回退到task1,所以這里我是擴展了可以駁回到任意階段節點任務 for (int j = 1; j <= i; j++) { process.addFlowElement(createSequenceFlow("createExclusiveGateway" + i, "task" + j, "不通過", "${result == '0' && returnLevel== '" + j + "'}")); }
// 操作結果為通過時,需要判斷是否為最后一個節點任務,若是則直接到end if (i == taskList.size() - 1) { process.addFlowElement( createSequenceFlow("createExclusiveGateway" + i, "end", "通過", "${result == '1'} ")); } else { process.addFlowElement(createSequenceFlow("createExclusiveGateway" + i, "task" + (i + 2), "通過", "${result == '1'}")); } } }
備注: (1)這里我判斷了是否為單節點任務,防止存在用戶就選擇了一個節點任務。
(2)其實核心就這兩步,第一步:構造任務節點以及該任務節點下面的網關(首節點通常一般不存在網關,直接到下一節點)
第二步: 構造連線+網關的流向(這里網關流向問題需要對每一種流向的可能性進行條件表達式的添加),案例中我設置了可以駁回到任意階段節點。
個人建議:在我剛看到網上針對動態創建流程時,只是測試了確實可以用,但是里面的執行邏輯還是不是很懂,平常也只是直接畫圖,生成流程。隨后我打開bpmn的xml文件,
再結合網上的,瞬間明白不少。所以個人建議大家可以打開xml文件一起看看,可能會事半功倍。
順帶提一點,駁回到任意階段的場景也是挺常見的,若是已畫好的流程圖,也可以在如下圖所示的地方配置條件表達式,原理都是一樣的,在網關流向配置。
3.生成圖像信息
new BpmnAutoLayout(model).execute();
4.部署流程
Deployment deployment = repositoryService.createDeployment().addBpmnModel("dynamic-model.bpmn", model)
.name("multiple process deployment").deploy();
5.啟動流程
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("multiple-process3");
System.out.println("流程實例ID---》》》" + processInstance.getId());
6.保存png圖片和xml文件(這一步可做可不做)
// 6. Save process diagram to a file InputStream processDiagram = repositoryService.getProcessDiagram(processInstance.getProcessDefinitionId()); FileUtils.copyInputStreamToFile(processDiagram, new File("target/multiple-process3-diagram.png")); // 7. Save resulting BPMN xml to a file InputStream processBpmn = repositoryService.getResourceAsStream(deployment.getId(), "dynamic-model.bpmn"); FileUtils.copyInputStreamToFile(processBpmn, new File("target/multiple-process3.bpmn20.xml"));
至此動態創建流程就已經完成了。具體代碼我已上傳到github:https://github.com/wcyzxs/activiti(我是在電腦中現有項目上直接進行測試的,所以拉下來的話,可能存在jar缺失,可以私聊找我要jar或者自己maven上面找也行)