关于事务流的思考

关于事务流的思考

1 事务流业务分析

​ 不同业务可能有不同的审核流程,而activiti为大家提供了一套公用的审核功能,基于这些功能我们可以根据自己的业务需求组合出我们自己的审核流程,而这里我要实现的事务流有如下功能:角色为结点审核人,指定审核人,退回到上一结点,完成任务,流程预判。

1.1 角色审核人

​ 也就是说在每一个审核结点都是某一个角色的所有用户作为候选审核人,当某个用户不再是这个角色的时候也就不能看到对应的事务流程。

1.2 指定审核人

​ 申请人可以指定一级审核人,审核人可以指定下一级审核人

1.3 退回到上一个结点

​ 可以退回到上一结点

1.4 退回到申请人(拒绝申请)

​ 终止流程,设置变量为不通过

1.5 完成任务

​ 审核人完成任务

1.6 流程预判

​ 可以追踪自己的申请流程

2 activiti框架

​ 我们从使用流程数据表activiti架构API整体代码示例五个方面来介绍。

2.1 activiti使用流程

定义流程模板

  1. 定义bpmn图

​ 流程模板有key和name两个主要的属性,key用于唯一标记此流程模板,name用于提示作用,一个流程模板可以部署多次。

image-20240601100206482

  1. 根据bpmn图部署一套事务流程模板

​ 部署流程模板会把此流程模型信息部署定义信息流程定义信息

(1)流程模型信息

​ 根据bpmn图部署一套事务流模板,activiti会把事务流模型以字节流的形式存储在数据库,所以,每次的流程判断都是要把此信息取出来做分析处理的。

(2)部署定义信息

​ 主要记录本次部署的相关信息,比如部署时间等,用处不大。

(3)流程定义数据

​ 记录流程定义相关重要信息,比如流程key、流程name等。与此同时,由于一个bpmn模板可以部署多次,所以有一个版本的属性来代表此bpmn流程模板的版本,所以此时唯一的id就变成了“流程key”和“版本号”的组合

创建流程实例

申请流程实例化对象——》审核结点完成任务——》结束,在这个过程中涉及多个概念,下面一一概述。

  1. 实例化对象

​ 选择一个事务流程模板,根据此模板实例化一个申请流程

  1. 变量

​ 开启一个流程实例的时候可以给整个流程传入各种变量

​ ①存储方式:变量信息存储在ACT_RU_VARIABLE表,是直接跟流程定义信息绑定的

​ ②用法介绍:有自定义使用和自动使用两种用法,前者指的是你可以在任何结点根据流程id获取变量自由使用,后者则是更加常用的一种方式,详见③

​ ③自动使用:在定义bpmn流程模板的时候,凡是可以自己去定义的数据比如审核人等都可以定义成变量,当实例化一个流程的时候只需要将变量和此流程绑定,后续就可以自动赋值(举例:审核人assignee定义为${assignee0},当实例化一个流程的时候只需要将assignee0绑定此流程即可)

  1. businessKey

​ 业务id,可以与流程实例绑定,后续可以根据流程实例取出

完成任务

  1. 任务

​ 流程走到哪个审核结点,都会给此审核结点生成一个对应的任务,不管审核通过还是退回,此结点都会被销毁(但是会被记录在历史记录里面)

  1. 审核结点

​ 审核人可以有一个或者多个,activiti针对这两种情况分别给出了对应的方案

(1)审核人类型

​ 审核人类型有Assignee、CandidateUser两种类型(CandidataGroups在版本7中弃用了)

​ **①Assignee类型:**指的是审核人,只能指定一个

​ **②CandidateUser类型:**指的是候选人,候选人可以有多个,在activiti中用","隔开就行

(2)完成任务

​ 在activiti中的任务没有审核通过和退回的操作,只有完成任务的操作,而不同的审核人类型完成任务的操作有些许不同。

​ **①Assignee类型:**当流程走到自己的审核结点的时候无需拾取任务,可以直接完成任务

​ **②CandidateUser类型:**当流程走到自己的审核结点的时候需要先拾取任务,再完成任务(所有候选人都可以拾取任务,拾取之后其他人将看不到自己的待办任务,然后拾取人完成任务)

2.2 数据库介绍

表名说明
ACT_GE_PROPERTY数据库表 ACT_GE_PROPERTY 用于存储全局属性(Global Properties)。这些全局属性包含了Activiti引擎的配置信息和状态信息。
NAME:属性名称。 VALUE:属性值。 REV_:属性的版本号。
数据库架构版本:Activiti引擎使用此属性来跟踪已安装数据库架构的版本。 ID生成器:用于生成唯一标识符(ID)的策略。 引擎配置:包括JDBC连接信息、事务管理器、任务执行者等。 引擎状态:例如引擎是否正常运行、是否已暂停等。
ACT_RE_PROCDEF用于存储流程定义的相关信息。
ACT_RU_TIMER_JOB用于存储定时任务(Timer Job)相关的信息。
ACT_PROCDEF_INFO用于存储流程定义的详细信息
INFO_JSON_ID_:指向 ACT_GE_BYTEARRAY 表中存储的流程定义详细信息的 JSON 对象的标识符。
表中的记录存储了与流程定义相关的详细信息,例如表单信息、变量定义等。通过 INFO_JSON_ID_ 列,可以检索到 ACT_GE_BYTEARRAY 表中存储的流程定义详细信息的 JSON 对象。
ACT_RE_DEPLOYMENT用于存储部署的流程定义文件信息
ACT_GE_BYTEARRAY用来存储流程定义文件路径、文件大小等信息(其中DEPLOYMENT_ID字段就是DEPLOYMENT表的ID)
ACT_HI_TASKINST用于存储已完成的任务实例的历史信息(细粒度)
ACT_HI_PROCINST用于存储已完成的任务实例的历史信息(粗粒度)
ACT_HI_ACTINST用于存储已完成的活动实例(包括任务、网关、事件等)的历史信息(任务层级,即每个每个实例的某个任务完成都会存储一条记录在这里)
ACT_HI_IDENTITYLINK用于存储流程实例和任务实例的身份关联历史信息。(USER_ID_:关联的用户标识符)
ACT_RU_EXECUTION一个运行时表,用于存储流程实例和执行实例的运行时信息。
ACT_RU_TASK用于存储任务实例的运行时信息。
ACT_RU_IDENTITYLINK用于存储流程实例和执行实例的运行时的身份关联历史信息。
ACT_HI_VARINST用于存储流程实例和任务实例的变量信息
ACT_RU_VARIABLE存储流程实例和任务实例的流程变量信息

