1.1什么是会签
即共同协商或审批。会签,又称会审,也就是流程中某个业务需要经过多人表决,并且根据表决意见的汇总结果,匹配设定的规则,决定流程的走向
1.2会签有多种类型
1.多人只发表意见,并决策;2.部分人决策、部分只发现意见;3.如有决策,决策规则有以下几类:i:一票通过;ii:一票否决;iii:计同意票数;iv:计同意票数占比等。
二、典型会签实现逻辑
2.1参与会签人均需发表意见,全部审批后进入下一节点;
2.2参与会签人可以进行同意不同意的决策 ,全部进行决策后进入下一节点由下一节点审批人人工统计票数决定会签是否通过
三、会签实现方案
jbpm并未直接提供会签机制,通过查阅资料以及前期对子任务及决策节点的理解,我们提出了一种基于子任务和决策的会签实现方案。
会签设置表结构如下:
<a href="http://s3.51cto.com/wyfs02/M02/59/9F/wKioL1TZ0CmwM6O5AAE9zWPX7Tc810.jpg" target="_blank"></a>
流程定义如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<code><</code><code>process</code> <code>xmlns</code><code>=</code><code>'http://jbpm.org/4.4/jpdl'</code><code>></code>
<code> </code><code><</code><code>start</code> <code>name</code><code>=</code><code>'start1'</code> <code>g</code><code>=</code><code>'195,109,48,48'</code><code>></code>
<code> </code><code><</code><code>transition</code> <code>name</code><code>=</code><code>'进入会签'</code> <code>g</code><code>=</code><code>'-25,-15'</code> <code>to</code><code>=</code><code>'会签节点'</code><code>/></code>
<code> </code><code></</code><code>start</code><code>></code>
<code> </code><code><</code><code>task</code> <code>g</code><code>=</code><code>'351.0909118652344,106.30681991577148,90,50'</code> <code>name</code><code>=</code><code>'会签节点'</code><code>></code>
<code> </code><code><</code><code>transition</code> <code>name</code><code>=</code><code>'进入决策'</code> <code>g</code><code>=</code><code>'-27,-11'</code> <code>to</code><code>=</code><code>'decision 1'</code><code>/></code>
<code> </code><code></</code><code>task</code><code>></code>
<code> </code><code><</code><code>decision</code> <code>g</code><code>=</code><code>'536.0909118652344,106.30681991577148,48,48'</code> <code>name</code><code>=</code><code>'decision 1'</code><code>></code>
<code> </code><code><</code><code>transition</code> <code>name</code><code>=</code><code>'决策路径1'</code> <code>g</code><code>=</code><code>'-61,-19'</code> <code>to</code><code>=</code><code>'end 1'</code><code>/></code>
<code> </code><code><</code><code>transition</code> <code>name</code><code>=</code><code>'决策路径2'</code> <code>g</code><code>=</code><code>'-47,45'</code> <code>to</code><code>=</code><code>'其他节点'</code><code>/></code>
<code> </code><code></</code><code>decision</code><code>></code>
<code> </code><code><</code><code>end</code> <code>g</code><code>=</code><code>'687.0909118652344,44.306819915771484,48,48'</code> <code>name</code><code>=</code><code>'end 1'</code><code>/></code>
<code> </code><code><</code><code>task</code> <code>g</code><code>=</code><code>'677.0909118652344,163.30681991577148,90,50'</code> <code>name</code><code>=</code><code>'其他节点'</code><code>></code>
<code> </code><code><</code><code>transition</code> <code>name</code><code>=</code><code>'正常结束'</code> <code>g</code><code>=</code><code>'-26,-21'</code> <code>to</code><code>=</code><code>'end 2'</code><code>/></code>
<code> </code><code><</code><code>end</code> <code>g</code><code>=</code><code>'840.0909118652344,163.30681991577148,48,48'</code> <code>name</code><code>=</code><code>'end 2'</code><code>/></code>
<code></</code><code>process</code><code>></code>
流程图如下:
如果工作流不是每一步关联不同表单,而采用同一表单根据步骤名称确定权限的话,需要特别注意子任务的任务名生成方法。子任务名而不是activityName将用作权限的确定。
18
19
20
21
22
23
24
25
26
27
28
29
30
<code>if</code><code>(!CollectionUtils.isEmpty(userIds) && !userIds.contains(</code><code>null</code><code>)) {</code>
<code> </code><code>if</code><code>(userIds.size() > </code><code>1</code><code>){</code>
<code> </code><code>String deploymentId = processDefineService.queryById(startFlowRunTime.getProcessDefineId()).getDeploymentId();</code>
<code> </code><code>if</code><code>(StringUtils.isNotEmpty(task.getId())){</code>
<code> </code><code>deploymentId = jbpmOperatorService.getProcessDefinitionByTaskId(task.getId()).getDeploymentId();</code>
<code> </code><code>}</code>
<code> </code><code>UserAssignEntity userAssign = userAssignService.queryUserAssignById(deploymentId, task.getActivityName(), UserAssignConstants.USERASSIGN_FLAG_ASSIGNEE,</code><code>""</code><code>);</code>
<code> </code><code>AssignsettingEntity assignsetting = </code><code>new</code> <code>AssignsettingEntity();</code>
<code> </code><code>assignsetting.setDeployId(deploymentId);</code>
<code> </code><code>assignsetting.setActivityName(task.getActivityName());</code>
<code> </code><code>List<AssignsettingEntity> assignsettingList = assignsettingService.queryAssignsettingList(assignsetting);</code>
<code> </code><code>//处理会签</code>
<code> </code><code>if</code><code>(userAssign != </code><code>null</code> <code>&& </code><code>"1"</code><code>.equals(userAssign.getIsSigned()) ){</code>
<code> </code><code>if</code><code>(CollectionUtils.isEmpty(assignsettingList)){</code>
<code> </code><code>jbpmOperatorService.createSubTask(task.getId(), userIds.toArray(</code><code>new</code> <code>String[userIds.size()]));</code>
<code> </code><code>}</code><code>else</code> <code>{</code>
<code> </code><code>//如需要通过子任务名确定表单权限</code>
<code> </code><code>try</code><code>{</code>
<code> </code><code>jbpmOperatorService.createAssignSubTask(task.getId(), assignsettingList);</code>
<code> </code><code>}</code><code>catch</code> <code>(Exception e) {</code>
<code> </code><code>throw</code> <code>new</code> <code>ProcessActivityException(e.getMessage());</code>
<code> </code><code>}</code>
<code> </code><code>}</code><code>else</code> <code>{</code>
<code> </code><code>//处理抢办</code>
<code> </code><code>for</code><code>(String userId : userIds){</code>
<code> </code><code>jbpmOperatorService.addTaskParticipatingUser(task.getId(),userId);</code>
<code> </code><code>}</code>
<code>}</code>
上面提到如需要通过会签子任务名确定表单权限,需特别注意createAssignSubTask方法:
31
<code>/**</code>
<code> </code><code>* </code>
<code> </code><code>* create sub task as assign task</code>
<code> </code><code>* @author chao.gao</code>
<code> </code><code>* @date 2015-2-10 上午10:09:58</code>
<code> </code><code>* @see com.gaochao.oa.module.bpm.workflow.api.server.service.IJbpmOperatorService#createAssignSubTask(java.lang.String, java.util.List)</code>
<code> </code><code>* @param id</code>
<code> </code><code>* @param assignsettingList</code>
<code> </code><code>* @throws Exception </code>
<code> </code><code>*/</code>
<code> </code><code>@Override</code>
<code> </code><code>public</code> <code>void</code> <code>createAssignSubTask(String parentTaskId, List<AssignsettingEntity> assignsettingList) </code><code>throws</code> <code>Exception{</code>
<code> </code><code>TaskServiceImpl taskServiceImpl = (TaskServiceImpl)processEngine.getTaskService();</code>
<code> </code>
<code> </code><code>Task parentTask = taskServiceImpl.getTask(parentTaskId);</code>
<code> </code><code>Map<String,Object> vars = </code><code>new</code> <code>HashMap<String,Object>();</code>
<code> </code><code>taskServiceImpl.setVariables(parentTaskId, vars);</code>
<code> </code><code>for</code><code>(AssignsettingEntity assignsetting : assignsettingList){</code>
<code> </code><code>TaskImpl task = (TaskImpl)taskServiceImpl.newTask(parentTaskId);</code>
<code> </code><code>task.setAssignee(assignsetting.getUserId());</code>
<code> </code><code>task.setName(parentTask.getName() + </code><code>"-"</code> <code>+ assignsetting.getTaskName());</code>
<code> </code><code>task.setActivityName(parentTask.getName());</code>
<code> </code><code>task.setProcessInstance(getTaskById(parentTaskId).getExecution());</code>
<code> </code><code>task.setDescription(parentTask.getDescription());</code>
<code> </code><code>taskServiceImpl.saveTask(task);</code>
在jbpm4_task表中生成的子任务列表如下,其中子任务通过SUPERTASK_字段与父任务关联起来:
子任务的办理:在办理子任务时首先获得其父任务的子任务列表,判断列表长度,如长度大于1,则只需要关闭本子任务;如子任务列表长度=1,说明目前仅有本子任务未办理,则将本任务及父任务同时关闭。
<code> </code><code>* 处理父子任务,会签任务,完成任务</code>
<code> </code><code>* @date 2014-4-3 下午2:55:57</code>
<code> </code><code>* @param parentTask</code>
<code> </code><code>* @param subTask</code>
<code> </code><code>* @param signalName</code>
<code> </code><code>* @param variables</code>
<code> </code><code>* @param opinion</code>
<code> </code><code>private</code> <code>void</code> <code>acch(TaskImpl parentTask, TaskImpl subTask, String signalName, Map<String,Object> variables, String opinion) {</code>
<code> </code><code>int</code> <code>subTasksSize = parentTask.getSubTasks().size();</code>
<code> </code><code>jbpmOperatorService.evict(subTask);</code>
<code> </code><code>jbpmOperatorService.evict(parentTask);</code>
<code> </code><code>if</code><code>(subTasksSize > </code><code>1</code><code>){</code><code>//如当前父任务的子任务列表大于1,直接完成任务审批</code>
<code> </code><code>jbpmOperatorService.completeTask(subTask.getId());</code>
<code> </code><code>}</code><code>else</code><code>{</code>
<code> </code><code>jbpmOperatorService.completeTask(subTask.getId());</code><code>//先完成子任务</code>
<code> </code><code>jbpmOperatorService.completeTask(parentTask.getId());</code><code>//关闭父任务</code>
<code> </code><code>//更新父任务状态</code>
<code> </code><code>ProcessTaskEntity pT = processTaskService.queryByTaskId(parentTask.getId());</code><code>//更新父任务状态</code>
<code> </code><code>if</code><code>(pT != </code><code>null</code><code>){</code>
<code> </code><code>pT.setStatus(</code><code>"1"</code><code>);</code>
<code> </code><code>}</code>
<code> </code><code>processTaskService.update(pT);</code>
会签是否通过的决策:
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<code>/** </code>
<code> </code><code>* 流程决策</code>
<code> </code><code>* @author chao.gao</code>
<code> </code><code>* @date 2014-2-13 上午10:01:36</code>
<code> </code><code>* @see org.jbpm.api.jpdl.DecisionHandler#decide(org.jbpm.api.model.OpenExecution)</code>
<code> </code><code>* @param arg0</code>
<code> </code><code>* @return</code>
<code> </code><code>*/</code>
<code>@Override</code>
<code>public</code> <code>String decide(OpenExecution openExecution) {</code>
<code> </code>
<code> </code><code>ProcessEngine processEngine = (ProcessEngine) SpringContextUtil.getApplicationContext().getBean(</code><code>"processEngine"</code><code>);</code>
<code> </code><code>IDecisionRuleService decisionRuleService= (IDecisionRuleService)SpringContextUtil.getApplicationContext().getBean(</code><code>"decisionRuleService"</code><code>);</code>
<code> </code><code>String processDefinitionId = openExecution.getProcessDefinitionId();</code>
<code> </code><code>ProcessDefinition processDefinition = processEngine.getRepositoryService()</code>
<code> </code><code>.createProcessDefinitionQuery()</code>
<code> </code><code>.processDefinitionId(processDefinitionId)</code>
<code> </code><code>.uniqueResult();</code>
<code> </code><code>String deploymentId = processDefinition.getDeploymentId();</code>
<code> </code><code>Activity curActivity = openExecution.getActivity();</code>
<code> </code><code>DecisionRuleEntity decisionRule = decisionRuleService.queryByDeployIdAndActivityName(deploymentId, curActivity.getName());</code>
<code> </code><code>if</code><code>(decisionRule!=</code><code>null</code><code>){</code>
<code> </code><code>Interpreter it = </code><code>new</code> <code>Interpreter();</code>
<code> </code><code>try</code><code>{</code>
<code> </code><code>Map<String, ?> vars = openExecution.getVariables();</code>
<code> </code><code>Iterator<?> iterator = vars.entrySet().iterator();</code>
<code> </code><code>while</code> <code>(iterator.hasNext()) {</code>
<code> </code><code>Map.Entry entry = (Entry) iterator.next();</code>
<code> </code><code>String key = (String)entry.getKey();</code>
<code> </code><code>Object val = entry.getValue();</code>
<code> </code><code>it.set(key.replace(</code><code>"."</code><code>, </code><code>"_"</code><code>), val);</code>
<code> </code><code>it.set(</code><code>"execution"</code><code>, openExecution);</code>
<code> </code><code>it.eval(decisionRule.getRuleExpression().replace(</code><code>"'"</code><code>, </code><code>""</code><code>));</code>
<code> </code><code>String tran = (String)it.get(</code><code>"tranTo"</code><code>);</code>
<code> </code><code>return</code> <code>tran;</code>
<code> </code><code>}</code><code>catch</code> <code>(Exception e) {</code>
<code> </code><code>//e.printStackTrace();</code>
<code> </code><code>throw</code> <code>new</code> <code>DecisionRuleException(</code><code>"条件判断表达式错误"</code><code>);</code>
<code> </code>
<code> </code><code>String defaultTran = </code><code>""</code><code>;</code>
<code> </code><code>List outs = curActivity.getOutgoingTransitions();</code>
<code> </code><code>if</code> <code>(outs.size() > </code><code>0</code><code>) {</code>
<code> </code><code>defaultTran = ((Transition)outs.get(</code><code>0</code><code>)).getName();</code>
<code> </code><code>}</code>
<code> </code><code>return</code> <code>defaultTran;</code>
通过以上逻辑,我们可以得到类似会签的效果,另外通过分配子任务方法我们也可以方便的进行加签(生成子任务)、减签(销毁子任务)。
四、其他会签方案
4.1 fork-join
<a href="http://s3.51cto.com/wyfs02/M00/59/9F/wKioL1TZ1A6RHI8zAADXIHZ3glI143.jpg" target="_blank"></a>
流程定义:
<code> </code><code><</code><code>start</code> <code>name</code><code>=</code><code>'start1'</code> <code>g</code><code>=</code><code>'250,100,48,48'</code><code>></code>
<code> </code><code><</code><code>transition</code> <code>name</code><code>=</code><code>'进行会签'</code> <code>g</code><code>=</code><code>'-28,-8'</code> <code>to</code><code>=</code><code>'fork 1'</code><code>/></code>
<code> </code><code><</code><code>fork</code> <code>g</code><code>=</code><code>'384.0909118652344,103.30681991577148,48,48'</code> <code>name</code><code>=</code><code>'fork 1'</code><code>></code>
<code> </code><code><</code><code>transition</code> <code>name</code><code>=</code><code>'会签分支1'</code> <code>g</code><code>=</code><code>'-68,-14'</code> <code>to</code><code>=</code><code>'task 1'</code><code>/></code>
<code> </code><code><</code><code>transition</code> <code>name</code><code>=</code><code>'会签分支2'</code> <code>g</code><code>=</code><code>'-67,31'</code> <code>to</code><code>=</code><code>'task 2'</code><code>/></code>
<code> </code><code></</code><code>fork</code><code>></code>
<code> </code><code><</code><code>task</code> <code>g</code><code>=</code><code>'486.0909118652344,35.306819915771484,90,50'</code> <code>name</code><code>=</code><code>'task 1'</code><code>></code>
<code> </code><code><</code><code>transition</code> <code>name</code><code>=</code><code>'会签汇聚1'</code> <code>to</code><code>=</code><code>'join 1'</code><code>/></code>
<code> </code><code><</code><code>task</code> <code>g</code><code>=</code><code>'494.0909118652344,169.30681991577148,90,50'</code> <code>name</code><code>=</code><code>'task 2'</code><code>></code>
<code> </code><code><</code><code>transition</code> <code>name</code><code>=</code><code>'会签汇聚1'</code> <code>g</code><code>=</code><code>'5,34'</code> <code>to</code><code>=</code><code>'join 1'</code><code>/></code>
<code> </code><code><</code><code>join</code> <code>g</code><code>=</code><code>'635.0909118652344,96.30681991577148,48,48'</code> <code>name</code><code>=</code><code>'join 1'</code><code>></code>
<code> </code><code><</code><code>transition</code> <code>name</code><code>=</code><code>'会签结束'</code> <code>g</code><code>=</code><code>'-27,-16'</code> <code>to</code><code>=</code><code>'end 1'</code><code>/></code>
<code> </code><code></</code><code>join</code><code>></code>
<code> </code><code><</code><code>end</code> <code>g</code><code>=</code><code>'771.0909118652344,96.30681991577148,48,48'</code> <code>name</code><code>=</code><code>'end 1'</code><code>/></code>
fork-join的join节点具有multiplicity属性,通过该属性标记是全部task完成\仅有一个task完成进入join,或者是几个task完成才进入join,这也是fork-join可以用来实现会签的原因。
4.2 for-each(动态分支)
流程图:
<a href="http://s3.51cto.com/wyfs02/M01/59/A3/wKiom1TZ1aGCR7sKAACG6-e4Qho926.jpg" target="_blank"></a>
流程定义:
<code> </code><code><</code><code>transition</code> <code>name</code><code>=</code><code>'进入会签'</code> <code>g</code><code>=</code><code>'-26,-18'</code> <code>to</code><code>=</code><code>'foreach 1'</code><code>/></code>
<code> </code><code><</code><code>foreach</code> <code>g</code><code>=</code><code>'357.0909118652344,99.30681991577148,48,48'</code> <code>name</code><code>=</code><code>'foreach 1'</code><code>></code>
<code> </code><code><</code><code>transition</code> <code>name</code><code>=</code><code>'进入会签任务'</code> <code>g</code><code>=</code><code>'-39,-17'</code> <code>to</code><code>=</code><code>'会签任务'</code><code>/></code>
<code> </code><code></</code><code>foreach</code><code>></code>
<code> </code><code><</code><code>task</code> <code>g</code><code>=</code><code>'512.0909118652344,98.30681991577148,90,50'</code> <code>name</code><code>=</code><code>'会签任务'</code><code>></code>
<code> </code><code><</code><code>transition</code> <code>name</code><code>=</code><code>'会签决策'</code> <code>g</code><code>=</code><code>'-23,-13'</code> <code>to</code><code>=</code><code>'join 1'</code><code>/></code>
<code> </code><code><</code><code>join</code> <code>g</code><code>=</code><code>'680.0909118652344,97.30681991577148,48,48'</code> <code>name</code><code>=</code><code>'join 1'</code><code>></code>
<code> </code><code><</code><code>transition</code> <code>name</code><code>=</code><code>'结束'</code> <code>g</code><code>=</code><code>'-18,-17'</code> <code>to</code><code>=</code><code>'end 1'</code><code>/></code>
<code> </code><code><</code><code>end</code> <code>g</code><code>=</code><code>'798.0909118652344,98.30681991577148,48,48'</code> <code>name</code><code>=</code><code>'end 1'</code><code>/></code>
foreach(动态分支)属于jbpm的高级应用,其能够实现会签的原理与fork-join相近,fork-join是fork之后生成多个分支,不同分支各是不同节点,最多分支量是提前确定的;而foreach则是通过设置其in\var等属性动态生成分支,其最多分支量可以进行设置。分支生成以后的会签工作与fork-join类似。
4.3 custom节点
我们看一下其定义:
<code><</code><code>custom</code> <code>class</code><code>=</code><code>"sofocus.bpm.countersign.CountersignActivity"</code> <code>g</code><code>=</code><code>"259,218,92,52"</code>
<code> </code><code>name</code><code>=</code><code>"领导会签"</code><code>> </code>
<code> </code><code><</code><code>property</code> <code>name</code><code>=</code><code>"description"</code><code>> </code>
<code> </code><code><</code><code>string</code> <code>value</code><code>=</code><code>"#{equipment}设备订购会签"</code> <code>/> </code>
<code> </code><code></</code><code>property</code><code>> </code>
<code> </code><code><</code><code>property</code> <code>name</code><code>=</code><code>"form"</code><code>> </code>
<code> </code><code><</code><code>string</code>
<code> </code><code>value</code><code>=</code><code>"/buyEquipment.do?action=countersign&amp;taskId=:{TASK_ID}"</code> <code>/> </code>
<code> </code><code><</code><code>property</code> <code>name</code><code>=</code><code>"strategy"</code><code>> </code>
<code> </code><code><</code><code>object</code> <code>class</code><code>=</code><code>"sofocus.bpm.countersign.AllAgreeStrategy"</code> <code>/> </code>
<code> </code><code><</code><code>property</code> <code>name</code><code>=</code><code>"passTransiton"</code><code>> </code>
<code> </code><code><</code><code>string</code> <code>value</code><code>=</code><code>""</code> <code>/> </code>
<code> </code><code><</code><code>property</code> <code>name</code><code>=</code><code>"nopassTransiton"</code><code>> </code>
<code> </code><code><</code><code>on</code> <code>event</code><code>=</code><code>"start"</code><code>> </code>
<code> </code><code><</code><code>event-listener</code> <code>class</code><code>=</code><code>"test.com.yy.ah.SetBuyEquipmetnCounterSignUsers"</code> <code>/> </code>
<code> </code><code></</code><code>on</code><code>> </code>
<code> </code><code><</code><code>transition</code> <code>g</code><code>=</code><code>"-41,-8"</code> <code>name</code><code>=</code><code>"to 会签结果判断"</code> <code>to</code><code>=</code><code>"会签结果判断"</code> <code>/> </code>
<code> </code><code></</code><code>custom</code><code>></code>
在这个定义中主要有两个类,一个是CountersignActivity,一个是AllAgreeStragy,另外还有一个分配子任务的类。其实现本身不复杂,也是利用子任务,但却实现了最大的发挥自由度。感兴趣的同学可以通过链接进一步学习其思路和实现方案。
4.4 assignHandler
<code> </code><code><</code><code>task</code> <code>g</code><code>=</code><code>"182,379,92,52"</code> <code>name</code><code>=</code><code>"审批"</code><code>> </code>
<code> </code><code><</code><code>assignment-handler</code> <code>class</code><code>=</code><code>"org.jbpm.examples.attendance.commercialtrip.CommercialTripAssignment"</code><code>></</code><code>assignment-handler</code><code>> </code>
<code> </code><code><</code><code>transition</code> <code>g</code><code>=</code><code>"147,329:-47,-17"</code> <code>name</code><code>=</code><code>"审批不通过"</code> <code>to</code><code>=</code><code>"申请"</code><code>/> </code>
<code> </code><code><</code><code>transition</code> <code>name</code><code>=</code><code>"审批通过"</code> <code>to</code><code>=</code><code>"end1"</code> <code>g</code><code>=</code><code>"-47,-17"</code><code>/> </code>
<code> </code><code></</code><code>task</code><code>></code>
本文转自 gaochaojs 51CTO博客,原文链接:http://blog.51cto.com/jncumter/1613419,如需转载请自行联系原作者