最近在工作中使用到了activiti 工作流引擎,跟大家遇到過的情況類似,在“中國式”的工作流中,常有一些需求是工作流引擎基本使用中無法實現的。在這過程中,我和我的小夥伴們也和大家一樣遇到很多困難,大海撈針似的在網上尋找着答案。特此,在這裏把我們遇到的需求和解決方案分享給大家,希望能幫助到你們!
以下是我們在項目中遇到的各(奇)種(葩)需求,如果您也遇到了相同的可以借鑑:
1、工作流會籤;
2、多人審批時一人通過即可;
3、在當前節點獲取下一節點的信息;
4、流程部署後未發佈之前獲取所有節點的信息;
5、流程啓動前傳入後續節點辦理人;
6、節點設置多個監聽。
1、 activiti 工作流會籤時,所有的都審批通過纔可進入下一環節:
1.1 編寫監聽類
public class MyTaksListener implements TaskListener {
public void notify(DelegateTask delegateTask) {
System.out.println("delegateTask.getEventName() = " + delegateTask.getEventName());
//添加會籤的人員,所有的都審批通過纔可進入下一環節
List<String> assigneeList = new ArrayList<String>();
assigneeList.add("wangba");
assigneeList.add("wangjiu");
delegateTask.setVariable("publicityList",assigneeList);
}
}
1.2 “員工請假申請”中添加此監聽類
1.3 “項目組長審批”中
isSequential=false時,表示的並行執行,即該節點下的多條任務可以同時執行。
activiti:collection:執行該會籤環節的參與人,此處是使用的一個名叫publicityList的流程變量
activiti:elementVariable:表示的是每一個分支都有一個名叫publicity的流程變量,和上方的activiti:assignee結合
1.4 項目組長審批時,通過taskAssignee來獲取個人任務
// 獲取總記錄數
total = taskService.createTaskQuery().taskAssignee(userId).taskNameLike("%" + s_name + "%").count();
taskList = taskService.createTaskQuery()
// 根據用戶id查詢
.taskAssignee(userId)
// 根據任務名稱查詢
.taskNameLike("%" + s_name + "%")
// 返回帶分頁的結果集合
.listPage(pageInfo.getPageIndex(), pageInfo.getPageSize());
==================================================================================
2. activiti 工作流會籤,一人通過即可進入下一環節:
2.1 編寫監聽類
public class MangerTaskHandlerCandidateUsers implements TaskListener{
public void notify(DelegateTask delegateTask) {
//添加審批的人員,以下任何一人通過即可進入下一環節
String[] empLoyees = {"wangba","wangjiu"};
delegateTask.addCandidateUsers(Arrays.asList(empLoyees));
}
}
2.2 “項目組長審批”中
2.3 項目組長審批時,通過taskCandidateUser來獲取節點任務
// 獲取總記錄數
total = taskService.createTaskQuery().taskCandidateUser(userId).taskNameLike("%" + s_name + "%").count();
taskList = taskService.createTaskQuery()
// 根據用戶id查詢
.taskCandidateUser(userId)
// 根據任務名稱查詢
.taskNameLike("%" + s_name + "%")
// 返回帶分頁的結果集合
.listPage(pageInfo.getPageIndex(), pageInfo.getPageSize());
============================================================================
3、在當前節點獲取下一節點的信息
/**
* 根據實例編號查找下一個任務節點
*
* @param String
* procInstId :實例編號
* @return
*/
@RequestMapping("/backTaskTab")
public TaskDefinition backTaskTab(String taskId) {
Task task = taskService.createTaskQuery() // 創建任務查詢
.taskId(taskId) // 根據任務id查詢
.singleResult();
String procInstId = task.getProcessInstanceId();
// 流程標示
String processDefinitionId = historyService.createHistoricProcessInstanceQuery().processInstanceId(procInstId)
.singleResult().getProcessDefinitionId();
ProcessDefinitionEntity def = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService)
.getDeployedProcessDefinition(processDefinitionId);
// 執行實例
ExecutionEntity execution = (ExecutionEntity) runtimeService.createProcessInstanceQuery()
.processInstanceId(procInstId).singleResult();
// 當前實例的執行到哪個節點
String activitiId = execution.getActivityId();
// 獲得當前任務的所有節點
List<ActivityImpl> activitiList = def.getActivities();
ActivityImpl activityImpl=null;
for(int i=0;i< activitiList.size();i++){
String flag=activitiList.get(i).getId();
if(flag.equals(activitiId)){
activityImpl=activitiList.get(i);
}
}
String id = null;
int num=activitiList.indexOf(activityImpl);
ActivityImpl activityImpl_=activitiList.get(num+1);
TaskDefinition taskDefinition = ((UserTaskActivityBehavior) activityImpl_.getActivityBehavior())
.getTaskDefinition();
// 獲取下一節點的代辦人
System.out.println(taskDefinition.getCandidateGroupIdExpressions().toArray()[0]);
return null;
}
============================================================================
4、流程部署後未發佈之前獲取所有節點的信息
解決思路是這樣的:部署完工作流之後,爲UserTask節點動態分配任務執行者,或者在分支節點上添加條件判斷的功能。爲了實現這個功能,需要解析流程定義文件,取出文件中定義的所有節點。這裏有兩個方法可以實現此功能:
方法一(流程部署至服務器上之後可使用):
//processDefinitionId爲流程定義Id,該Id可以通過多種方式獲得,如通過ProcessDefinitionQuery可以查詢一個 //ProcessDefinition對象,Task對象中也包含
processDefinitionIdBpmnModel model = repositoryService.getBpmnModel(processDefinitionId);
if (model != null) {
Collection<FlowElement> flowElements = model.getMainProcess().getFlowElements();
for (FlowElement e : flowElements) {
System.out.println("flowelement id:" + e.getId() + " name:" + e.getName() + " class:"
+ e.getClass().toString());
}
}
該方法適用於流程部署至服務器上之後,通過該方法可以簡單快速的獲取流程定義文件中各個節點信息。
方法二 讀取流程定義文件方式
InputStream resouceStream = this.getClass().getClassLoader().getResourceAsStream("leave- formkey.bpmn20.xml");
XMLInputFactory xif = XMLInputFactory.newInstance();
InputStreamReader in;
XMLStreamReader xtr;
try {
in = new InputStreamReader(resouceStream, "UTF-8");
xtr = xif.createXMLStreamReader(in);
BpmnModel model = new BpmnXMLConverter().convertToBpmnModel(xtr);
Collection<FlowElement> flowElements = model.getMainProcess().getFlowElements();
for (FlowElement e : flowElements) {
System.out.println("flowelement id:" + e.getId() + " name:" + e.getName() + " class:"
+ e.getClass().toString());
}
} catch (XMLStreamException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
兩次測試打印結果如下:
流程定義文件leave-formkey.bpmn20.xml:
5.流程啓動前傳入後續節點辦理人;
//下面name2和name3是前臺傳過來的第二個和第三個節點的辦理人
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("leaveId", leaveId);
variables.put("name2", "XXX");//(前臺傳過來的第二個節點的辦理人)
variables.put("name3", "YYY");//(前臺傳過來的第三個節點的辦理人)
// 啓動流程
pi = runtimeService.startProcessInstanceByKey("activitiemployeeProcess", variables);
在第一個節點指定第二個節點的監聽
public class MyTaksListener2 implements TaskListener {
public void notify(DelegateTask delegateTask) {
Map<String, Object> variables=delegateTask.getVariables();
variables.get("name2");(前臺傳過來的第二個節點的辦理人)
//拆分variables
List<String> assigneeList = new ArrayList<String>();
assigneeList.add("wangba");
delegateTask.setVariable("publicityList",assigneeList);
}
}
在第三個節點指定本節點的辦理人監聽
public class MyTaksListener3 implements TaskListener {
public void notify(DelegateTask delegateTask) {
Map<String, Object> variables=delegateTask.getVariables();
System.out.println(variables);
variables.get("name3");
// String result=(String) variables.get("name3");(前臺傳過來的第三個節點的辦理人)
String[] empLoyees = {"szx"};
delegateTask.addCandidateUsers(Arrays.asList(empLoyees));
}
}
============================================================================
6、節點設置多個監聽
在同一節點設置兩個監聽,一個是設置本節點的監聽,指定辦理人;另一個是設置下一個節點的監聽,指定會籤人。
設置本節點的監聽,指定辦理人
public class MyTaksListener3 implements TaskListener {
public void notify(DelegateTask delegateTask) {
Map<String, Object> variables=delegateTask.getVariables();
System.out.println(variables);
String result=(String) variables.get("name3");
String[] empLoyees = {"szx"};
delegateTask.addCandidateUsers(Arrays.asList(empLoyees));
}
}
設置下一個節點的監聽,指定會籤人
public class MyTaksListener4 implements TaskListener {
public void notify(DelegateTask delegateTask) {
Map<String, Object> variables=delegateTask.getVariables();
String result=(String)variables.get("name2");
List<String> assigneeList = new ArrayList<String>();
assigneeList.add("ss");
delegateTask.setVariable("publicityList",assigneeList);
}
}
至此,項目中遇到的各(奇)種(葩)問題迎刃而解。“中國式”工作流有時確實很讓人頭疼,但也體現了中國程序猿的強大。希望看到這裏的你也能從中得到啓發,儘早解決您在項目當中遇到的問題。
喜歡此文,或是能幫助您解決實際問題的話,歡迎轉載,以便能幫助到更多的人,謝謝!