应用场景:在企业或事业单位,经常需要把一个任务分派给多条线去处理,每条线可以由一个或多个步骤构成,多条线的任务完成后需要再汇总一起于某个任务上。如下例子为一个公文下发流程,这个流程就涉及到任务的两级分发。
图一 原流程定义图
图二 执行过程中流程图
以上黄色的代表任务分发,紫黄代表任务汇总。
解决方法一:
我们可以把多个任务线用子流程去实现也可以,这样在分发那里会产生多个子流程,子流程完成后,需要汇总。但若有多级分发与汇总,则需要子流程再嵌套子流程。
解决方法二:
把分发的任务线当作普通的任务来实现,该产生多少个任务可由分发任务决定,这些任务的名称是一样的,但任务实例id不一样,执行人不一样。
在jbpm4或Activiti5上,动态创建子流程及对子流程的处理上,相对要完成的工作多一些,主要是activity或jbpm4上没有提供这块 api。而动态创建任务在jbpm4或activiti5上也是没有提供的,只有activiti5上提供了一个 taskService.newTask,而该方法产生的新任务则跟流程定义无关,则表示该任务完成后,不能产生后续的任务。在此,我们先提供 activiti5的解决办法。Jbpm4的解决方法可以参照该方式实现,以下为解决方案的步骤:
1. 第二种解决方案的关键点在于如何动态创建任务,在这里,我们绕过activiti的API直接对activiti5表进行生成处理,让他生成我们需要流程数据。
如下所示:
- /**
- * 按任务Id,复制另一会签任务出来
- * @param orgTaskId
- * @param assignee
- * @return
- */
- public ProcessTask newTask(String orgTaskId,String assignee)
- {
- String newExecutionId=UniqueIdUtil.getNextId();
- String newTaskId=UniqueIdUtil.getNextId();
- TaskEntity taskEntity=getTask(orgTaskId);
- ExecutionEntity executionEntity=null;
- if(taskEntity.getExecution()!=null){
- executionEntity=taskEntity.getExecution();
- }else{
- executionEntity=getExecution(taskEntity.getExecutionId());
- }
- ProcessExecution newExecution=new ProcessExecution(executionEntity);
- newExecution.setId(newExecutionId);
- ProcessTask newTask=new ProcessTask(taskEntity);
- newTask.setId(newTaskId);
- newTask.setExecutionId(newExecutionId);
- newTask.setCreateTime(new Date());
- newTask.setAssignee(assignee);
- newTask.setOwner(assignee);
- ProcessTaskHistory newTaskHistory=new ProcessTaskHistory(taskEntity);
- newTaskHistory.setAssignee(assignee);
- newTaskHistory.setStartTime(new Date());
- newTaskHistory.setId(newTaskId);
- newTaskHistory.setOwner(assignee);
- executionDao.add(newExecution);
- taskDao.insertTask(newTask);
- taskHistoryDao.add(newTaskHistory);
- return newTask;
- }
说明:以上代码写在BpmService类里,关键是从原来的任务中复制一份新的数据出来,同时需要复制其Execution的记录以及执行历史的记录。
2. 需要记录分发与汇总的节点
所以在流程节点的设置上,我们提供了以下的配置实体。
- public class BpmNodeSet extends BaseModel
- {
- /**
- * 在线表单
- */
- public static Short FORM_TYPE_ONLINE=0;
- /**
- * URL表单
- */
- public static Short FORM_TYPE_URL=1;
- /**
- * 普通任务节点
- */
- public static Short NODE_TYPE_NORMAL=0;
- /**
- * 分发任务节点
- */
- public static Short NODE_TYPE_FORK=1;
- // setId
- protected Long setId;
- // 流程定义ID
- protected Long defId;
- // 节点名
- protected String nodeName;
- // Activiti流程定义ID
- protected String actDefId;
- // 节点ID
- protected String nodeId;
- // 表单类型(0:在线表单,1:URL表单)
- protected Short formType=-1;
- // 表单URL
- protected String formUrl;
- // 表单定义ID
- protected Long formDefId;
- // 表单名称
- protected String formDefName;
- /**
- * 任务类型:
- * 0=普通任务
- * 1=分发任务
- */
- protected Short nodeType;
- /**
- * 当任务类型=1时,可以指定汇总任务Key
- */
- protected String joinTaskKey;
- /**
- * 当任务类型=1时,指定的汇总任务名称
- */
- protected String joinTaskName;
- ...
- }
然后在任务的创建及完成的事件里加上监听若当前的任务为分发任务,则动态产生分发的任务。若为汇总任务,则只产生最后一个汇总,以免得由Activiti产生多个汇总任务。
在监听事件创建分发任务(TaskCreateListener.java类)
- BpmNodeSet bpmNodeSet=bpmNodeSetService.getByActDefIdNodeId(actDefId, nodeId);
- if(bpmNodeSet!=null && BpmNodeSet.NODE_TYPE_FORK.equals(bpmNodeSet.getNodeType())){//当前任务为分发任务
- Map<String,List<String>> nodeUserMap=taskUserAssignService.getNodeUserMap();
- //若当前的线程里包含了该任务对应的执行人员列表,则任务的分发用户来自于此
- if(nodeUserMap!=null && nodeUserMap.get(nodeId)!=null){
- List<String> userIds=nodeUserMap.get(nodeId);
- bpmService.newForkTasks((TaskEntity)delegateTask, userIds);
- //产生分发记录,以方便后续的任务汇总处理
- taskForkService.newTaskForks(delegateTask,bpmNodeSet.getJoinTaskName(), bpmNodeSet.getJoinTaskKey(), userIds.size());
- }else{
- ForkUser forkUser=taskUserAssignService.getForkUser();
- if(forkUser!=null){
- bpmService.newForkTasks((TaskEntity)delegateTask, forkUser.getForkUserIdsAsList());
- }
- }
- }
以上代码中,分发任务的创建对应的人员来自表单中指定的人员。
任务汇总时,我们需要记录完成的情况,若为汇总节点,我们会根据汇总完成的情况,进行处理,若汇总的任务尚没有全部完成,后续产生的汇总任务我们则采用删除策略,该方法定义在BpmService类中。
- /**
- * 检查及删除重复的汇总任务
- * @param processInstanceId
- */
- public void deleteRepeatJoinTask(String processInstanceId){
- List<TaskEntity> taskList=getTasks(processInstanceId);
- for(TaskEntity task:taskList){
- //判断后续的节点是否为汇总节点,若是,则需要检查是否需要产生后续的任务
- BpmNodeSet joinNodeSet=bpmNodeSetService.getByActDefIdJoinTaskKey(task.getProcessDefinitionId(), task.getTaskDefinitionKey());
- if(joinNodeSet!=null){
- TaskFork taskFork=taskForkService.getByInstIdJoinTaskKey(task.getProcessInstanceId(), task.getTaskDefinitionKey());
- if(taskFork!=null){
- if(taskFork.getFininshCount()<taskFork.getForkCount()-1){
- taskService.deleteTask(task.getId());
- //更新完成任务的个数
- taskFork.setFininshCount(taskFork.getFininshCount()+1);
- taskForkService.update(taskFork);
- }else{
- taskForkService.delById(taskFork.getTaskForkId());
- }
- }
- }
- }
- }
最终实现的效果可以如下所示:
在线演示链接如下:
http://www.jee-soft.cn/htsite/html/cpjfw/zxjc/bpmx3/2012/07/04/1341394649218.html