jbpm的委托与移交功能的实现

简介:

  在我们的工作流系统里已经有工作移交的功能,工作移交常用于用户离职、异动、岗位变化、长期不在岗位等情况,将已经分配给该用户的工作移交给其他用户。移交的本质是更改正在审批中的任务的审批人。而委托和转交功能中的转交功能与移交相似,移交功能由管理员发起,而转交则是由当前审批人主动发起的改变审批的行为。在日常运维中,经常出现类似这样的场景,某一工作被错误的移交到我手里,或者是我现在已经不办理类似流程了,我想主动的将该流程转给他人办理,这就是转交。另外,用户请假、外出、出差或针对某些低级别的流程(按照流程规定需要高层审批,比如总监级的请假单、加班单会由越级领导高级副总裁审批,而高级副对类似的单子不愿意去审批)希望委托他人审批,这即是一种事前的委托,而转交或转办也被称为审批中的委托。

  在中国特色的工作流系统中,委托与转交功这样较常见。以下是我对委托与转交功能的基本定义:

1.首先在t_bpm_process_task中需增加委托人和转交人,同理将审批人改变为被委托人或被转交人;

2.该功能由该任务的审批人主动发起;

3.委托人数据(被委托人、委托时间)来源暂定为请假表的代理人及请假的开始、结束时间;

4.所有单据均可委托

5.转交是当前审批人将该任务的审批权移交他人;

6.被转交人和被委托人不允许再将任务转交或委托他人;

7.我的工作中新增两个标签,我转交和我委托,转交人或委托人可以通过这两个模块查看自己转交或委托的任务的办理情况

以上功能为必要功能;

另外,还可增加更丰富的委托设置数据,比如

1.被委托人可以设置自己不允许被委托

2.委托任务可以设置为是否允许转委托

3.允许被委托或被转交人在委托/转交人恢复工作后仍未办理结束的任务退回委托/转交人;

  在实际编码的过程中,对一些未清晰的需求进一步明确:

1.关于抢办的转交功能,抢办点击转交相当于jbpm4中候选功能的pick,即将抢办的任务先只归到自己名下,然后再进行转交,所以转交后暂定为只有被转交人有审批权限;

2.非OA任务不准转交;

3.改变审批人、加签等情况不自动转给被委托人;

4.抢办的暂无查看委托任务办理详情;

5.菜单表中需增加我转交我委托的菜单

  我们先看看实现转交和委托所需要的表结构,实际是任务表,增加了两个字段用于表示委托人和转交人,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