ACT_RE_DEPLOYMENT

ACT_RE_DEPLOYMENT 是 Activiti 7 中的一个数据库表,用于存储部署的流程定义文件信息。

该表包含以下列:

  • ID_:部署记录的唯一标识符。
  • REV_:部署记录的版本号。
  • NAME_:部署的名称。
  • CATEGORY_:部署的类别。
  • KEY_:部署的关键字。
  • TENANT_ID_:部署所属的租户标识符。
  • DEPLOY_TIME_:部署时间。

ACT_RE_DEPLOYMENT 表中的记录存储了每次部署的流程定义文件的信息,例如流程定义的名称、类别、关键字和部署时间等。

ACT_RE_PROCDEF 表的区别在于:

  • ACT_RE_DEPLOYMENT 表存储的是每次部署的流程定义文件的整体信息,一次部署可以包含多个流程定义。
  • ACT_RE_PROCDEF 表存储的是具体的流程定义信息,每个流程定义对应一条记录。

通过 ACT_RE_DEPLOYMENT 表,您可以获取有关部署的流程定义文件的信息。如果您需要查询或操作流程定义的部署信息,可以使用 Activiti 提供的相应 API 来进行操作。在 ACT_RE_DEPLOYMENT 表中,ID_ 字段可以用来查看流程定义文件的整体信息。

每当部署一个流程定义文件时,会在 ACT_RE_DEPLOYMENT 表中创建一条记录,而该记录的 ID_ 字段就是唯一标识符,可以用来唯一地识别和查找该次部署对应的流程定义文件。

您可以通过查询 ACT_RE_DEPLOYMENT 表,并指定相应的条件和字段,获取与流程定义文件相关的信息。例如,可以使用 ID_ 字段来检索特定部署记录的详细信息,包括流程定义文件的名称、版本号、关键字、类别以及部署时间等。

请注意,ACT_RE_DEPLOYMENT 表存储的是流程定义文件的整体信息,在这个表中可以查看到与部署相关的流程定义文件属性,但不能直接访问流程定义文件的具体内容。要访问流程定义文件的具体内容,需要通过 Activiti 提供的 API 或其他工具进行操作。

ACT_HI_TASKINSTACT_HI_PROCINST

ACT_HI_TASKINSTACT_HI_PROCINST 是 Activiti 7 中的两个不同的历史表,用于存储不同类型的历史信息。

ACT_HI_TASKINST 表用于存储已完成的任务实例的历史信息。每当一个任务实例完成时,就会在 ACT_HI_TASKINST 表中创建一条记录,包括任务的名称、描述、开始时间、结束时间等相关信息。该表主要关注已经完成的个别任务实例的详细信息。

ACT_HI_PROCINST 表用于存储已完成的流程实例的历史信息。每当一个流程实例完成时,就会在 ACT_HI_PROCINST 表中创建一条记录,包括流程实例的开始时间、结束时间、持续时间等相关信息。该表主要关注整个流程实例的执行过程和结果。

因此,两者的区别在于关注的层级和粒度不同。ACT_HI_TASKINST 关注于任务级别的历史信息,而 ACT_HI_PROCINST 关注于流程实例级别的历史信息。

需要根据具体的需求选择适合的历史表进行查询和分析。如果需要查看任务的详细信息,可以使用 ACT_HI_TASKINST 表;如果需要了解流程实例的执行情况,可以使用 ACT_HI_PROCINST 表。同时,还可以结合其他的历史表,如 ACT_HI_ACTINSTACT_HI_VARINST 等,来获取更全面的历史信息。

ACT_RU_EXECUTION

该表包含以下列:

  • ID_:执行实例的唯一标识符。
  • PROC_DEF_ID_:流程定义的标识符。
  • PROC_INST_ID_:流程实例的标识符。
  • BUSINESS_KEY_:流程实例的业务关键字。
  • PARENT_ID_:父级执行实例的标识符(可选)。
  • ACT_ID_:当前活动节点的标识符。
  • IS_ACTIVE_:指示执行实例是否处于活动状态。
  • IS_CONCURRENT_:指示执行实例是否是并发执行。
  • IS_SCOPE_:指示执行实例是否是作用域。
  • START_TIME_:执行实例的开始时间。

ACT_RU_EXECUTION 表记录了流程实例和执行实例在运行时的相关信息。每当一个流程实例启动时,就会在该表中创建一条记录,并且在流程实例执行过程中会根据流程定义进行相应的状态更新。通过这个表,可以获取流程实例和执行实例的运行时信息,包括当前活动节点、是否活动、是否并发等。

需要注意的是,ACT_RU_EXECUTION 表中存储的是当前正在运行的流程实例和执行实例的信息。如果需要查询已完成的流程实例和执行实例的历史信息,可以使用相应的历史表,如 ACT_HI_PROCINSTACT_HI_ACTINST 等。

使用 ACT_RU_EXECUTION 表可以了解流程实例和执行实例在运行时的状态和属性信息,对于监控、跟踪和管理流程实例的执行过程非常有用。

ACT_RU_TASK

该表包含以下列:

  • ID_:任务实例的唯一标识符。
  • PROC_DEF_ID_:流程定义的标识符。
  • PROC_INST_ID_:流程实例的标识符。
  • EXECUTION_ID_:执行实例的标识符。
  • NAME_:任务名称。
  • PARENT_TASK_ID_:父级任务实例的标识符(可为空)。
  • DESCRIPTION_:任务描述。
  • OWNER_:任务的所有者。
  • ASSIGNEE_:任务的代理人。
  • CREATE_TIME_:任务创建时间。
  • DUE_DATE_:任务的到期时间。
  • SUSPENSION_STATE_:任务的暂停状态。

ACT_RU_TASK 表记录了任务实例在运行时的相关信息。每当一个任务实例创建时,就会在该表中创建一条记录,并且在任务的执行过程中会根据任务的状态进行相应的更新。通过这个表,可以获取任务实例的运行时信息,包括任务名称、所有者、代理人、创建时间等。

需要注意的是,ACT_RU_TASK 表中存储的是当前正在运行的任务实例的信息。如果需要查询已完成的任务实例的历史信息,可以使用相应的历史表,如 ACT_HI_TASKINST

使用 ACT_RU_TASK 表可以了解任务实例在运行时的状态和属性信息,对于任务的管理、分配和跟踪非常有用。

2.3 重要概念以及数据结构

image-20240605085714062

RepositoryService

常用方法:

方法名作用
createDeployment()部署流程模型
createDeploymentQuery()查询流程模型
delelteDeployment()删除流程模型
getModel()获取模型信息

