以下是我们在项目中遇到的各(奇)种(葩)需求,如果您也遇到了相同的可以借鉴:
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<String> assigneeList = new ArrayList<String>();
assigneeList.add("wangba");
assigneeList.add("wangjiu");
delegateTask.setVariable("publicityList",assigneeList);
}
}
1.2 “员工请假申请”中添加此监听类
1.3 “项目组长审批”中
isSequential=false时,表示的并行执行,即该节点下的多条任务可以同时执行。 activiti:collection:执行该会签环节的参与人,此处是使用的一个名叫publicityList的流程变量activiti:elementVariable:表示的是每一个分支都有一个名叫publicity的流程变量,和上方的activiti:assignee结合
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 “项目组长审批”中
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<ActivityImpl> 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<FlowElement> 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<FlowElement> 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引擎中的流程定义文件。两次测试打印结果如下:
流程定义文件leave-formkey.bpmn20.xml: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[html]<?xml version="1.0" encoding="UTF-8"?> <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="OFFICE"> <process id="leave-formkey" name="病事假申请"> <documentation>请假流程演示</documentation> <startEvent id="startevent1" name="Start" activiti:initiator="applyUserId"></startEvent> <userTask id="deptLeaderAudit" name="部门领导审批" activiti:assignee="${applyUserId}" activiti:formKey="leaveHandle.form"></userTask> <exclusiveGateway id="exclusivegateway5" name="Exclusive Gateway"></exclusiveGateway> <userTask id="modifyApply" name="调整申请" activiti:assignee="${applyUserId}" activiti:formKey="leaveApplyAgain.form"></userTask> <userTask id="hrAudit" name="人事审批" activiti:assignee="${applyUserId}" activiti:formKey="leaveHandle.form"></userTask> <exclusiveGateway id="exclusivegateway6" name="Exclusive Gateway"></exclusiveGateway> <userTask id="reportBack" name="销假" activiti:assignee="${applyUserId}" activiti:formKey="leaveHandle.form"></userTask> <endEvent id="endevent1" name="End"></endEvent> <exclusiveGateway id="exclusivegateway7" name="Exclusive Gateway"></exclusiveGateway> <sequenceFlow id="flow2" name="" sourceRef="startevent1" targetRef="deptLeaderAudit"></sequenceFlow> <sequenceFlow id="flow3" name="" sourceRef="deptLeaderAudit" targetRef="exclusivegateway5"></sequenceFlow> <sequenceFlow id="flow4" name="不同意" sourceRef="exclusivegateway5" targetRef="modifyApply"> <conditionExpression xsi:type="tFormalExpression"><![CDATA[${deptLeaderPass == 'false'}]]></conditionExpression> </sequenceFlow> <sequenceFlow id="flow5" name="同意" sourceRef="exclusivegateway5" targetRef="hrAudit"> <conditionExpression xsi:type="tFormalExpression"><![CDATA[${deptLeaderPass == 'true'}]]></conditionExpression> </sequenceFlow> <sequenceFlow id="flow6" name="" sourceRef="hrAudit" targetRef="exclusivegateway6"></sequenceFlow> <sequenceFlow id="flow7" name="同意" sourceRef="exclusivegateway6" targetRef="reportBack"> <conditionExpression xsi:type="tFormalExpression"><![CDATA[${hrPass == 'true'}]]></conditionExpression> </sequenceFlow> <sequenceFlow id="flow8" name="" sourceRef="reportBack" targetRef="endevent1"></sequenceFlow> <sequenceFlow id="flow9" name="不同意" sourceRef="exclusivegateway6" targetRef="modifyApply"> <conditionExpression xsi:type="tFormalExpression"><![CDATA[${hrPass == 'false'}]]></conditionExpression> </sequenceFlow> <sequenceFlow id="flow10" name="重新申请" sourceRef="exclusivegateway7" targetRef="deptLeaderAudit"> <conditionExpression xsi:type="tFormalExpression"><![CDATA[${reApply == 'true'}]]></conditionExpression> </sequenceFlow> <sequenceFlow id="flow11" name="" sourceRef="modifyApply" targetRef="exclusivegateway7"></sequenceFlow> <sequenceFlow id="flow12" name="结束流程" sourceRef="exclusivegateway7" targetRef="endevent1"> <conditionExpression xsi:type="tFormalExpression"><![CDATA[${reApply == 'false'}]]></conditionExpression> </sequenceFlow> </process> <bpmndi:BPMNDiagram id="BPMNDiagram_leave-formkey"> <bpmndi:BPMNPlane bpmnElement="leave-formkey" id="BPMNPlane_leave-formkey"> <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1"> <omgdc:Bounds height="35" width="35" x="10" y="90"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="deptLeaderAudit" id="BPMNShape_deptLeaderAudit"> <omgdc:Bounds height="55" width="105" x="90" y="80"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="exclusivegateway5" id="BPMNShape_exclusivegateway5"> <omgdc:Bounds height="40" width="40" x="250" y="87"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="modifyApply" id="BPMNShape_modifyApply"> <omgdc:Bounds height="55" width="105" x="218" y="190"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="hrAudit" id="BPMNShape_hrAudit"> <omgdc:Bounds height="55" width="105" x="358" y="80"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="exclusivegateway6" id="BPMNShape_exclusivegateway6"> <omgdc:Bounds height="40" width="40" x="495" y="87"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="reportBack" id="BPMNShape_reportBack"> <omgdc:Bounds height="55" width="105" x="590" y="80"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1"> <omgdc:Bounds height="35" width="35" x="625" y="283"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="exclusivegateway7" id="BPMNShape_exclusivegateway7"> <omgdc:Bounds height="40" width="40" x="250" y="280"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2"> <omgdi:waypoint x="45" y="107"></omgdi:waypoint> <omgdi:waypoint x="90" y="107"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3"> <omgdi:waypoint x="195" y="107"></omgdi:waypoint> <omgdi:waypoint x="250" y="107"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4"> <omgdi:waypoint x="270" y="127"></omgdi:waypoint> <omgdi:waypoint x="270" y="190"></omgdi:waypoint> <bpmndi:BPMNLabel> <omgdc:Bounds height="11" width="100" x="10" y="0"></omgdc:Bounds> </bpmndi:BPMNLabel> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow5" id="BPMNEdge_flow5"> <omgdi:waypoint x="290" y="107"></omgdi:waypoint> <omgdi:waypoint x="358" y="107"></omgdi:waypoint> <bpmndi:BPMNLabel> <omgdc:Bounds height="11" width="100" x="-24" y="-17"></omgdc:Bounds> </bpmndi:BPMNLabel> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow6" id="BPMNEdge_flow6"> <omgdi:waypoint x="463" y="107"></omgdi:waypoint> <omgdi:waypoint x="495" y="107"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow7" id="BPMNEdge_flow7"> <omgdi:waypoint x="535" y="107"></omgdi:waypoint> <omgdi:waypoint x="590" y="107"></omgdi:waypoint> <bpmndi:BPMNLabel> <omgdc:Bounds height="11" width="100" x="-22" y="-17"></omgdc:Bounds> </bpmndi:BPMNLabel> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow8" id="BPMNEdge_flow8"> <omgdi:waypoint x="642" y="135"></omgdi:waypoint> <omgdi:waypoint x="642" y="283"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow9" id="BPMNEdge_flow9"> <omgdi:waypoint x="515" y="127"></omgdi:waypoint> <omgdi:waypoint x="514" y="217"></omgdi:waypoint> <omgdi:waypoint x="323" y="217"></omgdi:waypoint> <bpmndi:BPMNLabel> <omgdc:Bounds height="11" width="100" x="10" y="0"></omgdc:Bounds> </bpmndi:BPMNLabel> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow10" id="BPMNEdge_flow10"> <omgdi:waypoint x="250" y="300"></omgdi:waypoint> <omgdi:waypoint x="142" y="299"></omgdi:waypoint> <omgdi:waypoint x="142" y="135"></omgdi:waypoint> <bpmndi:BPMNLabel> <omgdc:Bounds height="11" width="100" x="10" y="0"></omgdc:Bounds> </bpmndi:BPMNLabel> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow11" id="BPMNEdge_flow11"> <omgdi:waypoint x="270" y="245"></omgdi:waypoint> <omgdi:waypoint x="270" y="280"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow12" id="BPMNEdge_flow12"> <omgdi:waypoint x="290" y="300"></omgdi:waypoint> <omgdi:waypoint x="625" y="300"></omgdi:waypoint> <bpmndi:BPMNLabel> <omgdc:Bounds height="11" width="100" x="10" y="0"></omgdc:Bounds> </bpmndi:BPMNLabel> </bpmndi:BPMNEdge> </bpmndi:BPMNPlane> </bpmndi:BPMNDiagram> </definitions> ============================================================================
5.流程启动前传入后续节点办理人;
//下面name2和name3是前台传过来的第二个和第三个节点的办理人
Map<String, Object> variables = new HashMap<String, Object>(); 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<String, Object> variables=delegateTask.getVariables(); variables.get("name2");(前台传过来的第二个节点的办理人) //拆分variables List<String> assigneeList = new ArrayList<String>(); assigneeList.add("wangba"); delegateTask.setVariable("publicityList",assigneeList); }}
在第三个节点指定本节点的办理人监听
public class MyTaksListener3 implements TaskListener { public void notify(DelegateTask delegateTask) { Map<String, Object> 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<String, Object> 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<String, Object> variables=delegateTask.getVariables(); String result=(String)variables.get("name2"); List<String> assigneeList = new ArrayList<String>(); assigneeList.add("ss"); delegateTask.setVariable("publicityList",assigneeList); }}
至此,项目中遇到的各(奇)种(葩)问题迎刃而解。“中国式”工作流有时确实很让人头疼,但也体现了中国程序猿的强大。希望看到这里的你也能从中得到启发,尽早解决您在项目当中遇到的问题。