< resultMap  type = "com.fx.oa.module.bpm.workflow.api.shared.domain.ProcessTaskEntity"  id = "TaskResultMap" >
         < id   property = "id"  column = "id" />
         < result  property = "processExecutionId"  column = "processExecutionId"  jdbcType = "VARCHAR" />
         < result  property = "processDefineId"  column = "processDefineId"  jdbcType = "VARCHAR" />
         < result  property = "processDefineName"  column = "processDefineName"  jdbcType = "VARCHAR" />      
         < result  property = "activityName"  column = "activityName"  jdbcType = "VARCHAR" />
         < result  property = "finishTime"  column = "finishTime"  jdbcType = "TIMESTAMP" />
         < result  property = "timeConsuming"  column = "timeConsuming"  jdbcType = "BIGINT" />
         < result  property = "fromTaskId"  column = "fromTaskId"  jdbcType = "VARCHAR" />
         < result  property = "fromTaskName"  column = "fromTaskName"  jdbcType = "VARCHAR" />
         < result  property = "taskId"  column = "taskId"  jdbcType = "VARCHAR" />
         < result  property = "transTo"  column = "transTo"  jdbcType = "VARCHAR" />
         < result  property = "status"  column = "status"  jdbcType = "VARCHAR" />
         < result  property = "preProcessTaskId"  column = "preProcessTaskId"  jdbcType = "VARCHAR" />
         < result  property = "description"  column = "description"  jdbcType = "VARCHAR" />
         < result  property = "creatorName"  column = "creatorName"  jdbcType = "VARCHAR" />
         < result  property = "createTime"  column = "createTime"  jdbcType = "TIMESTAMP" />
         < result  property = "modifyTime"  column = "modifyTime"  jdbcType = "TIMESTAMP" />
         < result  property = "createUserCode"  column = "createUserCode"  jdbcType = "VARCHAR" />
         < result  property = "modifyUserCode"  column = "modifyUserCode"  jdbcType = "VARCHAR" />
         < result  property = "entrustedUserCode"  column = "entrustedUserCode"  jdbcType = "VARCHAR" />
         < result  property = "substituteUserCode"  column = "substiuteUserCode"  jdbcType = "VARCHAR" />         
     </ resultMap >

  我们先来看转交功能。对于前端页面我们直接使用单选的人员选择器,选中某用户后,点击确定后进入转交的处理,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/**
      * raid task to other people
      * @author chao.gao
      * @date 2015-3-11 上午11:39:04
      * @see com.gaochao.oa.module.bpm.workflow.api.server.service.IProcessActivityService#raidTo(com.gaochao.oa.module.bpm.workflow.api.shared.dto.FlowRunTime, com.gaochao.oa.module.bpm.workflow.api.shared.vo.TaskVo)
      * @param flowRunTime
      * @param taskVo
      */
     @Override
     public  boolean  raidTo(FlowRunTime flowRunTime, TaskVo task) {
         if (StringUtils.isNotEmpty(flowRunTime.getCurrentUser())){
             ProcessExecutionEntity processExecutionEntity = processExecutionService.queryProcessExecutionByPkValue(flowRunTime.getPkValue());
             
             List<Task> list = jbpmOperatorService.getTasksByProcessInstanceId(processExecutionEntity.getProcessInstanceId()); 
             processExecutionService.changePeople(flowRunTime);         
 
             MailSettingEntity mailSetting = mailSettingService.queryMailSetting();              
 
             Map<String, List<MailblackwhitelistEntity>> mailBlackWhiteListMap = mailblackwhitelistService.queryMailBlackWhiteList();
             //设置流程处理ID
             flowRunTime.setProcessExecutionId(processExecutionEntity.getId());
             
             for ( int  i= 0 ;i<list.size();i++){
                 Task task1 = list.get(i);
                 if (task1.getId().equals(task.getTaskId())){
 
                     task.setTaskId(task1.getId());
                     processTaskService.raidTo(task);
                     //设置任务Id
                     flowRunTime.setNextTaskId(task1.getId());
                     //经办人
                     final  String assign = flowRunTime.getCurrentUser();
                     jbpmOperatorService.assignTask(task1.getId(), assign);
                             
                     //发送邮件
                     EmailUtil.sendMail2Proxy(assign, flowRunTime, mailSetting, mailBlackWhiteListMap);
                 }
             }
         } else {
             throw  new  ProcessActivityException( "没有选择人员" );
         }
         
         return  true ;
         
     }

  在以上的代码中,共有三处引用,分别是jbpm分配任务、更新扩展任务列表和向转交的审批人发送邮件。我们着重看一下分配任务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
      * 分配任务
      * @author chao.gao
      * @date 2013-10-9 下午7:18:31
      * @param taskId 任务Id
      * @param userId 用户
      */
     public  void  assignTask(String taskId,String userId){
         UserEntity userEntity = userService.queryUserByUserCode(userId);
         if (userEntity ==  null ){
             throw  new  ProcessActivityException( "流程引擎运行错误:审批用户异常!" );
         }
         
         processEngine.getTaskService().assignTask(taskId,userId);
     }

  在更新扩展任务列表中,我们会将选中的用户用作当前任务的审批人,同时将转交人赋值为当前用户,即

