業(yè)務(wù)描述: 進行會簽任務(wù)時候其徙,時常會遇到任務(wù)執(zhí)行中,需要進行加簽,減簽。本章主要描述如何實現(xiàn)
版本:SpringBoot 1.5.19.RELEASE + Activiti 6.0
GitHub https://github.com/oldguys/ActivitiDemo
前置知識:
- org.activiti.engine.impl.interceptor.Command:任務(wù)命令接口滤钱,Activiti具有任務(wù)執(zhí)行機制,都是基于這個接口進行實現(xiàn)枷踏。如:
org.activiti.engine.impl.cmd.CompleteTaskCmd:任務(wù)完成命令- org.activiti.engine.ManagementService: 任務(wù)管理服務(wù)接口菩暗,負責(zé)管理服務(wù),用于完成 Command 任務(wù)
- org.activiti.engine.impl.persistence.entity.ExecutionEntityManager:用于管理Execution。
- Context.getAgenda().planContinueMultiInstanceOperation(newExecution); : 任務(wù)時間軸
- 多實例任務(wù)行為解釋器
org.activiti.bpmn.model.MultiInstanceLoopCharacteristics: 用于生成多實例任務(wù)
org.activiti.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior:并行任務(wù)解釋器;
org.activiti.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior: 串行任務(wù)解釋器- Activiti 是根據(jù)execution進行 流轉(zhuǎn)的旭蠕,在會簽任務(wù)的時候,會在生成 子級(多個)execution旷坦,當(dāng)所有子任務(wù)都完成的時候掏熬,銷毀 子 execution,回到 父級 execution 進行流轉(zhuǎn)秒梅。
- 參數(shù) ( act_ru_task):
(父級 execution 變量)
- nrOfInstances :子任務(wù)總個數(shù)
- nrOfCompletedInstances: 當(dāng)前已完成子任務(wù)個數(shù)
- nrOfActiveInstances: 當(dāng)前活躍任務(wù)個數(shù)(未完成)
(子級 execution 變量)
- loopCounter: 任務(wù)列表下標(biāo)
- assignee: 任務(wù)執(zhí)行人(可以根據(jù)需要配置不同變量 XML)
實現(xiàn)思路:
會簽 加簽
并行:Activiti會基于父級 execution 創(chuàng)建多個子 execution 再根據(jù)子execution 創(chuàng)建多個任務(wù)旗芬,所以實現(xiàn)加簽的時候,根據(jù) 父級 execution 和 節(jié)點 生成新的 execution 捆蜀,再生成任務(wù)疮丛。
串行:Activiti會基于 父級 execution 只創(chuàng)建一個 子execution幔嫂,每完成一個任務(wù),創(chuàng)建下一個任務(wù)誊薄。在開始串行會簽任務(wù)前履恩,需要傳入一個變量 assigneeList,而這個變量會被 序列號 到 act_ru_task 中呢蔫。在運行階段切心,根據(jù) loopCounter (數(shù)組下標(biāo)),從assigneeList中獲取 任務(wù)執(zhí)行人片吊。所以需要進行加簽時绽昏,只需要傳入列表并修改才行。
注意: 不論串行并行俏脊,在修改完任務(wù)之后全谤,都需要修改父級變量計數(shù)器
測試流程圖:
加簽任務(wù): com.oldguy.example.modules.workflow.commands.AddMultiInstanceExecutionCmd
public class AddMultiInstanceExecutionCmd extends AbstractCountersignCmd implements Command<String>, CountersigningVariables {
/**
* 當(dāng)前任務(wù)ID
*/
private String taskId;
/**
* 審核人
*/
private List<String> assigneeList;
/**
* 任務(wù)執(zhí)行人
*/
private String assignee;
public AddMultiInstanceExecutionCmd(String taskId, List<String> assigneeList) {
super();
if (ObjectUtils.isEmpty(assigneeList)) {
throw new RuntimeException("assigneeList 不能為空!");
}
this.taskId = taskId;
this.assigneeList = assigneeList;
}
public AddMultiInstanceExecutionCmd(String taskId, List<String> assigneeList, String assignee) {
super();
if (ObjectUtils.isEmpty(assigneeList)) {
throw new RuntimeException("assigneeList 不能為空!");
}
this.taskId = taskId;
this.assigneeList = assigneeList;
this.assignee = assignee;
}
@Override
public String execute(CommandContext commandContext) {
TaskEntityImpl task = (TaskEntityImpl) taskService.createTaskQuery().taskId(taskId).singleResult();
ExecutionEntityImpl execution = (ExecutionEntityImpl) runtimeService.createExecutionQuery().executionId(task.getExecutionId()).singleResult();
BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
Process process = bpmnModel.getProcesses().get(0);
UserTask userTask = (UserTask) process.getFlowElement(task.getTaskDefinitionKey());
if (userTask.getLoopCharacteristics() == null) {
// TODO
Log4jUtils.getInstance(getClass()).error("task:[" + task.getId() + "] 不是會簽節(jié)任務(wù)");
}
/**
* 獲取父級
*/
ExecutionEntityImpl parentNode = execution.getParent();
/**
* 獲取流程變量
*/
int nrOfInstances = (int) runtimeService.getVariable(parentNode.getId(), NUMBER_OF_INSTANCES);
int nrOfActiveInstances = (int) runtimeService.getVariable(parentNode.getId(), NUMBER_OF_ACTIVE_INSTANCES);
/**
* 獲取管理器
*/
ExecutionEntityManager executionEntityManager = Context.getCommandContext().getExecutionEntityManager();
Object behavior = userTask.getBehavior();
if (behavior instanceof ParallelMultiInstanceBehavior) {
Log4jUtils.getInstance(getClass()).info("task:[" + task.getId() + "] 并行會簽 加簽 任務(wù)");
/**
* 設(shè)置循環(huán)標(biāo)志變量
*/
runtimeService.setVariable(parentNode.getId(), NUMBER_OF_INSTANCES, nrOfInstances + assigneeList.size());
runtimeService.setVariable(parentNode.getId(), NUMBER_OF_ACTIVE_INSTANCES, nrOfActiveInstances + assigneeList.size());
/**
* 新建任務(wù)列表
*/
for (String assignee : this.assigneeList) {
/**
* 創(chuàng)建 子 execution
*/
ExecutionEntity newExecution = executionEntityManager.createChildExecution(parentNode);
newExecution.setActive(true);
newExecution.setVariableLocal(LOOP_COUNTER, nrOfInstances);
newExecution.setVariableLocal(ASSIGNEE_USER, assignee);
newExecution.setCurrentFlowElement(userTask);
/**
* 任務(wù)總數(shù) +1
*/
nrOfInstances++;
/**
* 推入時間表序列
*/
Context.getAgenda().planContinueMultiInstanceOperation(newExecution);
}
} else if (behavior instanceof SequentialMultiInstanceBehavior) {
Log4jUtils.getInstance(getClass()).info("task:[" + task.getId() + "] 串行會簽 加簽 任務(wù)");
/**
* 是否需要替換審批人
*/
boolean changeAssignee = false;
if (StringUtils.isEmpty(assignee)) {
assignee = task.getAssignee();
changeAssignee = true;
}
/**
* 當(dāng)前任務(wù)執(zhí)行位置
*/
int loopCounterIndex = -1;
for (int i = 0; i < assigneeList.size(); i++) {
String temp = assigneeList.get(i);
if (assignee.equals(temp)) {
loopCounterIndex = i;
}
}
if (loopCounterIndex == -1) {
throw new RuntimeException("任務(wù)審批人不存在于任務(wù)執(zhí)行人列表中");
}
/**
* 修改當(dāng)前任務(wù)執(zhí)行人
*/
if (changeAssignee) {
taskService.setAssignee(taskId, assignee);
execution.setVariableLocal(ASSIGNEE_USER, assignee);
}
/**
* 修改 計數(shù)器位置
*/
execution.setVariableLocal(LOOP_COUNTER, loopCounterIndex);
/**
* 修改全局變量
*/
Map<String, Object> variables = new HashMap<>(3);
variables.put(NUMBER_OF_INSTANCES, assigneeList.size());
variables.put(NUMBER_OF_COMPLETED_INSTANCES, loopCounterIndex);
variables.put(ASSIGNEE_LIST, assigneeList);
runtimeService.setVariables(parentNode.getId(), variables);
}
return "加簽成功";
}
}
會簽 減簽
并行:根據(jù)上面的解釋,同理爷贫,在進行會簽減簽的時候啼县,只需要刪除 相關(guān)的 子 execution 并且修改 父級計數(shù)器值就可以完成減簽。
串行:串行減簽與加簽邏輯相似沸久,只不過把加變成減而已
減簽:com.oldguy.example.modules.workflow.commands.DeleteMultiInstanceExecutionCmd
package com.oldguy.example.modules.workflow.commands;
import com.oldguy.example.modules.common.utils.Log4jUtils;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.UserTask;
import org.activiti.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior;
import org.activiti.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior;
import org.activiti.engine.impl.context.Context;
import org.activiti.engine.impl.interceptor.Command;
import org.activiti.engine.impl.interceptor.CommandContext;
import org.activiti.engine.impl.persistence.entity.ExecutionEntityImpl;
import org.activiti.engine.impl.persistence.entity.ExecutionEntityManager;
import org.activiti.engine.impl.persistence.entity.TaskEntityImpl;
import org.activiti.engine.task.Task;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import java.util.*;
/**
* @ClassName: DeleteMultiInstanceExecutionCmd
* @Author: ren
* @Description: 進行會簽減簽 flowable:org.flowable.engine.impl.cmd.DeleteMultiInstanceExecutionCmd
* @CreateTIme: 2019/5/9 0009 下午 3:05
**/
public class DeleteMultiInstanceExecutionCmd extends AbstractCountersignCmd implements Command<String>, CountersigningVariables {
/**
* 當(dāng)前任務(wù)ID
*/
private String taskId;
/**
* 審核人
*/
private List<String> assigneeList;
public DeleteMultiInstanceExecutionCmd(String taskId, List<String> assigneeList) {
super();
if (ObjectUtils.isEmpty(assigneeList)) {
throw new RuntimeException("assigneeList 不能為空!");
}
this.taskId = taskId;
this.assigneeList = assigneeList;
}
@Override
public String execute(CommandContext commandContext) {
TaskEntityImpl task = (TaskEntityImpl) taskService.createTaskQuery().taskId(taskId).singleResult();
BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
Process process = bpmnModel.getProcesses().get(0);
UserTask userTask = (UserTask) process.getFlowElement(task.getTaskDefinitionKey());
if (userTask.getLoopCharacteristics() == null) {
// TODO
Log4jUtils.getInstance(getClass()).error("task:[" + task.getId() + "] 不是會簽節(jié)任務(wù)");
}
ExecutionEntityImpl execution = (ExecutionEntityImpl) runtimeService.createExecutionQuery().executionId(task.getExecutionId()).singleResult();
ExecutionEntityImpl parentNode = execution.getParent();
/**
* 獲取任務(wù)完成數(shù)
*/
int nrOfCompletedInstances = (int) runtimeService.getVariable(parentNode.getId(), NUMBER_OF_COMPLETED_INSTANCES);
/**
* 轉(zhuǎn)換判斷標(biāo)識
*/
Set<String> assigneeSet = new HashSet<>(assigneeList);
ExecutionEntityManager executionEntityManager = Context.getCommandContext().getExecutionEntityManager();
Object behavior = userTask.getBehavior();
/**
* 進行并行任務(wù) 減簽
*/
if (behavior instanceof ParallelMultiInstanceBehavior) {
Log4jUtils.getInstance(getClass()).info("task:[" + task.getId() + "] 并行會簽 減簽 任務(wù)");
/**
* 當(dāng)前任務(wù)列表
*/
List<Task> taskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstance().getProcessInstanceId()).list();
List<Task> removeTaskList = new ArrayList<>(assigneeSet.size());
List<Task> existTaskList = new ArrayList<>(taskList.size() - assigneeSet.size());
taskList.forEach(obj -> {
if (assigneeSet.contains(obj.getAssignee())) {
removeTaskList.add(obj);
ExecutionEntityImpl temp = (ExecutionEntityImpl) runtimeService.createExecutionQuery().executionId(obj.getExecutionId()).singleResult();
executionEntityManager.deleteExecutionAndRelatedData(temp, "會簽減簽", true);
} else {
existTaskList.add(obj);
}
});
/**
* 修改已完成任務(wù)變量,增加被刪減任務(wù)
*/
runtimeService.setVariable(parentNode.getId(), NUMBER_OF_COMPLETED_INSTANCES, nrOfCompletedInstances + removeTaskList.size());
} else if (behavior instanceof SequentialMultiInstanceBehavior) {
Log4jUtils.getInstance(getClass()).info("task:[" + task.getId() + "] 串行會簽 減簽 任務(wù)");
Object obj = parentNode.getVariable(ASSIGNEE_LIST);
if (obj == null || !(obj instanceof ArrayList)) {
throw new RuntimeException("沒有找到任務(wù)執(zhí)行人列表");
}
ArrayList<String> sourceAssigneeList = (ArrayList) obj;
List<String> newAssigneeList = new ArrayList<>();
boolean flag = false;
int loopCounterIndex = -1;
String newAssignee = "";
for (String temp : sourceAssigneeList) {
if (!assigneeSet.contains(temp)) {
newAssigneeList.add(temp);
}
if (flag) {
newAssignee = temp;
flag = false;
}
if (temp.equals(task.getAssignee())) {
if (assigneeSet.contains(temp)) {
flag = true;
loopCounterIndex = newAssigneeList.size();
} else {
loopCounterIndex = newAssigneeList.size() - 1;
}
}
}
/**
* 修改計數(shù)器變量
*/
Map<String, Object> variables = new HashMap<>();
variables.put(NUMBER_OF_INSTANCES, newAssigneeList.size());
variables.put(NUMBER_OF_COMPLETED_INSTANCES, loopCounterIndex > 0 ? loopCounterIndex - 1 : 0);
variables.put(ASSIGNEE_LIST, newAssigneeList);
runtimeService.setVariables(parentNode.getId(), variables);
/**
* 當(dāng)前任務(wù)需要被刪除季眷,需要替換下一個任務(wù)審批人
*/
if (!StringUtils.isEmpty(newAssignee)) {
taskService.setAssignee(taskId, newAssignee);
execution.setVariable(LOOP_COUNTER, loopCounterIndex);
execution.setVariable(ASSIGNEE_USER, newAssignee);
}
}
return "減簽成功";
}
}
其他代碼
抽象父類: com.oldguy.example.modules.workflow.commands.AbstractCountersignCmd
package com.oldguy.example.modules.workflow.commands;
import com.oldguy.example.modules.common.utils.SpringContextUtils;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
/**
* @ClassName: AbstractCountersignCmd
* @Author: ren
* @Description:
* @CreateTIme: 2019/5/13 0013 下午 11:43
**/
public abstract class AbstractCountersignCmd {
protected RuntimeService runtimeService;
protected TaskService taskService;
protected RepositoryService repositoryService;
public AbstractCountersignCmd(){
runtimeService = SpringContextUtils.getBean(RuntimeService.class);
taskService = SpringContextUtils.getBean(TaskService.class);
repositoryService = SpringContextUtils.getBean(RepositoryService.class);
}
}
通用接口參數(shù): com.oldguy.example.modules.workflow.commands.CountersigningVariables
package com.oldguy.example.modules.workflow.commands;
import org.activiti.engine.impl.bpmn.behavior.MultiInstanceActivityBehavior;
/**
* Activiti 會簽任務(wù)中變量標(biāo)志
*
* {@link MultiInstanceActivityBehavior}
*/
public interface CountersigningVariables {
/**
* 默認審核人
*/
String ASSIGNEE_USER = "assignee";
/**
* 審核人集合
*/
String ASSIGNEE_LIST = "assigneeList";
/**
* 會簽任務(wù)總數(shù)
*/
String NUMBER_OF_INSTANCES = "nrOfInstances";
/**
* 正在執(zhí)行的會簽總數(shù)
*/
String NUMBER_OF_ACTIVE_INSTANCES = "nrOfActiveInstances";
/**
* 已完成的會簽任務(wù)總數(shù)
*/
String NUMBER_OF_COMPLETED_INSTANCES = "nrOfCompletedInstances";
/**
* 會簽任務(wù)表示
* collectionElementIndexVariable
*/
String LOOP_COUNTER = "loopCounter";
}