工作流駁回到指定連線節點上


因為公司項目需要,在工作流的任務節點中能手動駁回到某個任務節點上,所以要對工作流進行改造;眾所周知,工作流是通過連線去指定下個任務的,那么實際項目中只有審批是帶駁回按鈕的,針對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 liufei12581@sinosoft.com.cn
 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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM