因为公司项目需要,在工作流的任务节点中能手动驳回到某个任务节点上,所以要对工作流进行改造;众所周知,工作流是通过连线去指定下个任务的,那么实际项目中只有审批是带驳回按钮的,针对activiti配置流程图来讲,带审批的操作一定是带有网关的,那么带网关的节点肯定有一条连线是配置“通过”的连线,在这个网关上可能会配置一条或多条“不通过”的连线,那么我的改造就从这里开始下手。

一、流程图的改造,在配置流程图时,需要注意几点

   1、所有网关配置“通过”的那条连线,点击连线,选择Main Config ,在Condition里配置  ${flag=='1'} ,注意flag是判断走哪条连线的流程变量,驳回的连线也叫flag

    

  2、配置驳回的连线,驳回的连线设置流程变量为连线的目标节点id(数据库里也叫key)例如:${flag=='task5'}

再附上一张task5的节点id截图:

  3、因为activiti提供的表中没有存放连线信息的表,所以迫不得已想要获取数据,我采取了一种比较暴力的方法,就是从数据库中读取配置信息的配置文件,从配置文件中读取           想要的连线信息,因为用工具画出来的bpmn工作流文件本身实际上就是xml文件,只需要遍历它的节点信息从中找到规律即可掌握它的连线信息。

附上一个bpmn的xml文件:

<?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:xsd="http://www.w3.org/2001/XMLSchema" 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="http://www.activiti.org/test"> 
  <process id="taskFuncPro" name="测试订正功能" isExecutable="true"> 
    <documentation>cn/com/sinosoft/mywork/gotoProcinst.ac</documentation> 
    <extensionElements> 
      <activiti:executionListener event="end" class="cn.com.sinosoft.mywork.listen.TestEndListener"></activiti:executionListener> 
    </extensionElements> 
    <startEvent id="startevent1" name="Start"></startEvent> 
    <userTask id="task1" name="科长审批" activiti:candidateGroups="ICMIS90060101"> 
      <documentation>isShowBack:'Y'</documentation> 
      <extensionElements> 
        <activiti:taskListener event="create" class="cn.com.sinosoft.mywork.listen.TestListener"></activiti:taskListener> 
      </extensionElements> 
    </userTask> 
    <sequenceFlow id="flow5" sourceRef="startevent1" targetRef="task1"></sequenceFlow> 
    <userTask id="task2" name="主任审批" activiti:candidateGroups="ICMIS90060101"> 
      <extensionElements> 
        <activiti:taskListener event="create" class="cn.com.sinosoft.mywork.listen.TestListener"></activiti:taskListener> 
      </extensionElements> 
    </userTask> 
    <userTask id="task3" name="领导审批" activiti:candidateGroups="ICMIS90060101"> 
      <extensionElements> 
        <activiti:taskListener event="create" class="cn.com.sinosoft.mywork.listen.TestListener"></activiti:taskListener> 
      </extensionElements> 
    </userTask> 
    <exclusiveGateway id="exclusivegateway1" name="Exclusive Gateway"></exclusiveGateway> 
    <sequenceFlow id="flow11" sourceRef="task2" targetRef="exclusivegateway1"></sequenceFlow> 
    <sequenceFlow id="flow12" name="通过" sourceRef="exclusivegateway1" targetRef="task3"> 
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${flag=='1'}]]></conditionExpression> 
    </sequenceFlow> 
    <exclusiveGateway id="exclusivegateway2" name="Exclusive Gateway"></exclusiveGateway> 
    <userTask id="task4" name="大领导确认" activiti:candidateGroups="ICMIS90060101"> 
      <documentation>isShowBack:'N'</documentation> 
      <extensionElements> 
        <activiti:taskListener event="create" class="cn.com.sinosoft.mywork.listen.TestListener"></activiti:taskListener> 
      </extensionElements> 
    </userTask> 
    <sequenceFlow id="flow14" sourceRef="task3" targetRef="exclusivegateway2"></sequenceFlow> 
    <sequenceFlow id="flow15" name="通过" sourceRef="exclusivegateway2" targetRef="task4"> 
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${flag=='1'}]]></conditionExpression> 
    </sequenceFlow> 
    <sequenceFlow id="flow16" name="驳回到主任审批" sourceRef="exclusivegateway2" targetRef="task2"> 
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${flag=='task2'}]]></conditionExpression> 
    </sequenceFlow> 
    <endEvent id="endevent1" name="End"></endEvent> 
    <sequenceFlow id="flow18" sourceRef="task4" targetRef="endevent1"></sequenceFlow> 
    <userTask id="task5" name="科员订正" activiti:candidateGroups="ICMIS90060101"> 
      <documentation>url:'cn/com/sinosoft/mywork/testCorrect.ac', 
