04 完成审批任务
审批流程图
如下图,在此流程图中,存在两个UserTask节点,第一个节点是主管审批,第二个节点是产品经理审批,两个节点中间有一个排他网关,此网关用来对主管审批的结果进行判断,如果主管审批通过,则流程走到产品经理审批节点,如果主管审批拒绝,则流程走到结束节点。
主管审批节点通过UEL表达式${assignManager}动态赋值,产品经理审批节点通过UEL表达式${assignProductLineManager}动态赋值,网关节点通过UEL表达式${isPass}动态赋值。
完成审批任务
以 TaskService 接口的 complete(String, Map) 方法为源码入口。
public interface TaskService {public void complete(String taskId, Map<String, Object> variables)
}
拦截器链
TaskService 的实现类是 TaskServiceImpl,在代码中,把传入的 taskId 和变量 varriables 一起放入到了 CompleteTaskCmd 的构造函数中,用于构造一个 CompleteTaskCmd 实例,然后把这个实例放入到了 commandExecutor 执行器中。
public class TaskServiceImpl extends ServiceImpl implements TaskService {public void complete(String taskId, Map<String, Object> variables) {commandExecutor.execute(new CompleteTaskCmd(taskId, variables));}
}
CompleteTaskCmd 实例在执行器的逻辑和 StartProcessInstanceCmd 一样,都会经过执行器内部拦截器链中的四个拦截器。最后由处于最末端的拦截器 CommandInvoker 调用 CompleteTaskCmd 实例的 execute(CommandContext) 来完成审批任务。
完成审批
CompleteTaskCmd 的继承体系如下。
CompleteTaskCmd 本身并没有实现 Command 接口中的 execute(CommandContext) 方法,而是由父类 NeedsActiveTaskCmd 来实现的。
public abstract class NeedsActiveTaskCmd<T> implements Command<T>, Serializable {/*** 用户调用TaskService#complete(String taskId, xxx)后,CommandInvoker会调用此类,正真的执行此命令* @param commandContext* @return*/public T execute(CommandContext commandContext) {if (taskId == null) {throw new ActivitiIllegalArgumentException("taskId is null");}// 通过 taskId 从数据库中查询 TaskEntityTaskEntity task = commandContext.getTaskEntityManager().findById(taskId);if (task == null) {throw new ActivitiObjectNotFoundException("Cannot find task with id " + taskId, Task.class);}if (task.isSuspended()) {throw new ActivitiException(getSuspendedTaskException());}// 调用子类实现的 execute(commandContext, task) 方法return execute(commandContext, task);}
}
在父类 NeedsActiveTaskCmd 的 execute(CommandContext) 方法中会调用 CompleteTaskCmd 实现的 execute(CommandContext, TaskEntity) 方法。在方法里,把传入的变量绑定到 task 中。紧接着调用父类 AbstractCompleteTaskCmd 的 executeTaskComplete(CommandContext, TaskEntity, Map , boolean) 方法去完成任务的审批。
/*** 父类 NeedsActiveTaskCmd 的 execute(CommandContext commandContext) 方法会调用此方法* @param commandContext 命令上下文* @param task 任务* @return*/
protected Void execute(CommandContext commandContext, TaskEntity task) {if (variables != null) {// 绑定变量if (localScope) {task.setVariablesLocal(variables);} else if (task.getExecutionId() != null) {task.setExecutionVariables(variables);} else {task.setVariables(variables);}}if (transientVariables != null) {if (localScope) {task.setTransientVariablesLocal(transientVariables);} else {task.setTransientVariables(transientVariables);}}if(commandContext.getProcessEngineConfiguration().isCopyVariablesToLocalForTasks()){TaskVariableCopier.copyVariablesOutFromTaskLocal(task);}// 完成任务,此方法是父类 AbstractCompleteTaskCmd 的executeTaskComplete(commandContext, task, variables, localScope);return null;
}
AbstractCompleteTaskCmd 的 executeTaskComplete(CommandContext, TaskEntity, Map , boolean) 方法实现如下:
public abstract class AbstractCompleteTaskCmd extends NeedsActiveTaskCmd<Void> {private static final long serialVersionUID = 1L;public AbstractCompleteTaskCmd(String taskId) {super(taskId);}protected void executeTaskComplete(CommandContext commandContext, TaskEntity taskEntity, Map<String, Object> variables, boolean localScope) {// Task complete logicif (taskEntity.getDelegationState() != null && taskEntity.getDelegationState().equals(DelegationState.PENDING)) {throw new ActivitiException("A delegated task cannot be completed, but should be resolved instead.");}commandContext.getProcessEngineConfiguration().getListenerNotificationHelper().executeTaskListeners(taskEntity, TaskListener.EVENTNAME_COMPLETE);if (Authentication.getAuthenticatedUserId() != null && taskEntity.getProcessInstanceId() != null) {ExecutionEntity processInstanceEntity = commandContext.getExecutionEntityManager().findById(taskEntity.getProcessInstanceId());// 记录身份连接类型,实际就是记录 ExecutionEntity 和 User 的关联关系,信息会存储到 act_hi_identitylink 中commandContext.getIdentityLinkEntityManager().involveUser(processInstanceEntity, Authentication.getAuthenticatedUserId(), IdentityLinkType.PARTICIPANT);}ActivitiEventDispatcher eventDispatcher = Context.getProcessEngineConfiguration().getEventDispatcher();if (eventDispatcher.isEnabled()) {if (variables != null) {eventDispatcher.dispatchEvent(ActivitiEventBuilder.createEntityWithVariablesEvent(ActivitiEventType.TASK_COMPLETED, taskEntity, variables, localScope));} else {eventDispatcher.dispatchEvent(ActivitiEventBuilder.createEntityEvent(ActivitiEventType.TASK_COMPLETED, taskEntity));}}// 任务完成,删除任务commandContext.getTaskEntityManager().deleteTask(taskEntity, null, false, false);// Continue process (if not a standalone task)if (taskEntity.getExecutionId() != null) {ExecutionEntity executionEntity = commandContext.getExecutionEntityManager().findById(taskEntity.getExecutionId());// 计划触发执行操作,本质就是:当前任务已经完成,计划继续前往下一个节点。Context.getAgenda().planTriggerExecutionOperation(executionEntity);}}}
总结
用户完成审批任务这一操作,从 Activiti 框架角度来讲,就是调用了一次 TaskService 的 complete 方法,在这个方法内部的主要业务逻辑如下:
1. 通过 taskId 从数据库加载 TaskEntity;
2. 给 TaskEntity 绑定变量(前提是有变量传入);
3. 记录身份链接类型,把身份链接类型存储入库,审批完成;
4. 删除数据库中记录的 TaskEntity;
5. 继续前往下一个节点(如果有才往下一个节点走)。
这个逻辑上,并没有过多复杂业务,仅仅是记录当前 Task 由谁完成了,然后就前往下一个节点了。若要实现日常中常见的审批拒绝、审批驳回这些功能,需额外进行二次开发。