activiti 工作流会签 / 多人审批时若一人通过即可

最近在工作中使用到了activiti 工作流引擎,跟大家遇到过的情况类似,在“中国式”的工作流中,常有一些需求是工作流引擎基本使用中无法实现的。在这过程中,我和我的小伙伴们也和大家一样遇到很多困难,大海捞针似的在网上寻找着答案。特此,在这里把我们遇到的需求和解决方案分享给大家,希望能帮助到你们!

以下是我们在项目中遇到的各(奇)种()需求,如果您也遇到了相同的可以借鉴:

1、工作流会签;

2、多人审批时一人通过即可;

3、在当前节点获取下一节点的信息;

4、流程部署后未发布之前获取所有节点的信息;

5、流程启动前传入后续节点办理人;

6、节点设置多个监听。


1、 activiti 工作流会签时,所有的都审批通过才可进入下一环节:


1.1 编写监听类

public class MyTaksListener implements TaskListener {
    public void notify(DelegateTask delegateTask) {
        System.out.println("delegateTask.getEventName() = " + delegateTask.getEventName());

         //添加会签的人员,所有的都审批通过才可进入下一环节

        List assigneeList = new ArrayList();
        assigneeList.add("wangba");
        assigneeList.add("wangjiu");
        delegateTask.setVariable("publicityList",assigneeList);
    }
}


1.2 “员工请假申请”中添加此监听类

activiti 工作流会签 / 多人审批时若一人通过即可_第1张图片

1.3 “项目组长审批”中

activiti 工作流会签 / 多人审批时若一人通过即可_第2张图片


isSequential=false时,表示的并行执行,即该节点下的多条任务可以同时执行。
activiti:collection:执行该会签环节的参与人,此处是使用的一个名叫publicityList的流程变量
activiti:elementVariable:表示的是每一个分支都有一个名叫publicity的流程变量,和上方的activiti:assignee结合
 
  


activiti 工作流会签 / 多人审批时若一人通过即可_第3张图片


1.4 项目组长审批时,通过taskAssignee来获取个人任务

// 获取总记录数

total = taskService.createTaskQuery().taskAssignee(userId).taskNameLike("%" + s_name + "%").count(); 
taskList = taskService.createTaskQuery()
// 根据用户id查询
.taskAssignee(userId)
// 根据任务名称查询
.taskNameLike("%" + s_name + "%")
// 返回带分页的结果集合
.listPage(pageInfo.getPageIndex(), pageInfo.getPageSize());

==================================================================================




2. activiti 工作流会签,一人通过即可进入下一环节:


2.1 编写监听类

public class MangerTaskHandlerCandidateUsers implements TaskListener{
    public void notify(DelegateTask delegateTask) {
        //添加审批的人员,以下任何一人通过即可进入下一环节
        String[] empLoyees = {"wangba","wangjiu"};
        delegateTask.addCandidateUsers(Arrays.asList(empLoyees));
    }
}


2.2 “项目组长审批”中

activiti 工作流会签 / 多人审批时若一人通过即可_第4张图片


2.3 项目组长审批时,通过taskCandidateUser来获取节点任务

// 获取总记录数

total = taskService.createTaskQuery().taskCandidateUser(userId).taskNameLike("%" + s_name + "%").count(); 
taskList = taskService.createTaskQuery()
// 根据用户id查询
.taskCandidateUser(userId)
// 根据任务名称查询
.taskNameLike("%" + s_name + "%")
// 返回带分页的结果集合
.listPage(pageInfo.getPageIndex(), pageInfo.getPageSize());

============================================================================




3、在当前节点获取下一节点的信息


/**
     * 根据实例编号查找下一个任务节点
     * 
     * @param String
     *     procInstId :实例编号
     * @return
     */
    @RequestMapping("/backTaskTab")
    public TaskDefinition backTaskTab(String taskId) {

        Task task = taskService.createTaskQuery() // 创建任务查询
                .taskId(taskId) // 根据任务id查询
                .singleResult();

        String procInstId = task.getProcessInstanceId();
        // 流程标示
        String processDefinitionId = historyService.createHistoricProcessInstanceQuery().processInstanceId(procInstId)
                .singleResult().getProcessDefinitionId();

        ProcessDefinitionEntity def = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService)
                .getDeployedProcessDefinition(processDefinitionId);
        // 执行实例
        ExecutionEntity execution = (ExecutionEntity) runtimeService.createProcessInstanceQuery()
                .processInstanceId(procInstId).singleResult();
        // 当前实例的执行到哪个节点
        String activitiId = execution.getActivityId();
        // 获得当前任务的所有节点
        List activitiList = def.getActivities();
        ActivityImpl activityImpl=null;
        for(int i=0;i< activitiList.size();i++){
            String flag=activitiList.get(i).getId();
            if(flag.equals(activitiId)){
                activityImpl=activitiList.get(i);
            }
        }
        String id = null;
        int num=activitiList.indexOf(activityImpl);
        ActivityImpl activityImpl_=activitiList.get(num+1);
        TaskDefinition taskDefinition = ((UserTaskActivityBehavior) activityImpl_.getActivityBehavior())
                .getTaskDefinition();
        // 获取下一节点的代办人
        System.out.println(taskDefinition.getCandidateGroupIdExpressions().toArray()[0]);
        return null;
    }