1
substituteUserCode=OAUserContext.getUserCode;

  下面介绍一下委托,委托是一种事前的工作委托,即前一步用户在将工作下发时,即将任务下发给工作的代理人,这里需要配合授权模块。授权模块的核心字段是委托人工号、被委托人工号和委托单号。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
< resultMap  type = "com.fx.oa.module.auth.authorize.api.shared.domain.AuthorizeEntity"  id = "authorizeObject" >
              < id   property = "id"  column = "id" />
              < result  property = "staffId"  column = "staff_id" />
              < result  property = "staffName"  column = "staff_name" />
              < result  property = "authTime"  column = "auth_time" />
              < result  property = "pstaffId"  column = "p_staff_id" />
              < result  property = "pstaffName"  column = "p_staff_name" />
              < result  property = "authorizeStart"  column = "authorize_start" />
              < result  property = "authorizeEnd"  column = "authorize_end" />
              < result  property = "active"  column = "active" />
              < result  property = "failureTime"  column = "failure_time" />
              < result  property = "scopeKey"  column = "scope_key" />
              < result  property = "scopeName"  column = "scope_name" />
              < result  property = "remarks"  column = "remarks" />
              < result  property = "define1"  column = "define1" />
              < result  property = "define2"  column = "define2" />
              < result  property = "define3"  column = "define3" />
         </ resultMap >

  这种事前的委托体现在根据审批人设置规则筛选出审批人后,再根据审批人列表及授权设置将审批人及代理人显示给用户。

wKiom1UHfPuB4rmIAADxzfBN91A607.jpg

  后端的处理代码如下,这段代码主要实现这样一个功能,将审批人列表userList遍历看是否在授权表中存在活跃的授权,然后将被授权也就是被委托人拿出,组成一个新的list返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
      * get the final assignee based authorization
      * TODO(方法详细描述说明、方法参数的具体涵义)
      * @author chao.gao
      * @date 2015-3-11 下午4:10:15
      * @param userList
      * @return
      */
     private  List<UserEntity> filterByAuth(List<UserEntity> userList, String processDefineId) {
         List<UserEntity> userNewList =  new  ArrayList<UserEntity>();
         for (UserEntity user : userList){
             AuthorizeEntity authEntity = authorizeService.authorize(user.getStaffId(), processDefineId);
             if (authEntity ==  null )
                 userNewList.add(user);
             else  {
                 UserEntity user2 = userService.queryUserByStaffId(authEntity.getPstaffId());
                 if (user2 !=  null ){
                     UserEntity user3 =  new  UserEntity();
                     user3.setUserCode(user2.getUserCode());
                     user3.setUserName(user2.getUserName()+  "("  + user.getUserName() +  "的工作代理人)" );
                     user3.setDefine1(user.getUserCode());
                     userNewList.add(user3);
                 }
             }
         }
         return  userNewList;
     }

  前端我是这样处理的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
if (result.userList.length >0){
                 var  jbrArray = [];
                 var  hiddenWtr = [];
                 $.each(result.userList, function (i,r){
                     jbrArray += "<span class='input_checked'><input type='checkbox' class='check_box' name='jbr' checked='checked' value='" + r.userCode+ "'/></span>" +r.userName;
                     if (r.define1){
                         hiddenWtr +=  "<input name='wtr' value='"  +(r.userCode + ': ' + r.define1) + "'  type= 'hidden' />"
                     }
                     
                 });
                 if (jbrArray.length > 0){
                     $( "#jbr" ).append(jbrArray);
                     $( "#jbr" ).append(hiddenWtr);
                     if ($( "#jbr input[type='checkbox']" ).length == 1){
                         $( "#jbr input[type='checkbox']" ).first().attr( "checked" , "checked" )
                     }
                 }
             }
             ...