isCorrect:'Y'</documentation> 
      <extensionElements> 
        <activiti:taskListener event="create" class="cn.com.sinosoft.mywork.listen.TestListener"></activiti:taskListener> 
      </extensionElements> 
    </userTask> 
    <exclusiveGateway id="exclusivegateway3" name="Exclusive Gateway"></exclusiveGateway> 
    <sequenceFlow id="flow19" sourceRef="task1" targetRef="exclusivegateway3"></sequenceFlow> 
    <sequenceFlow id="flow20" name="通过" sourceRef="exclusivegateway3" targetRef="task2"> 
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${flag=='1'}]]></conditionExpression> 
    </sequenceFlow> 
    <sequenceFlow id="flow21" name="不通过" sourceRef="exclusivegateway3" targetRef="task5"> 
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${flag=='task5'}]]></conditionExpression> 
    </sequenceFlow> 
    <sequenceFlow id="flow22" sourceRef="task5" targetRef="task1"></sequenceFlow> 
    <sequenceFlow id="flow23" name="驳回到科长审批" sourceRef="exclusivegateway2" targetRef="task1"> 
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${flag=='task1'}]]></conditionExpression> 
    </sequenceFlow> 
    <sequenceFlow id="flow24" name="不通过" sourceRef="exclusivegateway1" targetRef="task1"> 
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${flag=='task1'}]]></conditionExpression> 
    </sequenceFlow> 
    <sequenceFlow id="flow25" name="驳回到科员" sourceRef="exclusivegateway2" targetRef="task5"> 
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${flag=='task5'}]]></conditionExpression> 
    </sequenceFlow> 
  </process> 
  <bpmndi:BPMNDiagram id="BPMNDiagram_taskFuncPro"> 
    <bpmndi:BPMNPlane bpmnElement="taskFuncPro" id="BPMNPlane_taskFuncPro"> 
      <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1"> 
        <omgdc:Bounds height="35.0" width="35.0" x="80.0" y="155.0"></omgdc:Bounds> 
      </bpmndi:BPMNShape> 
      <bpmndi:BPMNShape bpmnElement="task1" id="BPMNShape_task1"> 
        <omgdc:Bounds height="55.0" width="105.0" x="240.0" y="145.0"></omgdc:Bounds> 
      </bpmndi:BPMNShape> 
      <bpmndi:BPMNShape bpmnElement="task2" id="BPMNShape_task2"> 
        <omgdc:Bounds height="55.0" width="105.0" x="592.0" y="146.0"></omgdc:Bounds> 
      </bpmndi:BPMNShape> 
      <bpmndi:BPMNShape bpmnElement="task3" id="BPMNShape_task3"> 
        <omgdc:Bounds height="55.0" width="105.0" x="592.0" y="336.0"></omgdc:Bounds> 
      </bpmndi:BPMNShape> 
      <bpmndi:BPMNShape bpmnElement="exclusivegateway1" id="BPMNShape_exclusivegateway1"> 
        <omgdc:Bounds height="40.0" width="40.0" x="624.0" y="226.0"></omgdc:Bounds> 
      </bpmndi:BPMNShape> 
      <bpmndi:BPMNShape bpmnElement="exclusivegateway2" id="BPMNShape_exclusivegateway2"> 
        <omgdc:Bounds height="40.0" width="40.0" x="624.0" y="446.0"></omgdc:Bounds> 
      </bpmndi:BPMNShape> 
      <bpmndi:BPMNShape bpmnElement="task4" id="BPMNShape_task4"> 
        <omgdc:Bounds height="55.0" width="105.0" x="592.0" y="536.0"></omgdc:Bounds> 
      </bpmndi:BPMNShape> 
      <bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1"> 
        <omgdc:Bounds height="35.0" width="35.0" x="742.0" y="546.0"></omgdc:Bounds> 
      </bpmndi:BPMNShape> 
      <bpmndi:BPMNShape bpmnElement="task5" id="BPMNShape_task5"> 
        <omgdc:Bounds height="55.0" width="105.0" x="240.0" y="298.0"></omgdc:Bounds> 
      </bpmndi:BPMNShape> 
      <bpmndi:BPMNShape bpmnElement="exclusivegateway3" id="BPMNShape_exclusivegateway3"> 
        <omgdc:Bounds height="40.0" width="40.0" x="432.0" y="153.0"></omgdc:Bounds> 
      </bpmndi:BPMNShape> 
      <bpmndi:BPMNEdge bpmnElement="flow5" id="BPMNEdge_flow5"> 
        <omgdi:waypoint x="115.0" y="172.0"></omgdi:waypoint> 
        <omgdi:waypoint x="240.0" y="172.0"></omgdi:waypoint> 
      </bpmndi:BPMNEdge> 
      <bpmndi:BPMNEdge bpmnElement="flow11" id="BPMNEdge_flow11"> 
        <omgdi:waypoint x="644.0" y="201.0"></omgdi:waypoint> 
        <omgdi:waypoint x="644.0" y="226.0"></omgdi:waypoint> 
      </bpmndi:BPMNEdge> 
      <bpmndi:BPMNEdge bpmnElement="flow12" id="BPMNEdge_flow12"> 
        <omgdi:waypoint x="644.0" y="266.0"></omgdi:waypoint> 
        <omgdi:waypoint x="644.0" y="336.0"></omgdi:waypoint> 
        <bpmndi:BPMNLabel> 
          <omgdc:Bounds height="14.0" width="24.0" x="653.0" y="285.0"></omgdc:Bounds> 
        </bpmndi:BPMNLabel> 
      </bpmndi:BPMNEdge> 
      <bpmndi:BPMNEdge bpmnElement="flow14" id="BPMNEdge_flow14"> 
        <omgdi:waypoint x="644.0" y="391.0"></omgdi:waypoint> 
        <omgdi:waypoint x="644.0" y="446.0"></omgdi:waypoint> 
      </bpmndi:BPMNEdge> 
      <bpmndi:BPMNEdge bpmnElement="flow15" id="BPMNEdge_flow15"> 
        <omgdi:waypoint x="644.0" y="486.0"></omgdi:waypoint> 
        <omgdi:waypoint x="644.0" y="536.0"></omgdi:waypoint> 
        <bpmndi:BPMNLabel> 
          <omgdc:Bounds height="14.0" width="24.0" x="654.0" y="486.0"></omgdc:Bounds> 
        </bpmndi:BPMNLabel> 
      </bpmndi:BPMNEdge> 
      <bpmndi:BPMNEdge bpmnElement="flow16" id="BPMNEdge_flow16"> 
        <omgdi:waypoint x="624.0" y="466.0"></omgdi:waypoint> 
        <omgdi:waypoint x="544.0" y="465.0"></omgdi:waypoint> 
        <omgdi:waypoint x="544.0" y="329.0"></omgdi:waypoint> 
        <omgdi:waypoint x="544.0" y="174.0"></omgdi:waypoint> 
        <omgdi:waypoint x="592.0" y="173.0"></omgdi:waypoint> 
        <bpmndi:BPMNLabel> 
          <omgdc:Bounds height="14.0" width="84.0" x="461.0" y="387.0"></omgdc:Bounds> 
        </bpmndi:BPMNLabel> 
      </bpmndi:BPMNEdge> 
      <bpmndi:BPMNEdge bpmnElement="flow18" id="BPMNEdge_flow18"> 
        <omgdi:waypoint x="697.0" y="563.0"></omgdi:waypoint> 
        <omgdi:waypoint x="742.0" y="563.0"></omgdi:waypoint> 
      </bpmndi:BPMNEdge> 
      <bpmndi:BPMNEdge bpmnElement="flow19" id="BPMNEdge_flow19"> 
        <omgdi:waypoint x="345.0" y="172.0"></omgdi:waypoint> 
        <omgdi:waypoint x="432.0" y="173.0"></omgdi:waypoint> 
      </bpmndi:BPMNEdge> 
      <bpmndi:BPMNEdge bpmnElement="flow20" id="BPMNEdge_flow20"> 
        <omgdi:waypoint x="472.0" y="173.0"></omgdi:waypoint> 
        <omgdi:waypoint x="592.0" y="173.0"></omgdi:waypoint> 
        <bpmndi:BPMNLabel> 
          <omgdc:Bounds height="14.0" width="24.0" x="511.0" y="155.0"></omgdc:Bounds> 
        </bpmndi:BPMNLabel> 
      </bpmndi:BPMNEdge> 
      <bpmndi:BPMNEdge bpmnElement="flow21" id="BPMNEdge_flow21"> 
        <omgdi:waypoint x="452.0" y="193.0"></omgdi:waypoint> 
        <omgdi:waypoint x="451.0" y="325.0"></omgdi:waypoint> 
        <omgdi:waypoint x="345.0" y="325.0"></omgdi:waypoint> 
        <bpmndi:BPMNLabel> 
          <omgdc:Bounds height="14.0" width="36.0" x="463.0" y="239.0"></omgdc:Bounds> 
        </bpmndi:BPMNLabel> 
      </bpmndi:BPMNEdge> 
      <bpmndi:BPMNEdge bpmnElement="flow22" id="BPMNEdge_flow22"> 
        <omgdi:waypoint x="292.0" y="298.0"></omgdi:waypoint> 
        <omgdi:waypoint x="292.0" y="200.0"></omgdi:waypoint> 
      </bpmndi:BPMNEdge> 
      <bpmndi:BPMNEdge bpmnElement="flow23" id="BPMNEdge_flow23"> 
        <omgdi:waypoint x="624.0" y="466.0"></omgdi:waypoint> 
        <omgdi:waypoint x="188.0" y="465.0"></omgdi:waypoint> 
        <omgdi:waypoint x="188.0" y="172.0"></omgdi:waypoint> 
        <omgdi:waypoint x="240.0" y="172.0"></omgdi:waypoint> 
        <bpmndi:BPMNLabel> 
          <omgdc:Bounds height="14.0" width="84.0" x="383.0" y="473.0"></omgdc:Bounds> 
        </bpmndi:BPMNLabel> 
      </bpmndi:BPMNEdge> 
      <bpmndi:BPMNEdge bpmnElement="flow24" id="BPMNEdge_flow24"> 
        <omgdi:waypoint x="664.0" y="246.0"></omgdi:waypoint> 
        <omgdi:waypoint x="740.0" y="245.0"></omgdi:waypoint> 
        <omgdi:waypoint x="740.0" y="89.0"></omgdi:waypoint> 
        <omgdi:waypoint x="669.0" y="89.0"></omgdi:waypoint> 
        <omgdi:waypoint x="292.0" y="89.0"></omgdi:waypoint> 
        <omgdi:waypoint x="292.0" y="145.0"></omgdi:waypoint> 
        <bpmndi:BPMNLabel> 
          <omgdc:Bounds height="14.0" width="36.0" x="749.0" y="167.0"></omgdc:Bounds> 
        </bpmndi:BPMNLabel> 
      </bpmndi:BPMNEdge> 
      <bpmndi:BPMNEdge bpmnElement="flow25" id="BPMNEdge_flow25"> 
        <omgdi:waypoint x="624.0" y="466.0"></omgdi:waypoint> 
        <omgdi:waypoint x="292.0" y="466.0"></omgdi:waypoint> 
        <omgdi:waypoint x="292.0" y="353.0"></omgdi:waypoint> 
        <bpmndi:BPMNLabel> 
          <omgdc:Bounds height="14.0" width="60.0" x="302.0" y="399.0"></omgdc:Bounds> 
        </bpmndi:BPMNLabel> 
      </bpmndi:BPMNEdge> 
    </bpmndi:BPMNPlane> 
  </bpmndi:BPMNDiagram> 
