亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Activiti如何實現流程的回退

發布時間:2020-07-03 15:59:16 來源:網絡 閱讀:3987 作者:爆破者 欄目:軟件技術

1.概述

流程回退一直以來是個老舊的難題,也一直沒有好的解決方法,本文就來詳述流程回退的解決辦法。首先我們來分析一下不同的流程審批情況,并在對應的節點上實現流程的回退處理,以及應該提供的回退處理,當然我們說的回退不是指通過在流程節點上畫一條線回退到想回的節點上。


回退時,需要解決兩種情況:

  •     回退到發起人

  •     回退到上一步及逐步回退

因為回退至任一節點上,Activiti本身的api是不支持的,我們只能通過擴展activiti的的api,以實現自由跳轉才達到回退至任一節點上,但有情況是例外的,回退的時候,需要注意,否則activiti在跳轉的時候,數據是容易出問題的,主要是在并發的節點分支里跳到外面時(如下圖所示,B、D節點回到A節點時),其執行的實例Id會變化,因此,需要注意對這種情況下的流程跳轉作一些限制。

Activiti如何實現流程的回退

那么我們需要在當前審批的任務上,需要進行回退到任何一個節點,實現自由跳轉時,如何擴展,如下為我們擴展activiti來實現自由跳轉的實現方式:

/**
  * 將節點之后的節點刪除然后指向新的節點。 
  * @param actDefId   流程定義ID
  * @param nodeId   流程節點ID
  * @param aryDestination 需要跳轉的節點
  * @return Map<String,Object> 返回節點和需要恢復節點的集合。
  */@SuppressWarnings("unchecked")
private Map<String,Object>  prepare(String actDefId,String nodeId,String[] aryDestination){
  Map<String,Object> map=new HashMap<String, Object>();
 
 //修改流程定義 ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity)repositoryService.getProcessDefinition(actDefId);
 
  ActivityImpl curAct= processDefinition.findActivity(nodeId);
  List<PvmTransition> outTrans= curAct.getOutgoingTransitions();
 try{
   List<PvmTransition> cloneOutTrans=(List<PvmTransition>) FileUtil.cloneObject(outTrans);
   map.put("outTrans", cloneOutTrans);
 }
 catch(Exception ex){
  
 }
 
 /**
   * 解決通過選擇自由跳轉指向同步節點導致的流程終止的問題。
   * 在目標節點中刪除指向自己的流轉。
   */ for(Iterator<PvmTransition> it=outTrans.iterator();it.hasNext();){
   PvmTransition transition=it.next();
   PvmActivity activity= transition.getDestination();
   List<PvmTransition> inTrans= activity.getIncomingTransitions();
  for(Iterator<PvmTransition> itIn=inTrans.iterator();itIn.hasNext();){
    PvmTransition inTransition=itIn.next();
   if(inTransition.getSource().getId().equals(curAct.getId())){
     itIn.remove();
   }
  }
 }
 
 
  curAct.getOutgoingTransitions().clear();
 
 if(aryDestination!=null && aryDestination.length>0){
  for(String dest:aryDestination){
   //創建一個連接   ActivityImpl destAct= processDefinition.findActivity(dest);
    TransitionImpl transitionImpl = curAct.createOutgoingTransition();
    transitionImpl.setDestination(destAct);
  }
 }
 
  map.put("activity", curAct);
 
 
 return map;
 
}/**
  * 將臨時節點清除掉,加回原來的節點。
  * @param map 
  * void
  */@SuppressWarnings("unchecked")
private void restore(Map<String,Object> map){
  ActivityImpl curAct=(ActivityImpl) map.get("activity");
  List<PvmTransition> outTrans=(List<PvmTransition>) map.get("outTrans");
  curAct.getOutgoingTransitions().clear();
  curAct.getOutgoingTransitions().addAll(outTrans);
}/**
  * 通過指定目標節點,實現任務的跳轉
  * @param taskId 任務ID
  * @param destNodeIds 跳至的目標節點ID
  * @param vars 流程變量
  */public synchronized void completeTask(String taskId,String[] destNodeIds,Map<String,Object> vars) {
  TaskEntity task=(TaskEntity)taskService.createTaskQuery().taskId(taskId).singleResult();
 
  String curNodeId=task.getTaskDefinitionKey();
  String actDefId=task.getProcessDefinitionId();
 
  Map<String,Object> activityMap= prepare(actDefId, curNodeId, destNodeIds);
 try{
   taskService.complete(taskId);
 }
 catch(Exception ex){
  throw new RuntimeException(ex);
 }
 finally{
  //恢復  restore(activityMap);
 }
}

 

