目錄
一、前言
流程流轉時,某個環節的任務處理人變更(即改派)是一種非常常見的需求,本來任務處理人可能是A,但是A因爲有事處理不了,管理員需要將該單子改派給B臨時處理下。
在中國式流程中,流程的 回退 也是必不可少的,流程流轉到某個環節時,處理人一看:咦 這填的都是啥玩意兒,根本不符合要求,想直接打回給上一環節或者走過的任意環節,那麼就需要支持回退功能;有時候流程方面還有特殊的需求,比如越級上報,某些處理人權限比較高運行跳過某些環節直接執行,甚至說可以一步走到底,這便依賴Activiti的自由流程
本文就簡單介紹下Activiti中如何實現改派和回退。
二、改派實現
在實際使用Activiti中,絕大多數公司都是有一套自己的用戶管理系統,而且Activiti自身的用戶和用戶組功能其實也並不能滿足部分需求,比如想分配給一個部門、一個角色等等。因此Activiti中的人員信息其實不是那麼重要,在我們項目中甚至不會從裏面去獲取人員信息,僅僅只是設值。Activiti的任務處理人主要是存放在 ACT_RU_TASK 的 ASSIGNEE_ 字段中以及ACT_RU_IDENTITYLINK,前者主要是單個審覈人的情況,後者主要是候選人的情況。
其實改派的實現很簡單,就是爲當前任務重新設置處理人,調用如下方法即可:
taskService.setAssignee("taskId", "userId");
當使用流程變量的方式設置處理人時,在業務上可能是多個處理人或處理組,因此在 ASSIGNEE_ 中存的可能是個用戶數組,形如:[a,b,b] 。由於 ASSIGNEE_ 字段是varchar類型且僅爲255,因此用戶id如果是uuid格式的話,最多隻能存儲7個...
設置處理人的api還挺多,還有一種方式也行:
taskService.unclaim("taskId");
taskService.claim("taskId", "userId");
該方式與上面的主要區別是,claim方法會去檢查當前是否已經有處理人,如果有則會拋錯,需要先調用 unclaim 方法。
三、自由跳轉實現
Activiti6的最大變化便是對代碼的重構,其去掉了原先的pvm,直接針對bpmn處理,因此性能上會有些許提升(也沒試驗過,之前項目中使用的是Activiti5.22,因此原先的自由跳轉與6的實現已經完全不一樣了,題外話,pvm主要就是負責流程整個運行期的執行、流轉等所有運行操作,其所有實現類都在org.activiti.engine.impl.pvm包裏,但是6全部移除了...)
這裏主要演示回退的場景,先看上面的流程圖,假設當前任務已經流轉到領導審批,領導一看,你這審批信息填的不正確鴨,需要駁回,啪一點,此時流程需回退到審批信息填寫環節,如何實現呢?
思路1
在運行過程中,動態修改Activiti的流程定義,修改領導審批環節的出線,使之流轉到審批信息填寫,在流轉完成後再恢領導審批原先的出線(即結束環節),該方式雖然也能實現自由跳轉,但是存在併發的問題,假設A點擊回退,此時流程定義已經被修改,領導審批的出線已經變爲審批信息填寫,同時B點擊提交,卻發現流程流轉到了審批信息填寫...B說簡直見了鬼。因此不可取
思路2
使用執行計劃直接指定當前流程實例執行所選環節,由於在併發情況下當前任務的執行id是唯一的,因此該方式也沒有帶來不安全性,下面基於此實現貼代碼:
首先是兩個命令,即刪除當前任務命令以及執行自由跳轉命令:
/**
* 繼承NeedsActiveTaskCmd主要是爲了在跳轉時要求當前任務不能是掛起狀態,也可以直接實現Command接口
* Created by xujia on 2020/2/10
*/
public class DeleteTaskCmd extends NeedsActiveTaskCmd<String> {
public DeleteTaskCmd(String taskId){
super(taskId);
}
public String execute(CommandContext commandContext, TaskEntity currentTask){
TaskEntityManagerImpl taskEntityManager = (TaskEntityManagerImpl)commandContext.getTaskEntityManager();
// 獲取當前任務的執行對象實例
ExecutionEntity executionEntity = currentTask.getExecution();
// 刪除當前任務,來源任務
taskEntityManager.deleteTask(currentTask, "jumpReason", false, false);
// 返回當前任務的執行對象id
return executionEntity.getId();
}
public String getSuspendedTaskException() {
return "掛起的任務不能跳轉";
}
}
/**
* 執行自由跳轉命令
* Created by xujia on 2020/2/10
*/
public class SetFLowNodeAndGoCmd implements Command<Void> {
/**
* 目標節點對象
*/
private FlowNode flowElement;
/**
* 當前任務執行id
*/
private String executionId;
public SetFLowNodeAndGoCmd(FlowNode flowElement,String executionId){
this.flowElement = flowElement;
this.executionId = executionId;
}
public Void execute(CommandContext commandContext){
// 獲取目標節點的來源連線
List<SequenceFlow> flows = flowElement.getIncomingFlows();
if(flows==null || flows.size()<1){
throw new ActivitiException("回退錯誤,目標節點沒有來源連線");
}
// 隨便選一條目標節點的入線來執行,使當前執行計劃爲:從所選擇的流程線流轉到目標節點,實現跳轉
ExecutionEntity executionEntity = commandContext.getExecutionEntityManager().findById(executionId);
executionEntity.setCurrentFlowElement(flows.get(0));
commandContext.getAgenda().planTakeOutgoingSequenceFlowsOperation(executionEntity, true);
return null;
}
}
然後是調用:
@Test
public void listTaskTest() {
// 獲取當前任務
Task currentTask = taskService.createTaskQuery().taskId("132502").singleResult();
BpmnModel bpmnModel = repositoryService.getBpmnModel(currentTask.getProcessDefinitionId());
// 獲取流程定義
Process process = bpmnModel.getMainProcess();
// 獲取目標節點定義
FlowNode targetNode = (FlowNode) process.getFlowElement("sid-C24BA4F5-F744-4DD7-8D51-03C3698044D2");
// 刪除當前運行任務,同時返回執行id,該id在併發情況下也是唯一的
String executionEntityId = managementService.executeCommand(new DeleteTaskCmd(currentTask.getId()));
// 流程執行到來源節點
managementService.executeCommand(new SetFLowNodeAndGoCmd(targetNode, executionEntityId));
}
運行之後就會發現流程直接流轉到審批信息填寫環節,且處理人也變成原先的處理人,靈活運用可實現多種多樣的需求。