近期在学习 JBPM 工作流,上一篇博文描述了实现一个简单请假工作流的过程
之后花了好几天的时间来实现一个基于泳道的任务发布工作流:
简要流程如下:
某级部门(暂定为A级部门)manager 启动一个新任务发布工作流
通过泳道将该任务发布给A级部门的子部门(B)和孙子部门(C)
B和C部门的所有成员都能够看到该任务,并可以点击链接查看该任务的详细信息
一旦有成员apply 该任务,则其他人都不能再次apply该任务
如果该成员只是C级部门的成员,不具有manager 的角色,则该次申请还需要提交给C级部门的经理进行审核。
如果该成员是C级部门的经理,则不需要审核,直接进入tryfinish 待办任务
当获取了任务的assignee 认为自己完成了该项任务时,则可以提交tryfinish 任务,然后该请求进入工作流发布人的待办任务列表中
如果工作流发布者批准该次任务完成,则该次工作流结束。否则重新进入tryfinish 环节。
具体jbpm。xml 代码如下所示
<?xml version="1.0" encoding="UTF-8"?>
<process name="assignment" xmlns="http://jbpm.org/4.4/jpdl">
<swimlane candidate-groups="#{assignGroup}" name="assignmentswin">
</swimlane>
<start g="336,33,64,58" name="start1">
<transition to="assign"/>
</start>
<task assignee="#{assignOwner}" form="/SSH/LeaveJSP/assign_add.jsp" g="322,107,80,40" name="assign">
<transition to="tryAssign"/>
</task>
<task form="/SSH/LeaveJSP/assign_trytake.jsp" g="323,180,92,52" name="tryAssign" swimlane="assignmentswin">
<transition to="exclusive1"/>
</task>
<decision expr="#{role=='manager'? 'totryFinish':'tomanagerAprove'}" g="351,278,48,48" name="exclusive1">
<transition g="-92,-18" name="tomanagerAprove" to="managerAprove"/>
<transition to="tryFinish" name="totryFinish"/>
</decision><!--
<task assignee="#{assignManager}" form="leaveAction!toAssign_Manager.action" g="118,287,92,52" name="managerAprove">
-->
<task form="leaveAction!toAssign_Manager.action" g="118,287,92,52" name="managerAprove">
<assignment-handler class="com.util.AssignTask">
<field name="assignee">
<string value="1"/>
</field>
</assignment-handler>
<transition g="-61,-18" name="approveTry" to="tryFinish"/>
<transition g="-59,-18" name="notApproveTry" to="tryAssign"/>
</task>
<task form="leaveAction!toAssign_tryFinish.action" g="332,464,92,52" name="tryFinish" swimlane="assignmentswin">
<transition to="ifFinish"/>
</task>
<end g="355,711,48,48" name="end1"/>
<task assignee="#{assignOwner}" form="leaveAction!toAssign_ifFinish.action" g="339,611,92,52" name="ifFinish">
<transition name="AcceptFinish" to="end1" g="-65,-18"/>
<transition g="504,599:-82,-18" name="NotAcceptFinish" to="tryFinish"/>
</task>
</process>
这个版本的jbpm。xml 文件采用了 assignment-handler 来实现根据C级部门成员apply 任务时确定C级部门的manager ,即确定批准C级成员apply 该任务的manager。
我这次的工作就花了很长的时间在确定manager 上。
当C级部门成员apply 该才任务时,工作流进入了部门manager 批准的环节,在这个环节中使用到了 assignment-handler,通过该环节的executionId来获取到该次工作流的历史任务列表。然后从历史人物列表的最后一个历史任务中取到apply 任务的C级部门成员的assign name。取到了上一个任务的assign name ,也就可以将 assign 所在部门的manager name 放入到当前任务的assingee 。从而确定了当前任务的assignee。
下面是AssignTask 的代码。
AssignTask.java
package com.util;
import java.util.List;
import org.jbpm.api.ProcessEngine;
import org.jbpm.api.TaskService;
import org.jbpm.api.history.HistoryTask;
import org.jbpm.api.model.OpenExecution;
import org.jbpm.api.task.Assignable;
import org.jbpm.api.task.AssignmentHandler;
import org.jbpm.api.task.Task;
import org.jbpm.pvm.internal.history.model.HistoryTaskImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.model.User;
import com.service.UserService;
import com.service.imp.UserServiceImp;
public class AssignTask implements AssignmentHandler{
String assignee;
private UserService userService;
privateProcessEngine processEngine;
@Override
public void assign(Assignable assignable, OpenExecution openExecution) throws Exception {
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
processEngine=(ProcessEngine)context.getBean("processEngine");
userService=(UserService)context.getBean("userService");
String executionId= openExecution.getId();
System.out.println("executionId-----------"+executionId);
List <HistoryTask>list = processEngine.getHistoryService().createHistoryTaskQuery().executionId(executionId).list();
String tryAssignUserName="";
for(int i=0;i<list.size();i++){
HistoryTaskImpl taskImpl=(HistoryTaskImpl)list.get(i);
tryAssignUserName= taskImpl.getAssignee();
Task task = processEngine.getTaskService().getTask(list.get(i).getId());
if(task!=null&&task.getName().equals("tryAssign")){
}
}
if(!tryAssignUserName.equals("")){
User user1=userService.findUserByName(tryAssignUserName);
User assignManager=user1.getDepartment().getManagerUser();
assignable.setAssignee(assignManager.getUsername());
}
}
}
对这段代码需要作如下的说明:
// 我们已经知道了上一个任务节点的名称是“tryAssign”,所以可以对历史任务列表中的每一个历史任务进行取出任务名称并和“tryAssign”进行比对,如果相同,则将该历史任务的assignee 取出。该assingee 就是C级部门apply 任务的组成员名。
不过在这里只能取到 tryAssign 的HistoryTaskImpl 而不能取到 task,不过由于上一个任务节点肯定是在历史任务列表的最后一个位置,所以可以通过取巧的方式获取到历史人物列表最后一个HistoryTaskImpl 的assignee 来确定tryAssign 的assingee。
对泳道的说明:
在jbpm.xml 文件中,有两处swimlane="assignmentswin" ,分别是在tryAssign 和tryFinish 任务节点时。
因为在tryAssign 时,泳道确定了获取tryAssign 任务的assignee ,则jbpm 会将assignee 保存起来,在下次D任务节点用到swimlane="assignmentswin" 时,D任务节点会将任务的assingee 自动分配给这个泳道的assignee
下面是action 中tryAssign 方法的代码:
public String applayAssign(){
Map map=new HashMap();
String tryAssignUserName="";
User user=(User)ActionContext.getContext().getSession().get("user");
User user2=user.getDepartment().getManagerUser();
if(user.equals(user2)){
map.put("role", "manager");
}
else {map.put("role", "common");
}
map.put("tryTime", tryTime);
map.put("tryCondition", tryCondition);
taskService.takeTask(taskId,user.getUsername() );
String executionId = processEngine.getTaskService().getTask(taskId).getExecutionId();
List <HistoryTask>list = processEngine.getHistoryService().createHistoryTaskQuery().executionId(executionId).list();
for(int i=0;i<list.size();i++){
HistoryTaskImpl taskImpl=(HistoryTaskImpl)list.get(i);
String tempName= taskImpl.getAssignee();
Task task = processEngine.getTaskService().getTask(list.get(i).getId());
if(task!=null&&task.getName().equals("tryAssign")){
tryAssignUserName=tempName;
}
}
User user4= this.userServcie.findUserByName(tryAssignUserName);
User assignManager=user4.getDepartment().getManagerUser();
// map.put("assignManager", assignManager.getUsername());
taskService.completeTask(taskId,map);
return "addLeaveApplay";
}
方法一和方法二在这里的区别,就是没有将 assignManager进行赋值并传递给 managerAprove 任务中。