</definitions>
bpmn文件的xml格式

二、流程图按照约定配置好后,接下来开始配置java文件了

  1、我们先想一下,如何减少对流程定义文件(*.bpmn)的读写操作,但又能及时的获取想要的连线信息呢,很简单我们只要在创建流程实例的时候将连线的信息从xml中读取出来并存放到流程变量当中,那么我们就可以随时的取用啦,好了废话不多讲,直接上干货:

  1     public String startProcess(String processDefinitionKey, 
  2             Map<String, Object> vMap,String targetFilePath) { 
  3         RuntimeService rs = this.processEngine.getRuntimeService(); 
  4         //设置发起人,这个方法是通过线程判断是一个了流程实例的 
  5         this.processEngine.getIdentityService().setAuthenticatedUserId(vMap.get(WorkFlowConstant.START_USER).toString()); 
  6         User user = this.processEngine.getIdentityService().createUserQuery() 
  7                 .userId(vMap.get(WorkFlowConstant.START_USER).toString()) 
  8                 .singleResult(); 
  9         vMap.put("ac", processDefinitionKey); 
 10         ProcessDefinition processDefinition = this 
 11                 .getProcessDefinitionByKey(processDefinitionKey); 
 12         vMap.put(WorkFlowConstant.VIEW_URL, processDefinition.getDescription()); 
 13          
 14          
 15         Map<String,Object> map=getSeqFlowByBpmn(processDefinition.getDeploymentId(),targetFilePath); 
 16         vMap.put(WorkFlowConstant.SEQUENCEFLOW, map); 
 17          
 18         // 保存日志 
 19         ProcessInstance instance = rs.startProcessInstanceByKey( 
 20                 processDefinitionKey, vMap); 
 21         TaskMarkModel taskMarkModel = new TaskMarkModel(); 
 22         taskMarkModel.setAuditRemark("发起任务"); 
 23         //排序使用 
 24         taskMarkModel.setCreateDate(new Date(new Date().getTime()-100000)); 
 25         taskMarkModel.setDoDate(new Date(new Date().getTime()-100000)); 
 26         taskMarkModel.setDoResult("发起任务"); 
 27         taskMarkModel.setDoUserName(user.getFirstName()); 
 28         taskMarkModel.setPid(instance.getProcessInstanceId()); 
 29         taskMarkModel.setpName(processDefinition.getName()); 
 30         taskMarkModel.setTaskName("发起任务"); 
 31         this.saveTaskMark(taskMarkModel); 
 32         // 保存日志结束 
 33         return instance.getProcessInstanceId(); 
 34     } 
 35  
 36 /** 
 37      * 获取每个任务的连线. 
 38      * 
 39      * @Title: getSeqFlowByBpmn 
 40      * @author [email protected] 
 41      * @param deploymentid 部署id 
 42      * @param targetFilePath 路径 
 43      * @return 
 44      */ 
 45     public Map getSeqFlowByBpmn(String deploymentid,String targetFilePath){ 
 46         Map<String,List<String>> map=new HashMap<String, List<String>>(); 
 47         Map<String,String> taskmap=new HashMap<String, String>(); 
 48         Map allmap=new HashMap(); 
 49         try { 
 50             InputStream in=null; 
 51             List<String> list = getProcessEngine().getRepositoryService()// 
 52                     .getDeploymentResourceNames(deploymentid); 
 53             //定义图片资源的名称 
 54             String resourceName = ""; 
 55             if(list!=null && list.size()>0){ 
 56                 for(String name:list){ 
 57                     if(name.indexOf(".bpmn")>=0){ 
 58                         resourceName = name; 
 59                     } 
 60                 } 
 61             } 
 62             //获取数据库里部署的bpmn文件 
 63             in = getProcessEngine().getRepositoryService() 
 64                     .getResourceAsStream(deploymentid, resourceName); 
 65             //将bpmn文件另存到本地并写入xml文件 
 66             String fileName=resourceName.substring(0,resourceName.indexOf("."))+".xml"; 
 67             File file = new File(targetFilePath+fileName); 
 68         //    if(file.exists()){//判断文件是否存在 
 69             System.out.println(targetFilePath); 
 70             file.delete();//如果存在则先删除 
 71             file.createNewFile();  //创建文件 
 72             OutputStream output = new FileOutputStream(file); 
 73             BufferedOutputStream bufferedOutput = new BufferedOutputStream(output); 
 74             bufferedOutput.write(IcmisUnit.toByteArray(in)); 
 75         //    } 
 76             //获取dom工厂得到dom对象 
 77             DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();//步骤1 
 78             DocumentBuilder builder = factory.newDocumentBuilder();//步骤2 
 79             //将保存的xml解析成dom树 
 80             Document doc = builder.parse(file);//步骤3 
 81             //获取连线的所有node 
 82             NodeList sequenceFlow = doc.getElementsByTagName("sequenceFlow"); 
 83             //获取网关的所有node 
 84             NodeList exclusiveGateway = doc.getElementsByTagName("exclusiveGateway"); 
 85             //获取任务的所有node 
 86             NodeList userTask = doc.getElementsByTagName("userTask"); 
 87              
 88             //循环任务节点 
 89             for (int i = 0; i < userTask.getLength(); i++) { 
 90                 //获取任务节点的id属性 
 91                 String id=userTask.item(i).getAttributes().getNamedItem("id").getNodeValue(); 
 92                 //获取任务节点的name属性 
 93                 String name=userTask.item(i).getAttributes().getNamedItem("name").getNodeValue(); 
 94                 taskmap.put(id, name); 
 95             } 
 96              
 97              
 98              
 99             //循环连线节点 
100             for (int i = 0; i < sequenceFlow.getLength(); i++) { 
101                 //获取连线的起始节点 
102                 String sourceTask=sequenceFlow.item(i).getAttributes().getNamedItem("sourceRef").getNodeValue(); 
103                 //获取连线的目标节点 
104                 String targetTask=sequenceFlow.item(i).getAttributes().getNamedItem("targetRef").getNodeValue(); 
105                 boolean bool=true;//用来判断是否是通过的连线,下面的操作是为了过滤描述通过的连线 
106                  
107                 //获取连线节点下的配置信息节点,一个连线应该只有一个配置节点 
108                 NodeList  nl=  sequenceFlow.item(i).getChildNodes(); 
109                     for (int j = 0; j <nl.getLength(); j++) { 
110                         if(nl.item(j).getNodeType()==Node.ELEMENT_NODE){ 
111                             //找到配置节点,并得到配置的值 
112                             if("conditionExpression".equals(nl.item(j).getNodeName())){   
113                                 //这里要注意一下:配置通过的连线一定要写成${flag=='1'}或"${flag==\"1\"} 
114                                 String flag=Util.nulltostr(nl.item(j).getFirstChild().getNodeValue()); 
115                                 if("${flag=='1'}".equals(flag)||"${flag==\"1\"}".equals(flag)){ 
116                                     //表示是通过的连线,如果是通过的连线则不做处理,这里用到了上面的boolean变量,通过变量来控制过滤节点 
117                                     bool=false; 
118                                      
119                                 } 
120                             } 
121                         } 
122                     } 
123                 //通过变量过滤已通过的连线节点 
124                 if(bool){     
125                     //存连线的开始任务 
126                     if(map.containsKey(sourceTask)){ 
127                         //表示存在 
128                         map.get(sourceTask).add(targetTask); 
129                     }else{ 
130                         //表示不存在 
131                         List<String> targetlist=new ArrayList<String>(); 
132                         targetlist.add(targetTask); 
133                         map.put(sourceTask, targetlist); 
134                     } 
135                 } 
136             } 
137  
138              
139            //默认取出来的连线针对网关记录的是网关的节点,但是实际操作中想要得到网关的节点id是不可能的,所以加了一步处理,获取连接网关的上个节点 
140            //这里要说明一下,连接到网关的连线  也就是targetRef肯定是只有一条连线,所以顺藤摸瓜找到了连接网关的任务节点,这样在实际项目中 
141            //通过任务节点即可找到驳回连线的targetRef任务节点,即可展示到前台让客户去选择驳回的连线啦 
142            //循环网关node  
143            for (int i = 0; i < exclusiveGateway.getLength(); i++) { 
144             //得到网关id 
145                String exclusiveGatewayid=exclusiveGateway.item(i).getAttributes().getNamedItem("id").getNodeValue(); 
146                //通过循环所有的连线,比对得到那条唯一连接网关的连线将数据重新防止到map当中 
147             for (String key : map.keySet()) { 
148                 for (String target : map.get(key)) { 
149                     if(exclusiveGatewayid.equals(target)){ 
150                         map.put(key, map.get(exclusiveGatewayid)); 
151                     } 
152                      
153                 } 
154             } 
155                 
156            } 
157             
158              
159         } catch (Exception e) { 
160             e.printStackTrace(); 
161         } 
162          
163         allmap.put(WorkFlowConstant.SEQ, map); 
164         allmap.put(WorkFlowConstant.USERTASK, taskmap); 
165         return allmap; 
166     } 
167     
创建流程实例的方法(包含读取流程定义文件连线的操作)

