How to use a custom organizational structure in Activiti

1 Overview

      We know that activiti is a good process engine. It has its own personnel organization structure, but it is limited to the management of users and user groups. The task (UserTask) generated by the process involves the owner of the task (Owner), the task's Executor (assignee), as well as task candidates, candidate users, etc. In China's process business needs, there is no way to meet the current business needs only by looking for people in this area. As an example of a leave request process, the process is as follows:

1.png

【Explanation】: Among them, the superior supervisor and the leader of the department are related to the organizational structure of the sponsor. This search algorithm can be understood as the search processing of the reporting line. In addition, in the domestic process processing scheme, there are also some personnel search algorithms such as other businesses. Therefore, we generally need to use the organizational structure of our business to implement process processing.

2. Let the Activiti engine hook up its own organizational structure

To realize the integration related to the organizational structure in the process, we need to first understand which business requirements currently use the organizational structure requirements. Based on our previous implementation of a large number of domestic business processes, we summarize the following points :

  1. Assignment of task performers
  2. agent of the task
  3. task notification
  4. Permission to start the process

In Activiti's integration process between the process engine and the organizational structure, only the first item is related to the organizational structure, and the other aspects only need to be implemented through our own expansion table.

2.1 Assignment of handlers for tasks

2.1.1. The default handling of organizational hooks with people in activiti

 In Activiti, there are only the following tables related to the organization frame. We show its table structure as follows:

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 'Creator ID',
  `UPDATE_BY_` varchar(64) COLLATE utf8_bin DEFAULT NULL COMMENT '更新人ID',
  `UPDATE_TIME_` datetime DEFAULT NULL COMMENT 'Update time',
  `SOL_ID_` varchar(64) COLLATE utf8_bin DEFAULT NULL COMMENT 'Business solution 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;
 

[Description] Among them, the owner and assignee in act_ru_task are the owner and executor of the task, and act_ru_identitylink is the personnel association table of the task. In the fields, groupId is the user group ID, userId is the user ID, and taskId is the associated task ID. type is the type of user participating in the task.

If we want to simplify the personnel's participation algorithm for tasks, we can expand our participation table without needing the act_ru_identitylink table, but this table can currently meet our personnel computing needs for tasks.

2.1.2. Personnel assignment of tasks

How to grant personnel through activiti's native api? First of all, let's talk about the timing of granting. Activiti's task is generated when the state of the process jumps to a task node, and it will generate a record in the act_ru_task table. At this time, we need to define the personnel through the process when it is generated. Configure attributes, calculate the people and groups involved in the task based on your own organizational structure and business lookup (such as reporting lines), and assign tasks to these users. In addition, tasks are manually assigned and authorized.
Let's talk about the first one, personnel authorization is performed when the task is generated

Activiti provides a task creation event, so we can define a listener on this event. How to configure this listener, please refer to our other article

Activiti's event mechanism and its listening processing

Regarding the global event definition of activiti, we only need to define the following task creation listener (TaskCreateListener), and obtain the task entity object TaskEntity, and change the task executor and task owner through setAssignee and setOwner.

taskEntity.setAssignee(nodePath.getAssignee());
taskEntity.setOwner(userId);
taskEntity.addCandidateUsers(Arrays.asList(uIds));
taskEntity.addCandidateGroup(identityInfo.getIdentityInfoId());
 

Now let's talk about another: manual assignment of tasks when tasks are generated

This method needs to be implemented through the following api of taskService

2.png

2.2. Extend your own people search architecture

It is difficult to provide a set of general configuration rules for the personnel configuration of the nodes of the process to realize the user search, because we only set the attribute configuration of the config for the personnel search of the node, and the development user implements the corresponding definition classification according to these configurations, and implements the Find your own process.

The process is configured as follows:

3.png

We provide a general staffing classification so that we can display the following staffing classification list in the staffing of the process node:

4.png

 

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;

/**
 * Entity type classification service class
 * @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中,我们的界面如下,提供按时间、事项名称、状态等来查找我的待办列表,并且分页返回,其界面如下:

5.png

其查找的自定义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_ &gt; = # {createtime1 }
        </ if >
        < if test = "createtime2" >
            and CREATE_TIME_ &lt; = # {createtime2 }
        </ if >
        < if test = "orderByClause!=null" >
            ORDER BY ${orderByClause }
        </ if >
    </ select >

For more details on the integration of task personnel and other personnel, please refer to the online demo version:

http://www.redxun.cn:8020/saweb/index.do

Guess you like

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