var  wtrArr =  new  Array();
         $( "input[name='jbr']:checked" ).each( function (index, element){
             var  userCode = $(element).val()
             if (userCode!= null &&userCode!= "" ){
                 jbrArr.push(userCode);
             }          
             flag++;
         });
         $( "input[name='wtr']" ).each( function (index, element){
             var  userCode = $(element).val()
             if (userCode!= null &&userCode!= "" ){
                 wtrArr.push(userCode);
             }          
         });

  初看下来这样写其实是有问题的,其实没有问题。虽然审批人列表长度可能大于1,在这种情况下,如非会签,我们可以选择一到多个审批人,在审批人的数组里我们只push了选中的审批人,但在委托人的数组里却未对是否选中进行甄别。但这并不影响我们的正确使用,接下来看:

1
2
3
4
5
6
7
8
  if (StringUtils.isNotEmpty(task.getAssignee())){
                   taskForm.setCreateUserCode(task.getAssignee());
                   Map<String, String> wtrMap = queryWtr(startFlowRunTime);
                   if (StringUtils.isNotEmpty(wtrMap.get(task.getAssignee()))){
                       taskForm.setEntrustedUserCode(wtrMap.get(task.getAssignee()));
                   }
                   this .processTaskService.save(taskForm);
               }

  jbpm的分配任务跟普通任务是一样的,因为我们通过授权的筛选已经将审批人替换为被委托人,又通过上述代码委托人作为扩展任务的trustedUserCode作为主动委托人供委托模块查询。我们再看一下queryWtr这个方法是如何处理我们前端传入的“被委托人:委托人”格式的委托人列表的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
     
      * get entrustedUserCode based wtrList whose style as a : b
      * @author chao.gao
      * @date 2015-3-13 上午8:56:15
      * @param startFlowRunTime
      * @return
      */
     private  Map<String, String> queryWtr(FlowRunTime startFlowRunTime) {
         Map<String, String> map =  new  HashMap<String, String>();
         List<String> wtrList = startFlowRunTime.getWtrList();
         if (!CollectionUtils.isEmpty(wtrList)){
             for (String wtr : wtrList){
                 if (wtr.contains( ":" )){
                     String[] wtrs = wtr.split( ":" );
                     if (wtrs.length ==  2 ){
                         map.put(wtrs[ 0 ], wtrs[ 1 ]);
                     }
                 }
             }
         }
         return  map;
     }

  最后是查询模块,委托和转交的查询语句是相似的,我们拿委托模块举例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
     <!-- 查询我委托工作信息集合  -->
     < select  id = "queryMyWtTask"  resultMap = "ExecutionResultMap"   parameterType = "com.fx.oa.module.bpm.workflow.api.shared.domain.ProcessExecutionEntity" >
         SELECT
             DISTINCT(Execution.id),
             Execution.code,
             Execution.userId,
             Execution.processDefineId,
             Execution.processInstanceId,
             Execution.status,
             Execution.tableName,
             Execution.pkValue,
             Execution.formDefineId,
             Execution.urgencyDegree,
             ProcessDefine.description,
             Execution.createTime,
             Execution.modifyTime,
             sysUser1.userName AS createUserCode,
             sysUser2.userName AS modifyUserCode,
             ProcessDefine.name,
             Execution.active,
             RejectWork.status as rejectStatus,
             Task.NAME_ as activityName,
             task3.activityName AS joinActivityName,
             Task.ASSIGNEE_ as currentHandler
         FROM T_BPM_PROCESS_DEFINE ProcessDefine,T_BPM_PROCESS_TASK TASK3,T_BPM_PROCESS_EXECUTION Execution
         LEFT JOIN T_BPM_REJECTWORK RejectWork
            ON Execution.pkValue = RejectWork.pkValue
         LEFT JOIN jbpm4_task task
             ON Execution.processInstanceId = task.EXECUTION_ID_
         LEFT JOIN T_SYS_USER sysUser1 
             ON Execution.createUserCode = sysUser1.userCode 
         LEFT JOIN T_SYS_USER sysUser2 
             ON Execution.modifyUserCode = sysUser2.userCode  
         WHERE 1 = 1
             AND Execution.ID = task3.processExecutionId
             AND task3.entrustedUserCode = #{createUserCode}
             AND Execution.processDefineId = ProcessDefine.ID              
         < choose >
             < when  test = "status == '11'" >
                <!-- AND rejectWork.status = '1' -->    AND Execution.status = '2' AND Execution.active != 'Y'
             </ when >
             < when  test = "status == '2'" >
                <!--  AND rejectWork.status != '1' -->  AND Execution.active != 'N'
             </ when >
             < otherwise
                 AND Execution.active != 'N'
             </ otherwise
         </ choose >         
             AND Execution.createUserCode != #{createUserCode}
         < if  test = "createUserName != null and createUserName !=''" >
             AND Execution.createUserCode = (SELECT userCode
                 FROM T_SYS_USER sysUser
                 WHERE sysUser.userName= #{createUserName} LIMIT 0,1)
         </ if >
         < if  test = "processDefineId != null and processDefineId !=''" >
             AND Execution.processDefineId =  #{processDefineId}  
         </ if >
         < if  test = "status != null and status !='' and status !='11'" >
             AND Execution.status = #{status}
         </ if >
         < if  test = "name != null and name !=''" >
             AND ProcessDefine.name LIKE '%' #{name} '%'  
         </ if >
         < if  test = "startCreateTime != null and startCreateTime !=''" >
             AND Execution.createTime  <![CDATA[>= #{startCreateTime} ]]>   
         </ if >
         < if  test = "endCreateTime != null and endCreateTime !=''" >
             AND Execution.createTime  <![CDATA[<= #{endCreateTime}]]>    
         </ if >
         GROUP BY Execution.id
         ORDER BY Execution.createTime DESC
     </ select >






     本文转自 gaochaojs 51CTO博客,原文链接:http://blog.51cto.com/jncumter/1621312,如需转载请自行联系原作者

相关文章
|
6月前
|
存储 数据库
13activiti - 流程管理定义(部署流程定义)
13activiti - 流程管理定义(部署流程定义)
35 0
|
6月前
22activiti - 流程管理定义(查询流程状态)
22activiti - 流程管理定义(查询流程状态)
52 0
|
6月前
|
XML 缓存 数据格式
12activiti - 流程管理定义(设计流程定义文档)
12activiti - 流程管理定义(设计流程定义文档)
31 0
|
6月前
15activiti - 流程管理定义(删除流程定义)
15activiti - 流程管理定义(删除流程定义)
54 0
|
6月前
|
数据库
19activiti - 流程管理定义(启动流程实例)
19activiti - 流程管理定义(启动流程实例)
25 0
|
6月前
14activiti - 流程管理定义(查看流程定义)
14activiti - 流程管理定义(查看流程定义)
32 0
|
6月前
17activiti - 流程管理定义(查询最新版本的流程定义)
17activiti - 流程管理定义(查询最新版本的流程定义)
19 0
|
9月前
|
XML Java 机器人
webservice接口调用OA方法
最近一直在做这样一个需求,在OA中写一个webservice接口,通过调用OA中更新的方法来同步上级主管,我们公司的OA系统是买的产品,一无所知的我就这样开启了无悔的探索寻求之路~
|
9月前
|
设计模式 存储 Java
Java版事件与委托实现自动创建工厂并热加载
Java版事件与委托实现自动创建工厂并热加载
Java版事件与委托实现自动创建工厂并热加载
|
C++
【.Net】使用委托实现被引用的项目向上级项目的消息传递事件
在实际项目过程中,经常可能遇到被引用的项目要向上传递消息,但是又不能通过方法进行返回等操作,这个时候委托就派上用场了。以下使用委托,来实现被引用的项目向上传递消息的小教程,欢迎各位大佬提供建议。1、新增控制台项目(一般在CS架构中会用的比较多,用于跨线程传递消息使用)。此处用一个控制台项目来模拟演示使用委托进行消息事件的传递教程。
75 0
【.Net】使用委托实现被引用的项目向上级项目的消息传递事件