Activiti workflow countersign development design ideas

In process business management, tasks are usually handled by one person, and multiple people handle a task at the same time. We call this task a countersignature task. This kind of business requirement is also very common. For example, a payment request form needs to be signed by the leaders of multiple departments during the approval process of the leader. In the process business, we can define the link of each leader's signature as a task, but if so, one thing of this process business is fixed, that is, the signer is fixed. The task is signed by one leader and then transferred to another leader. Of course, multiple leaders can sign at the same time.

The traditional solution with process business can adopt the following approaches:

 
Serial countersign
Serial countersign
 
parallel countersign
 
 

 

The former is called serial countersignature in process business, that is, one leader signs and then another leader signs. The latter is called a parallel countersignature, which means that several leaders are issuing at the same time, and it is not clear who will sign first.

 

The above solutions cannot be met under two major business requirements. If the leader of the countersignature is not fixed, the approver of the previous task can choose at will before submitting it. The other is for the countersignature business. Approved by the leaders, that is, go straight down, and do not need all leaders to approve. In addition, in this case, it is also difficult to count the results of the final leaders' countersignature, that is, there is no way to know whether the opinions on the approval form are agree or veto. The above two business requirements are also very common daily requirements, but if we use fixed process nodes, we cannot achieve them. Here, multiple instances of Activiti nodes can be used for processing, and the above process can be simplified as follows:

 
Activiti countersign process design
 
What is a multitasking instance node? The analysis on Activiti5 is a dynamic multi-task node, which can dynamically generate tasks according to the number of incoming dynamic personnel. The number of generated tasks is not fixed, and can be countersigned in parallel or in series. Whether the countersign task needs to be executed in the end is constrained by the rules set by the countersignature. For example, we can routinely set up countersignature rules such as "one-vote approval", "one-vote veto", and "minority service majority". Therefore, we need to bind our design rules on the countersign node. The counter-signature rule setting interface is as follows:
 
Countersignature rule design
 
Through the countersignature design rules, the voting results of the final countersigners can be made clear. Its data structure is as follows:
Countersignature Rules Sheet
 
The definition of the countersignature task itself has been implemented by Activiti, but the number of dynamic personnel needs to be dynamically passed in
<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)。其获取会签的用户值来自两个方面,一个在界面中指定的会签人员,另一个在后台会签节点上配置的人员。
 
Countersigning staff settings
后台会签节点人员设置
 
Countersigning Person Setting 2
任务审批面上选择下一任务会签人员
 
<completeCondition>为完成会签的条件signComplete.isComplete(execution),可以在这里根据我们的会签规则及目前的会签情况,决定会签是否完成。其实现如下所示:
 
最终实现逻辑:
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;  
            //vote  
            if(percents>=bpmNodeSign.getVoteAmount()){  
                voteResult=new VoteResult(SIGN_RESULT_REFUSE, true);  
            }  
            // Voting has been completed  
            else if(completeCounter.equals(instanceOfNumbers)){  
                voteResult=new VoteResult(SIGN_RESULT_PASS, true);  
            }  
        }  
    }  
    //Vote by absolute number of votes  
    else{  
        //decide by the number of votes  
        if(BpmNodeSign.DECIDE_TYPE_PASS.equals(bpmNodeSign.getDecideType())){  
            //Vote yes votes are eligible  
            if(agreeVotesCounts>=bpmNodeSign.getVoteAmount()){  
                voteResult=new VoteResult(SIGN_RESULT_PASS, true);  
            }  
            // Voting has been completed  
            else if(completeCounter.equals(instanceOfNumbers)){  
                voteResult=new VoteResult(SIGN_RESULT_REFUSE, true);  
            }  
        }  
        //decide by the number of negative votes  
        else{  
            //vote  
            if(refuseVotesCounts>=bpmNodeSign.getVoteAmount()){  
                voteResult=new VoteResult(SIGN_RESULT_REFUSE, true);  
            }  
            // Voting has been completed  
            else if(completeCounter.equals(instanceOfNumbers)){  
                voteResult=new VoteResult(SIGN_RESULT_PASS, true);  
            }  
        }  
    }  
    return voteResult;  
}  
 

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=327058479&siteId=291194637