RuntimeService

常用方法:

方法名作用
startProcessInstanceById()生成一个流程实例
createProcessInstanceQuery()查询流程实例
delelteProcessInstance()删除流程实例
getVariables()获取变量信息
setVariables()

TaskService

常用方法:

方法名作用
createTaskQuery()查询任务信息
setAssignee()设置任务的执行人
claim()候选人拾取任务
complete()执行人完成任务
addComment()给任务节点添加备注
getVariables()获取流程实例变量

HistoryService

2.4 API代码示例

​ 在版本7中identityService和FormService弃用了

image-20240601110416384

部署流程模板

​ 两种方法:绝对路径和资源路径

  1. 方式一:绝对路径
    @Test
    void deployProcessDefinition() throws FileNotFoundException {
        String processDefinitionFilePath = "D:\\reponsitory\\maven+activiti7\\springboot_activiti\\springboot_activiti\\src\\main\\resources\\test.bpmn20.xml";
        Deployment deployment = this.repositoryService.createDeployment()
                .addInputStream(processDefinitionFilePath, new FileInputStream(processDefinitionFilePath))
                .name("test")//设置部署定义名
                .key("test")//设置部署定义key
                .deploy();
        System.out.println("部署流程定义成功:"+ deployment);
    }
  1. 方式二:资源路径
    @Test
    public void testDeployment(){
        // 使用RepositoryService进行部署
        Deployment deployment = repositoryService.createDeployment()
                .addClasspathResource("test.bpmn20.xml") // 添加bpmn资源,在resouces里面的路径
                .name("test")//设置部署定义名
                .key("test")//设置部署定义key
                .deploy();
        //输出部署信息
        System.out.println("部署流程定义成功:"+ deployment);
    }

启动流程实例

 @Test
    void startProcessInstance() {

        //1 启动流程时传递的参数列表 这里根据实际情况 也可以选择不传
        Map<String, Object> variables = new HashMap<String, Object>();
        variables.put("assignee1", "张三");
        
        //2 定义businessKey  businessKey一般为流程实例key与实际业务数据的结合
        String businessKey = "1001";//假设一个请假的业务 在数据库中的id是1001
        //3 设置启动流程的人
        Authentication.setAuthenticatedUserId("sheshe");

        //4 根据流程定义ID查询流程定义
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .deploymentId("69317be4-1f50-11ef-915a-00ff011acb82")
                .singleResult();

        //5 根据流程定义key启动一个流程实例,传入业务id以及变量
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinition.getKey(), businessKey, variables);
        processInstance.getProcessDefinitionId();
        System.out.println("流程启动成功:" + processInstance);
    }

查找组任务

候选人查找可以拾取的任务

    @Test
    public void findGroupTaskList(){
        //1 流程定义的Key(注意这里是流程定义key,不是部署定义key)
        String key = "test";
        //2 任务候选人
        String candidateUser = "2022073";
        //3 查询组任务
        List<Task> taskList = taskService.createTaskQuery()
                .processDefinitionKey(key)
                .taskCandidateUser(candidateUser) //根据候选人查询任务
                .list();
        for (Task task : taskList) {
            System.out.println("========================");
            System.out.println("流程实例ID="+task.getProcessInstanceId());
            System.out.println("任务id="+task.getId());
            System.out.println("任务负责人="+task.getAssignee());
        }
    }

候选人拾取任务

    @Test
    public void claimTask(){
        // 当前任务的id
        String taskId = "75002";
        // 任务候选人
        String candidateUser = "wangwu";
        //1 查询任务
        Task task = taskService.createTaskQuery()
                .taskId(taskId)
                .taskCandidateUser(candidateUser)
                .singleResult();
        //2 拾取任务
        if(task != null){
            taskService.claim(taskId,candidateUser);
            System.out.println("taskid-"+taskId+"-用户-"+candidateUser+"-拾取任务完成");
        }
    }

完成任务

    @Test
    public void completTask(){
        // 返回一个任务对象
        Task task = taskService.createTaskQuery()
                .processDefinitionKey("test") //流程Key
                .taskAssignee("worker")  //要查询的负责人
                .singleResult();

        // 完成任务,参数:任务id
        taskService.complete(task.getId());
    }

指定审核人

指定下一个审核人

    @Test
    public void testAssigneeToCandidateUser(){
        // 当前任务的id
        String taskId = "75002";
        // 要指定的任务负责人
        String assignee = "lisi";
        // 查询任务
        Task task = taskService.createTaskQuery()
                .taskId(taskId)
                .singleResult();
        // 交接任务
        if(task != null){
            taskService.setAssignee(taskId,assignee);
            System.out.println("taskid-"+taskId+"-交接任务完成");
        }
    }

流程预判

public class ApproveNode {
    private String nodeName;
    private String approvers;

    public ApproveNode(String nodeName, String approvers) {
        this.nodeName = nodeName;
        this.approvers = approvers;
    }

    //getter and setter
    //....
}
import com.roy.demo.pojo.ApproveNode;
import de.odysseus.el.ExpressionFactoryImpl;
import de.odysseus.el.util.SimpleContext;
import org.activiti.bpmn.model.*;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.el.ExpressionFactory;
import javax.el.ValueExpression;
import java.util.*;

/**
 * @Description:
 * @author: jianfeng.zheng
 * @since: 2021/1/28 12:40 上午
 * @history: 1.2021/1/28 created by jianfeng.zheng
 */
@Service
public class PreviewProcessService {

    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private RepositoryService repositoryService;


    public List<ApproveNode> getPreviewNodes(String taskId) {
        /**
         * 获取代办任务信息
         */
        Task task = taskService.createTaskQuery()
                .taskId(taskId)
                .singleResult();
        //获取流程模型
        BpmnModel model = repositoryService.getBpmnModel(task.getProcessDefinitionId());
        //获取当前节点
        FlowElement flowElement = model.getFlowElement(task.getTaskDefinitionKey());
        //获取流程变量
        Map<String, Object> params = runtimeService.getVariables(task.getExecutionId());
        //保存访问过的节点,避免死循环
        Set<String> visitedElements = new HashSet<>();
        //递归获取所有预测节点
        List<ApproveNode> approveNodes = visiteElement(flowElement, params, visitedElements);
        return approveNodes;
    }