若我們需要進行跳轉,就需要知道回退上一步時,其上一步是什么節點。如何僅是通過流程獲得其回退的節點,這是達不到業務的需求的,因為有時我們需要回退到某個節點處理后,下一步需要回到原來的節點上,如我們在上圖E節點上,回退時,E回退需要回到D或C上,完成后再回到B,這種情況下我們可以要求E必須需要去到G1節點上,往下執行。這種回退就會顯得人性化,同時也保證流程實例在后續的執行過程中,其信號及各參數是正常的,這時就要求我們需要有一個完整記錄流程實例執行經過的各個節點ID的數據,并且通過以下的數據可以快速找到當前節點回退時,應該回退到哪一個節點上,并且當時這個節點的執行人員是誰。

2.如何記錄流程的執行過程

為了更好記錄流程經過的樹節點,我們采用了一個樹結構來存儲流程實例執行時,經過的流程節點,如上圖所示,其執行的樹型圖所示所示:

Activiti如何實現流程的回退

我們需要在各個節點那里可以找到其退回至上一步環節的父節點那里,這需要一個算法,如在B或D那里回退,我們讓他退回A,在C回退我們讓他回到B,若我們在E位置回退,我們需要讓他回到G1那里。這個算法的實現不算復雜,有這個樹型的執行樹數據后,一切變得很簡單。但要注意一點,我們在回退時,需要記錄他是從哪個節點回退過來的,若用戶處理完成后,可以要求他直接回到原回退的節點去,也可以按流程定義重新走一次審批。假如執行到E,讓他回退時并且重新審批,其執行的樹圖如下所示:

Activiti如何實現流程的回退

注意G1,那里有指向E,當完成時,可以讓他來跳到E上,這就是任務完成后,可以找到它應該跳至哪一個任務節點上。

3.擴展表記錄流程的執行的路徑

/*==============================================================*//* Table: BPM_RU_PATH                                           *//*==============================================================*/CREATE TABLE BPM_RU_PATH
(
   PATH_ID_             VARCHAR(64) NOT NULL,
   INST_ID_             VARCHAR(64) NOT NULL COMMENT '流程實例ID',
   ACT_DEF_ID_          VARCHAR(64) NOT NULL COMMENT 'Act定義ID',
   ACT_INST_ID_         VARCHAR(64) NOT NULL COMMENT 'Act實例ID',
   SOL_ID_              VARCHAR(64) NOT NULL COMMENT '解決方案ID',
   NODE_ID_             VARCHAR(255) NOT NULL COMMENT '節點ID',
   NODE_NAME_           VARCHAR(255) COMMENT '節點名稱',
   NODE_TYPE_           VARCHAR(50) COMMENT '節點類型',
   START_TIME_          DATETIME NOT NULL COMMENT '開始時間',
   END_TIME_            DATETIME COMMENT '結束時間',
   DURATION_            INT COMMENT '持續時長',
   DURATION_VAL_        INT COMMENT '有效審批時長',
   ASSIGNEE_            VARCHAR(64) COMMENT '處理人ID',
   TO_USER_ID_          VARCHAR(64) COMMENT '代理人ID',
   IS_MULTIPLE_         VARCHAR(20) COMMENT '是否為多實例',
   EXECUTION_ID_        VARCHAR(64) COMMENT '活動執行ID',
   USER_IDS_            VARCHAR(300) COMMENT '原執行人IDS',
   PARENT_ID_           VARCHAR(64) COMMENT '父ID',
   LEVEL_               INT COMMENT '層次',
   OUT_TRAN_ID_         VARCHAR(255) COMMENT '跳出路線ID',
   TOKEN_               VARCHAR(255) COMMENT '路線令牌',
   JUMP_TYPE_           VARCHAR(50) COMMENT '跳到該節點的方式
            正常跳轉
            自由跳轉
            回退跳轉',
   NEXT_JUMP_TYPE_      VARCHAR(50) COMMENT '下一步跳轉方式',
   OPINION_             VARCHAR(500) COMMENT '審批意見',
   REF_PATH_ID_         VARCHAR(64) COMMENT '引用路徑ID
            當回退時,重新生成的結點,需要記錄引用的回退節點,方便新生成的路徑再次回退。',
   TENANT_ID_           VARCHAR(64) COMMENT '租用機構ID',
   CREATE_BY_           VARCHAR(64) COMMENT '創建人ID',
   CREATE_TIME_         DATETIME COMMENT '創建時間',
   UPDATE_BY_           VARCHAR(64) COMMENT '更新人ID',
   UPDATE_TIME_         DATETIME COMMENT '更新時間',
  PRIMARY KEY (PATH_ID_)
);