ps:TaskMarkModel这个类是我记录历史信息的类,没用工作流自带的历史表,如果各位看官使用的是自带的工作流 只需要去掉记录历史这段即可,如果也想自己记录文档下面我会将这个改造包打包,并附上创建表的sql脚本。

  2、通过上面的操作,我们已经将连线的信息以及所有的任务节点id和名称都存到了流程变量当中,那么如何获取呢?当我在页面上点击驳回按钮时:

 1     //驳回 
 2     function rebutFn() { 
 3         $("#mytr").show(); 
 4         url="${ctx}/cn/com/sinosoft/mywork/seqBackTask.ac?taskid=${taskid}"; 
 5         backTask.setUrl(url); 
 6         //设置默认驳回到上个任务节点 
 7         var backnum=backTask.getCount(); 
 8         //其实通过(确认)节点 是有连线的,只在第一次未产生历史记录时会判断,如果数据已产生历史记录是可以找到节点的,此处应在配置流程图中精确配置信息 
 9         if(backnum<1){ 
10             mini.alert("流程图配置有误,当前节点未配置驳回连线,<br/>请配置好连线后再点击驳回!"); 
11             return; 
12         } 
13         backnum=backnum<1?0:backnum-1; 
14         backTask.select(backTask.getAt(backnum)); 
15         mini.get("auditType").setValue("2"); 
16         showAtPos(); 
17          
18     }
jsp里点击驳回按钮函数

