关于activiti:SpringBoot-Activiti6系列教程八流程拒绝实现

64次阅读

共计 7112 个字符,预计需要花费 18 分钟才能阅读完成。

通用回绝

从这章开始,就正式进入 activiti 的实战开发,应用 activiti 实现各种审批动作,包含一些中国式流程操作,比方回退,咨询等,这些操作 activiti 的规范性能是没有的,但因为 activiti 不算简单,也比拟灵便,因而能够通过一些技巧或者变通的办法实现,这章就探讨 通用回绝 的实现。为什么叫通用回绝,因为在 activiti 里,失常的回绝都是通过连接线加条件判断实现,你能够定义一个变量如 outcome,回绝的时候给这个变量赋值REJECT,在连接线上设置条件表达式从而实现回绝操作。如图:

项目经理回绝到发起人的表达式为${outcome=='REJECT'},在流程里设置好变量就能实现回绝操作:

taskService.setVariable(taskId, "outcome", "approve");
taskService.complete(taskId);

这种回绝实现形式长处是简略,规范反对,灵活性强,可能从任意节点回绝回任意节点,但毛病也是显著的

  • 个别流程每个节点都有可能回绝,那就意味着每个节点都须要设置判断条件,如果都要回绝回发起人,那么都要跟发起人节点进行连贯,如果节点多的话会大大增加流程图的复杂度,让流程图变成一张“蜘蛛网”。

因而咱们须要一个通用回绝的性能,需要是,在任意节点回绝后主动回到发起人节点,发起人从新提交后流程从新开始。

那么面临的两个问题是

  • 流程图中没有发起人节点,怎么造出这个发起人节点
  • 流程曾经在流转中了,如何从新流转

咱们顺次解决以上两个问题

发起人节点解决

activiti 提供动静批改流程模型的 api,但批改流程模型后全局失效,所有的流程都会受影响,因而就算咱们能通过代码“造出”发起人几点,也是不可取的。其实认真想想,咱们是须要一个发起人节点,还是须要一个审批人是发起人的节点,显然,咱们的需要是前面那个,明确了这个情理后,问题就变得简略了,如何让以后节点的审批人变成发起人,计划能够是这样的:

  • 删除以后节点所有的待办,只保留一个待办
  • 将保留下来的那个待办审批人设置为发起人

通过以上两个步骤咱们能够实现回绝后将待办转移到发起人那里,当然为了在流程里可能获取到发起人,你应该在流程发动的时候将发起人信息存储到变量中。

那么问题又来了,咱们这是将以后节点伪造成了发起人节点,但假的毕竟是假的,等发起人一审批,就露馅了,因为流程会持续往下走,那么为了达到以假乱真的境地,咱们要持续实现以下两件事

  • 审批接口须要晓得以后节点的审批是否是“伪造”的发起人节点
  • 如果审批接口晓得了以后节点的审批是发起人发动的,那么就须要将流程从新拨回到第一个节点

第一个需要能够通过设置一个变量进行标识,第二个需要是咱们的下一个议题。

回绝实现代码参考:

public TaskResponse reject(TaskResponse task, String user) {
    // 删除所有以后 task,保留一个,并且将该 task 的审批人设为发起人
    // 设置 reject 标记
    Task t = taskService.createTaskQuery()
            .taskId(task.getTaskId())
            .singleResult();
    String instanceId = t.getProcessInstanceId();
    List<Task> tasks = taskService.createTaskQuery()
            .processInstanceId(instanceId)
            .list();
    Task luckyTask = tasks.get(0);
    managementService.executeCommand(new ExecutionVariableDeleteCmd(t.getExecutionId()));
    for (int i = 1; i < tasks.size(); ++i) {managementService.executeCommand(new TaskDeleteCmd(tasks.get(i).getId()));
        managementService.executeCommand(new ExecutionVariableDeleteCmd(tasks.get(i).getExecutionId()));
    }
    // 将发起人设置为以后审批人
    taskService.setAssignee(luckyTask.getId(), (String) taskService.getVariable(luckyTask.getId(), "submitter"));
    // 设置变量标识以后状态是已回绝状态
    taskService.setVariable(luckyTask.getId(), "status", "reject");
    return this.taskResponse(t, instanceId);
}

审批的代码参考如下:

String status = (String) taskService.getVariable(taskId, "status");
if ("reject".equals(status)) {
    // 发起人从新发动
    this.rollbackFirstask(task, user);
} else {
    // 失常审批
    taskService.complete(taskId, taskParams);
}

流程从新发动

剩下最初一个问题,就算下面的代码中 rollbackFirstask 如何实现,该办法将流程拨回到第一个节点从新开始,在探讨实现之前,咱们须要理解下命令模式,也是设计模式中的一种,其实并不生疏,咱们能够在日常的开发中就用到了,但并不知道原来这个还有一个专门的名字。简略说就像 Linux 下的 shell 脚本,调用一个个命令一样,将每个独立的操作封装成一个命令(Command),由命令调用者(Command Executor)进行调用,每个命令只负责本人的业务逻辑,不与其余命令交互,上下文信息(Command Context)由命令调用者提供。activiti 就是采纳命令模式对流程资源进行操作,比方删除一个工作,会有一个 DeleteTaskCmd 的命令类。activiti 命令申明如下:

public interface Command<T> {T execute(CommandContext commandContext);

}

