编辑
我们知道,activiti是一个不错的流程引擎,它有自身的人员组织架构,但仅限于用户、用户组的管理,流程产生的任务(UserTask),就涉及到任务的所属人(Owner),任务的执行人(assignee),还有任务的候选人、候选用户等。而在中国的流程业务需求里,仅靠这块的人员查找是没有办法满足目前的业务需求的。举个请假流程的例子,其流程如下所示:
【说明】:其中上级主管、及所在部门的领导都跟发起人所有的组织架构有关,这种查找算法可以理解为汇报线的查找处理。另外在国内的流程处理方案中,还存在一些如其他业务的人员查找算法。因此,我们一般都是需要使用我们的业务的组织架构来实现流程的处理。
编辑
要实现流程中的与组织架构有关的整合,我们需要先了解一下目前在哪些业务需求上使用了组织架构的需求,在我们以往的大量实施国内的业务流程的基础上,我们总结有以下几点:
任务的执行人员的分配
任务的代理
任务的通知
流程启动的权限
而Activiti在流程引擎与组织架构的整合过程中,只有第一项跟组织架构是有关的,其他的方面只需要通过我们自身的扩展表来实现即可。
2.1 任务的处理人分配
编辑
2.1.1. activiti中对与人员的组织挂接的默认处理
在Activiti中,跟组织架有关的只有以下几个表,我们把它的表结构展示如下:
CREATE TABLE `act_ru_task` (
`ID_` varchar(64) COLLATE utf8_bin NOT NULL DEFAULT '',
`REV_` int(11) DEFAULT NULL,
`EXECUTION_ID_` varchar(64) COLLATE utf8_bin DEFAULT NULL,
`PROC_INST_ID_` varchar(64) COLLATE utf8_bin DEFAULT NULL,
`PROC_DEF_ID_` varchar(64) COLLATE utf8_bin DEFAULT NULL,
`NAME_` varchar(255) COLLATE utf8_bin DEFAULT NULL,
`PARENT_TASK_ID_` varchar(64) COLLATE utf8_bin DEFAULT NULL,
`DESCRIPTION_` varchar(4000) COLLATE utf8_bin DEFAULT NULL,
`TASK_DEF_KEY_` varchar(255) COLLATE utf8_bin DEFAULT NULL,
`OWNER_` varchar(255) COLLATE utf8_bin DEFAULT NULL,
`ASSIGNEE_` varchar(255) COLLATE utf8_bin DEFAULT NULL,
`DELEGATION_` varchar(64) COLLATE utf8_bin DEFAULT NULL,
`PRIORITY_` int(11) DEFAULT NULL,
`CREATE_TIME_` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
`DUE_DATE_` datetime(3) DEFAULT NULL,
`CATEGORY_` varchar(255) COLLATE utf8_bin DEFAULT NULL,
`SUSPENSION_STATE_` int(11) DEFAULT NULL,
`TENANT_ID_` varchar(255) COLLATE utf8_bin DEFAULT '',
`FORM_KEY_` varchar(255) COLLATE utf8_bin DEFAULT NULL,
`CREATE_BY_` varchar(64) COLLATE utf8_bin DEFAULT NULL COMMENT '创建人ID',
`UPDATE_BY_` varchar(64) COLLATE utf8_bin DEFAULT NULL COMMENT '更新人ID',
`UPDATE_TIME_` datetime DEFAULT NULL COMMENT '更新时间',
`SOL_ID_` varchar(64) COLLATE utf8_bin DEFAULT NULL COMMENT '业务解决方案ID',
`AGENT_USER_ID_` varchar(64) COLLATE utf8_bin DEFAULT NULL COMMENT '代理人ID',
PRIMARY KEY (`ID_`),
KEY `ACT_IDX_TASK_CREATE` (`CREATE_TIME_`),
KEY `ACT_FK_TASK_EXE` (`EXECUTION_ID_`),
KEY `ACT_FK_TASK_PROCINST` (`PROC_INST_ID_`),
KEY `ACT_FK_TASK_PROCDEF` (`PROC_DEF_ID_`),
CONSTRAINT `ACT_FK_TASK_EXE` FOREIGN KEY (`EXECUTION_ID_`) REFERENCES `act_ru_execution` (`ID_`),
CONSTRAINT `ACT_FK_TASK_PROCDEF` FOREIGN KEY (`PROC_DEF_ID_`) REFERENCES `act_re_procdef` (`ID_`),
CONSTRAINT `ACT_FK_TASK_PROCINST` FOREIGN KEY (`PROC_INST_ID_`) REFERENCES `act_ru_execution` (`ID_`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
CREATE TABLE `act_ru_identitylink` (
`ID_` varchar(64) COLLATE utf8_bin NOT NULL DEFAULT '',
`REV_` int(11) DEFAULT NULL,
`GROUP_ID_` varchar(255) COLLATE utf8_bin DEFAULT NULL,
`TYPE_` varchar(255) COLLATE utf8_bin DEFAULT NULL,
`USER_ID_` varchar(255) COLLATE utf8_bin DEFAULT NULL,
`TASK_ID_` varchar(64) COLLATE utf8_bin DEFAULT NULL,
`PROC_INST_ID_` varchar(64) COLLATE utf8_bin DEFAULT NULL,
`PROC_DEF_ID_` varchar(64) COLLATE utf8_bin DEFAULT NULL,
PRIMARY KEY (`ID_`),
KEY `ACT_IDX_IDENT_LNK_USER` (`USER_ID_`),
KEY `ACT_IDX_IDENT_LNK_GROUP` (`GROUP_ID_`),
KEY `ACT_IDX_ATHRZ_PROCEDEF` (`PROC_DEF_ID_`),
KEY `ACT_FK_TSKASS_TASK` (`TASK_ID_`),
KEY `ACT_FK_IDL_PROCINST` (`PROC_INST_ID_`),
CONSTRAINT `ACT_FK_ATHRZ_PROCEDEF` FOREIGN KEY (`PROC_DEF_ID_`) REFERENCES `act_re_procdef` (`ID_`),
CONSTRAINT `ACT_FK_IDL_PROCINST` FOREIGN KEY (`PROC_INST_ID_`) REFERENCES `act_ru_execution` (`ID_`),
CONSTRAINT `ACT_FK_TSKASS_TASK` FOREIGN KEY (`TASK_ID_`) REFERENCES `act_ru_task` (`ID_`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;【说明】其中act_ru_task中的owner,assignee为任务的所属人与执行人,而act_ru_identitylink为任务的人员关联表,里面的字段,groupId为用户组Id,userId为用户Id,taskId为关联的任务Id,type为用户参与任务的类型。
若我们为了更加简化人员对任务的参与算法,可以不需要act_ru_identitylink表,进而扩展自己的参与表,不过这个表从目前来说是可以满足我们对任务的人员计算需求的。
2.1.2.任务的人员授予
如何通过activiti原生的api来实现人员的授予?首先我们来说授予的时机,activiti的任务产生是在流程的状态跳至某个任务节点时,其会产生一条记录至act_ru_task表中,这时我们需要在其产生的时候,通过流程定义的人员配置属性,结合自身的组织架构及业务查找(如汇报线)计算出参与该任务的人与组,从而把任务分配给这些用户。另外是任务手工进行分配授权。
我们来说第一种,任务产生时进行人员授权activiti提供了任务的创建事件,所以我们可以在它的这个事件上定义一个监听即可,如何配置这个监听,请参考我们另一个文章
Activiti的事件机制及其监听处理
关于activiti的全局事件定义,我们只需要定义以下任务创建监听器(TaskCreateListener),并且获得任务的实体对象TaskEntity,通过setAssignee及setOwner改变任务的执行人、任务的所属人即可。
taskEntity.setAssignee(nodePath.getAssignee());
taskEntity.setOwner(userId);taskEntity.addCandidateUsers(Arrays.asList(uIds));
taskEntity.addCandidateGroup(identityInfo.getIdentityInfoId());现在来说另一种:任务产生时进行任务手工分配
这种方式就需要通过taskService以下api实现即可
2.2.扩展自身的人员查找架构
编辑
流程的节点的人员配置很难提供一组通用的配置规则以实现用户的查找,因为,我们只为节点的人员查找设置config的属性配置,开发用户则根据这些配置实现对应的定义分类,并且实现自己的流程查找方式。
流程的配置方式如下所示:
我们提供一个总的人员计算分类,以使得我们在流程节点的人员配置中可以显示如下的人员配置分类列表:
package com.redxun.bpm.core.identity.service;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.InitializingBean;
/**
* 实体类型分类服务类
* @author csx
*
*/
public class IdentityTypeService implements InitializingBean{
//流程任务人员计算服务类映射
private Map<String, IdentityCalService> identityCalServicesMap=new LinkedHashMap<String, IdentityCalService>();
//程任务人员计算服务类
private List<IdentityCalService> identityCalServices=new ArrayList<IdentityCalService>();
@Override
public void afterPropertiesSet() throws Exception {
for(IdentityCalService service:identityCalServices){
identityCalServicesMap.put(service.getTypeKey(), service);
}
}
public List<IdentityCalService> getIdentityCalServices() {
return identityCalServices;
}
public void setIdentityCalServices(List<IdentityCalService> identityCalServices) {
this.identityCalServices = identityCalServices;
}
public Map<String, IdentityCalService> getIdentityCalServicesMap() {
return identityCalServicesMap;
}
}
同时以根据流程的节点配置,实现用户的信息计算,以获得人员配置的信息,由用户根据这个人员的配置实现人员的查找,其接口的定义如下:import java.util.Collection;
import com.redxun.org.api.model.IdentityInfo;
/**
* 任务人员计算服务接口类
* @author mansan
*
*/
public interface IdentityCalService {
//人员计算类型
public String getTypeKey();
//人员计算名称
public String getTypeName();
//人员计算描述
public String getDescp();
/**
* 计算节点返回的人员实体
* @param idCalConfig
* @return
*/
public Collection<IdentityInfo> calIdentities(IdentityCalConfig idCalConfig);
}其中用户组的配置及人员查找如下所示:
package com.redxun.bpm.core.identity.service;
/**
* 抽象的实体计算服务类
* @author csx
*
*/
public abstract class AbstractIdentityCalService implements IdentityCalService {
//分类Key
protected String typeKey;
//分类名称
protected String typeName;
//分类描述
protected String description;
//处理的类名
protected String handlerClass;
public String getTypeKey() {
return typeKey;
}
public void setTypeKey(String typeKey) {
this.typeKey = typeKey;
}
public String getTypeName() {
return typeName;
}
public void setTypeName(String typeName) {
this.typeName = typeName;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public String getDescp() {
return this.description;
}
}package com.redxun.bpm.core.identity.service.impl;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import com.redxun.bpm.core.identity.service.AbstractIdentityCalService;
import com.redxun.bpm.core.identity.service.IdentityCalConfig;
import com.redxun.core.constants.MBoolean;
import com.redxun.org.api.model.IdentityInfo;
import com.redxun.saweb.context.ContextUtil;
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.OsRelTypeManager;
import com.redxun.sys.org.manager.OsUserManager;
/**
* 用户组计算
* @author mansan
*
*/
public class GroupCalServiceImpl extends AbstractIdentityCalService{
@Resource
private OsGroupManager osGroupManager;
@Resource
private OsRelTypeManager osRelTypeManager;
@Resource
private OsUserManager osUserManager;
@Override
public Collection<IdentityInfo> calIdentities(IdentityCalConfig idCalConfig) {
OsRelType osRelType=osRelTypeManager.getBelongRelType();
//是否需要计算用户
boolean isCalUsers=MBoolean.YES.name().equals(idCalConfig.getIsCalUser());
List<IdentityInfo> identityList=new ArrayList<IdentityInfo>();
//获得流程的节点配置信息,并且根据节点获得用户或组
String jsonConfig=idCalConfig.getJsonConfig();
if(StringUtils.isNotEmpty(jsonConfig)){
String[] groupIds=jsonConfig.split("[,]");
for(String gId:groupIds){
if(isCalUsers){//计算用户
List<OsUser> users=osUserManager.getByGroupIdRelTypeId(gId, osRelType.getId());
identityList.addAll(users);
}else{//仅计算其用户组
OsGroup osGroup=osGroupManager.get(gId);
if(osGroup!=null){
identityList.add(osGroup);
}
}
}
}
return identityList;
}
}
2.3.自定义查找我的待办
编辑
虽然TaskService有提供按人员的查找任务API,但从我的个人来看,那些是不能满足我们的查找算法的,因此,很有必要自定义查找我的任务列表。查找无非是按Activiti的act_ru_task表来查,若结合了用户对应的用户组,还需要结合act_ru_identitylink来查找。这块看你的底层的数据库访问的采用是什么ORM框架,在JSAAS中,我们的界面如下,提供按时间、事项名称、状态等来查找我的待办列表,并且分页返回,其界面如下:
其查找的自定义Sql如下所示:
<!-- 按用户及条件查找待办列表 -->
<select id="getByUserIdRelTypeId" parameterType="java.util.Map" resultMap="BpmTask">
SELECT V.* FROM(
SELECT T.* FROM ACT_RU_TASK T WHERE T.ASSIGNEE_=#{userId}
UNION
SELECT T.* FROM ACT_RU_TASK T LEFT JOIN ACT_RU_IDENTITYLINK I ON T.ID_=I.TASK_ID_ WHERE I.USER_ID_=#{userId} AND I.TYPE_='candidate' AND T.ASSIGNEE_ IS NULL
UNION
SELECT T.* FROM ACT_RU_TASK T,ACT_RU_IDENTITYLINK I,OS_REL_INST R WHERE T.ASSIGNEE_ IS NULL AND T.ID_=I.TASK_ID_ AND I.GROUP_ID_=R.PARTY1_ and R.PARTY2_=#{userId} and I.TYPE_='candidate' AND R.REL_TYPE_ID_=#{relTypeId}
) V
WHERE 1=1
<if test="name!=null">
and NAME_ like #{name}
</if>
<if test="description!=null">
and DESCRIPTION_ like #{description}
</if>
<if test="createtime1">
and CREATE_TIME_ >= #{createtime1}
</if>
<if test="createtime2">
and CREATE_TIME_ <= #{createtime2}
</if>
<if test="orderByClause!=null">
ORDER BY ${orderByClause}
</if>
</select>
更多的关于任务的人员及其他人员的整合处理细节,请参考在线的演示版本:
http://www.redxun.cn:8020/saweb/index.do