可以看到通过配置struts的配置文件找到了后台进入action层,在action层里获取业务层传回来的指定格式的字符串,并输出到前台,前台判断下拉框是否有值来提示是否有连线。

ps:前台框架使用的是jqueryminiUI 来展现数据的, backTask.setUrl(url); 其中backTask是js声明的全局对象,初始化方法里赋值backTask=mini.get("backTask"); 这其实就是个下拉框对象,没有用juqeyminiUI的朋友也不要紧 使用ajax将当前的任务id传到后台即可获取该任务的所有连线,驳回的连线中已经过滤掉通过的连线了。

后台方法:

 1 public String getSeqByTaskid(String taskid) { 
 2         Task t=getProcessEngine().getTaskService().createTaskQuery().taskId(taskid).singleResult(); 
 3         Map<String,Object> vars = getProcessEngine().getTaskService().getVariables(taskid); 
 4         Map allmap=(Map)vars.get(WorkFlowConstant.SEQUENCEFLOW); 
 5         Map<String,List<String>> map=(Map<String,List<String>> )allmap.get(WorkFlowConstant.SEQ); 
 6         Map<String,String> taskmap=(Map<String,String> )allmap.get(WorkFlowConstant.USERTASK); 
 7         List<String> list=map.get(t.getTaskDefinitionKey()); 
 8         JSONObject Jobj = null; 
 9         JSONArray JArray = new JSONArray(); 
10         if(list!=null&&list.size()>0){ 
11             for (String taskdefkey : list) { 
12                  Jobj =new JSONObject(); 
13                  Jobj.put("ID",Util.nulltostr(taskdefkey)); 
14                  if(taskmap.containsKey(taskdefkey)){ 
15                      Jobj.put("NAME",Util.nulltostr(taskmap.get(taskdefkey))); 
16                  }else{ 
17                      Jobj.put("NAME",""); 
18                      System.out.println("未找到配置的节点"); 
19                  } 
20                  JArray.add(Jobj); 
21             } 
22         }else{ 
23             System.out.println("该任务节点未找到连线"); 
24         } 
25         return JArray.toString(); 
26     }
后台获取指定任务id的连线信息,并转成指定字符串格式

