注意:如下規則是我爲了規範流程的處理過程,不是Activiti公司的官方規定。html
好比完成一個「請假銷假」的任務,須要流程發起者銷假,銷假環節就能找到正確的簽收者(activiti:assignee)了:前端
<startevent id="startevent1" name="Start" activiti:initiator="initUserId"></startevent> <usertask id="reportBack" name="銷假" activiti:assignee="${initUserId}"></usertask>
業務鍵 = 流程ID + 實體實例ID;
businessKey = procDefId + "." + objIdjava
在流程啓動的時候,咱們已經定義了業務Key,那麼只須要反查,便可獲得流程實例node
//根據業務鍵獲取流程實例 public ProcessInstance getProInstByBusinessKey(String businessKey) { return runtimeService.createProcessInstanceQuery().processInstanceBusinessKey("LeaveBill.1").singleResult(); } //根據業務鍵獲取任務 public List<Task> getTasksByBusinessKey(String businessKey) { return taskService.createTaskQuery().processInstanceBusinessKey("LeaveBill.1").list(); }
//一、經過任務對象獲取流程實例 ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult(); //二、經過流程實例獲取「業務鍵」 String businessKey = pi.getBusinessKey();
String processInstanceId="1401"; // 經過流程實例ID查詢流程實例 ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult(); if(pi!=null){ System.out.println("當前流程節點在:" + pi.getActivityId()); }else{ System.out.println("流程已結束!!"); }
「候選公共任務」的認領者即屬於一堆候選人其中一個,好比財務審批能夠由張3、李4、王五審批,誰批均可以,手快者先認領就是簽收者。
這個查詢就是把符合條件的候選者的任務查出來,通常能夠和「我的任務」合併一塊兒放在「待辦任務」菜單裏。
也針對於把Task分配給一個角色時,例如部門領導,由於部門領導角色能夠指定多我的因此須要先簽收再辦理,特色:搶佔式。git
// 建立任務查詢對象 TaskQuery taskQuery = taskService.createTaskQuery(); // 配置查詢對象 String candidateUser="張三"; taskQuery // 過濾條件 .taskCandidateUser(candidateUser) // 排序條件 .orderByTaskCreateTime().desc(); // 執行查詢 List<Task> tasks = taskQuery.list(); System.out.println("======================【"+candidateUser+"】的候選公共任務列表================="); for (Task task : tasks) { System.out.print("id:"+task.getId()+","); System.out.print("name:"+task.getName()+","); System.out.print("createTime:"+task.getCreateTime()+","); System.out.println("assignee:"+task.getAssignee()); }
好比銷假人被變量明確指定了:
<usertask id="reportBack" name="銷假" activiti:assignee="${initUserId}"></usertask>github
// 建立任務查詢對象 TaskQuery taskQuery = taskService.createTaskQuery(); // 配置查詢對象 // String assignee="user"; String assignee="李四"; taskQuery // 過濾條件 .taskAssignee(assignee) // 分頁條件 // .listPage(firstResult, maxResults) // 排序條件 .orderByTaskCreateTime().desc(); // 執行查詢 List<Task> tasks = taskQuery.list(); System.out.println("======================【"+assignee+"】的代辦任務列表================="); for (Task task : tasks) { System.out.print("id:"+task.getId()+","); System.out.print("name:"+task.getName()+","); System.out.print("createTime:"+task.getCreateTime()+","); System.out.println("assignee:"+task.getAssignee()); }
// claim 認領 String taskId="1404"; String userId="李四"; // 讓指定userId的用戶認領指定taskId的任務 taskService.claim(taskId, userId);
String formId = request.getParameter("formId"); String procInstId = request.getParameter("procInstId"); //流程實例ID Map<String, String[]> flowData = new HashMap<String, String[]>(); //將表單提交數據注入表單變量 flowData = request.getParameterMap(); formHelper.submitTaskFormData(request.getParameter("taskId"), flowData); // 完成任務 taskService.complete(taskId );
Activiti的簽收人中只有候選人、候選組、分配人的概念,若是要實現更業務相關的簽收邏輯,須要擴展監聽器
好比MyLeaderHandler,即擴展實現了TaskListener接口:web
<userTask id="task1" name="My task" > <extensionElements> <activiti:taskListener event="create" class="org.activiti.MyLeaderHandler" /> </extensionElements> </userTask>
//動態實現任務分配 public class MyLeaderHandler implements TaskListener { public void notify(DelegateTask delegateTask) { LeaderService ls =.... String userLeader = ls.findLeaderbyUserId(XXXXXXX); delegateTask.setAssignee(userLeader); delegateTask.addCandidateUser(XXX); delegateTask.addCandidateGroup(XXXX); ... } }
還有一種更方便的方法,即經過el表達式:spring
可使用表達式把任務監聽器設置爲spring代理的bean, 讓這個監聽器監放任務的建立事件。
下面的例子中,執行者會經過調用ldapService這個spring bean的findManagerOfEmployee方法得到。
流程變量emp會做爲參數傳遞給bean。sql
<userTask id="task" name="My Task" activiti:assignee="${ldapService.findManagerForEmployee(emp)}"/>數據庫
也能夠用來設置候選人和候選組:
<userTask id="task" name="My Task" activiti:candidateUsers="${ldapService.findAllSales()}"/>
ps:注意方法返回類型只能爲String或Collection<String> (對應候選人和候選組):
public class FakeLdapService { public String findManagerForEmployee(String employee) { return "Kermit"; } public List<String> findAllSales() { return Arrays.asList("kermit", "gonzo", "fozzie"); } }
例如,一個任務必須全部領導都經過了才往下走。
activiti其實已經很是優雅的實現了,網上有一些繁瑣的實現,其實徹底沒有必要,好比下面:
http://jee-soft.cn/htsite/html/fzyyj/jsyj/2012/08/08/1344421504026.html
正確的打開方式是經過在Task節點增長multiInstanceCharacteristics節點,設置 collection和 elementVariable屬性
例子:
能夠指定一個(判斷完成)表達式,只有true的狀況下所有實例完成,流程繼續往下走。
若是表達式返回true,全部其餘的實例都會銷燬,多實例節點也會結束。 這個表達式必須定義在completionCondition子元素中。
<userTask id="miTasks" name="My Task" activiti:assignee="${assignee}"> <multiInstanceLoopCharacteristics isSequential="false" activiti:collection="assigneeList" activiti:elementVariable="assignee" > <completionCondition>${nrOfCompletedInstances/nrOfInstances >= 0.6 }</completionCondition> </multiInstanceLoopCharacteristics> </userTask>
這裏例子中,會爲assigneeList集合的每一個元素建立一個並行的實例。 當60%的任務完成時,其餘任務就會刪除,流程繼續執行。
會籤環節中涉及的幾個默認的自帶流程變量:
實現會籤人員分配
public class AssgineeMultiInstancePer implements JavaDelegate { @Override public void execute(DelegateExecution execution) throws Exception { System.out.println("設置會籤環節的人員."); execution.setVariable("pers", Arrays.asList("張三", "李四", "王五", "趙六")); } }
設置完成會籤條件:
public class MulitiInstanceCompleteTask implements Serializable { private static final long serialVersionUID = 1L; public boolean completeTask(DelegateExecution execution) { System.out.println("總的會籤任務數量:" + execution.getVariable("nrOfInstances") + "當前獲取的會籤任務數量:" + execution.getVariable("nrOfActiveInstances") + " - " + "已經完成的會籤任務數量:" + execution.getVariable("nrOfCompletedInstances")); System.out.println("I am invoked."); return false; } }
官方網站專門有一節(8.5.14. Multi-instance)詳細介紹:
http://www.activiti.org/userguide/index.html#bpmnMultiInstance
也能夠看:
http://itindex.net/detail/54540-activiti
https://my.oschina.net/winHerson/blog/139267
http://itindex.net/detail/51509-activiti-%E6%8C%82%E8%B5%B7-%E6%A0%B8%E5%BF%83
http://blog.csdn.net/chq1988/article/details/41513451
https://github.com/bluejoe2008/openwebflow
Activiti其實已經很是優雅的實現了,網上有一些繁瑣的實現,其實徹底沒有必要
實際上一行代碼就能實現:
//人員加簽: runtimeService.addUserIdentityLink(String processInstanceId, String userId, String identityLinkType) //還能夠這樣實現,實際上是addUserIdentityLink的一個繼承,只是類型是IdentityLinkType.CANDIDATE,即參與者 runtimeService.addParticipantUser(String processInstanceId, String userId) //組加簽: runtimeService.addParticipantGroup(String processInstanceId, String groupId) runtimeService.deleteParticipantUser(String processInstanceId, String userId) //人員、組減籤: runtimeService.deleteParticipantUser(String processInstanceId, String userId) runtimeService.deleteParticipantGroup(String processInstanceId, String groupId)
值得注意的是,認證鏈類型IdentityLinkType是枚舉,有5個:
使用這裏的ASSIGNEE和OWNER還能夠有更加豐富的想象力。
http://www.verydemo.com/demo_c128_i11037.html
https://my.oschina.net/winHerson/blog/118172
http://rongjih.blog.163.com/blog/static/335744612012631112640968/
http://www.kafeitu.me/activiti/2012/04/23/synchronize-or-redesign-user-and-role-for-activiti.html
這個想法挺好:
http://blog.csdn.net/tuzongxun/article/details/51093881
但距離實用還差很遠,實現產品化也是實現代價很是高,仍是使用jsp + bootstrip做爲前端最實際,也更容易擴展
筆者正好也作過動態自定義表單:
積累過一些技術和思想。
在實際的開發中,實際上其自身的內置表單無任何意義,而Activiti還不具有所見即所得的自動錶單設計器(畢竟是開源產品),但另外一方面這也帶來了一個好處:便可以爲所欲爲的設計表單前端,能夠知足任務的可控性和可變性,很是強大,而不是限制在廠商的表單技術裏動彈不得,外置表單能夠用到的技術:JSP、Spring MVC視圖、Struct視圖。
Activiti提供的集成方式異常簡單而優雅,只須要注意4點:
StartFormData FormService.getStartFormData(String processDefinitionId)
//Or
TaskFormdata FormService.getTaskFormData(String taskId) String formKey = formData.getFormKey()
...
Map map=request.getParameterMap(); Set set=map.keySet();//全部參數名的set集 Iterator it=set.iterator(); while(it.hasNext()){ String s=(String)it.next();//枚舉出參數 String values[]=request.getParameterValues(s);//取得對應的參數值返回的數組 而後幹你要乾的操做,好比 for(int i=0;i<values.length;i++){ out.println(values[i]); } }
提交:
ProcessInstance FormService.submitStartFormData(String processDefinitionId, Map<String,String> properties)
void FormService.submitStartFormData(String taskId, Map<String,String> properties)
實際上在業務開發中能夠不須要用到表單屬性,只用表單變量便可,須要注意表單變量的做用域,有兩個
在Activit內部,定義了流程屬性,會自動增長流程變量,也能夠手動設置關聯, 手動流程屬性和變量的關聯關係舉例:
一、屬性speaker 和 變量SpeakerName 相互關聯
<activiti:formProperty id="speaker" name="Speaker" variable="SpeakerName" type="string" />
二、和bean關聯
<activiti:formProperty id="street" expression="#{address.street}" required="true" />
form property的5種類型:
string
(org.activiti.engine.impl.form.StringFormType
long
(org.activiti.engine.impl.form.LongFormType)
enum
(org.activiti.engine.impl.form.EnumFormType)
date
(org.activiti.engine.impl.form.DateFormType)
boolean
(org.activiti.engine.impl.form.BooleanFormType)
獲取表單屬性的辦法:
List<FormProperty> formService.getStartFormData(String processDefinitionId).getFormProperties()
List<FormProperty> formService.getTaskFormData(String taskId).getFormProperties()
FormProperty實際是一個接口,允許去你去自由擴展,具體定義:
public interface FormProperty { /** the key used to submit the property in {@link FormService#submitStartFormData(String, java.util.Map)} * or {@link FormService#submitTaskFormData(String, java.util.Map)} */ String getId(); /** the display label */ String getName(); /** one of the types defined in this interface like e.g. {@link #TYPE_STRING} */ FormType getType(); /** optional value that should be used to display in this property */ String getValue(); /** is this property read to be displayed in the form and made accessible with the methods * {@link FormService#getStartFormData(String)} and {@link FormService#getTaskFormData(String)}. */ boolean isReadable(); /** is this property expected when a user submits the form? */ boolean isWritable(); /** is this property a required input field */ boolean isRequired(); }
獲取表單屬性的名稱:
formProperty.getType().getName()
獲取表單屬性的值:
formProperty.getType().getValue()
獲取某一屬性,好比XML定義:
<activiti:formProperty id="start" type="date" datePattern="dd-MMM-yyyy" />
formProperty.getType().getInformation("datePattern")
獲取枚舉值:
formProperty.getType().getInformation("values")
Activit內置的待辦任務查詢相似:
List<Task> tasks = taskService.createTaskQuery() .taskAssignee("kermit") .processVariableValueEquals("orderId", "0815") .orderByDueDate().asc() .list();
若是要直接SQL查詢,能夠這樣:
List<Task> tasks = taskService.createNativeTaskQuery() .sql("SELECT count(*) FROM " + managementService.getTableName(Task.class) + " T WHERE T.NAME_ = #{taskName}") .parameter("taskName", "gonzoTask") .list(); long count = taskService.createNativeTaskQuery() .sql("SELECT count(*) FROM " + managementService.getTableName(Task.class) + " T1, " + managementService.getTableName(VariableInstanceEntity.class) + " V1 WHERE V1.TASK_ID_ = T1.ID_") .count();
首先須要明確一些容易混淆的概念。
"execution是什麼?"
就是流程實例的當前執行節點;
"execution node和process instance的區別是什麼?"
執行節點和流程實例的關係很是密切,能夠想象一個樹形結構,流程實例(process instance)是主幹,由無數的執行樹枝(execution)組成的;
一個重要的公式:process instance id = the root execution id
但一旦流程不是一條主幹線一直到結束,而是分裂爲多個分支了,那麼就有多個execution id,能夠當作樹枝。
"activity、task、job的區別是什麼?"
設置流程變量(RuntimeService)的方法:
void setVariable(String executionId, String variableName, Object value); void setVariableLocal(String executionId, String variableName, Object value); void setVariables(String executionId, Map<String, ? extends Object> variables); void setVariablesLocal(String executionId, Map<String, ? extends Object> variables);
讀取流程變量的方法:
//讀取變量(基於TaskService) Map<String, Object> getVariables(String executionId); Map<String, Object> getVariablesLocal(String executionId); Map<String, Object> getVariables(String executionId, Collection<String> variableNames); Map<String, Object> getVariablesLocal(String executionId, Collection<String> variableNames); Object getVariable(String executionId, String variableName); <T> T getVariable(String executionId, String variableName, Class<T> variableClass); //讀取變量(基於execution 執行分支對象) execution.getVariables(); execution.getVariables(Collection<String> variableNames); execution.getVariable(String variableName); execution.setVariables(Map<String, object> variables); execution.setVariable(String variableName, Object value);
Activiti5.17版本以後的新API
因爲歷史版本緣由,在執行任何上述方法的時候,activiti默認是將全部的變量從數據庫取出來,意味着數據庫存有10個變量,如今你須要取出名叫myVariable的變量,可是其他的9個也會被取出來而且緩存起來。這並非不好,由於後期你取變量就不會再從數據庫取出,固然若是你有大量的變量或者在查詢方面你想進一步控制數據庫,這時所有取出就不怎麼合適了。從Activiti5.17版本開始,新添加了的方法支持是否所有查詢輸出到緩存:若是是true則所有抓取
Map<String, Object> getVariables(Collection<String> variableNames, boolean fetchAllVariables); Object getVariable(String variableName, boolean fetchAllVariables); void setVariable(String variableName, Object value, boolean fetchAllVariables);