    /**
     * 递归获取预测节点列表
     *
     * @param flowElement
     * @param params
     * @param visitedElements
     * @return
     */
    private List<ApproveNode> visiteElement(FlowElement flowElement, Map<String, Object> params, Set<String> visitedElements) {
        String id = flowElement.getId();
        //如果之前访问过的节点就不再访问
        if (visitedElements.contains(id)) {
            return Collections.EMPTY_LIST;
        }
        visitedElements.add(id);
        List<ApproveNode> nodes = new ArrayList<>();
        //UserTask是实际的审批节点,如果是UserTask就可以加入到预测的节点中
        if (flowElement instanceof UserTask) {
            UserTask item = (UserTask) flowElement;
            nodes.add(new ApproveNode(item.getName(), this.executeExpression(item.getAssignee(), params, String.class)));
        }

        //获取所有的出口,也就是流程模型中的连线
        List<SequenceFlow> sequenceFlows = this.getElementSequenceFlow(flowElement);
        if (sequenceFlows == null || sequenceFlows.isEmpty()) {
            return nodes;
        }
        FlowElement nextElement = null;
        if (sequenceFlows.size() == 1 && sequenceFlows.get(0).getConditionExpression() == null) {
            /**
             * 如果只有一条连线并且没有设置流转条件,直接获取连线目标节点作为下一节点
             */
            nextElement = sequenceFlows.get(0).getTargetFlowElement();
        } else {
            for (SequenceFlow seq : sequenceFlows) {
                if (seq.getConditionExpression() == null) {
                    /**
                     * 如果没有条件符合,那么就取获取到的第一条条件为空的节点
                     */
                    if (nextElement == null) {
                        nextElement = seq.getTargetFlowElement();
                    }
                } else {
                    /**
                     * 计算条件
                     */
                    boolean value = this.verificationExpression(seq.getConditionExpression(), params);
                    if (value) {
                        nextElement = seq.getTargetFlowElement();
                        break;
                    }
                }
            }
        }
        nodes.addAll(this.visiteElement(nextElement, params, visitedElements));
        return nodes;
    }

    /**
     * 获取流程连线
     *
     * @param flowElement
     * @return
     */
    private List<SequenceFlow> getElementSequenceFlow(FlowElement flowElement) {
        if (flowElement instanceof FlowNode) {
            return ((FlowNode) flowElement).getOutgoingFlows();
        }
        return Collections.EMPTY_LIST;
    }

    /**
     * 执行表达式计算
     * @param expression
     * @param variableMap
     * @param returnType
     * @param <T>
     * @return
     */
    private <T> T executeExpression(String expression, Map<String, Object> variableMap, Class<T> returnType) {
        if (expression == null) {
            return null;
        }
        ExpressionFactory factory = new ExpressionFactoryImpl();
        SimpleContext context = new SimpleContext();
        for (String k : variableMap.keySet()) {
            context.setVariable(k, factory.createValueExpression(variableMap.get(k), Object.class));
        }
        ValueExpression valueExpression = factory.createValueExpression(context, expression, returnType);
        return (T) valueExpression.getValue(context);

    }

    /**
     * 验证表达式结果 true/false
     * @param expression
     * @param variableMap
     * @return
     */
    private Boolean verificationExpression(String expression, Map<String, Object> variableMap) {
        Boolean value = this.executeExpression(expression, variableMap, Boolean.class);
        return value == null ? false : value;
    }
}

2.5 整体代码示例

@Service
public class WorkflowServiceImpl implements WorkflowService {

    @Autowired
    private PreviewWorkFlowProcessUtil previewWorkFlowProcessUtil;

    @Autowired
    private WfRoleMapService wfRoleMapService;

