activiti已結束子流程退回


用戶需求

有客戶反映一個子流程審批時將審批通過看成了退回修改,想讓其退回到上一步。
局部流程圖如下圖

 

 

 這是並行網關下的一個分支流程,該訂單在另一個分支中已經走了多步,當前分支的情況是:在審批這個節點本來要選擇退回待修改結果誤選了審批通過以致該該子流程直接結束。我在網上搜了體面的流程退回,但有流程不能已結束的要求,我也搜到一篇手動退回一個結束的流程的文章(文末鏈接),就按照該文章的思路嘗試將已結束的流程退回到子流程審批的節點。
先查看流程相關表的執行情況(具體流程可能會有不同情況),在備份庫中,手動在子流程審批節點推進到結束節點,查看sql執行。

insert into ACT_HI_VARINST (ID_, PROC_INST_ID_, EXECUTION_ID_, TASK_ID_, NAME_, REV_, VAR_TYPE_, BYTEARRAY_ID_, DOUBLE_, LONG_ , TEXT_, TEXT2_, CREATE_TIME_, LAST_UPDATED_TIME_) values ('1895008', '1892897', '1892903', '1892991',...

update ACT_HI_TASKINST set PROC_DEF_ID_ = 'Process_1:62:572507', EXECUTION_ID_ = '1892903',...
 WHERE ID_ = '1892991';
 
update ACT_RU_EXECUTION set REV_ = 5, BUSINESS_KEY_ = null, PROC_DEF_ID_ = 'Process_1:62:572507', ACT_ID_ = 'EndEvent_1vt7vcc', IS_ACTIVE_ = false, IS_CONCURRENT_ = false, IS_SCOPE_ = false, IS_EVENT_SCOPE_ = false, IS_MI_ROOT_ = false, PARENT_ID_ = '1892897', ...
 WHERE ID_ = '1892903' and REV_ = 4;
 
select *
 FROM ACT_RU_VARIABLE
 WHERE TASK_ID_ = '1892991' and NAME_= 'result';
 
select *
 FROM ACT_RU_VARIABLE
 WHERE EXECUTION_ID_ = '1892903' and NAME_= 'result' and TASK_ID_ is null;
 
 delete
 FROM ACT_RU_IDENTITYLINK
 WHERE ID_ = '1892992';
 
 delete
 FROM ACT_RU_TASK
 WHERE ID_ = '1892991' and REV_ = 4;
 
 delete
 FROM ACT_RU_EXECUTION
 WHERE ID_ = '1892903' and REV_ = 5;
#等等

語句非常多,一部分任務表,節點表和詳情表的修改操作忽略了,值得關注的是插入了一條歷史變量表,即判斷審批通過的變量;更新了歷史任務表;更新了當前execution,當前節點變成EndEvent,即結束節點。后面又刪除了運行時的人員表,任務表和execution。根據參考文章,手動恢復ACT_RU_TASK,ACT_RU_EXECUTION,ACT_RU_IDENTITYLINK和ACT_RU_VARIABLE。涉及到審批相關變量result的ACT_RU_VARIABLE表的兩個查詢語句,第一據查詢 WHERE TASK_ID_ = '1892991' and NAME_= 'result';這個task_id是指子流程審批任務的id,第二句execution_id是該分支的execution,以及task_id為空,這里需要注意,如果手動退回后報無法解析expression表達式的錯,可能是查找變量時沒有找到,這里如果不恢復ACT_RU_VARIABLE表查找的也可能為全局變量result。
后面回退遇到問題時也可以結合執行的sql找出問題,這里不必贅述。

代碼實現回退邏輯

 
         
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.history.HistoricVariableInstance;
import org.activiti.engine.impl.persistence.entity.*;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;

/**
* phone為審批人的assignee值,nodeName是子流程審批節點名 */ @Transactional public void revoke(String businessId, String phone, String nodeName) { // 1.找到taskId,下面選取符合的assignee可以直接在query中篩選同businessId List<HistoricTaskInstance> htiList = historyService.createHistoricTaskInstanceQuery() .processInstanceBusinessKey(businessId) .orderByTaskCreateTime() .desc() .list(); String myTaskId = null; HistoricTaskInstance myTask = null; for(HistoricTaskInstance hti : htiList) { if(phone.equals(hti.getAssignee()) && nodeName.equals(hti.getName())) { myTaskId = hti.getId(); // 選取最后一個符合條件的task,即為最近的task myTask = hti; break; } } if(null==myTaskId) { throw new MyException(101,"該任務非當前用戶提交,無法撤回"); } /* 關於實體類,自己創建太麻煩,我直接使用activiti自帶的類,格式一般是“xxxEntityImpl”比如execution的實體用 ExecutionEntityImpl,具體的字段信息可以跟正規流程的一一對照。這里有個坑,ExecutionEntityImpl對象的isScope屬性初始化為true,必須修改為false,因為一個流程*實例只有一個主execution的isScope字段為true,其他execution的必須為false,如果設置錯了,手動退回后只能正常推進一次節點,然后該execution會被異常地刪除,導致流轉異常。 execution的acti_id字段指當前正在執行的節點id,ExecutionEntityImpl對象通過setcurrentFlowElement的方式賦值,需要從流程定義中獲取。 推進時流程的exection對象會根據這條記錄獲取,對象包含自身execution,主execution和活動節點activity對象 */ // 2.創建execution // 獲取當前節點對象,塞到executionEntity中 FlowElement flowElement = repositoryService.getBpmnModel(myTask.getProcessDefinitionId()).getMainProcess().getFlowElement(myTask.getTaskDefinitionKey()); ExecutionEntityImpl executionEntity = new ExecutionEntityImpl(); executionEntity.setId(myTask.getExecutionId()); executionEntity.setProcessInstanceId(myTask.getProcessInstanceId()); executionEntity.setProcessDefinitionId(myTask.getProcessDefinitionId()); executionEntity.setParentId(myTask.getProcessInstanceId()); executionEntity.setRootProcessInstanceId(myTask.getProcessInstanceId()); executionEntity.setCurrentFlowElement(flowElement); executionEntity.setStartTime(new Date()); // 子execution分支的is_scope的值為0,主分支的為1。這里一定要設置,否則只能暫時退回,再走一個節點該execution就會被刪除 executionEntity.setScope(false); executionEntity.setActive(true); /* mapper對象的sql可以寫對應表的增刪改查,我拿的是activiti中自身的mapperxml文件的對應語句(源碼activiti-engine-6.0.0的db.mapping包下),稍作修改 */ int ie = activitiMapper.insertExecution(executionEntity); if ( ie == 1) { logger.error("新添execution成功,id={}",myTask.getExecutionId()); } /* task的assignee字段其他的ru_task沒有,我也沒有加 */ // 3.insert task into ACT_RU_TASK TaskEntityImpl runTask = new TaskEntityImpl(); runTask.setId(myTask.getId()); runTask.setName(myTask.getName()); runTask.setPriority(myTask.getPriority()); //runTask.setAssignee(myTask.getAssignee()); runTask.setCreateTime(new Date()); runTask.setExecutionId(myTask.getExecutionId()); runTask.setProcessInstanceId(myTask.getProcessInstanceId()); runTask.setProcessDefinitionId(myTask.getProcessDefinitionId()); runTask.setTaskDefinitionKey(myTask.getTaskDefinitionKey()); int it = activitiMapper.insertTask(runTask); if( it == 1) { logger.error("新添task成功,id={}",myTask.getId()); } /* identityLink只需要添加該任務的一條記錄,用taskId篩選即可 */ // 4.insert task identitylink into ACT_RUN_IDENTITYLINK List<HistoricIdentityLinkEntityImpl> historicIdentityLinkEntities = activitiMapper.selectHistoricIdentityLinksByProcessInstanceAndTaskId(myTask.getProcessInstanceId(),myTask.getId()); for ( HistoricIdentityLinkEntity e: historicIdentityLinkEntities) { if (e.getTaskId() != null && e.getTaskId().equals(myTaskId)) { IdentityLinkEntityImpl identityLink = new IdentityLinkEntityImpl(); identityLink.setId(e.getId()); identityLink.setType(e.getType()); identityLink.setUserId(e.getUserId()); identityLink.setGroupId(e.getGroupId()); identityLink.setTaskId(e.getTaskId()); identityLink.setProcessInstanceId(e.getProcessInstanceId()); activitiMapper.insertIdentityLink(identityLink); } } /* 這里的variable根據情況加一條審批相關的變量,可能不用加;加了話需要注意子流程審批用到的變量是什么語句查詢的,有executionId和taskId的條件話就按條件給字段賦值。推節點時如果查不到會報無法解析表達式異常 */ // 5.insert variables into ACT_RU_VARIABLE List<HistoricVariableInstance> historicVariableInstances = processEngine.getHistoryService().createHistoricVariableInstanceQuery().executionId(myTask.getExecutionId()).list(); List<VariableInstanceEntity> variables = new ArrayList<>(); for (HistoricVariableInstance e : historicVariableInstances) { if ("result".equals(e.getVariableName())) { VariableInstanceEntityImpl v = new VariableInstanceEntityImpl(); v.setName(e.getVariableName()); v.setId(e.getId()); v.setTypeName(e.getVariableTypeName()); v.setExecutionId(myTask.getExecutionId()); v.setProcessInstanceId(e.getProcessInstanceId()); v.setTextValue(String.valueOf(e.getValue())); variables.add(v); break; } } int ih = activitiMapper.bulkInsertVariableInstance(variables); // 6.更改業務表,修改業務相關的邏輯 }

因為理解不了源碼,對流程的實際流轉過程認識有限,就只能通過多次嘗試,觀察相關表的記錄變更查看執行情況:查看act_ru_exection表,business_key篩選該訂單,然后根據該實例proc_inst_id查看task/variable/identityLink表,根據taskid查看variable/identitylink的該任務的記錄。在修改了不少細節,檢查了是否對后續子流程和其他子流程有影響后,成功回退了流程。

相關鏈接

https://blog.csdn.net/jiajane/article/details/103901187           -【Activiti】activiti 手動回退一個結束的流程
感謝這位博主的思路和邏輯


免責聲明!

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



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