============================================================================




4、流程部署后未发布之前获取所有节点的信息


解决思路是这样的:部署完工作流之后,为UserTask节点动态分配任务执行者,或者在分支节点上添加条件判断的功能。为了实现这个功能,需要解析流程定义文件,取出文件中定义的所有节点。这里有两个方法可以实现此功能:

方法一(流程部署至服务器上之后可使用):


//processDefinitionId为流程定义Id,该Id可以通过多种方式获得,如通过ProcessDefinitionQuery可以查询一个 //ProcessDefinition对象,Task对象中也包含    

processDefinitionIdBpmnModel model = repositoryService.getBpmnModel(processDefinitionId);
        if (model != null) {
            Collection flowElements = model.getMainProcess().getFlowElements();
            for (FlowElement e : flowElements) {
                System.out.println("flowelement id:" + e.getId() + "  name:" + e.getName() + "   class:"
                        + e.getClass().toString());
            }
        }


该方法适用于流程部署至服务器上之后,通过该方法可以简单快速的获取流程定义文件中各个节点信息。


方法二 读取流程定义文件方式


InputStream resouceStream = this.getClass().getClassLoader().getResourceAsStream("leave-  formkey.bpmn20.xml");
        XMLInputFactory xif = XMLInputFactory.newInstance();
        InputStreamReader in;
        XMLStreamReader xtr;
        try {
            in = new InputStreamReader(resouceStream, "UTF-8");
            xtr = xif.createXMLStreamReader(in);
            BpmnModel model = new BpmnXMLConverter().convertToBpmnModel(xtr);
            Collection flowElements = model.getMainProcess().getFlowElements();
            for (FlowElement e : flowElements) {
                System.out.println("flowelement id:" + e.getId() + "  name:" + e.getName() + "   class:"
                        + e.getClass().toString());
            }
        } catch (XMLStreamException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }


        该方法使用到了activiti的activiti-bpmn-converter-5.20.0.jar和activiti-bpmn-model-5.20.0.jar,用到了其中比较关键的一个类BpmnXMLConverter,该类将xml定义文件解析成BpmnModel对象,使用BpmnModel的getMainProcess()获取一个Process对象,该对象实际是一个继承自BaseElement、FlowElementContainer的节点容器,通过getFlowElements()获取当前流程定义文件中所有的节点对象。该方法的好处在于可以解析本地或者未部署至Activiti引擎中的流程定义文件。

两次测试打印结果如下:

flowelement id:startevent1  name:Start   class:class org.activiti.bpmn.model.StartEvent  
flowelement id:deptLeaderAudit  name:部门领导审批   class:class org.activiti.bpmn.model.UserTask  
flowelement id:exclusivegateway5  name:Exclusive Gateway   class:class org.activiti.bpmn.model.ExclusiveGateway  
flowelement id:modifyApply  name:调整申请   class:class org.activiti.bpmn.model.UserTask  
flowelement id:hrAudit  name:人事审批   class:class org.activiti.bpmn.model.UserTask  
flowelement id:exclusivegateway6  name:Exclusive Gateway   class:class org.activiti.bpmn.model.ExclusiveGateway  
flowelement id:reportBack  name:销假   class:class org.activiti.bpmn.model.UserTask  
flowelement id:endevent1  name:End   class:class org.activiti.bpmn.model.EndEvent  
flowelement id:exclusivegateway7  name:Exclusive Gateway   class:class org.activiti.bpmn.model.ExclusiveGateway  
flowelement id:flow2  name:   class:class org.activiti.bpmn.model.SequenceFlow  
flowelement id:flow3  name:   class:class org.activiti.bpmn.model.SequenceFlow  
flowelement id:flow4  name:不同意   class:class org.activiti.bpmn.model.SequenceFlow  
flowelement id:flow5  name:同意   class:class org.activiti.bpmn.model.SequenceFlow  
flowelement id:flow6  name:   class:class org.activiti.bpmn.model.SequenceFlow  
flowelement id:flow7  name:同意   class:class org.activiti.bpmn.model.SequenceFlow  
flowelement id:flow8  name:   class:class org.activiti.bpmn.model.SequenceFlow  
flowelement id:flow9  name:不同意   class:class org.activiti.bpmn.model.SequenceFlow  
flowelement id:flow10  name:重新申请   class:class org.activiti.bpmn.model.SequenceFlow  
flowelement id:flow11  name:   class:class org.activiti.bpmn.model.SequenceFlow  
flowelement id:flow12  name:结束流程   class:class org.activiti.bpmn.model.SequenceFlow 