    @Autowired
    private WfRoleMapMapper wfRoleMapMapper;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    SystemAPI systemAPI;
    // 部署流程模板
    @Override
    public String deployProcessDefinition(String business, String businessName, String tableName) {
        // 使用RepositoryService进行部署
//        String processDefinitionFilePath = "D:\\reponsitory\\collegeManageSystem\\server\\wisdomAcademy-microservices-new\\publicservice\\src\\main\\resources\\bpmn\\employmentIntention.bpmn20.xml";
        Deployment deployment = repositoryService.createDeployment()
//                .addInputStream(processDefinitionFilePath, new FileInputStream(processDefinitionFilePath))
                .addClasspathResource("bpmn/"+ business +".bpmn20.xml") // 添加bpmn资源,在resouces里面的路径
                .name(businessName)//设置部署定义名
                .key(business)//设置部署定义key
                .deploy();
        //4 根据流程部署ID查询流程定义
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deployment.getId())
                .singleResult();
        //输出部署信息
        System.out.println("流程部署id:"+ deployment.getId());
        System.out.println("流程定义id:"+ processDefinition.getId());
        WfRoleMap wfRoleMap = new WfRoleMap();
        wfRoleMap.setDefinitionId(processDefinition.getId());
        wfRoleMap.setDeploymentId(deployment.getId());
        wfRoleMap.setBusinessTable(tableName);
        wfRoleMap.setBusinessName(businessName);
        return "流程部署成功";
    }


    // 提交一个申请,可以指定一级审核人
    @Override
    public String startProcessInstance(String tableName, String businessKey, String assignee, String apply) {
        String deploymentId = "";
        String definitionId = "";

        List<WfRoleMap> wfRoleMapList = wfRoleMapService.list(new LambdaQueryWrapper<WfRoleMap>().eq(WfRoleMap::getBusinessTable, tableName));
        if (wfRoleMapList.size() != 0){
            //1. 获取此流程所需哪些变量以及对应的业务id
            deploymentId = wfRoleMapList.get(0).getDeploymentId();
            definitionId = wfRoleMapList.get(0).getDefinitionId();
            // 获取角色信息并存储变量
            Map<String, Object> variables = new HashMap<String, Object>();
            for (int i = 0; i < wfRoleMapList.size(); i++) {
                WfRoleMap wfRoleMap = wfRoleMapList.get(i);
                List<SysUserDTO> sysUserDTOList = systemAPI.getUsersByRoleId(wfRoleMap.getRoleId());
                variables.put(wfRoleMap.getRoleType(), sysUserDTOList.stream().map(SysUserDTO::getUserAccount).collect(Collectors.joining(",")));
            }
            //2. 设置启动流程的人
            Authentication.setAuthenticatedUserId(apply);
            //3. 启动流程实例
            ProcessInstance processInstance = runtimeService.startProcessInstanceById(definitionId, businessKey, variables);
            System.out.println("流程启动成功:" + processInstance.getProcessInstanceId());
            //4. 如果指定了一级审核人就设置上
            if (!StringUtils.isEmpty(assignee)){
                Task task = taskService.createTaskQuery()
                        .processInstanceId(processInstance.getProcessInstanceId())
                        .singleResult();
                taskService.setAssignee(task.getId(), assignee);
            }
            return WorkFlowENUM.GLOBAL_PENDING.getAbbreviation();
        }
        return "失败";
    }

    // 返回自己待办的任务
    @Override
    public List<WfTaskInfoAuitVO> findTaskUnCompleted(String account, String tableName) {
        // 查询组任务
        TaskQuery taskQuery = taskService.createTaskQuery().taskCandidateOrAssigned(account);

        // 如果tableName不为空,则只查询此业务的任务
        if (!StringUtils.isEmpty(tableName)) {
            List<WfRoleMap> wfRoleMapList = wfRoleMapService.list(new LambdaQueryWrapper<WfRoleMap>().eq(WfRoleMap::getBusinessTable, tableName));
            if (!wfRoleMapList.isEmpty()) {
                taskQuery.processDefinitionId(wfRoleMapList.get(0).getDefinitionId());
            } else {
                // 如果不存在则填入一个不存在的流程定义
                taskQuery.processDefinitionId("0");
            }
        }

        List<Task> taskList = taskQuery.list();
        List<WfTaskInfoVO> wfTaskInfoVOList = new ArrayList<>();

        for (Task task : taskList) {
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
            if (processInstance != null) {
                WfTaskInfoVO wfTaskInfoVO = new WfTaskInfoVO();
                wfTaskInfoVO.setTaskId(task.getId());
                wfTaskInfoVO.setNodeName(task.getName());
                wfTaskInfoVO.setApplyUser(UserUtil.getSysUserDTO(processInstance.getStartUserId())); // 获取启动实例的人
                wfTaskInfoVO.setApplyTime(processInstance.getStartTime());
                wfTaskInfoVO.setBusinessName(processInstance.getName());
                wfTaskInfoVO.setBusinessKey(processInstance.getBusinessKey());
                wfTaskInfoVOList.add(wfTaskInfoVO);
            } else {
                System.out.println("ProcessInstance not found for taskId: " + task.getId());
            }
        }

        // 按照申请时间排序
        wfTaskInfoVOList.sort(Comparator.comparing(WfTaskInfoVO::getApplyTime));
        List<WfTaskInfoAuitVO> wfTaskInfoAuitVOList = new ArrayList<>();
        for (WfTaskInfoVO wfTaskInfoVO : wfTaskInfoVOList) {
            WfTaskInfoAuitVO wfTaskInfoAuitVO = new WfTaskInfoAuitVO();
            wfTaskInfoAuitVO.setTaskId(wfTaskInfoVO.getTaskId());
            wfTaskInfoAuitVO.setApplyUser(wfTaskInfoVO.getApplyUser());
            wfTaskInfoAuitVO.setBusinessName(wfTaskInfoVO.getBusinessName());
            wfTaskInfoAuitVO.setApplyTime(wfTaskInfoVO.getApplyTime());
            wfTaskInfoAuitVO.setBusinessKey(wfTaskInfoVO.getBusinessKey());
            wfTaskInfoAuitVO.setNodeName(wfTaskInfoVO.getNodeName());
            wfTaskInfoAuitVO.setStatus("1");
            wfTaskInfoAuitVOList.add(wfTaskInfoAuitVO);
        }
        return wfTaskInfoAuitVOList;
    }



    // 获取自己的申请(正在进行中的)
    @Override
    public List<WfTaskInfoVO> findTaskByStartUser(String account, String tableName){
        List<WfTaskInfoVO> wfTaskInfoVOList = new ArrayList<>();
        ProcessInstanceQuery processInstanceQuery = runtimeService.createProcessInstanceQuery().startedBy(account);
        // 如果tablename不为空,则只查询此业务的任务
        if (StringUtils.isEmpty(tableName)){
            List<WfRoleMap> wfRoleMapList = wfRoleMapService.list(new LambdaQueryWrapper<WfRoleMap>().eq(WfRoleMap::getBusinessTable, tableName));
            if (wfRoleMapList.size() != 0){
                processInstanceQuery.processDefinitionId(wfRoleMapList.get(0).getDefinitionId());
            }else {
                processInstanceQuery.processDefinitionId("0");// 如果不存在则填入一个不存在的流程定义
            }
        }
        List<ProcessInstance> processInstanceList = processInstanceQuery.list();
        for (int i = 0; i < processInstanceList.size(); i++) {
            ProcessInstance processInstance = processInstanceList.get(i);
            Task task = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).singleResult();
            WfTaskInfoVO wfTaskInfoVO = new WfTaskInfoVO();
            wfTaskInfoVO.setBusinessName(processInstance.getName());
            wfTaskInfoVO.setBusinessKey(processInstance.getBusinessKey());
            wfTaskInfoVO.setApplyUser(UserUtil.getSysUserDTO(account));
            wfTaskInfoVO.setApplyTime(processInstance.getStartTime());
            wfTaskInfoVO.setNodeName(task.getName());
            wfTaskInfoVOList.add(wfTaskInfoVO);
        }
        // 按照申请时间排序
        wfTaskInfoVOList.sort(Comparator.comparing(WfTaskInfoVO::getApplyTime));
        return wfTaskInfoVOList;
    }

    // 查看自己已完成的任务
    @Override
    public List<WfTaskInfoVO> findTaskCompleted(String account, String tableName){
        // 查询已完成的任务
        HistoricTaskInstanceQuery historicTaskInstanceQuery = historyService
                .createHistoricTaskInstanceQuery()
                .finished()
                .taskAssignee(account);
        // 如果tablename不为空,则只查询此业务的任务
        if (StringUtils.isEmpty(tableName)){
            List<WfRoleMap> wfRoleMapList = wfRoleMapService.list(new LambdaQueryWrapper<WfRoleMap>().eq(WfRoleMap::getBusinessTable, tableName));
            if (wfRoleMapList.size() != 0){
                historicTaskInstanceQuery.processDefinitionId(wfRoleMapList.get(0).getDefinitionId());
            }else {
                historicTaskInstanceQuery.processDefinitionId("0");// 如果不存在则填入一个不存在的流程定义
            }
        }
        List<HistoricTaskInstance> historicTaskInstances = historicTaskInstanceQuery.list();
        // 设置返回信息
        List<WfTaskInfoVO> wfTaskInfoVOList = new ArrayList<>();
        for (HistoricTaskInstance task : historicTaskInstances) {
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
            String startUserId = "";
            String name = "";
            Date startTime = new Date();
            if (processInstance == null){
                HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
                startUserId = historicProcessInstance.getStartUserId();
                startTime = historicProcessInstance.getStartTime();
                name = historicProcessInstance.getName();
            }else {
                startUserId = processInstance.getStartUserId();
                startTime = processInstance.getStartTime();
                name = processInstance.getName();
            }
            WfTaskInfoVO wfTaskInfoVO = new WfTaskInfoVO();
            wfTaskInfoVO.setTaskId(task.getId());
            wfTaskInfoVO.setApplyUser(UserUtil.getSysUserDTO(startUserId));// 获取启动实例的人
            wfTaskInfoVO.setApplyTime(startTime);
            wfTaskInfoVO.setBusinessName(name);
            wfTaskInfoVOList.add(wfTaskInfoVO);
        }
        // 按照申请时间排序:由近及远
        wfTaskInfoVOList.sort(Comparator.comparing(WfTaskInfoVO::getApplyTime).reversed());
        for(WfTaskInfoVO wfTaskInfoVO:wfTaskInfoVOList){
            if(wfTaskInfoVO.getBusinessName()==null){
                wfTaskInfoVO.setBusinessName("学生活动");
            }
        }
        return wfTaskInfoVOList;
    }


    @Override
    public String completeTaskByBusinessKey(String tableName, String businessKey, String remark, String assignee, String nextAssignee){
        ProcessInstance processInstance = this.getProcessInstanceByBusinessKey(tableName, businessKey);
        Task task = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).singleResult();
        String result = this.completeTask(task.getId(), assignee, remark, nextAssignee);
        return result;
    }


    // 完成任务,并指定下一审核人(下一级审核人可省略)
    public String completeTask(String taskId, String assignee, String remark, String nextAssignee) {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
        // 候选人认领任务
        taskService.claim(taskId, assignee);
        // 添加评论到任务APPROVED通过审核
        taskService.addComment(taskId, processInstance.getProcessInstanceId(), "APPROVED");
        // 完成任务
        taskService.complete(taskId);
        // 添加备注(暂未实现)

        // 判断流程是否结束,如果没结束进行接下来的操作
        ProcessInstance processInstance1 = runtimeService.createProcessInstanceQuery().processInstanceId(processInstance.getProcessInstanceId()).singleResult();
        if (processInstance1 == null){
            return WorkFlowENUM.GLOBAL_SUCCESS.getAbbreviation();
        }
        // 如果nextAssignee不空,则指定下一级审核人
        if (!StringUtils.isEmpty(nextAssignee)){
            Task task1 = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).singleResult();
            taskService.setAssignee(task1.getId(), nextAssignee);
        }
        return WorkFlowENUM.GLOBAL_PENDING.getAbbreviation();
    }

    @Override
    public String refuseTaskByBusinessKey(String tableName, String businessKey, String reason, String assignee){
        ProcessInstance processInstance = this.getProcessInstanceByBusinessKey(tableName, businessKey);
        Task task = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).singleResult();
        String result = this.refuseTask(task.getId(), reason, assignee);
        return result;
    }

    // 退回任务并备注理由
    public String refuseTask(String taskId, String reason, String assignee) {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        // 设置审批结果为不通过
        runtimeService.setVariable(task.getProcessInstanceId(), "status", WorkFlowENUM.GLOBAL_REFUSE.getAbbreviation());
        runtimeService.setVariable(task.getProcessInstanceId(), "reason", reason);
        // 添加评论到任务REJECTED不通过
        taskService.addComment(taskId, task.getProcessInstanceId(), WorkFlowENUM.NODE_REFUSE.getAbbreviation());
        // 拾取任务
        taskService.setAssignee(taskId, assignee);
        // 结束流程
        this.endTask(task.getId());
        return WorkFlowENUM.GLOBAL_REFUSE.getAbbreviation();
    }

    @Override
    public String cancelProcessByBusinessKey(String tableName, String businessKey){
        ProcessInstance processInstance = this.getProcessInstanceByBusinessKey(tableName, businessKey);
        Task task = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).singleResult();
        String result = this.cancelProcess(task.getId());
        return result;
    }

    // 撤销流程
    public String cancelProcess(String taskId){
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
        // 设置这个任务的审核人是发起人,然后结束流程
        task.setAssignee(processInstance.getStartUserId());
        runtimeService.setVariable(task.getProcessInstanceId(), "status", WorkFlowENUM.GLOBAL_CANCEL.getAbbreviation());
        this.endTask(taskId);
        return WorkFlowENUM.GLOBAL_CANCEL.getAbbreviation();
    }


    // 预测正在执行中的任务流程
    @Override
    public WfProcessPreviewVO previewProcess(String tableName, String businessKey) {
        Map<String, String> processDefinition = this.getProcessIdByTableName(tableName);
        WfProcessPreviewVO wfProcessPreviewVo = new WfProcessPreviewVO();// 初始化返回信息
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceBusinessKey(businessKey).processDefinitionId(processDefinition.get("definitionId")).singleResult();// 获取流程实例
        if (processInstance != null){
            // processInstance 不为空说明流程还没结束
            String processInstanceId = processInstance.getProcessInstanceId();
            Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();// 获取当前任务
            BpmnModel model = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());// 获取流程模型
            // 这里将分为三种情况:审核过的结点、审核中的结点、未审核的结点
            // 1. 获取整个流程
            List<WfApproveNodeBO> wfApproveNodeBOList = previewWorkFlowProcessUtil.getCurrentPreviewNodes(processInstance.getProcessDefinitionId(), processInstance.getProcessInstanceId());// 获取整个流程图
            // 2. 设置结点信息
            for (int i = 0; i < wfApproveNodeBOList.size(); i++) {
                WfApproveNodeBO wfApproveNodeBO = wfApproveNodeBOList.get(i);
                // 如果到了正在审核的结点
                if (task.getName().equals(wfApproveNodeBO.getNodeName())){
                    // 审核状态之前存储到comment里面了
                    wfApproveNodeBO.setStatus(WorkFlowENUM.NODE_APPROVING.getAbbreviation());
                    wfApproveNodeBO.setAssignee(UserUtil.getSysUserDTO(task.getAssignee()));
                    break;
                }
                // 否则就查找历史任务信息
                HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery().processInstanceId(processInstanceId).taskDefinitionKey(wfApproveNodeBO.getTaskDefinitionKey()).singleResult();
                // 审核状态之前存储到comment里面了
                wfApproveNodeBO.setStatus(taskService.getTaskComments(historicTaskInstance.getId()).get(0).getFullMessage());
                wfApproveNodeBO.setAssignee(UserUtil.getSysUserDTO(historicTaskInstance.getAssignee()));
                // 存储审核时间
                wfApproveNodeBO.setReviewTime(historicTaskInstance.getEndTime());
            }
            Map<String, Object> map = processInstance.getProcessVariables();
            wfProcessPreviewVo.setWfApproveNodeBOList(wfApproveNodeBOList);
            wfProcessPreviewVo.setApplyTime(processInstance.getStartTime());
            wfProcessPreviewVo.setApply(UserUtil.getSysUserDTO(processInstance.getStartUserId()));
            Object status = map.get("status");
            if (status != null && StringUtils.isEmpty(status.toString()) && WorkFlowENUM.GLOBAL_REFUSE.getAbbreviation().equals(status.toString())){
                // 如果是已拒绝状态
                wfProcessPreviewVo.setStatus(status.toString());
            }else if (status == null || StringUtils.isEmpty(status.toString()) || !WorkFlowENUM.GLOBAL_REFUSE.getAbbreviation().equals(status.toString())){
                // 如果status是空或者不是空但是没有被拒绝,则是“审核中”(因为已结束的流程使用runtimeService查不出来)
                wfProcessPreviewVo.setStatus(WorkFlowENUM.GLOBAL_PENDING.getAbbreviation());
            }
            if (map.get("reason") != null){
                wfProcessPreviewVo.setComment(map.get("reason").toString());
            }
        }else {
            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processDefinitionId(processDefinition.get("definitionId")).processInstanceBusinessKey(businessKey).singleResult();
            String historicProcessInstanceId = historicProcessInstance.getId();
            // 这里分为三种情况:审核通过的结点、拒绝的结点、未审核的结点
            // 1. 获取整个流程
            List<WfApproveNodeBO> wfApproveNodeBOList = previewWorkFlowProcessUtil.getCurrentPreviewNodes(historicProcessInstance.getProcessDefinitionId(), historicProcessInstanceId);// 获取整个流程图
//            // 预设状态为已通过
//            wfProcessPreviewVo.setStatus(WorkFlowENUM.GLOBAL_SUCCESS.getAbbreviation());
            // 2. 设置结点信息
            for (int i = 0; i < wfApproveNodeBOList.size(); i++) {
                WfApproveNodeBO wfApproveNodeBO = wfApproveNodeBOList.get(i);
                // 查找历史任务信息
                HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery().processInstanceId(historicProcessInstanceId).taskDefinitionKey(wfApproveNodeBO.getTaskDefinitionKey()).singleResult();
                // 如果此任务已经完成
                if (historicTaskInstance != null){
                    // 审核状态之前存储到comment里面了
                    wfApproveNodeBO.setStatus(taskService.getTaskComments(historicTaskInstance.getId()).get(0).getFullMessage());
                    wfApproveNodeBO.setAssignee(UserUtil.getSysUserDTO(historicTaskInstance.getAssignee()));
                    // 存储审核时间
                    wfApproveNodeBO.setReviewTime(historicTaskInstance.getEndTime());
                }
                // 如果任务尚未完成说明前面有节点是拒绝状态
                else {
                    wfApproveNodeBO.setStatus(WorkFlowENUM.NODE_ENDING.getAbbreviation());
                    wfProcessPreviewVo.setStatus(WorkFlowENUM.GLOBAL_REFUSE.getAbbreviation());
                }
            }
            wfProcessPreviewVo.setWfApproveNodeBOList(wfApproveNodeBOList);
            wfProcessPreviewVo.setApplyTime(historicProcessInstance.getStartTime());
            wfProcessPreviewVo.setApply(UserUtil.getSysUserDTO(historicProcessInstance.getStartUserId()));
            if (StringUtils.isEmpty(wfProcessPreviewVo.getStatus())){
                Map<String, Object> map = historicProcessInstance.getProcessVariables();
                Object status = map.get("status");
                if (status != null && StringUtils.isEmpty(status.toString()) && WorkFlowENUM.GLOBAL_REFUSE.getAbbreviation().equals(status.toString())){
                    // 如果是已拒绝状态
                    wfProcessPreviewVo.setStatus(status.toString());
                    if (map.get("reason") != null){
                        wfProcessPreviewVo.setComment(map.get("reason").toString());
                    }
                }else if (status == null || StringUtils.isEmpty(status.toString()) || !WorkFlowENUM.GLOBAL_REFUSE.getAbbreviation().equals(status.toString())){
                    // 如果status是空或者不是空但是没有被拒绝,则是“已通过”(因为historic查出来的都是结束的,要么通过要么拒绝)
                    wfProcessPreviewVo.setStatus(WorkFlowENUM.GLOBAL_SUCCESS.getAbbreviation());
                }
            }
        }


        return wfProcessPreviewVo;
    }

    public Map<String, String> getProcessIdByTableName(String tableName){
        Map<String, String> map = new HashMap<>();
        // 获取此流程所需哪些变量以及对应的业务id
        List<WfRoleMap> wfRoleMapList = wfRoleMapMapper.selectList(new LambdaQueryWrapper<WfRoleMap>().eq(WfRoleMap::getBusinessTable, tableName));
        if (wfRoleMapList.size() != 0) {
            map.put("deploymentId", wfRoleMapList.get(0).getDeploymentId());
            map.put("definitionId", wfRoleMapList.get(0).getDefinitionId());
        }
        return map;
    }


    /**
     * 结束任务,直达结束节点
     * @param taskId    当前任务ID
     */
    public void endTask(String taskId) {
        //  当前任务
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();

        BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
        List endEventList = bpmnModel.getMainProcess().findFlowElementsOfType(EndEvent.class);
        FlowNode endFlowNode = (FlowNode) endEventList.get(0);
        FlowNode currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(task.getTaskDefinitionKey());

        //  临时保存当前活动的原始方向
        List originalSequenceFlowList = new ArrayList<>();
        originalSequenceFlowList.addAll(currentFlowNode.getOutgoingFlows());
        //  清理活动方向
        currentFlowNode.getOutgoingFlows().clear();

        //  建立新方向
        SequenceFlow newSequenceFlow = new SequenceFlow();
        newSequenceFlow.setId("newSequenceFlowId");
        newSequenceFlow.setSourceFlowElement(currentFlowNode);
        newSequenceFlow.setTargetFlowElement(endFlowNode);
        List newSequenceFlowList = new ArrayList<>();
        newSequenceFlowList.add(newSequenceFlow);
        //  当前节点指向新的方向
        currentFlowNode.setOutgoingFlows(newSequenceFlowList);

        //  完成当前任务
        taskService.complete(task.getId());

        //  可以不用恢复原始方向,不影响其它的流程
        currentFlowNode.setOutgoingFlows(originalSequenceFlowList);
    }


    /**
     * 根据业务id和业务表获取运行中的流程实例
     * @param tableName
     * @param businessKey
     * @return
     */
    public ProcessInstance getProcessInstanceByBusinessKey(String tableName, String businessKey){
        Map<String, String> processDefinition = this.getProcessIdByTableName(tableName);
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processDefinitionId(processDefinition.get("definitionId")).processInstanceBusinessKey(businessKey).singleResult();
        return processInstance;
    }
}