ALTER TABLE BPM_RU_PATH COMMENT ‘流程實例運行路線’;

4.如何創建執行路徑

有了面的表結構后,如何讓activiti在執行的過程中,往上面的表加上我們需要的數據,這時我們就需要利用activiti的全局事件監聽器,具體的實現請參考我的全局事件監聽處理。

<bean id="globalEventListener" class="com.redxun.bpm.activiti.listener.GlobalEventListener">  <property name="handlers"> <map>  <entry key="TASK_CREATED" value="taskCreateListener"/>  <entry key="TASK_COMPLETED" value="taskCompleteListener"/>  <entry key="TASK_ASSIGNED" value="taskAssignedListener"/>  <entry key="PROCESS_COMPLETED" value="processCompleteListener"/>  <entry key="ACTIVITY_STARTED" value="activityStartedListener"/>  <entry key="ACTIVITY_COMPLETED" value="activityCompletedListener"/>  <entry key="ACTIVITY_SIGNALED" value="activitySignaledListener"/>  <entry key="PROCESS_STARTED" value="processStartEventListener"/> </map></property> </bean>

其中Activiti提供了兩個不錯的事件監聽,一個是執行實體創建事件ACTIVITY_STARTED,一個實體完成的事件ACTIVITY_COMPLETED。我們分別在這兩個事件上加上bpm_ru_path表的記錄創建與更新即可。在其回退的時候,通過算法找到其需要回退的節點,然后通過上文提供的自由跳轉方法,即可以實現流程的回退。

5.流程回退處理

有了以上的執行數據,流程的回退,就可以通過算法找到其需要回退的流程節點,從而可以實現流程的回退處理,注意以下的獲得當前任務的回退節點Id,然后指定這個節點Id為執行完成后,需要跳轉至這個節點上。
注意這部分代碼 BpmRuPath bpmRuPath = getBackNodeId(task.getProcessInstanceId(), task.getTaskDefinitionKey());

