jbpm会签的实现

一、会签的概念

1.1什么是会签

  即共同协商或审批。会签,又称会审,也就是流程中某个业务需要经过多人表决,并且根据表决意见的汇总结果,匹配设定的规则,决定流程的走向

1.2会签有多种类型

  1.多人只发表意见,并决策;2.部分人决策、部分只发现意见;3.如有决策,决策规则有以下几类:i:一票通过;ii:一票否决;iii:计同意票数;iv:计同意票数占比等。

二、典型会签实现逻辑

2.1参与会签人均需发表意见,全部审批后进入下一节点;

2.2参与会签人可以进行同意不同意的决策 ,全部进行决策后进入下一节点由下一节点审批人人工统计票数决定会签是否通过

三、会签实现方案

  jbpm并未直接提供会签机制,通过查阅资料以及前期对子任务及决策节点的理解,我们提出了一种基于子任务和决策的会签实现方案。

  会签设置表结构如下:

wKioL1TZ0CmwM6O5AAE9zWPX7Tc810.jpg

  流程定义如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
< process  xmlns = 'http://jbpm.org/4.4/jpdl' >
   < start  name = 'start1'  g = '195,109,48,48' >
     < transition  name = '进入会签'  g = '-25,-15'  to = '会签节点' />
   </ start >
   < task  g = '351.0909118652344,106.30681991577148,90,50'  name = '会签节点' >
     < transition  name = '进入决策'  g = '-27,-11'  to = 'decision 1' />
   </ task >
   < decision  g = '536.0909118652344,106.30681991577148,48,48'  name = 'decision 1' >
     < transition  name = '决策路径1'  g = '-61,-19'  to = 'end 1' />
     < transition  name = '决策路径2'  g = '-47,45'  to = '其他节点' />
   </ decision >
   < end  g = '687.0909118652344,44.306819915771484,48,48'  name = 'end 1' />
   < task  g = '677.0909118652344,163.30681991577148,90,50'  name = '其他节点' >
     < transition  name = '正常结束'  g = '-26,-21'  to = 'end 2' />
   </ task >
   < end  g = '840.0909118652344,163.30681991577148,48,48'  name = 'end 2' />