预测流程信息

import com.hebut.publicservice.common.utils.StringUtils;
import com.hebut.publicservice.common.utils.UserUtil;
import com.hebut.publicservice.entity.BO.WfApproveNodeBO;
import com.hebut.publicservice.entity.DTO.SysUserDTO;
import de.odysseus.el.ExpressionFactoryImpl;
import de.odysseus.el.util.SimpleContext;
import org.activiti.bpmn.model.*;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricVariableInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.el.ExpressionFactory;
import javax.el.ValueExpression;
import java.util.*;

/**
 * @Description:
 * @author: sheshe
 * @since:
 * @history: created by sheshe
 */
@Component
public class PreviewWorkFlowProcessUtil {
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private HistoryService historyService;
    // 获取当前节点以后得流程(需要注意的是,这里获取的是流程定义模板的流程信息而不是流程实例的流程实例信息)
    public List<WfApproveNodeBO> getCurrentPreviewNodes(String processDefinitionId, String processInstanceId) {
        /**
         * 获取代办任务信息
         */
        //获取流程模型
        BpmnModel model = repositoryService.getBpmnModel(processDefinitionId);
        List<FlowElement> flowElements = new ArrayList<>(model.getMainProcess().getFlowElements());
        //获取第一个审核节点
        FlowElement flowElement = model.getFlowElement(flowElements.get(1).getId());
        //获取运行时流程变量
        Map<String, Object> params = new HashMap<>();
        if (runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult() == null){
            List<HistoricVariableInstance> variableInstances = historyService.createHistoricVariableInstanceQuery().processInstanceId(processInstanceId).list();
            for (HistoricVariableInstance variableInstance : variableInstances) {
                params.put(variableInstance.getVariableName(), variableInstance.getValue());
            }
        }else {
            params = runtimeService.getVariables(processInstanceId);
        }
        //保存访问过的节点,避免死循环
        Set<String> visitedElements = new HashSet<>();
        //递归获取所有预测节点
        List<WfApproveNodeBO> approveNodes = visiteElement(flowElement, params, visitedElements);
        return approveNodes;
    }