流程定义文件leave-formkey.bpmn20.xml:
[html] 
 
 
   
    请假流程演示  
     
     
     
     
     
     
     
     
     
     
     
     
       
   
 
     
       
   
 
     
     
       
   
 
     
     
       
   
 
     
       
   
 
     
     
       
   
 
 
 
   
     
       
         
     
 
       
         
     
 
       
         
     
 
       
         
     
 
       
         
     
 
       
         
     
 
       
         
     
 
       
         
     
 
       
         
     
 
       
         
         
     
 
       
         
         
     
 
       
         
         
         
           
       
 
     
 
       
         
         
         
           
       
 
     
 
       
         
         
     
 
       
         
         
         
           
       
 
     
 
       
         
         
     
 
       
         
         
         
         
           
       
 
     
 
       
         
         
         
         
           
       
 
     
 
       
         
         
     
 
       
         
         
         
           
       
 
     
 
   
 
 
 
 

============================================================================




5.流程启动前传入后续节点办理人;


//下面name2和name3是前台传过来的第二个和第三个节点的办理人

Map variables = new HashMap();
        variables.put("leaveId", leaveId);
        variables.put("name2", "XXX");//(前台传过来的第二个节点的办理人)
        variables.put("name3", "YYY");//(前台传过来的第三个节点的办理人)
        // 启动流程
        pi = runtimeService.startProcessInstanceByKey("activitiemployeeProcess", variables);

在第一个节点指定第二个节点的监听

public class MyTaksListener2 implements TaskListener {

    public void notify(DelegateTask delegateTask) {
        Map variables=delegateTask.getVariables();
       variables.get("name2");(前台传过来的第二个节点的办理人)
        //拆分variables
        List assigneeList = new ArrayList(); 
        assigneeList.add("wangba");
        delegateTask.setVariable("publicityList",assigneeList);
    }
}


在第三个节点指定本节点的办理人监听

public class MyTaksListener3 implements TaskListener {

    public void notify(DelegateTask delegateTask) {
        Map variables=delegateTask.getVariables();
        System.out.println(variables);
        variables.get("name3");
//        String result=(String) variables.get("name3");(前台传过来的第三个节点的办理人)
        String[] empLoyees = {"szx"};
        delegateTask.addCandidateUsers(Arrays.asList(empLoyees));
    }
}


============================================================================




6、节点设置多个监听


在同一节点设置两个监听,一个是设置本节点的监听,指定办理人;另一个是设置下一个节点的监听,指定会签人。


设置本节点的监听,指定办理人

public class MyTaksListener3 implements TaskListener {

    public void notify(DelegateTask delegateTask) {
        Map variables=delegateTask.getVariables();
        System.out.println(variables);
        String result=(String) variables.get("name3");
        
        String[] empLoyees = {"szx"};
        delegateTask.addCandidateUsers(Arrays.asList(empLoyees));
    }
}

设置下一个节点的监听,指定会签人

public class MyTaksListener4 implements TaskListener {

    public void notify(DelegateTask delegateTask) {
        Map variables=delegateTask.getVariables();
        String result=(String)variables.get("name2");
        
        List assigneeList = new ArrayList(); 
        assigneeList.add("ss");
        delegateTask.setVariable("publicityList",assigneeList);
    }
}





至此,项目中遇到的各(奇)种(葩)问题迎刃而解。“中国式”工作流有时确实很让人头疼,但也体现了中国程序猿的强大。希望看到这里的你也能从中得到启发,尽早解决您在项目当中遇到的问题。

喜欢此文,或是能帮助您解决实际问题的话,欢迎转载,以便能帮助到更多的人,谢谢!

 
  
 
  
 
  
 
  
 
  
 
  
 
  
 
  
 
  
 
 

你可能感兴趣的:(Activiti)