1.什么是会签?
在流程业务管理中,任务是通常都是由一个人去处理的,而多个人同时处理一个任务,这种任务我们称之为会签任务。这种业务需求很常见,如一个请款单,领导审批环节中,就需要多个部门领导签字。在流程业务中,我们可以把每个领导签字的环节都定义为任务,并且这个会签的人员是不固定的,若固定的我们可以通过Activiti的并行任务或串行任务来处理。会签的引入说明,无非就是为了流程流转至某一环节点,其审批的人员是动态的,并且需要根据会签审批的结果实现流程的不同流转。
2.中国特色的会签需求是什么?
会签需求主要有以下两方面:
- 会签的参与人员
- 会签审批的顺序
- 会签审批的结果
- 动态加签
以下我们就是围绕以上的需求进行扩展实现的
3.Activiti对于会签的实现
BPMN2的标准中并没有对以上这种情景提供完善的支持,因此要在Activiti中实现会签审批,我们需要结合Activiti提供的流程任务的多实例特性,进行一些必要的扩展,以支持我们的中国特色的会签需求。
会签任务也是一种人工任务,其在activiti的定义中,也是使用UserTask来定义,但在属性上我们需要对这个定义的类型进行特殊的配置,即为多任务实例类型(并行或串行)任何一种。另外需要定义会签的参与人员,再定义会签的完成条件(若不定义,表示其是所有参与人均完成后,流程才往下跳转)。
3.1.多实例的人工任务配置
通过在UserTask节点的属性上配置,如下所示:
其生成的BPMN的配置文件如下所示:
<multiInstanceLoopCharacteristics isSequential="false" activiti:collection="${counterSignService.getUsers(execution)}">
<completionCondition>${counterSignService.isComplete(execution)}</completionCondition>
</multiInstanceLoopCharacteristics>
</userTask>
【说明】:
- isSequential="false" 表示这是非串行会签,即为并行会签,如三个人参与会签,是三个人同时收到待办,任务实例是同时产生的。
- activiti:collection 表示是会签的参与人员集合,用户可以通过定义自身的服务类来获取
- completionCondition 表示是任务往下跳转的完成条件,返回true是,表示条件成立,流程会跳至下一审批环节。
我们就是围绕着这几点来实现中国式的流程会签的
3.2 会签任务的人员集合计算处理
我们在Spring容器中定义一个会签服务类(counterSignService)里面提供两个api接口,一个是获得任务的人员集合,另一个是判断当前任务是否已经完成了会签的计算,其参考代码如下所示:
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; } }
【说明】
以上的代码的人员计算有几个来源:
- 流程回退时获得原来参与的人员
- 直接从流程变量中获取
- 从界面中的提交变量取用户
- 从数据库中读取节点人员配置获得参与人员列表
会签的投票处理结果放至流程变量中去,供后续的分支条件来处理,我们把会签的配置设置管理如下:
我们提供了按票数、百分比的投票处理,同时允许有特权的用户的投票规则以支持灵活的会签结果运算。
实现了以上配置后,流程的会签就比较简单,具体的效果如下视频演示:
http://www.redxun.cn/vedio/taskSign.htm
演示效果如:
相关推荐
在 Activiti 流程设计中,会签任务允许多个参与者同时处理同一个任务。这通常通过 BPMN 2.0 的并行多实例(Parallel Multi-instance)概念来实现,用以模拟工作流中的“所有人必须同意”或“多数同意”的场景。`...
【标题】"Activiti多实例任务实现会签"是指在Activiti工作流引擎中,如何配置和使用多实例任务来实现会签功能。在企业级应用中,会签常常用于决策过程,要求多个参与者共同审批,只有当所有参与者都完成审批后,流程...
在用activiti的时候经常遇到取会签人员的问题,这个文档解决怎么获取会签人员。
在 Activiti-5.4 版本中,会签(Concurrent Signatures)是流程设计中的一个重要特性,允许多个参与者同时对同一任务进行处理,提高了工作效率。下面我们将详细探讨如何在Activiti-5.4中实现会签以及相关的技术细节...
【会签功能详解】 会签,作为一种协同决策的机制,是指多个参与者对同一项事务进行共同审查和签署,以达成共识。...在实际开发中,可以根据具体业务需求调整会签规则和流程逻辑,确保工作的顺利进行。
《Activiti7开发指南》是一本专注于Java领域流程自动化技术的专著,主要围绕Activiti7框架进行深入探讨。Activiti7是Alfresco Software公司推出的一个开源工作流引擎,它基于模型驱动的设计理念,旨在简化企业业务...
本指南将引导你如何搭建Activiti开发环境,并创建一个简单的Activiti项目。 首先,要搭建Activiti开发环境,你需要在Eclipse中安装Activiti插件。在Eclipse中点击“Add”,在Name选项中填写一个描述性名称,例如...
Jeecg集成activiti.docx Jeecg集成activiti是一个关于如何将activiti集成到Jeecg平台上的指南。下面是从给定的文件中提取的相关知识点: 1. activiti项目结构:activiti项目是一个Maven项目,包含了数据库文件,...
Activiti 是一个开源的工作流和业务自动化引擎,广泛应用于企业级流程管理。它提供了一整套数据结构来支持流程定义...此外,深入理解这些数据结构也有助于开发与Activiti集成的应用程序,实现更高效的企业流程自动化。
伪汇总审批,就是每一条流程都是独立的,这些独立的流程在走到某个节点的时候,这个节点的审批人可以一次性进行多个任务的审批
Activiti6是一款强大的工作流引擎,专为Java开发人员设计,尤其适合初学者。它提供了丰富的功能,包括流程定义、部署、执行以及监控等。本指南将深入介绍Activiti6的各项核心概念和操作。 1. **简介** - **执照**...
总的来说,Activiti为开发人员提供了一整套工作流自动化解决方案,通过合理设计和使用Activiti数据库中的表结构,可以有效地实现企业级流程自动化的需求。对于开发者而言,理解Activiti数据库表结构对于优化工作流程...
开发者可以通过继承或扩展Activiti7提供的类,或者使用提供的SPI(Service Provider Interface)机制,来定制自己的工作流行为。例如,可以自定义任务监听器、事件处理器或者实现特定的流程行为。 6. **Activiti7...
Activiti二次开发适配达梦数据库教程 Activiti是一款流行的开源业务流程管理系统,旨在帮助企业自动化业务流程、提高效率和降低成本。然而,Activiti默认情况下不支持达梦数据库,需要进行二次开发来适配达梦数据库...
通过阅读《_activiti7cloud_英文开发指南.pdf》,开发者可以深入了解如何在微服务环境中设计、部署和管理Activiti 7流程,以及如何利用其英文API进行集成和扩展。这份指南将提供详细的示例和最佳实践,帮助开发者...
在开发过程中,`activiti5.13`压缩包包含了一系列必要的JAR文件,这些文件是Activiti运行和功能实现的基础。以下是一些关键知识点: 1. **Activiti Engine JAR**:这是核心引擎库,包含了流程定义、执行、任务管理...
10. **插件和扩展**:Activiti 支持扩展,如表单引擎、规则引擎等,5.22版可能包括如何开发和使用这些插件的信息。 "activiti-5-22.pdf"这份文档将覆盖上述所有内容,并可能包含示例代码和实践指导,帮助开发者深入...
这个压缩包"Activiti开发Jar"显然是针对开发者设计的,包含了进行Activiti开发和测试所需的全部JAR库。在Java开发环境中,JAR(Java Archive)文件是用于封装多个类文件和其他资源的容器,便于分发和执行。 首先,...