在流程業務管理中,任務是通常都是由一個人去處理的,而多個人同時處理一個任務,這種任務我們稱之爲會籤任務。這種業務需求也很常見,如一個請款單,領導審批環節中,就需要多個部門領導簽字。在流程業務中,我們可以把每個領導簽字的環節都定義爲任務,但若這樣,這個流程業務有一點是固定的,就是籤批人是固定的。而任務是由一個領導簽完再到另一領導,當然也可以由多個領導同時簽字。
傳統的用流程業務來解決可以採用以下的做法:
並行會籤
串行會籤
前者在流程業務中,叫串行會籤,也即是由一個領導簽完再至另一領導籤。後者我們稱之爲並行會籤,表示幾個領導同時進行簽發,而不清楚最終是誰先簽。
以上的解決方式有兩大業務需求下是不能滿足的,若會籤的領導不是固定的,即可以由上一任務審批人提交前隨意進行選擇,另一種是對於會籤業務中,要求若其中一部分領導審批通過,即直接往下走,不需要全部領導進行審批。另外,對於這種情況下,統計最終領導會籤的結果也是比較困難的,即對審批單的意見是同意還是否決沒有辦法清楚。以上兩種業務需求也是很常見的日常需求,但我們若採用了固定的流程節點,則不能實現。在這裏,可以採用Activiti的節點多實例來處理,以上流程則可以簡化爲下:
何謂多任務實例節點?在Activiti5上的解析則爲動態的多任務節點,可以根據傳入的動態人員數進行動態生成任務。生成的任務數則不固定,可以進行並行會籤,也可以進行串行會籤。會籤任務最終是否需要往下執行,由會籤設置的規則來進行約束。如我們可以常規去設置“一票通過”、“一票否決”、“少數服務多數”等會籤規則。因此,我們需要在會籤節點上綁定我們的設計規則。會籤規則設置界面如下:
通過會籤設計規則,可以清楚最終會籤人員的投票結果。其數據結構如下所示:
會籤任務的定義本身已經由Activiti來實現了,但需要動態傳入動態的人員數
Java代碼 [url=][/url]
<userTask activiti:assignee="${assignee}" id="SignTask1" name="領導會籤">
<extensionElements>
<activiti:taskListener class="com.hotent.platform.service.bpm.listener.TaskSignCreateListener" event="create"/>
<activiti:taskListener class="com.hotent.platform.service.bpm.listener.TaskAssignListener" event="assignment"/>
<activiti:taskListener class="com.hotent.platform.service.bpm.listener.TaskCompleteListener" event="complete"/>
</extensionElements>
<multiInstanceLoopCharacteristics activiti:elementVariable="assignee" isSequential="false" activiti:collection="${taskUserAssignService.getSignUser(execution)}">
<completionCondition>${signComplete.isComplete(execution)}</completionCondition>
</multiInstanceLoopCharacteristics>
</userTask>
其中,isSequential爲true則爲串行會籤,若爲false則爲並行會籤,而activiti:collection可以來自我們Spring容器中的接口及方法,表示獲取會籤用戶集合,taskUserAssignService.getSignUser(execution)。其獲取會籤的用戶值來自兩個方面,一個在界面中指定的會籤人員,另一個在後臺會籤節點上配置的人員。
後臺會籤節點人員設置
<completeCondition>爲完成會籤的條件signComplete.isComplete(execution),可以在這裏根據我們的會籤規則及目前的會籤情況,決定會籤是否完成。其實現如下所示:
最終實現邏輯:
Java代碼 [url=][/url]
..
@Override
public boolean isComplete(ActivityExecution execution) {
logger.debug("entert the SignComplete isComplete method...");
String nodeId=execution.getActivity().getId();
String actInstId=execution.getProcessInstanceId();
ProcessDefinition processDefinition=bpmService.getProcessDefinitionByProcessInanceId(actInstId);
//取得會籤設置的規則
BpmNodeSign bpmNodeSign=bpmNodeSignService.getByDefIdAndNodeId(processDefinition.getId(), nodeId);
//完成會籤的次數
Integer completeCounter=(Integer)execution.getVariable("nrOfCompletedInstances");
//總循環次數
Integer instanceOfNumbers=(Integer)execution.getVariable("nrOfInstances");
//計算投票結果。
VoteResult voteResult=calcResult(bpmNodeSign, actInstId, nodeId, completeCounter,instanceOfNumbers);
String signResult=voteResult.getSignResult();
boolean isCompleted=voteResult.getIsComplete();
/**
* 會簽完成做的動作。
* 1.***會籤的流程變量。
* 2.將會籤數據更新爲完成。
* 3.設置會簽結果變量。
* 4.更新會籤節點結果。
* 5.清除會籤用戶。
*/
if(isCompleted){
//***會籤的變量。
//*** assignee,loopCounter變量。
bpmService.delLoopAssigneeVars(execution.getId());
logger.debug("set the sign result + " + signResult);
//將會籤數據更新爲完成。
taskSignDataService.batchUpdateCompleted(actInstId, nodeId);
//設置會籤的結果
execution.setVariable("signResult_" + nodeId , signResult);
//更新會籤節點的狀態。
Short status=TaskOpinion.STATUS_PASSED;
if(signResult.equals(SIGN_RESULT_REFUSE)){
status=TaskOpinion.STATUS_NOT_PASSED;
}
//更新會籤節點的狀態。
bpmProStatusDao.updStatus(actInstId, nodeId,status);
//清除會籤用戶。
taskUserAssignService.clearSignUser();
}
return isCompleted;
}
**
* 根據會籤規則計算投票結果。
* <pre>
* 1.如果會籤規則爲空,那麼需要所有的人同意通過會籤,否則不通過。
* 2.否則按照規則計算投票結果。
* </pre>
* @param bpmNodeSign 會籤規則
* @param actInstId 流程實例ID
* @param nodeId 節點id名稱
* @param completeCounter 循環次數
* @param instanceOfNumbers 總的會籤次數。
* @return
*/
private VoteResult calcResult(BpmNodeSign bpmNodeSign,String actInstId,String nodeId,Integer completeCounter,Integer instanceOfNumbers){
VoteResult voteResult=new VoteResult();
//沒有會籤實例
if(instanceOfNumbers==0){
return voteResult;
}
//投同意票數
Integer agreeVotesCounts=taskSignDataService.getAgreeVoteCount(actInstId, nodeId);
//沒有設置會籤規則
//(那麼得全部會籤通過才通過,否則不通過)
if(bpmNodeSign==null){
//還沒有完成可以退出。
if(completeCounter<instanceOfNumbers){
return voteResult;
}
else{
//完成了 (全部同意才通過)
if(agreeVotesCounts.equals(instanceOfNumbers)){
return new VoteResult(SIGN_RESULT_PASS,true);
}
else{
return new VoteResult(SIGN_RESULT_REFUSE,true);
}
}
}
//投反對票數
Integer refuseVotesCounts=taskSignDataService.getRefuseVoteCount(actInstId, nodeId);
//檢查投票是否完成
if(BpmNodeSign.VOTE_TYPE_PERCENT.equals(bpmNodeSign.getVoteType())){
float percents=0;
//按同意票數進行決定
if(BpmNodeSign.DECIDE_TYPE_PASS.equals(bpmNodeSign.getDecideType())){
percents=agreeVotesCounts/instanceOfNumbers;
//投票同意票符合條件
if(percents>=bpmNodeSign.getVoteAmount()){
voteResult=new VoteResult(SIGN_RESULT_PASS, true);
}
//投票已經全部完成
else if(completeCounter.equals(instanceOfNumbers)){
voteResult=new VoteResult(SIGN_RESULT_REFUSE, true);
}
}
//按反對票數進行決定
else{
percents=refuseVotesCounts/instanceOfNumbers;
//投票
if(percents>=bpmNodeSign.getVoteAmount()){
voteResult=new VoteResult(SIGN_RESULT_REFUSE, true);
}
//投票已經全部完成
else if(completeCounter.equals(instanceOfNumbers)){
voteResult=new VoteResult(SIGN_RESULT_PASS, true);
}
}
}
//按絕對票數投票
else{
//按同意票數進行決定
if(BpmNodeSign.DECIDE_TYPE_PASS.equals(bpmNodeSign.getDecideType())){
//投票同意票符合條件
if(agreeVotesCounts>=bpmNodeSign.getVoteAmount()){
voteResult=new VoteResult(SIGN_RESULT_PASS, true);
}
//投票已經全部完成
else if(completeCounter.equals(instanceOfNumbers)){
voteResult=new VoteResult(SIGN_RESULT_REFUSE, true);
}
}
//按反對票數進行決定
else{
//投票
if(refuseVotesCounts>=bpmNodeSign.getVoteAmount()){
voteResult=new VoteResult(SIGN_RESULT_REFUSE, true);
}
//投票已經全部完成
else if(completeCounter.equals(instanceOfNumbers)){
voteResult=new VoteResult(SIGN_RESULT_PASS, true);
}
}
}
return voteResult;
}
最終實現效果,可以在線訪問
http://www.jee-soft.cn:10080/bpm3/login.jsp
csx/1
可以通過訪問流程管理體驗效果
最終展示視頻可以看以下鏈接:
http://www.jee-soft.cn/htsite/html/cpjfw/zxjc/bpmx3/index.html