写到了这步基本上实现大部分功能了,在前台页面上展示一下驳回的节点信息 然后让用户手动选择驳回的节点,点击提交后将节点值传递到后台,设置连线的流程变量flag为传递过来的值,认领任务,完成任务,就实现驳回操作啦,给大家展示下代码和效果吧。

前台让用户选择要驳回的任务节点:

jsp页面提交代码:

 1  //提交保存操作 
 2      function save(){ 
 3          workflowform.validate(); 
 4             if (workflowform.isValid() == false) { 
 5                 showFormErrorTexts(workflowform.getErrorTexts()); 
 6                 return; 
 7             }else{ 
 8              mini.mask({ 
 9                 el : document.body, 
10                 cls : 'mini-mask-loading', 
11                 html : '提交中...' 
12             }); 
13             $.ajax({ 
14                 type : "post", 
15                 url : "${ctx}/cn/com/sinosoft/mywork/workflowAudit.ac", 
16                 data : { 
17                     "taskid" : "${taskid}", 
18                     "auditremark" :mini.get("describe.auditremark").getValue(), 
19                     "handlingresults" : mini.get("auditType").getValue(), 
20                     "taskDefKey":backTask.getValue() 
21                 }, 
22                 success : function(text) { 
23                     CloseWindow('save'); 
24                 } 
25             });   
26         } 
27      }
页面提交方法