命令调用者执行命令的 execute 办法,命令能够通过 commandContext 获取上下文,commandContext 里蕴含了对所有资源的治理类。理解了命令模式后,咱们就能够开始执行咱们的回滚计划了,具体计划步骤:

    1. 革除现场,革除所有两头过程的变量
    1. 找到开始节点,调用 api 将流程拨回到开始节点

实现代码如下:

/**
 * 流程回退到第一个节点
 *
 * @param context
 * @param request
 * @param user
 * @return
 */
public TaskResponse rollbackFirstask(String taskId, String user) {
    // 移除标记 REJECT 的 status
    taskService.removeVariable(taskId, "status");
    Task task = taskService.createTaskQuery().taskId(taskId).singleResult();

    // 删除工作
    managementService.executeCommand(new TaskDeleteCmd(request.getTaskId()));
    // 删除变量
    managementService.executeCommand(new ExecutionVariableDeleteCmd(task.getExecutionId()));
    // 将流程回滚到第一个节点
    managementService.executeCommand(new FlowToFirstCmd(task));
    return this.taskResponse(task.getProcessInstanceId());
}

几个命令的实现如下:

TaskDeleteCmd

import org.activiti.engine.impl.cmd.NeedsActiveTaskCmd;
import org.activiti.engine.impl.interceptor.Command;
import org.activiti.engine.impl.interceptor.CommandContext;
import org.activiti.engine.impl.persistence.entity.*;

import java.util.List;

/**
 * @Copyright: Shanghai Definesys Company.All rights reserved.
 * @Description:
 * @author: jianfeng.zheng
 * @since: 2019/9/24 6:09 PM
 * @history: 1.2019/9/24 created by jianfeng.zheng
 */
public class TaskDeleteCmd extends NeedsActiveTaskCmd<String> {public TaskDeleteCmd(String taskId) {super(taskId);
    }

    @Override
    public String execute(CommandContext commandContext, TaskEntity currentTask) {TaskEntityManagerImpl taskEntityManager = (TaskEntityManagerImpl) commandContext.getTaskEntityManager();
        ExecutionEntity executionEntity = currentTask.getExecution();
        taskEntityManager.deleteTask(currentTask, "reject", false, false);
        return executionEntity.getId();}
}

ExecutionVariableDeleteCmd

import org.activiti.engine.impl.interceptor.Command;
import org.activiti.engine.impl.interceptor.CommandContext;
import org.activiti.engine.impl.persistence.entity.VariableInstanceEntity;
import org.activiti.engine.impl.persistence.entity.VariableInstanceEntityManager;

import java.util.List;

/**
 * @Copyright: Shanghai Definesys Company.All rights reserved.
 * @Description:
 * @author: jianfeng.zheng
 * @since: 2019/9/24 6:10 PM
 * @history: 1.2019/9/24 created by jianfeng.zheng
 */
public class ExecutionVariableDeleteCmd implements Command<String> {
    private String executionId;

    public ExecutionVariableDeleteCmd(String executionId) {this.executionId = executionId;}

    @Override
    public String execute(CommandContext commandContext) {VariableInstanceEntityManager vm = commandContext.getVariableInstanceEntityManager();
        List<VariableInstanceEntity> vs = vm.findVariableInstancesByExecutionId(this.executionId);
        for (VariableInstanceEntity v : vs) {vm.delete(v);
        }
        return executionId;
    }
}

FlowToFirstCmd

import com.definesys.mpaas.common.exception.MpaasBusinessException;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.impl.interceptor.Command;
import org.activiti.engine.impl.interceptor.CommandContext;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.task.Task;

import java.util.List;

/**
 * @Copyright: Shanghai Definesys Company.All rights reserved.
 * @Description:
 * @author: jianfeng.zheng
 * @since: 2019/9/25 12:36 AM
 * @history: 1.2019/9/25 created by jianfeng.zheng
 */
public class FlowToFirstCmd implements Command<String> {

    private Task task;

    public FlowToFirstCmd(Task task) {this.task = task;}

    @Override
    public String execute(CommandContext context) {FlowElement startNode = this.getFirstNode(this.task, context);
        ExecutionEntity executionEntity = context.getExecutionEntityManager().findById(task.getExecutionId());
        executionEntity.setCurrentFlowElement(startNode);
        context.getAgenda().planTakeOutgoingSequenceFlowsOperation(executionEntity, true);
        return executionEntity.getId();}

    private FlowElement getFirstNode(Task task, CommandContext context) {HistoryService historyService = context.getProcessEngineConfiguration().getHistoryService();
        HistoricActivityInstance startNode = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(task.getProcessInstanceId())
                .activityType("startEvent")
                .singleResult();
        if (startNode == null) {throw new MpaasBusinessException("未找到开始节点");
        }
        RepositoryService repositoryService = context.getProcessEngineConfiguration().getRepositoryService();
        org.activiti.bpmn.model.Process process = repositoryService.getBpmnModel(task.getProcessDefinitionId()).getMainProcess();
        FlowElement node = process.getFlowElement(startNode.getActivityId());
        return node;
    }
}

总结

其实,略微革新下 FlowToFirstCmd 命令,就能将流程路由到任意节点,一开始咱们也想靠这个实现任意节点路由的性能,但认真一想外面的坑十分多,遇到子流程,并行审批等简单的流程时,会产生很多矛盾点,想想也就放弃了。

正文完
 0