springboot如何集成flowable,如何部署flowable在線編輯器畫bpm圖以及bpm圖的畫法,我在上一篇博客中寫了,這里直接上代碼(源碼地址:晚安/flowable_holiday (gitee.com))。
這是我畫的請假流程bpm圖。
然會到代碼部分。
首先,先寫一個config類,避免生成的bpm圖中文亂碼。
package com.example.config; import org.flowable.spring.SpringProcessEngineConfiguration; import org.flowable.spring.boot.EngineConfigurationConfigurer; import org.springframework.context.annotation.Configuration; @Configuration public class FlowableConfig implements EngineConfigurationConfigurer<SpringProcessEngineConfiguration> { @Override public void configure(SpringProcessEngineConfiguration springProcessEngineConfiguration) { springProcessEngineConfiguration.setActivityFontName("宋體"); springProcessEngineConfiguration.setLabelFontName("宋體"); springProcessEngineConfiguration.setAnnotationFontName("宋體"); } }
由於我在畫bpm圖的時候,給“經理審批”節點和“老板審批”節點設置了監聽器。所以接下來寫監聽器類
- 經理審批節點監聽器。指定任務受理人為“領導”。
package com.example.listen; import org.flowable.engine.delegate.TaskListener; import org.flowable.task.service.delegate.DelegateTask; public class ManagerTaskHandler implements TaskListener { @Override public void notify(DelegateTask delegateTask) { delegateTask.setAssignee("領導"); System.out.println("執行監聽器————————————————————————————————————"); } }
- 老板審批節點監聽器。指定任務受理人為“老板”。
package com.example.listen; import org.flowable.engine.delegate.TaskListener; import org.flowable.task.service.delegate.DelegateTask; public class BossTaskHandler implements TaskListener { @Override public void notify(DelegateTask delegateTask) { delegateTask.setAssignee("老板"); } }
接下來到controller。
- 發起請假流程。這里要說明一下,只有在上一個節點完成任務后,流程才會執行到下一個節點。所以這里在發起流程后,就手動完成了任務,讓流程進行到下一個節點。
/** * 發起請假 * * @param userId 用戶Id * @param days 請假天數 * @param descption 描述 */ @RequestMapping(value = "/add") @ResponseBody public String addExpense(String userId, Integer money, String descption) { //啟動流程 HashMap<String, Object> map = new HashMap<>(); map.put("taskUser", userId); map.put("days", days); ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("leave-flow", map); Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); taskService.complete(task.getId()); return "提交成功.流程Id為:" + processInstance.getId() + "任務id: " + task.getId() ; }
- 其他操作。
/** * 獲取審批管理列表 */ @RequestMapping(value = "/list") @ResponseBody public Object list(String userId) { List<Task> tasks = taskService.createTaskQuery().taskAssignee(userId).orderByTaskCreateTime().desc().list(); for (Task task : tasks) { } System.out.println(tasks.toString()); return tasks.toString(); } /** * 批准 * * @param taskId 任務ID */ @RequestMapping(value = "apply") @ResponseBody public String apply(String taskId) { List<Task> t = taskService.createTaskQuery().list(); Task task = taskService.createTaskQuery().taskId(taskId).singleResult(); //Task task = taskService.createTaskQuery().taskId(taskId).singleResult(); if (task == null) { throw new RuntimeException("流程不存在"); } //通過審核 HashMap<String, Object> map = new HashMap<>(); map.put("result", "同意"); taskService.complete(taskId, map); return "processed ok!"; } /** * 拒絕 */ @ResponseBody @RequestMapping(value = "reject") public String reject(String taskId) { HashMap<String, Object> map = new HashMap<>(); map.put("result", "駁回"); taskService.complete(taskId, map); return "reject"; } /** * 生成流程圖 * * @param processId 任務ID */ @RequestMapping(value = "processDiagram") public void genProcessDiagram(HttpServletResponse httpServletResponse, String processId) throws Exception { List<ProcessInstance> t = runtimeService.createProcessInstanceQuery().list(); ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult(); //流程走完的不顯示圖 if (pi == null) { return; } Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult(); //使用流程實例ID,查詢正在執行的執行對象表,返回流程實例對象 String InstanceId = task.getProcessInstanceId(); List<Execution> executions = runtimeService .createExecutionQuery() .processInstanceId(InstanceId) .list(); //得到正在執行的Activity的Id List<String> activityIds = new ArrayList<>(); List<String> flows = new ArrayList<>(); for (Execution exe : executions) { List<String> ids = runtimeService.getActiveActivityIds(exe.getId()); activityIds.addAll(ids); } //獲取流程圖 BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId()); ProcessEngineConfiguration engconf = processEngine.getProcessEngineConfiguration(); ProcessDiagramGenerator diagramGenerator = engconf.getProcessDiagramGenerator(); // InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png", activityIds, flows, engconf.getActivityFontName(), engconf.getLabelFontName(), engconf.getAnnotationFontName(), engconf.getClassLoader(), 1.0); InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png", activityIds, Collections.emptyList(), engconf.getActivityFontName(), engconf.getLabelFontName(), engconf.getAnnotationFontName(), null, 1.0, false); OutputStream out = null; byte[] buf = new byte[1024]; int legth = 0; try { out = httpServletResponse.getOutputStream(); while ((legth = in.read(buf)) != -1) { out.write(buf, 0, legth); } } finally { if (in != null) { in.close(); } if (out != null) { out.close(); } } }
測試。
- 發起流程。可以看到后台打印了監聽器中的內容,說明執行了監聽器。
- 獲取審批列表。
- 通過經理審批。taskId可以從act_ru_task表中查看。
- 查看流程圖。
- 老板駁回請求。
-
再次查看流程圖。可以看到駁回請求后,又回到申請人節點。
- 如果老板也通過的話,整個流程就結束了。
整個流程到這里就演示完了。當時在學的時候,流程卡在第一個節點不往下執行,最后發現只有上一個節點任務完成,才會到下一個節點,發起人節點要手動完成任務。其實在實際開發中,當發起人提交表單時,任務也就完成了,流程會接着往下執行。
最后補充一下個人認為比較重要的幾個表:
act_ru_execution:運行流程實例表。發起的運行中流程可以在此表中看到。
act_ru_task:運行時任務表。可以查看運行的任務id和任務執行到哪個節點。
act_re_procdef:已經發布定義的流程可以在此表中看到。
act_id_user:用戶信息表。