    /**
     * 递归获取预测节点列表
     *
     * @param flowElement
     * @param params
     * @param visitedElements
     * @return
     */
    private List<WfApproveNodeBO> visiteElement(FlowElement flowElement, Map<String, Object> params, Set<String> visitedElements) {
        String id = flowElement.getId();
        //如果之前访问过的节点就不再访问
        if (visitedElements.contains(id)) {
            return Collections.EMPTY_LIST;
        }
        visitedElements.add(id);
        List<WfApproveNodeBO> nodes = new ArrayList<>();
        //UserTask是实际的审批节点,如果是UserTask就可以加入到预测的节点中
        if (flowElement instanceof UserTask) {
            UserTask item = (UserTask) flowElement;
            List<SysUserDTO> sysUserDTOList = new ArrayList<>();
            // 遍历键的集合,逐个取出对应的值
            for (String key : params.keySet()) {
                if (item.getCandidateUsers().get(0).equals("${" + key + "}")){
                    List<String> accounts = StringUtils.splitList(params.get(key).toString());
                    for (int i = 0; i < accounts.size(); i++) {
                        sysUserDTOList.add(UserUtil.getSysUserDTO(accounts.get(i)));
                    }
                    break;
                }
            }
            SysUserDTO assignee = new SysUserDTO();
            if (item.getAssignee() != null){
                assignee = UserUtil.getSysUserDTO(item.getAssignee());
            }
            nodes.add(new WfApproveNodeBO(item.getName(), assignee, sysUserDTOList, "PENDING", item.getId()));
        }

        //获取所有的出口,也就是流程模型中的连线
        List<SequenceFlow> sequenceFlows = this.getElementSequenceFlow(flowElement);
        if (sequenceFlows == null || sequenceFlows.isEmpty()) {
            return nodes;
        }
        FlowElement nextElement = null;
        if (sequenceFlows.size() == 1 && sequenceFlows.get(0).getConditionExpression() == null) {
            /**
             * 如果只有一条连线并且没有设置流转条件,直接获取连线目标节点作为下一节点
             */
            nextElement = sequenceFlows.get(0).getTargetFlowElement();
        } else {
            for (SequenceFlow seq : sequenceFlows) {
                if (seq.getConditionExpression() == null) {
                    /**
                     * 如果没有条件符合,那么就取获取到的第一条条件为空的节点
                     */
                    if (nextElement == null) {
                        nextElement = seq.getTargetFlowElement();
                    }
                } else {
                    /**
                     * 计算条件
                     */
                    boolean value = this.verificationExpression(seq.getConditionExpression(), params);
                    if (value) {
                        nextElement = seq.getTargetFlowElement();
                        break;
                    }
                }
            }
        }
        nodes.addAll(this.visiteElement(nextElement, params, visitedElements));
        return nodes;
    }

