You understand what is countersigned? Workflow application countersigned +

1. What is the countersign?

 

In business process management, the task is usually one person to deal with, but more than one person to handle a task, this mission we call countersigned task. This is very common business needs, such as a payment request, the leadership of approval procedures, we need leadership to sign a number of departments. In the business process, we can link the signature of every leader is defined for the task, and countersigned by the person is not fixed, fixed if we can be handled by a serial or parallel tasks Activiti task. Introducing countersigned described, nothing more than to the point of a link to the process flow, which is dynamic approval art, and require different processes to achieve the results of flow countersigned approval.

2. What countersigned Chinese characteristics is the demand?

 

Countersigned demand mainly in the following two aspects:

  1. Countersigned participants
  2. Countersigned the order of approval
  3. Countersigned results approval
  4. Dynamic endorsement

Below we realize that extended around the above requirements

3.Activiti for implementation countersigned

 

BPMN2 the standard and does not provide complete support for this scenario above, so to achieve countersigned for approval Activiti, we need to combine multi-instance process task Activiti provided some of the necessary scale to support our Chinese characteristics countersigned needs.
Countersigned task is also a human task, its definition in activiti, UserTask is used to define, but the properties we require special configuration of the type defined, is the multi-task instance type (parallel or serial) any one kind. Also need to define the participants countersigned, and then define the conditions to complete countersigned (if not defined, it said it is after all participating per capita is complete, the process was jump down).

Human tasks 3.1. Multi-instance configuration

 

By arranging on the property UserTask node, as follows:

1.png

BPMN it generates configuration file as follows:

 <userTask id=”sid-78A17A9B-1185-48AA-A1CA-611421251D52″ name=”经理会签”>
<multiInstanceLoopCharacteristics isSequential=”false” activiti:collection=”${counterSignService.getUsers(execution)}”>
<completionCondition>${counterSignService.isComplete(execution)}</completionCondition>
</multiInstanceLoopCharacteristics>
</userTask>

[Description]:

  1. isSequential = "false" indicates that this is a non serial countersigned, i.e. parallel countersigned, such as participation countersigned three, three people also receive Upcoming, task instances are generated simultaneously.
  2. activiti: collection represents a collection of participants countersigned, users can get the service by defining a class of its own
  3. completionCondition representation is the task of closing conditions to jump down, return is true, represents a condition is satisfied, the process will skip to the next approval procedures.

We are surrounded by Chinese-style process to achieve these points countersigned

Staff 3.2 countersigned tasks set calculation process

 

We define a Spring container countersigned service class (counterSignService) api which provides two interfaces, a person is to get a set of tasks, the other is to determine whether the current task has been completed countersigned calculated, its reference code as follows:

package com.redxun.bpm.core.service.sign;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import javax.annotation.Resource;

import org.activiti.engine.impl.pvm.delegate.ActivityExecution;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.redxun.bpm.activiti.util.ProcessHandleHelper;
import com.redxun.bpm.core.entity.BpmDestNode;
import com.redxun.bpm.core.entity.BpmRuPath;
import com.redxun.bpm.core.entity.BpmSignData;
import com.redxun.bpm.core.entity.IExecutionCmd;
import com.redxun.bpm.core.entity.ProcessMessage;
import com.redxun.bpm.core.entity.config.MultiTaskConfig;
import com.redxun.bpm.core.entity.config.TaskVotePrivConfig;
import com.redxun.bpm.core.entity.config.UserTaskConfig;
import com.redxun.bpm.core.identity.service.BpmIdentityCalService;
import com.redxun.bpm.core.manager.BpmNodeSetManager;
import com.redxun.bpm.core.manager.BpmSignDataManager;
import com.redxun.bpm.enums.TaskOptionType;
import com.redxun.org.api.model.IdentityInfo;
import com.redxun.sys.org.entity.OsGroup;
import com.redxun.sys.org.entity.OsRelType;
import com.redxun.sys.org.entity.OsUser;
import com.redxun.sys.org.manager.OsGroupManager;
import com.redxun.sys.org.manager.OsUserManager;

/**
 * 会签配置服务类
 * @author csx
 *
 */
