http://man1900.iteye.com/blog/1607753
在流程業務管理中,任務是通常都是由一個人去處理的,而多個人同時處理一個任務,這種任務我們稱之為會簽任務。這種業務需求也很常見,如一個請款單,領導審批環節中,就需要多個部門領導簽字。在流程業務中,我們可以把每個領導簽字的環節都定義為任務,但若這樣,這個流程業務有一點是固定的,就是簽批人是固定的。而任務是由一個領導簽完再到另一領導,當然也可以由多個領導同時簽字。
傳統的用流程業務來解決可以采用以下的做法:

串行會簽

並行會簽
前者在流程業務中,叫串行會簽,也即是由一個領導簽完再至另一領導簽。后者我們稱之為並行會簽,表示幾個領導同時進行簽發,而不清楚最終是誰先簽。
以上的解決方式有兩大業務需求下是不能滿足的,若會簽的領導不是固定的,即可以由上一任務審批人提交前隨意進行選擇,另一種是對於會簽業務中,要求若其中一部分領導審批通過,即直接往下走,不需要全部領導進行審批。另外,對於這種情況下,統計最終領導會簽的結果也是比較困難的,即對審批單的意見是同意還是否決沒有辦法清楚。以上兩種業務需求也是很常見的日常需求,但我們若采用了固定的流程節點,則不能實現。在這里,可以采用Activiti的節點多實例來處理,以上流程則可以簡化為下:

何謂多任務實例節點?在Activiti5上的解析則為動態的多任務節點,可以根據傳入的動態人員數進行動態生成任務。生成的任務數則不固定,可以進行並行會簽,也可以進行串行會簽。會簽任務最終是否需要往下執行,由會簽設置的規則來進行約束。如我們可以常規去設置“一票通過”、“一票否決”、“少數服務多數”等會簽規則。因此,我們需要在會簽節點上綁定我們的設計規則。會簽規則設置界面如下:

通過會簽設計規則,可以清楚最終會簽人員的投票結果。其數據結構如下所示:

會簽任務的定義本身已經由Activiti來實現了,但需要動態傳入動態的人員數
- <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),可以在這里根據我們的會簽規則及目前的會簽情況,決定會簽是否完成。其實現如下所示:
最終實現邏輯:
- @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;
- }