工做流(Workflow),就是「業務過程的部分或總體在計算機應用環境下的自動化」,它主要解決的是「使在多個參與者之間按照某種預約義的規則傳遞文檔、信息或任務的過程自動進行,從而實現某個java
預期的業務目標,或者促使此目標的實現」。spring
個人理解:工做流將一套大的業務邏輯分解成業務邏輯段, 並統一控制這些業務邏輯段的執行條件,執行順序以及相互通訊。 實現業務邏輯的分解和解耦。數據庫
官方介紹 Activiti是一個輕量級的工做流程和業務流程管理(BPM)平臺,面向業務人員,開發人員和系統管理員。它的核心是使用Java開發的極速且穩定的的BPMN 2流程引擎。它是開源的,並在api
Apache許可下分發。Activiti可在任何Java應用程序,服務器,羣集或雲中運行。它與Spring完美集成,很是輕巧。服務器
都是BPMN2過程建模和執行環境。 都是BPM系統(符合BPM規範)。 都是開源項目-遵循ASL協議( Apache的 軟件許可)。 都源自JBoss(Activiti5是jBPM4的衍生,jBPM5則基於Drools Flow)。eclipse
都很成熟,從無到有,雙方開始約始於2年半前。 都有對人工任務的生命週期管理。 Activiti5和jBPM5惟一的區別是jBPM5基於WebService - HumanTask標準來描述人工任務和管理生命週期。 若有編輯器
興趣瞭解這方面的標準及其優勢,可參閱WS - HT規範介紹 。 都使用了不一樣風格的 Oryx 流程編輯器對BPMN2建模。 jBPM5採用的是 Intalio 維護的開源項目分支。 Activiti5則使用了Signavio維護的ide
分支。性能
總結:測試
雖然是比較,但不必定要有勝負,只有適合本身的纔是最好的,要針對具體的項目區別對待。對咱們本身的項目,其實我更關注的是咱們項目將來的流程引擎的執行效率以及性能,每小時幾十萬甚至上百萬的
流程須要執行,須要多少個服務,集羣、負載的策略是什麼,會不會有衝突?目前這方面的資料仍是比較少的,不少問題只有實際遇用到的時候纔會去想辦法解決。不過就我我的的感受而言,Activiti上手比較
快,界面也比較簡潔、直觀,值得一試,同時activiti的社區更新與支持也更及時,暫時推薦使用activiti。
>在線安裝
>離線安裝
IDEA 在插件倉庫搜索actiBPM安裝便可(這個插件,我很是想吐槽,畫個直線是真的難,即使在編輯器裏面是直線,後面用Activiti的API生成的圖也是彎的。)
1.第一次啓動流程引擎的時候由Activiti自動建立28張數據表(不一樣版本表單數量,可能略有差別,本教程使用的是activiti6.0);
2.提供核心7大接口調用;
3.提供工做流API,簡化流程操做;
4.提供全面的可視化流程管理服務,使流程更加直觀。
一、act_ge_ 通用數據表,ge是general的縮寫
二、act_hi_ 歷史數據表,hi是history的縮寫,對應HistoryService接口
三、act_id_ 身份數據表,id是identity的縮寫,對應IdentityService接口
四、act_re_ 流程存儲表,re是repository的縮寫,對應RepositoryService接口,存儲流程部署和流程定義等靜態數據
五、act_ru_ 運行時數據表,ru是runtime的縮寫,對應RuntimeService接口和TaskService接口,存儲流程實例和用戶任務等動態數據
1.RepositoryService:提供一系列管理流程部署和流程定義的API。
2.RuntimeService:在流程運行時對流程實例進行管理與控制。
3.TaskService:對流程任務進行管理,例如任務提醒、任務完成和建立任務等。
4.IdentityService:提供對流程角色數據進行管理的API,這些角色數據包括用戶組、用戶及它們之間的關係。
5.ManagementService:提供對流程引擎進行管理和維護的服務。
6.HistoryService:對流程的歷史數據進行操做,包括查詢、刪除這些歷史數據。
7.FormService:表單服務。
<process id="activitiInsurancePlanProcess" name="activitiInsurancePlanProcess" isExecutable="true" isClosed="false" processType="None">
<startEvent id="startEvent" name="開始流程"></startEvent>
<sequenceFlow id="flow2" sourceRef="startEvent" targetRef="usertask2"></sequenceFlow>
<userTask id="usertask2" name="二級子公司員工" activiti:assignee="${userId}" activiti:candidateGroups="22"></userTask>
<userTask id="usertask4" name="二級子公司領導" activiti:candidateGroups="21"></userTask>
<sequenceFlow id="flow4" sourceRef="usertask2" targetRef="usertask4"></sequenceFlow>
<userTask id="usertask5" name="財務專員" activiti:candidateGroups="32"></userTask>
<sequenceFlow id="flow5" sourceRef="usertask4" targetRef="usertask5"></sequenceFlow>
<userTask id="usertask6" name="財務領導" activiti:candidateGroups="31"></userTask>
<sequenceFlow id="flow6" sourceRef="usertask5" targetRef="usertask6"></sequenceFlow>
<endEvent id="endevent1" name="End"></endEvent>
<sequenceFlow id="flow7" sourceRef="usertask6" targetRef="endevent1"></sequenceFlow>
</process>
流程定義的xml文件只貼出process 定義部分,座標未列出;
須要注意的幾個關鍵參數有:
id:在撤回、駁回時,會常常用到每一個節點的ID;
assignee:受理人,表示當前節點由誰處理或者受理;
candidateGroups:分配任務負責的組,查詢待辦任務時,相應的組全部成員都可以看到此組的待辦任務,可是隻有一我的能處理
以上三個參數是最基礎最多見使用的最多的參數。
package com.dd.activiti.admin.test; import com.huatonghh.AdminApplication; import com.huatonghh.activiti.util.Jump2TargetFlowNodeCommand; import com.huatonghh.common.util.SpringUtil; import org.activiti.engine.*; import org.activiti.engine.history.HistoricActivityInstance; import org.activiti.engine.history.HistoricActivityInstanceQuery; import org.activiti.engine.history.HistoricDetail; import org.activiti.engine.history.HistoricFormProperty; import org.activiti.engine.history.HistoricVariableUpdate; import org.activiti.engine.identity.Group; import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl; import org.activiti.engine.impl.persistence.entity.HistoricDetailVariableInstanceUpdateEntity; import org.activiti.engine.repository.Deployment; import org.activiti.engine.repository.ProcessDefinition; import org.activiti.engine.repository.ProcessDefinitionQuery; import org.activiti.engine.runtime.ProcessInstance; import org.activiti.engine.task.Task; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.HashMap; import java.util.List; import java.util.Map; import static com.huatonghh.common.util.SpringUtil.getBean; //@RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = AdminApplication.class) //@WebAppConfiguration public class ActivitiResourceIntTest { @Autowired private RuntimeService runtimeService; @Autowired private TaskService taskService; @Autowired private HistoryService historyService; @Autowired private RepositoryService repositoryService; @Autowired private ProcessEngine processEngine; @Autowired IdentityService identityService; @Autowired ManagementService managementService; // @Autowired // private ProcessRuntime processRuntime; @BeforeEach public void setup() { if(repositoryService ==null){ repositoryService = (RepositoryService)SpringUtil.getBean("RepositoryService"); } } /** * 流程定義的部署 * 影響的activiti表有哪些 * act_re_deployment 部署信息 * act_re_procdef 流程定義的一些信息 * act_ge_bytearray 流程定義的bpmn文件以及png文件 */ // @Test public void ActivitiDeployment(){ //1.建立ProcessEngine對象 // ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); // // //2.獲得RepositoryService實例 // RepositoryService repositoryService = processEngine.getRepositoryService(); //3.進行部署 Deployment deployment = repositoryService.createDeployment()//建立Deployment對象 .addClasspathResource("processes/activitiUserRoleProcess.bpmn")//添加bpmn文件 .addClasspathResource("processes/activitiUserRoleProcess.png")//添加png文件 .name("請假申請單流程") .deploy();//部署 //4.輸出部署的一些信息 System.out.println(deployment.getName()); System.out.println(deployment.getId()); } /** * Zip文件部署流程 * 影響的activiti表有哪些 * act_re_deployment 部署信息 * act_re_procdef 流程定義的一些信息 * act_ge_bytearray 流程定義的bpmn文件以及png文件 */ public void ActivitiZipDeployment (){ //先將bpmn文件和png文件壓縮成zip文件。可是activiti最終也是以單個文件形式保存,說明activiti進行了解壓工做。 //1.建立ProcessEngine對象 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); //2.獲得RepositoryService實例 RepositoryService repositoryService = processEngine.getRepositoryService(); //3.進行部署 Deployment deployment = repositoryService.createDeployment()//建立Deployment對象 .addClasspathResource("diagram/holiday.bpmn")//添加bpmn文件 .addClasspathResource("diagram/holiday.png")//添加png文件 .name("請假申請單流程") .deploy();//部署 //4.輸出部署的一些信息 System.out.println(deployment.getName()); System.out.println(deployment.getId()); //用戶信息初始化 //初始化4級子公司角色及如下的員工 Group group1 = identityService.newGroup("threelevel"); group1.setName("三級及如下子公司"); group1.setType("assignment"); identityService.saveGroup(group1); Group group2 = identityService.newGroup("secondlevel"); group2.setName("二級子公司"); group2.setType("assignment"); identityService.saveGroup(group2); Group group3 = identityService.newGroup("financelevel"); group3.setName("財務公司"); group3.setType("assignment"); identityService.saveGroup(group3); Group group4 = identityService.newGroup("insurancelevel"); group4.setName("保險公司"); group4.setType("assignment"); identityService.saveGroup(group4); } /** * 啓動一個工做流流程進行流程測試 */ @Test public void ActivitiStartInstance() { //1.獲得ProcessEngine對象 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); //2.獲得RunService對象 RuntimeService runtimeService = processEngine.getRuntimeService(); //3.建立流程實例(關鍵步驟)即 啓動流程實例 //須要知道流程定義的Key:holiday(找key的方法 1:bpmn文件中的id,它對應的值就是key // 2:直接看數據庫中流程定義表act_re_procdet的key值) String userId= "sjb"; Map<String,Object> map=new HashMap<String,Object>(); map.put("userId", userId);//標識爲工做流程的工做,區別其餘單獨使用的流程 ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("activitiUserRoleProcess",map); runtimeService.addUserIdentityLink(processInstance.getId(), "w6", "participant"); runtimeService.addGroupIdentityLink(processInstance.getId(), "gly", "candidate"); //4.輸出實例的相關信息 System.out.println("流程部署ID="+processInstance.getDeploymentId());//null System.out.println("流程定義ID="+processInstance.getProcessDefinitionId());//holiday:1:4 System.out.println("流程實例ID="+processInstance.getId());//2501 System.out.println("流程活動ID="+processInstance.getActivityId());//獲取當前具體執行的某一個節點的ID(null) } /** * 查詢當前用戶的任務列表 */ public void ActivitiTaskQuery () { //lisi完成本身任務列表的查詢 //1.獲得ProcessEngine對象 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); //2.獲得TaskService對象 TaskService taskService = processEngine.getTaskService(); //3.根據流程定義的key以及負責人assignee來實現當前用戶的任務列表查詢 List<Task> taskList = taskService.createTaskQuery() .processDefinitionKey("holiday") .taskAssignee("lisi") .list();//這裏還有一個查詢惟一結果的方法:singleResult();、還有分頁查詢listPage(index,limit); //4.任務列表展現 for (Task task : taskList) { //查的act_hi_procinst表的id System.out.println("流程實例ID="+task.getProcessInstanceId()); //查的act_hi_taskinst表的id System.out.println("任務ID="+task.getId()); //查的act_hi_taskinst表的Assignee_ System.out.println("任務負責人名稱="+task.getAssignee()); //查的act_hi_taskinst表的NAME_ System.out.println("任務名稱="+task.getName()); } } /** * 處理當前用戶的任務列表 * 背後操做到的表: * act_hi_actinst * act_hi_identitylink * act_hi_taskinst * act_ru_execution * act_ru_identitylink * act_ru_task //只放當前要執行的任務 */ public void ActivitiCompleteTask (){ /** * 李四完成本身的任務 */ //1.獲得ProcessEngine對象 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); //2.獲得TaskService對象 TaskService taskService = processEngine.getTaskService(); //3.處理任務,結合當前用戶任務列表的查詢操做的話,能夠知道任務ID=5002(實際操做中應該與查詢寫在一塊兒) taskService.complete("5002"); } /** * 查詢流程定義信息 **/ public void QueryProcessDefinition (){ //1.建立ProcessEngine對象 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); //2.建立RepositoryService對象 RepositoryService repositoryService = processEngine.getRepositoryService(); //3.獲得ProcessDefinitionQuery對象,能夠認爲它就是一個查詢器 ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery(); //4.設置條件,並查詢出當前的全部流程定義 查詢條件:流程定義的key=holiday //.orderByProcessDefinitionVersion() 設置排序方式,根據流程定義的版本號進行排序。 List<ProcessDefinition> list = processDefinitionQuery.processDefinitionKey("holiday") .orderByProcessDefinitionVersion() .desc().list(); //5.輸出流程定義信息 for (ProcessDefinition processDefinition : list) { System.out.println("流程定義ID" + processDefinition.getId()); System.out.println("流程定義名稱" + processDefinition.getName()); System.out.println("流程定義Key" + processDefinition.getKey()); System.out.println("流程定義的版本號" + processDefinition.getVersion()); } } /** * 刪除已經部署的流程定義 * 影響的activiti表有哪些 * act_re_deployment 部署信息 * act_re_procdef 流程定義的一些信息 * act_ge_bytearray 流程定義的bpmn文件以及png文件 **/ public void DeleteProcessDefinition (){ //1.建立ProcessEngine對象 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); //2.建立RepositoryService對象 RepositoryService repositoryService = processEngine.getRepositoryService(); //3.執行刪除流程定義,參數表明流程部署的id //參數true表明級聯刪除,此時就會先刪除沒有完成的流程節點,最後就能夠刪除流程定義信息,false表明不級聯 repositoryService.deleteDeployment("1"); } /** * 需求 * 歷史數據的查看 **/ public void HistoryQuery (){ //1.獲得ProcessEngine對象 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); //2.獲得HistoryService HistoryService historyService = processEngine.getHistoryService(); //3.獲得HistoricActivitiInstanceQuery對象 HistoricActivityInstanceQuery historicActivityInstanceQuery = historyService.createHistoricActivityInstanceQuery(); historicActivityInstanceQuery.processInstanceId("2501");//設置流程實例的id //4.執行查詢 List<HistoricActivityInstance> list = historicActivityInstanceQuery .orderByHistoricActivityInstanceStartTime() .asc()//根據流程開始進行的時間來排序 .list(); //5.遍歷查詢結果 for (HistoricActivityInstance instance : list) { System.out.println("節點ID:" + instance.getActivityId()); System.out.println("節點名稱:" + instance.getActivityName()); System.out.println("流程定義ID:" + instance.getProcessDefinitionId()); System.out.println("流程實例ID:" + instance.getProcessInstanceId()); System.out.println("================================="); } } /** * 工做流的掛起與激活; */ // @Test public void ActivitiSuspendAndActivate() { // 經過流程實例ID來掛起流程實例 runtimeService.suspendProcessInstanceById("activitiInsurancePlanProcess:1:42504"); // 經過流程實例ID來激活流程實例 runtimeService.activateProcessInstanceById("42504"); } /* * 查詢組用戶的任務 */ //@Test public void ActivitiGroupTaskQuery() { String candidateUser = "user_secondlevel"; List<Task> list = processEngine.getTaskService()// 與正在執行的任務管理相關的Service .createTaskQuery()// 建立任務查詢對象 /** 查詢條件(where部分) */ // .taskAssignee(assignee)//指定我的任務查詢,指定辦理人 .taskCandidateUser(candidateUser)// 組任務的辦理人查詢 // .processDefinitionId(processDefinitionId)//使用流程定義ID查詢 // .processInstanceId(processInstanceId)//使用流程實例ID查詢 // .executionId(executionId)//使用執行對象ID查詢 /** 排序 */ .orderByTaskCreateTime().asc()// 使用建立時間的升序排列 /** 返回結果集 */ // .singleResult()//返回唯一結果集 // .count()//返回結果集的數量 // .listPage(firstResult, maxResults);//分頁查詢 .list();// 返回列表 if (list != null && list.size() > 0) { for (Task task : list) { System.out.println("任務ID:" + task.getId()); System.out.println("任務名稱:" + task.getName()); System.out.println("任務的建立時間:" + task.getCreateTime()); System.out.println("任務的辦理人:" + task.getAssignee()); System.out.println("流程實例ID:" + task.getProcessInstanceId()); System.out.println("執行對象ID:" + task.getExecutionId()); System.out.println("流程定義ID:" + task.getProcessDefinitionId()); System.out.println("########################################################"); } } else { System.out.println("未查詢到組用戶的任務"); } } //駁回至 上一節點 private void jumpBeforeNode(String taskId,String msg){ Task task = taskService.createTaskQuery().taskId(taskId).singleResult(); String processInstanceId = task.getProcessInstanceId(); // 一、首先是根據流程ID獲取當前任務: List<Task> tasks = processEngine.getTaskService().createTaskQuery().processInstanceId(processInstanceId).list(); // 獲取流程中已經執行的節點,按照執行前後順序排序 List<HistoricActivityInstance> historicActivityInstances = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId) .orderByHistoricActivityInstanceId().asc().list(); //上一節點 節點ID HistoricActivityInstance historicActivityInstance = historicActivityInstances.get(historicActivityInstances.size()-2); managementService.executeCommand(new Jump2TargetFlowNodeCommand(taskId, historicActivityInstance.getActivityId(),msg)); tasks = taskService.createTaskQuery().processInstanceId(processInstanceId).list(); //同步計劃信息 for (Task ts : tasks) { ts.setDescription(task.getDescription()); taskService.saveTask(ts); } } /** * 查詢 每一個節點的備註信息 * @param processInstanceId * @return */ private Map<String,Object> packetVariables(String processInstanceId){ Map<String,Object> historyVariables=new HashMap<String,Object>(); //查詢act_hi_detail表中proc_inst_id爲processInstance.getId() 的全部數據並返回 List<HistoricDetail> list=historyService.createHistoricDetailQuery().processInstanceId(processInstanceId).list(); for(HistoricDetail historicDetail:list){ if(historicDetail instanceof HistoricFormProperty){ HistoricFormProperty field=(HistoricFormProperty)historicDetail; historyVariables.put(field.getPropertyId(), field.getPropertyValue()); System.out.println("form field:taskId="+field.getTaskId()+",="+field.getPropertyId()+"="+field.getPropertyValue()); }else if (historicDetail instanceof HistoricVariableUpdate){ HistoricDetailVariableInstanceUpdateEntity variable=(HistoricDetailVariableInstanceUpdateEntity)historicDetail; historyVariables.put(variable.getName(), variable.getValue()); System.out.println("variable:"+variable.getVariableName()+",="+variable.getValue()); } } return historyVariables; } }
駁回代碼
1 package com.huatonghh.activiti.util; 2 import org.activiti.bpmn.model.FlowElement; 3 import org.activiti.bpmn.model.Process; 4 import org.activiti.engine.ActivitiEngineAgenda; 5 import org.activiti.engine.impl.interceptor.Command; 6 import org.activiti.engine.impl.interceptor.CommandContext; 7 import org.activiti.engine.impl.persistence.entity.ExecutionEntity; 8 import org.activiti.engine.impl.persistence.entity.ExecutionEntityManager; 9 import org.activiti.engine.impl.persistence.entity.HistoricTaskInstanceEntity; 10 import org.activiti.engine.impl.persistence.entity.TaskEntity; 11 import org.activiti.engine.impl.persistence.entity.TaskEntityManager; 12 import org.activiti.engine.impl.util.ProcessDefinitionUtil; 13 14 /** 15 * 跳轉到指定節點代碼 16 * 17 * @author juyanming 18 * 19 */ 20 public class Jump2TargetFlowNodeCommand implements Command<Void> { 21 private String curTaskId; 22 23 private String targetFlowNodeId; 24 25 private String deleteReason; 26 27 public Jump2TargetFlowNodeCommand(String curTaskId, String targetFlowNodeId,String deleteReason) { 28 super(); 29 this.curTaskId = curTaskId; 30 this.targetFlowNodeId = targetFlowNodeId; 31 this.deleteReason = deleteReason; 32 } 33 34 @Override 35 public Void execute(CommandContext commandContext) { 36 System.out.println("跳轉到目標流程節點:" + targetFlowNodeId); 37 System.out.println("駁回緣由:" + deleteReason); 38 ExecutionEntityManager executionEntityManager = commandContext.getExecutionEntityManager(); 39 TaskEntityManager taskEntityManager = commandContext.getTaskEntityManager(); 40 // 獲取當前任務的來源任務及來源節點信息 41 TaskEntity taskEntity = taskEntityManager.findById(curTaskId); 42 ExecutionEntity executionEntity = executionEntityManager.findById(taskEntity.getExecutionId()); 43 Process process = ProcessDefinitionUtil.getProcess(executionEntity.getProcessDefinitionId()); 44 // 刪除當前節點 45 taskEntityManager.deleteTask(taskEntity, deleteReason, true, true); 46 HistoricTaskInstanceEntity historicTaskInstance = commandContext.getDbSqlSession().selectById(HistoricTaskInstanceEntity.class,curTaskId); 47 48 if (historicTaskInstance != null) { 49 historicTaskInstance.setDeleteReason(deleteReason); 50 commandContext.getDbSqlSession().update(historicTaskInstance); 51 } 52 53 // 獲取要跳轉的目標節點 54 FlowElement targetFlowElement = process.getFlowElement(targetFlowNodeId); 55 executionEntity.setCurrentFlowElement(targetFlowElement); 56 ActivitiEngineAgenda agenda = commandContext.getAgenda(); 57 agenda.planContinueProcessInCompensation(executionEntity); 58 59 return null; 60 } 61 62 public String getCurTaskId() { 63 return curTaskId; 64 } 65 66 public void setCurTaskId(String curTaskId) { 67 this.curTaskId = curTaskId; 68 } 69 70 public String getTargetFlowNodeId() { 71 return targetFlowNodeId; 72 } 73 74 public void setTargetFlowNodeId(String targetFlowNodeId) { 75 this.targetFlowNodeId = targetFlowNodeId; 76 } 77 78 public String getDeleteReason() { 79 return deleteReason; 80 } 81 82 public void setDeleteReason(String deleteReason) { 83 this.deleteReason = deleteReason; 84 } 85 86 }