/**
  * 任務往下跳轉
  * 
  * @param taskId
  * @param jsonData
  * @param vars
  * @throws Exception
  */public void doNext(ProcessNextCmd cmd) throws Exception {
 
 boolean isSetBackPath = false;
 try {
   TaskEntity task = (TaskEntity) taskService.createTaskQuery().taskId(cmd.getTaskId()).singleResult();
  
   UserTaskConfig userTaskConfig=bpmNodeSetManager.getTaskConfig(task.getSolId(), task.getTaskDefinitionKey());
    
  //String processInstanceId = task.getProcessInstanceId();  // 加上executionId,用來記錄執行的路徑  cmd.setNodeId(task.getTaskDefinitionKey());
  // 加上線程變量  ProcessHandleHelper.setProcessCmd(cmd);
   BpmInst bpmInst = bpmInstManager.getByActInstId(task.getProcessInstanceId());
   BpmFormInst bpmFormInst = bpmFormInstManager.get(bpmInst.getFormInstId());
  try {
    String newJson = JSONUtil.copyJsons(bpmFormInst.getJsonData(), cmd.getJsonData());
    bpmFormInst.setJsonData(newJson);
    bpmFormInstManager.saveOrUpdate(bpmFormInst);
  } catch (Exception ex) {
    logger.error(ex.getCause());
  }
   Map<String, Object> vars = handleTaskVars(task, cmd.getJsonData());
  // 加上外圍傳過來的變量  if (cmd.getVars() != null) {
    vars.putAll(cmd.getVars());
  }
  // 若為回退,則處理回退的操作  if (TaskOptionType.BACK.name().equals(cmd.getJumpType())) {
    BpmRuPath bpmRuPath = getBackNodeId(task.getProcessInstanceId(), task.getTaskDefinitionKey());
   // 沒有找到回退的節點,提示用戶   if (bpmRuPath == null) {
     ProcessHandleHelper.getProcessMessage().getErrorMsges().add("本環節不能回退!沒有找到上一步的回退審批環節!");
    return;
   } else {// 設置回退的節點    cmd.setDestNodeId(bpmRuPath.getNodeId());
     ProcessHandleHelper.setBackPath(bpmRuPath);
     isSetBackPath = true;
   }
  } else if (TaskOptionType.BACK_TO_STARTOR.name().equals(cmd.getJumpType())) {// 回退至發起人   ActNodeDef afterNode = actRepService.getNodeAfterStart(task.getProcessDefinitionId());
   if (afterNode == null) {
     ProcessHandleHelper.getProcessMessage().getErrorMsges().add("沒有找到發起人所在的審批環節!");
    return;
   } else {
     cmd.setDestNodeId(afterNode.getNodeId());
   }
  } else {
   // 查找是否為原路返回的模式,即當前任務是否由回退處理的   BpmRuPath ruPath = bpmRuPathManager.getFarestPath(task.getProcessInstanceId(), task.getTaskDefinitionKey());
   if (ruPath != null && "".equals(ruPath.getNextJumpType())) {
     BpmRuPath toNodePath = bpmRuPathManager.get(ruPath.getParentId());
    if (toNodePath != null) {
      cmd.setDestNodeId(toNodePath.getNodeId());
    }
   }
  }
  
  //加上前置處理  if(StringUtils.isNotEmpty(userTaskConfig.getPreHandle())){
    Object preBean=AppBeanUtil.getBean(userTaskConfig.getPreHandle());
   if(preBean instanceof TaskPreHandler){
     TaskPreHandler handler=(TaskPreHandler)preBean;
     handler.taskPreHandle(cmd, task, bpmInst.getBusKey());
   }   
  }
  // 以下為任務的跳轉處理  if (StringUtils.isNotEmpty(cmd.getDestNodeId())) {// 進行指定節點的跳轉   actTaskService.completeTask(cmd.getTaskId(), new String[] { cmd.getDestNodeId() }, vars);
  } else {// 正常跳轉   taskService.complete(cmd.getTaskId(), vars);
  }
  
  //加上后置處理  if(StringUtils.isNotEmpty(userTaskConfig.getAfterHandle())){
    Object preBean=AppBeanUtil.getBean(userTaskConfig.getAfterHandle());
   if(preBean instanceof TaskAfterHandler){
     TaskAfterHandler handler=(TaskAfterHandler)preBean;
     handler.taskAfterHandle(cmd, task.getTaskDefinitionKey(), bpmInst.getBusKey());
   } 
  }
 } catch (Exception e) {
   e.printStackTrace();
   logger.error(e.getCause());
  throw e;
 } finally {
   ProcessHandleHelper.clearProcessCmd();
  if (isSetBackPath) {
    ProcessHandleHelper.clearBackPath();
  }
 }
}

 

具體的實現效果可以參考如下在線示例,


需要在流程解決方案的節點配置中,打開回退按鈕,如下圖所示:

Activiti如何實現流程的回退

了解咨詢QQ:1361783075


向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

舒兰市| 郑州市| 华容县| 古田县| 华蓥市| 萝北县| 仙游县| 镇远县| 元朗区| 福州市| 华蓥市| 米脂县| 四川省| 东源县| 罗甸县| 咸丰县| 广丰县| 安吉县| 抚松县| 奎屯市| 栾川县| 林甸县| 曲周县| 青岛市| 江油市| 车险| 利津县| 蒙自县| 新竹县| 繁昌县| 内乡县| 通海县| 沙坪坝区| 昭觉县| 忻州市| 大港区| 巴彦县| 宝山区| 甘泉县| 台山市| 集安市|