Action里将值都获取到后传入业务层方法中

 1 public void saveWorkflowAudit(String taskId,String handlingResult,String auditMark, String taskDefKey) { 
 2         UserView user = (UserView) ServletActionContext.getRequest() 
 3                 .getSession().getAttribute(FrameConstant.SESSION_USERVIEW); 
 4         TaskDescribe describe = this.getDescribe(taskId); 
 5         Task task = this.getWorkflowBaseService().getTaskById(taskId); 
 6         ProcessDefinition processDefinition = this.getWorkflowBaseService().getProcessDefinitionById(task.getProcessDefinitionId()); 
 7          
 8         try { 
 9         //非空验证 
10         if(!Util.isNullOrEmpty(describe)){ 
11             getWorkflowBaseService().claim(taskId, user.getUsername()); 
12             Map<String,Object> vMap = new HashMap<String, Object>(); 
13             //设置连线指向 目前通过的连线flag都配置1,驳回的连线配置驳回节点的配置id 
14             vMap.put("flag", WorkFlowConstant.AUDIT_SEQ_VIA.equals(handlingResult)?handlingResult:taskDefKey); 
15             //完成处理 
16             getWorkflowBaseService().completeTask(taskId, vMap); 
17             //保存痕迹 
18             String doResult ="1".equals(handlingResult)?"通过":"不通过"; 
19             TaskMarkModel tmm = new TaskMarkModel(); 
20             tmm.setAuditRemark(auditMark); 
21             tmm.setCreateDate(task.getCreateTime()); 
22             tmm.setDoDate(new Date()); 
23             tmm.setDoResult(doResult); 
24             tmm.setDoUserName(user.getName()); 
25             tmm.setPid(task.getProcessInstanceId()); 
26             tmm.setpName(processDefinition.getName()); 
27             tmm.setTaskName(task.getName()); 
28             tmm.setUsername(user.getUsername()); 
29             tmm.setTaskId(taskId); 
30             tmm.setTaskdefKey(task.getTaskDefinitionKey()); 
31             this.workflowBaseService.saveTaskMark(tmm); 
32             //保存痕迹结束 
33         } 
34          
35          
36         } catch (Exception e) { 
37             e.printStackTrace(); 
38         } 
39     }
业务层保存方法

好啦,大功告成,就这样我们又开始快乐滴编程啦!

附件(选择复制到浏览器打开,360浏览器选择复制—打开链接,即可下载):

http://files.cnblogs.com/files/mycifeng/mywork.zip

发布评论

分享到:

IT虾米网

微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!

activiti和SSH项目做整合详解
你是第一个吃螃蟹的人
发表评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。