    /**
     * 获取流程连线
     *
     * @param flowElement
     * @return
     */
    private List<SequenceFlow> getElementSequenceFlow(FlowElement flowElement) {
        if (flowElement instanceof FlowNode) {
            return ((FlowNode) flowElement).getOutgoingFlows();
        }
        return Collections.EMPTY_LIST;
    }

    /**
     * 执行表达式计算
     * @param expression
     * @param variableMap
     * @param returnType
     * @param <T>
     * @return
     */
    private <T> T executeExpression(String expression, Map<String, Object> variableMap, Class<T> returnType) {
        if (expression == null) {
            return null;
        }
        ExpressionFactory factory = new ExpressionFactoryImpl();
        SimpleContext context = new SimpleContext();
        for (String k : variableMap.keySet()) {
            context.setVariable(k, factory.createValueExpression(variableMap.get(k), Object.class));
        }
        ValueExpression valueExpression = factory.createValueExpression(context, expression, returnType);
        return (T) valueExpression.getValue(context);

    }

    /**
     * 验证表达式结果 true/false
     * @param expression
     * @param variableMap
     * @return
     */
    private Boolean verificationExpression(String expression, Map<String, Object> variableMap) {
        Boolean value = this.executeExpression(expression, variableMap, Boolean.class);
        return value == null ? false : value;
    }

}
  • 12
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值