在企业或事业单位,经常需要把一个任务分派给多条线去处理,每条线可以由一个或多个步骤构成,多条线的任务完成后需要再汇总一起于某个任务上。最常见的应用例子就是总经理把一个任务分发下去给多个部门领导,每个部门领导再分发给多个主管,每个主管再分发给多个人处理,然后再汇总给每个主管,每个主管再汇总给部门领导,每个部门领导再汇总至总经理。这就是我们说的任务层层分发,再层层汇总的概念,我们统称为多级分发与汇总。
以下示意图为该流程在运行过程中的实例产生任务的示例,可以看到分发出来的任务是动态的,并且其后续的任务也会随着产生出来。
解决方法一:
我们可以把多个任务线用子流程去实现也可以,这样在分发那里会产生多个子流程,子流程完成后,需要汇总。但若有多级分发与汇总,则需要子流程再嵌套子流程。
解决方法二:
把分发的任务线当作普通的任务来实现,该产生多少个任务可由分发任务决定,这些任务的名称是一样的,但任务实例id不一样,执行人不一样。
在Jbpm4或Activiti5上,动态创建子流程及对子流程的处理上,相对要完成的工作多一些,主要是activity或jbpm4上没有提供这块 api。而动态创建任务在jbpm4或activiti5上也是没有提供的,只有activiti5上提供了一个 taskService.newTask,而该方法产生的新任务则跟流程定义无关,则表示该任务完成后,不能产生后续的任务。在此,我们先提供 activiti5的解决办法。Jbpm4的解决方法可以参照该方式实现,以下为解决方案的步骤:
以下我们来分析如按第二种方法来实现,首先要解决两个问题,一个是分发,另一个是汇总。
分发任务其实就是复制任务的实现,Activiti任务产生后,我们再基于该任务复制新的任务出来以实现任务分发的功能。实现代码如下所示:
/**
* 从一个任务中复制另一任务出来
* @param taskEntity
* @param uIds 用户Id或组织Id或角色Id或岗位ID
* @param userType 值来自TaskUser里的type值
*/
public void newForkTasks(TaskEntity taskEntity,Set<String>uIds,String userType){
//获取当前任务的分发的令牌, value as T_1 or T_1_2
String token=(String)taskEntity.getVariableLocal(TaskFork.TAKEN_VAR_NAME);
if(token==null) token=TaskFork.TAKEN_PRE;
Iterator<String> uIt=uIds.iterator();
int i=0;
while(uIt.hasNext()){
if(i++==0){//原activiti产生的流程仅需要进行人员授权
assignTask(taskEntity,uIt.next(),userType);
//加上分发令牌变量
taskEntity.setVariableLocal(TaskFork.TAKEN_VAR_NAME, token+ "_"+ i);
//同时改变其Execution,以防止当该线的任务进行汇总时,其exectionId及proc_inst_id_相同时,删除该任务引起关联级别的错误
changeTaskExecution(taskEntity);
}else{//扩展产生的流程任务,另需要加上审批意见的记录
ProcessTask processTask=newTask(taskEntity,uIt.next(),userType);
TaskEntity newTask=getTask(processTask.getId());
//加上新的任务至新任务列队,以方便后续的任务回退处理
TaskThreadService.addTask(newTask);
//加上分发令牌变量
taskService.setVariableLocal(processTask.getId(),TaskFork.TAKEN_VAR_NAME, token+ "_"+ i);
TaskOpinion taskOpinion=new TaskOpinion(processTask);
try{
taskOpinion.setOpinionId(UniqueIdUtil.genId());
taskOpinion.setTaskToken(token);
taskOpinionService.add(taskOpinion);
}catch(Exception ex){
logger.error(ex.getMessage());
}
}
}
}
在任务分发时,还包括了分发的令牌处理,令牌的作用是用于区别不同分发线上的任务,如上图,“主管2分发”退回至“部门领导分发 ”时,不会对主管3的任务产生影响,当主管1,主管2汇总任务时,仅产生一个“部门领导汇总”的任务。令牌的变化规则如下图所示:
T令牌格式为:T_层序号,分发的时候,不断加上层序号,如两层分发则令牌变化为T_层序号_层序号。令牌的保存目前是存于任务的变量中,以方便任务获取,作用域仅限于任务。任务的汇总就是需要把多个任务合并回一个任务,实现该目标的方法就是利用任务跳转时,检查其后面的任务节点是否为汇总节点,若是的话,还要看同线上的兄弟节点是否已经完成,若没有完成,则仅是完成该任务则可,若已经完成,则产生后续的汇总节点。实现的代码如下所示:
//若目标节点为空,则代表没有进行自由跳转,同时后续的节点也不为汇总节点
if (destAct == null){
//完成当前任务
taskService.complete(task.getId(), variables);
return;
}
//是否需要去除当前任务的后续跳转线
boolean isNeedRemoveTran=false;
//检查目标任务是否为汇总节点,若为汇总节点,需要拿到当前任务的令牌,检查该令牌对应的TaskFork的已经汇总的任务个数,若目前的任务为最后一个汇总,则删除TaskFork,并且任务进行下一步的跳转,
//若目前任务不是最后一个汇总,则需要更新汇总完成的任务个数,并且只是完成当前任务,不作跳转。
if(bpmNodeSet==null){
bpmNodeSet=bpmNodeSetService.getByActDefIdJoinTaskKey(task.getProcessDefinitionId(), destAct.getId());
}
if(bpmNodeSet!=null){//目前该目标任务为分发汇总
//取当前任务的分发令牌
String token=(String)taskService.getVariableLocal(task.getId(),TaskFork.TAKEN_VAR_NAME);
if(token!=null){
TaskFork taskFork =taskForkService.getByInstIdJoinTaskKeyForkToken(task.getProcessInstanceId(), destAct.getId(), token);
if(taskFork!=null){
if(taskFork.getFininshCount()<taskFork.getForkCount()-1){
//更新完成任务的个数
taskFork.setFininshCount(taskFork.getFininshCount()+1);
taskForkService.update(taskFork);
//更新Token
String[]tokenSplits=token.split("[_]");
if(tokenSplits.length==2){//若为最外层的汇总,格式如T_1,则需要删除该令牌
taskService.setVariableLocal(task.getId(), TaskFork.TAKEN_VAR_NAME, null);
}
isNeedRemoveTran=true;
}else{
taskForkService.delById(taskFork.getTaskForkId());
//更新Token
String[]tokenSplits=token.split("[_]");
if(tokenSplits.length==2){//若为最外层的汇总,格式如T_1,则需要删除该令牌
taskService.setVariableLocal(task.getId(), TaskFork.TAKEN_VAR_NAME, null);
}else if(tokenSplits.length>=3){//更新token,转换如:T_1_1转成T_1
String newToken=token.substring(0, token.lastIndexOf("_"+tokenSplits[tokenSplits.length-1]));
taskService.setVariableLocal(task.getId(), TaskFork.TAKEN_VAR_NAME, newToken);
}
}
}
}
}
//进行锁定
lockTransto.lock();
//记录原来的跳转线
List<PvmTransition> backTransList = new ArrayList<PvmTransition>();
backTransList.addAll(curActi.getOutgoingTransitions());
try{
// 清除当前任务的外出节点
curActi.getOutgoingTransitions().clear();
if(!isNeedRemoveTran){
//创建一个连接
TransitionImpl transitionImpl = curActi.createOutgoingTransition();
transitionImpl.setDestination(destAct);
}
//完成当前任务
taskService.complete(task.getId(), variables);
}
finally{
// 删除创建的输出
curActi.getOutgoingTransitions().clear();
//加回原来的旧连接
curActi.getOutgoingTransitions().addAll(backTransList);
}
//解除锁定。
lockTransto.unlock();
数据结构
表名:BPM_TASK_FORK
功能说明:任务的分发与汇总记录
字段名 字段类型 备注
TASKFORKID NUMBER(18) TASKFORKID
ACTINSTID VARCHAR2(128) 流程实例ID
FORKTASKNAME VARCHAR2(256) 分发任务名称
FORKTASKKEY VARCHAR2(256) 分发任务Key
FORKSN NUMBER(0,0) 分发序号
FORKCOUNT NUMBER(0,0) 分发个数
FININSHCOUNT NUMBER(0,0) 完成个数
FORKTIME DATE 分发时间
JOINTASKNAME VARCHAR2(256) 汇总任务名称
JOINTASKKEY VARCHAR2(256) 汇总任务Key
FORKTOKENS VARCHAR2(512) 分发令牌号 格式如: T_1_1,T_1_2,T_1_3, 或 T_1,T_2,T_3,
FORKTOKENPRE VARCHAR2(64) 格式为T_ 或格式T_1 或T_1_2等
当任务进行分发时,需要在此表加记录,任务完成时,也需要对此表进行记录,任务的分发与汇总设置在BPM_NODE_SET表中也有记录,如下表的红色部分:
表名:BPM_NODE_SET
功能说明:BPM_NODE_SET
字段名 字段类型 备注
SETID NUMBER(18) SETID
DEFID NUMBER(18) 流程定义ID
NODENAME VARCHAR2(128) 节点名
NODEID VARCHAR2(128) activiti 节点ID
FORMTYPE NUMBER(0,0) 0=在线流程表单 1=URL表单
FORMURL VARCHAR2(128) 当表单类型为1时,表单对应的url
ACTDEFID VARCHAR2(127) Act流程发布ID
FORMDEFNAME VARCHAR2(128) FORMDEFNAME
NODETYPE NUMBER(0,0) NODETYPE
JOINTASKKEY VARCHAR2(128) JOINTASKKEY
JOINTASKNAME VARCHAR2(128) JOINTASKNAME
BEFOREHANDLER VARCHAR2(100) 前置处理器
AFTERHANDLER VARCHAR2(100) 后置处理器
ISALLOWBACK NUMBER(0,0) 1=允许 2=不允许
JUMPTYPE VARCHAR2(32) JUMPTYPE
SETTYPE NUMBER(1) 设置类型 0.任务节点1.开始表单2.默认表单
FORMKEY NUMBER(18) 表单KEY
剩下的工作则是进行分发节点的设置与如何根据分发的用户人数进行分发处理。
分发节点的设置类似如下所示:
任务的分发其实就是任务复制的过程,可以在TaskCreateListener里进行处理,任务汇总时,需要进行任务的合并,则不是最后一个分支,不进行新的后续的任务产生则可。
相关推荐
2. **jBPM4和jBPM5的改进** - 可能会讨论这些版本引入的新特性,如基于JPA的持久化、增强的GUI工具、流程建模语言BPMN 2.0的支持等。 3. **Activiti5** - Activiti5是一个基于jBPM设计理念的流程引擎,可能会介绍它...
与此同时,Activiti5作为jBPM的一个分支,保留了jBPM4的许多优点,但也引入了新的特性,如更简洁的API,更强大的流程设计器,以及对Web服务和社交网络集成的支持。 总结来说,jBPM与Activiti都是为了提高企业流程...
5. **数据库存储(Persistence)**:jbpm4将流程实例、任务和其他相关数据持久化存储,以便在系统间或不同时间点恢复和跟踪流程状态。 在这个“jbpmDemo”压缩包中,可能包含以下组件: 1. **流程定义文件(.bpmn...
在实际应用中,开发者需要了解JBPM4的工作原理,包括流程实例、任务实例、信号和事件的概念,以及如何通过API或服务任务与外部系统交互。熟悉Maven的使用也是至关重要的,包括理解POM文件的结构、如何添加和排除依赖...
总的来说,这个主题资料包提供了jbpm4和jbpm5的基础知识、实战经验和用户操作指导,对于想要学习或深化jbpm流程管理框架理解的开发者非常有价值。通过学习这些文档,开发者可以掌握jbpm的核心概念,熟练地设计和部署...
TomBaeyens离开的具体原因尚不清楚,但他的离开产生了两个结果:一是jBPM的下一个版本jBPM5完全放弃了jBPM4的基础代码,基于Drools Flow重头来过;二是TomBaeyens加入Alfresco后很快推出了新的基于jBPM4的开源工作流...
- **JBPM4_HIST_TASK**:流程任务实例的历史记录。 - **JBPM4_HIST_VAR**:流程变量的历史记录。 - **JBPM4_ID_GROUP**:组表,用于权限管理。 - **JBPM4_ID_MEMBERSHIP**:用户与角色关联表。 - **JBPM4_ID_USER**...
通过编写Java代码并与jbpm4引擎交互,可以实现流程的动态启动、任务分配、流程监控等功能,从而提高工作效率,规范业务流程。而上述提供的代码和文档,就是实现这一目标的基础。对于开发者来说,深入学习和理解这些...
在这个教程中,我们将深入理解jbpm4的工作原理以及如何将其与SSH框架集成,以构建一个完整的业务流程管理系统。 jbpm4是Java Business Process Management的第四个版本,它是一个开源的工作流和业务流程管理(BPM)...
Activiti 是一个开源的工作流系统,它基于 jBPM4 技术,为企业的业务流程自动化提供了强大的支持。本10分钟入门指南旨在帮助初学者快速理解 Activiti 的基本概念和API接口,通过一个简单的业务流程示例进行讲解。 ...
Activiti 是由 jBPM 的创建者 Tom Baeyens 离开 JBoss 之后建立的项目,构建在开发 jBPM 版本 1 到 4 时积累的多年经验的基础之上,旨在创建下一代的 BPM 解决方案。 Activiti是一个开源的工作流引擎,它实现了...
### 工作流系统技术选型可行性分析:JBPM4与JBPM5 #### 一、引言 在数字化转型的背景下,工作流系统成为提高组织效率的关键技术之一。医院作为一个复杂的组织机构,其内部流程的优化对于提升服务质量至关重要。...
20. **ACT_RU_IDENTITYLINK** - 身份联系表,处理运行时的用户、用户组与任务或流程实例的关联。 21. **ACT_RU_JOB** - 运行中的任务表,存储定时任务和异步任务的相关信息。 22. **ACT_RU_TASK** - 运行时任务...
总的来说,掌握BPMN、jBPM和Activiti不仅可以提升个人在工作流管理领域的专业技能,也有助于企业实现高效、灵活的业务流程管理。通过深入学习这三个工具,你将能够为企业构建出更加智能和自动化的业务流程,提高工作...
### Activiti流程学习总结 #### 一、工作流的基本概念 在深入了解Activiti之前,我们需要先理解工作流的概念。工作流是一种将业务过程部分或全部自动化的方法,它可以帮助组织提高工作效率,减少错误,并且能够更...
在JBoss JBPM4中,一个流程通常由一系列任务组成,这些任务可以由系统自动执行,也可以由人参与完成。请假流程就是一个典型的人工参与的任务流程,包括请假申请、审批等步骤。在这个示例中,我们将探讨以下几个关键...
在jbpm4web中,用户还可以进行流程控制操作,如驳回任务回到上一步骤,或者在某些情况下终止流程。这些控制功能有助于适应业务变化,确保流程的灵活性。 5. **流程监控**: 为了评估流程效率和找出改进点,jbpm4...