public class CounterSignService {
@Resource
private BpmSignDataManager bpmSignDataManager;
@Resource
private BpmNodeSetManager bpmNodeSetManager;
@Resource
 BpmIdentityCalService bpmIdentityCalService;
@Resource
private OsGroupManager osGroupManager;
@Resource
private OsUserManager osUserManager;

private Log logger=LogFactory.getLog(CounterSignService.class);
/**
  * 获得会签任务中的人员计算集合
  * @param execution
  * @return
  */
public Set<String> getUsers(ActivityExecution execution){
  
  logger.debug("enter the CounterSignService ");
  
  Set<String> userIds=new LinkedHashSet<String>();
  String nodeId=execution.getActivity().getId();
  //1.回退处理通过
  BpmRuPath backRuPath=ProcessHandleHelper.getBackPath();
  if(backRuPath!=null && "YES".equals(backRuPath.getIsMultiple())){
   String uIds=backRuPath.getUserIds();
   userIds.addAll(Arrays.asList(uIds.split("[,]"))); 
   execution.setVariable("signUserIds_"+nodeId,uIds);
   return userIds;
  }

  //2.通过变量来判断是否第一次进入该方法
  String signUserIds=(String)execution.getVariable("signUserIds_"+nodeId);
  
  if(StringUtils.isNotEmpty(signUserIds)){
   String[]uIds=signUserIds.split("[,]");
   userIds.addAll(Arrays.asList(uIds));
   return userIds;
  }
  
  //3.从界面中的提交变量取用户
  IExecutionCmd nextCmd=ProcessHandleHelper.getProcessCmd();
  BpmDestNode bpmDestNode=nextCmd.getNodeUserMap().get(nodeId);

  if(bpmDestNode!=null && StringUtils.isNotEmpty(bpmDestNode.getUserIds())){
   //加至流程变量中,以使后续继续不需要从线程及数据库中获取
   execution.setVariable("signUserIds_"+nodeId,bpmDestNode.getUserIds());
   execution.setVariable("priority_"+nodeId,bpmDestNode.getPriority());
   execution.setVariable("expiretime_"+nodeId, bpmDestNode.getExpireTime());
   
   String[]uIds=bpmDestNode.getUserIds().split("[,]");
   userIds.addAll(Arrays.asList(uIds));
   return userIds;
  }
  
  //4.从数据库中读取节点人员配置获得参与人员列表
  Collection<IdentityInfo> idInfoList=bpmIdentityCalService.calNodeUsersOrGroups(execution.getProcessDefinitionId(), execution.getCurrentActivityId(),execution.getVariables());
  
  for(IdentityInfo identityInfo:idInfoList){
   if(IdentityInfo.IDENTIFY_TYPE_USER.equals(identityInfo.getIdentityType())){
    userIds.add(identityInfo.getIdentityInfoId());
   }else{
    List<OsUser> users= osUserManager.getByGroupIdRelTypeId(identityInfo.getIdentityInfoId(), OsRelType.REL_CAT_GROUP_USER_BELONG_ID);
    for(OsUser u:users){
     userIds.add(u.getUserId());
    }
   }
  }
  if(userIds.size()>0){
   StringBuffer sb=new StringBuffer();
   for(String uId:userIds){
    sb.append(uId).append(",");
   }
   if(sb.length()>0){
    sb.deleteCharAt(sb.length()-1);
   }
   execution.setVariable("signUserIds_"+nodeId,sb.toString());
  }else{
   String name=(String)execution.getActivity().getProperty("name");
   ProcessMessage msg=ProcessHandleHelper.getProcessMessage();
   msg.getErrorMsges().add("会签节点["+name+"]没有设置执行人员,请联系管理员!");
  }
   
  
  
  return userIds;
}

/**
  * 会签是否计算完成
  * @param execution
  * @return
  */
public boolean isComplete(ActivityExecution execution){
  //完成会签的次数
  Integer completeCounter=(Integer)execution.getVariable("nrOfCompletedInstances");
  //总循环次数
  Integer instanceOfNumbers=(Integer)execution.getVariable("nrOfInstances");
  
  String solId=(String)execution.getVariable("solId");
  String nodeId=execution.getActivity().getId();
  UserTaskConfig taskConfig=bpmNodeSetManager.getTaskConfig(solId, nodeId);
  
  //获得任务及其多实例的配置,则任务不进行任何投票的设置及处理,即需要所有投票完成后来才跳至下一步。
  if(taskConfig.getMultiTaskConfig()==null){
   return completeCounter==instanceOfNumbers;
  }

  //获得会签的数据
  List<BpmSignData> bpmSignDatas=bpmSignDataManager.getByInstIdNodeId(execution.getProcessInstanceId(), nodeId);
  MultiTaskConfig multiTask=taskConfig.getMultiTaskConfig();
  //通过票数
  int passCount=0;
  //反对票数
  int refuseCount=0;
  //弃权票数
  int abstainCount=0;
  
  for(BpmSignData data:bpmSignDatas){
   int calCount=1;
   //弃权不作票数统计
   if(TaskOptionType.ABSTAIN.name().equals(data.getVoteStatus())){
    abstainCount++;
    continue;
   }
   String userId=data.getUserId();
   //检查是否有特权的处理
   if(multiTask.getVotePrivConfigs().size()>0){
    //计算用户的用户组
    List<OsGroup> osGroups=osGroupManager.getBelongGroups(userId);
    
    for(TaskVotePrivConfig voteConfig:multiTask.getVotePrivConfigs()){
     //是否在特权里
     boolean isInPriv=false;
     //为用户类型
     if(TaskVotePrivConfig.USER.equals(voteConfig.getIdentityType()) 
       && voteConfig.getIdentityIds().contains(userId)){
      isInPriv=true;
     }else{//为用户组类型
      for(OsGroup osGroup:osGroups){
       if(voteConfig.getIdentityIds().contains(osGroup.getGroupId())){
        isInPriv=true;
        break;
       }
      }
     }
     //若找到特权,则计算其值
     if(isInPriv){
      calCount=voteConfig.getVoteNums();
      break;
     }
    }
   }
   //统计同意票数
   if(TaskOptionType.AGREE.name().equals(data.getVoteStatus())){
    passCount+=calCount;
   }else{//统计反对票数
    refuseCount+=calCount;
   }
  }
  
  logger.debug("==============================passCount:"+passCount
    +" refuseCount:" + refuseCount +" abstainCount:"+abstainCount);
  //是否可以跳出会签
  boolean isNext=false;
  String result=null;
  
  //按投票通过数进行计算
  if(MultiTaskConfig.VOTE_TYPE_PASS.equals(multiTask.getVoteResultType())){
   //计算是否通过
   //按投票数进行统计
   if(MultiTaskConfig.CAL_TYPE_NUMS.equals(multiTask.getCalType())){
    //代表通过
    if(passCount>=multiTask.getVoteValue()){
     isNext=true;
     result="PASS";
    }
   }else{//按百分比进行计算
    int resultPercent=new Double(passCount*100/(passCount+refuseCount+abstainCount)).intValue();
    //代表通过
    if(resultPercent>=multiTask.getVoteValue()){
     isNext=true;
     result="PASS";
    }
   }
  }else{//按投票反对数进行计算
   //计算是否通过
   //按投票数进行统计
   if(MultiTaskConfig.CAL_TYPE_NUMS.equals(multiTask.getCalType())){
    //代表通过
    if(refuseCount>=multiTask.getVoteValue()){
     isNext=true;
     result="REFUSE";
    }
   }else{//按百分比进行计算
    int resultPercent=new Double(refuseCount*100/(passCount+refuseCount+abstainCount)).intValue();
    //代表通过
    if(resultPercent>=multiTask.getVoteValue()){
     isNext=true;
     result="REFUSE";
    }
   }
  }
  
  if((MultiTaskConfig.HANDLE_TYPE_DIRECT.equals(multiTask.getHandleType())&& isNext)//直接处理
    || (MultiTaskConfig.HANDLE_TYPE_WAIT_TO.equals(multiTask.getHandleType()) && completeCounter==instanceOfNumbers)){//等待所有的处理完
   execution.setVariable("voteResult_"+nodeId, result);
   //删除该节点的会签数据
   for(BpmSignData data:bpmSignDatas){
    bpmSignDataManager.deleteObject(data);
   }
   return true;
  }
  
  return false;
  
}
}

 

[Description]
people above code is calculated from several sources:

  1. When people get involved in the original process fallback
  2. Directly from the process variables
  3. Taken from the user interface to submit a variable
  4. Who read from the database node configured to obtain a list of participants

The results of the voting process countersigned put to process variables to go for follow-up of the branch condition to deal with, we countersigned configuration settings management as follows:

We offer a number of votes according to the percentage of the voting process, while allowing privileged users voting rules to support flexible countersigned by the result of the operation.

Consultation to understand

QQ: 1950148199

Phone: 18620763495

Phone: 020-29026351


Released four original articles · won praise 2 · views 20000 +

Guess you like

Origin blog.csdn.net/wo1950149188/article/details/80455019