</ process >

  流程图如下:

  wKioL1TZzrDD3KWxAADEQVT5D6k979.jpg

  如果工作流不是每一步关联不同表单,而采用同一表单根据步骤名称确定权限的话,需要特别注意子任务的任务名生成方法。子任务名而不是activityName将用作权限的确定。

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
if (!CollectionUtils.isEmpty(userIds) && !userIds.contains( null )) {
     if (userIds.size() >  1 ){
         String deploymentId = processDefineService.queryById(startFlowRunTime.getProcessDefineId()).getDeploymentId();
         if (StringUtils.isNotEmpty(task.getId())){
             deploymentId = jbpmOperatorService.getProcessDefinitionByTaskId(task.getId()).getDeploymentId();
         }
         UserAssignEntity userAssign = userAssignService.queryUserAssignById(deploymentId, task.getActivityName(), UserAssignConstants.USERASSIGN_FLAG_ASSIGNEE, "" );
     AssignsettingEntity assignsetting =  new  AssignsettingEntity();
     assignsetting.setDeployId(deploymentId);
     assignsetting.setActivityName(task.getActivityName());
     List<AssignsettingEntity> assignsettingList = assignsettingService.queryAssignsettingList(assignsetting);
     //处理会签
     if (userAssign !=  null  &&  "1" .equals(userAssign.getIsSigned()) ){
         if (CollectionUtils.isEmpty(assignsettingList)){
             jbpmOperatorService.createSubTask(task.getId(), userIds.toArray( new  String[userIds.size()]));
         } else  {
             //如需要通过子任务名确定表单权限
             try {
                 jbpmOperatorService.createAssignSubTask(task.getId(), assignsettingList);
                 } catch  (Exception e) {
                     throw  new  ProcessActivityException(e.getMessage());
                 }
         }
     } else  {
         //处理抢办
         for (String userId : userIds){
             jbpmOperatorService.addTaskParticipatingUser(task.getId(),userId);
         }
     }
}

  上面提到如需要通过会签子任务名确定表单权限,需特别注意createAssignSubTask方法

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
/**
     
      * create sub task as assign task
      * @author chao.gao
      * @date 2015-2-10 上午10:09:58
      * @see com.gaochao.oa.module.bpm.workflow.api.server.service.IJbpmOperatorService#createAssignSubTask(java.lang.String, java.util.List)
      * @param id
      * @param assignsettingList
      * @throws Exception 
      */
     @Override
     public  void  createAssignSubTask(String parentTaskId, List<AssignsettingEntity> assignsettingList)  throws  Exception{
     TaskServiceImpl taskServiceImpl = (TaskServiceImpl)processEngine.getTaskService();
         
         Task parentTask = taskServiceImpl.getTask(parentTaskId);
 
         Map<String,Object> vars =  new  HashMap<String,Object>();
         
         
         taskServiceImpl.setVariables(parentTaskId, vars);
         
         for (AssignsettingEntity assignsetting : assignsettingList){
             TaskImpl task = (TaskImpl)taskServiceImpl.newTask(parentTaskId);
             task.setAssignee(assignsetting.getUserId());
             task.setName(parentTask.getName() +  "-"  + assignsetting.getTaskName());
             task.setActivityName(parentTask.getName());
             task.setProcessInstance(getTaskById(parentTaskId).getExecution());
             task.setDescription(parentTask.getDescription());
             taskServiceImpl.saveTask(task);
         }
     }

  在jbpm4_task表中生成的子任务列表如下,其中子任务通过SUPERTASK_字段与父任务关联起来:

wKiom1TZ0QXTEhPOAAMK4z2vWd0366.jpg  

  子任务的办理:在办理子任务时首先获得其父任务的子任务列表,判断列表长度,如长度大于1,则只需要关闭本子任务;如子任务列表长度=1,说明目前仅有本子任务未办理,则将本任务及父任务同时关闭。

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
/**
      * 处理父子任务,会签任务,完成任务
      * @author chao.gao
      * @date 2014-4-3 下午2:55:57
      * @param parentTask
      * @param subTask
      * @param signalName
      * @param variables
      * @param opinion
      */
     private  void  acch(TaskImpl parentTask, TaskImpl subTask, String signalName, Map<String,Object> variables, String opinion) {
         int  subTasksSize = parentTask.getSubTasks().size();
 
         jbpmOperatorService.evict(subTask);
         jbpmOperatorService.evict(parentTask);
         if (subTasksSize >  1 ){ //如当前父任务的子任务列表大于1,直接完成任务审批
             jbpmOperatorService.completeTask(subTask.getId());
         } else {
             jbpmOperatorService.completeTask(subTask.getId()); //先完成子任务
             jbpmOperatorService.completeTask(parentTask.getId()); //关闭父任务
             //更新父任务状态
             ProcessTaskEntity pT = processTaskService.queryByTaskId(parentTask.getId()); //更新父任务状态
             if (pT !=  null ){
                 pT.setStatus( "1" );
             }
             processTaskService.update(pT);
         }
     }

  会签是否通过的决策:

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
/** 
  * 流程决策
  * @author chao.gao
  * @date 2014-2-13 上午10:01:36
  * @see org.jbpm.api.jpdl.DecisionHandler#decide(org.jbpm.api.model.OpenExecution)
  * @param arg0
  * @return
  */
@Override
public  String decide(OpenExecution openExecution) {
     
     ProcessEngine processEngine = (ProcessEngine) SpringContextUtil.getApplicationContext().getBean( "processEngine" );
     
     IDecisionRuleService decisionRuleService= (IDecisionRuleService)SpringContextUtil.getApplicationContext().getBean( "decisionRuleService" );
     
     String processDefinitionId = openExecution.getProcessDefinitionId();
     ProcessDefinition processDefinition = processEngine.getRepositoryService()
             .createProcessDefinitionQuery()
             .processDefinitionId(processDefinitionId)
             .uniqueResult();
     
     String deploymentId = processDefinition.getDeploymentId();
     
     Activity curActivity = openExecution.getActivity();
     DecisionRuleEntity decisionRule = decisionRuleService.queryByDeployIdAndActivityName(deploymentId, curActivity.getName());
     if (decisionRule!= null ){
         Interpreter it =  new  Interpreter();
         try {
             Map<String, ?> vars = openExecution.getVariables();
             Iterator<?> iterator = vars.entrySet().iterator();
             while  (iterator.hasNext()) {
                 Map.Entry entry = (Entry) iterator.next();
                 String key = (String)entry.getKey();
                 Object val = entry.getValue();
                 it.set(key.replace( "." "_" ), val);
             }
              it.set( "execution" , openExecution);
              it.eval(decisionRule.getRuleExpression().replace( "'" "" ));
              String tran = (String)it.get( "tranTo" );
              return  tran;
         } catch  (Exception e) {
             //e.printStackTrace();
             throw  new  DecisionRuleException( "条件判断表达式错误" );
         }
             
     }
     
      String defaultTran =  "" ;
      List outs = curActivity.getOutgoingTransitions();
      if  (outs.size() >  0 ) {
          defaultTran = ((Transition)outs.get( 0 )).getName();
      }
      return  defaultTran;
}

  通过以上逻辑,我们可以得到类似会签的效果,另外通过分配子任务方法我们也可以方便的进行加签(生成子任务)、减签(销毁子任务)。

四、其他会签方案

4.1 fork-join

  流程图如下:

wKioL1TZ1A6RHI8zAADXIHZ3glI143.jpg


  流程定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
< process  xmlns = 'http://jbpm.org/4.4/jpdl' >
   < start  name = 'start1'  g = '250,100,48,48' >
     < transition  name = '进行会签'  g = '-28,-8'  to = 'fork 1' />
   </ start >
   < fork  g = '384.0909118652344,103.30681991577148,48,48'  name = 'fork 1' >
     < transition  name = '会签分支1'  g = '-68,-14'  to = 'task 1' />
     < transition  name = '会签分支2'  g = '-67,31'  to = 'task 2' />
   </ fork >
   < task  g = '486.0909118652344,35.306819915771484,90,50'  name = 'task 1' >
     < transition  name = '会签汇聚1'  to = 'join 1' />
   </ task >
   < task  g = '494.0909118652344,169.30681991577148,90,50'  name = 'task 2' >
     < transition  name = '会签汇聚1'  g = '5,34'  to = 'join 1' />
   </ task >
   < join  g = '635.0909118652344,96.30681991577148,48,48'  name = 'join 1' >
     < transition  name = '会签结束'  g = '-27,-16'  to = 'end 1' />
   </ join >
   < end  g = '771.0909118652344,96.30681991577148,48,48'  name = 'end 1' />
</ process >

  fork-join的join节点具有multiplicity属性,通过该属性标记是全部task完成\仅有一个task完成进入join,或者是几个task完成才进入join,这也是fork-join可以用来实现会签的原因。

4.2 for-each(动态分支)

  流程图:

wKiom1TZ1aGCR7sKAACG6-e4Qho926.jpg

       流程定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
< process  xmlns = 'http://jbpm.org/4.4/jpdl' >
   < start  name = 'start1'  g = '250,100,48,48' >
     < transition  name = '进入会签'  g = '-26,-18'  to = 'foreach 1' />
   </ start >
   < foreach  g = '357.0909118652344,99.30681991577148,48,48'  name = 'foreach 1' >
     < transition  name = '进入会签任务'  g = '-39,-17'  to = '会签任务' />
   </ foreach >
   < task  g = '512.0909118652344,98.30681991577148,90,50'  name = '会签任务' >
     < transition  name = '会签决策'  g = '-23,-13'  to = 'join 1' />
   </ task >
   < join  g = '680.0909118652344,97.30681991577148,48,48'  name = 'join 1' >
     < transition  name = '结束'  g = '-18,-17'  to = 'end 1' />
   </ join >
   < end  g = '798.0909118652344,98.30681991577148,48,48'  name = 'end 1' />
</ process >

  foreach(动态分支)属于jbpm的高级应用,其能够实现会签的原理与fork-join相近,fork-join是fork之后生成多个分支,不同分支各是不同节点,最多分支量是提前确定的;而foreach则是通过设置其in\var等属性动态生成分支,其最多分支量可以进行设置。分支生成以后的会签工作与fork-join类似。

4.3 custom节点

  custom也是jbpm的高级应用,这里有一个详细的例子介绍通过custom实现会签。通过custom即定制化,我们可以任意定制custom的属性、逻辑、规则,所以通过custom是较自由、但也较复杂的会签实现方案。通过会签的custom实现,我们其实也可以发现,custom就是jbpm提供给我们的扩展接口,不仅会签,其他的工作流动作也可以通过定制实现。

  我们看一下其定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
< custom  class = "sofocus.bpm.countersign.CountersignActivity"  g = "259,218,92,52"  
           name = "领导会签" >  
     < property  name = "description" >  
       < string  value = "#{equipment}设备订购会签"  />  
     </ property >  
     < property  name = "form" >  
       < string   
         value = "/buyEquipment.do?action=countersign&amp;taskId=:{TASK_ID}"  />  
     </ property >  
     < property  name = "strategy" >  
       < object  class = "sofocus.bpm.countersign.AllAgreeStrategy"  />  
     </ property >  
     < property  name = "passTransiton" >  
       < string  value = ""  />  
     </ property >  
     < property  name = "nopassTransiton" >  
       < string  value = ""  />  
     </ property >  
     < on  event = "start" >  
       < event-listener  class = "test.com.yy.ah.SetBuyEquipmetnCounterSignUsers"  />  
     </ on >  
     < transition  g = "-41,-8"  name = "to 会签结果判断"  to = "会签结果判断"  />  
   </ custom >

  在这个定义中主要有两个类,一个是CountersignActivity,一个是AllAgreeStragy,另外还有一个分配子任务的类。其实现本身不复杂,也是利用子任务,但却实现了最大的发挥自由度。感兴趣的同学可以通过链接进一步学习其思路和实现方案。

4.4 assignHandler

  通过assigHandler实现会签的思路也与我们的思路相似,即子任务,也不做赘述。定义如下:

1
2
3
4
5
  < task  g = "182,379,92,52"  name = "审批" >  
       < assignment-handler  class = "org.jbpm.examples.attendance.commercialtrip.CommercialTripAssignment" ></ assignment-handler >  
       < transition  g = "147,329:-47,-17"  name = "审批不通过"  to = "申请" />  
       < transition  name = "审批通过"  to = "end1"  g = "-47,-17" />  
  </ task >

  





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



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值