关于事务流的思考

关于事务流的思考

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. 方式一:绝对路径
    @Testvoid 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. 方式二:资源路径
    @Testpublic void testDeployment(){// 使用RepositoryService进行部署Deployment deployment = repositoryService.createDeployment().addClasspathResource("test.bpmn20.xml") // 添加bpmn资源,在resouces里面的路径.name("test")//设置部署定义名.key("test")//设置部署定义key.deploy();//输出部署信息System.out.println("部署流程定义成功:"+ deployment);}

启动流程实例

 @Testvoid 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);}

查找组任务

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

    @Testpublic 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());}}

候选人拾取任务

    @Testpublic void claimTask(){// 当前任务的idString 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+"-拾取任务完成");}}

完成任务

    @Testpublic void completTask(){// 返回一个任务对象Task task = taskService.createTaskQuery().processDefinitionKey("test") //流程Key.taskAssignee("worker")  //要查询的负责人.singleResult();// 完成任务,参数:任务idtaskService.complete(task.getId());}

指定审核人

指定下一个审核人

    @Testpublic void testAssigneeToCandidateUser(){// 当前任务的idString 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 {@Autowiredprivate RuntimeService runtimeService;@Autowiredprivate TaskService taskService;@Autowiredprivate 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 {@Autowiredprivate PreviewWorkFlowProcessUtil previewWorkFlowProcessUtil;@Autowiredprivate WfRoleMapService wfRoleMapService;@Autowiredprivate WfRoleMapMapper wfRoleMapMapper;@Autowiredprivate RuntimeService runtimeService;@Autowiredprivate TaskService taskService;@Autowiredprivate RepositoryService repositoryService;@Autowiredprivate HistoryService historyService;@AutowiredSystemAPI systemAPI;// 部署流程模板@Overridepublic 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 "流程部署成功";}// 提交一个申请,可以指定一级审核人@Overridepublic 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. 获取此流程所需哪些变量以及对应的业务iddeploymentId = 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 "失败";}// 返回自己待办的任务@Overridepublic 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;}// 获取自己的申请(正在进行中的)@Overridepublic 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;}// 查看自己已完成的任务@Overridepublic 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;}@Overridepublic 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();}@Overridepublic 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();}@Overridepublic 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();}// 预测正在执行中的任务流程@Overridepublic 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<>();// 获取此流程所需哪些变量以及对应的业务idList<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 {@Autowiredprivate RuntimeService runtimeService;@Autowiredprivate TaskService taskService;@Autowiredprivate RepositoryService repositoryService;@Autowiredprivate 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;}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/29078.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

售前方案VS产品经理

售前和产品经理分别是做什么的 售前方案&#xff1a;主要负责在销售过程中与客户沟通&#xff0c;了解客户需求&#xff0c;提供技术支持和解决方案演示的技术人员。协助销售团队与客户沟通&#xff0c;帮助客户理解产品特性和技术细节&#xff0c;确保产品与客户需求匹配。售前…

智能体(Agent)实战——从gpts到auto gen

一.GPTs 智能体以大模型作为大脑,同时配备技能,使其能够完成具体的任务。同时,为了应用于垂直领域,我们需要为大模型定义一个角色,并构建知识库。最后,定义完整的流程,使其完成整个任务。以组会汇报的智能体为例,定义如下 1.创建自己的gpt 2.角色定义 该 Agent 是一种智…

计算机图形学入门13:纹理映射常见问题、MipMap

上一章介绍了纹理映射&#xff0c;这一章介绍纹理映射常见的问题。 1.纹理太小 1.1产生原因 例如要渲染一面墙&#xff0c;它的分辨率4K&#xff0c;但与它对应的纹理大小是256x256&#xff0c;这样要怎样&#xff1f;显然纹理会被拉大。当墙面上一个点去查询纹理时&#xff0…

研发管理平台有哪些?符合软件公司需求的工具要具备这几个特征!

本人从事TOB行业十余年&#xff0c;目前就职的就是一家软件公司。下面&#xff0c;本人就站在软件公司的角度来讲一讲&#xff1a;我们公司做项目研发时&#xff0c;会选择一个什么样的研发管理工具来辅助&#xff1f;供大家参考。 众所周知&#xff0c;软件研发项目是一个复杂…

电商商品项目||电商竞品分析|主流电商商品API接口在竞品分析中的重要应用

竞争数据采集 竞争数据是对在电子商务业务中彼此存在竞争关系的商家、品牌、产品(即竞争对手&#xff09;等各项运营数据的总称&#xff0c;在电子商务企业的经营过程中&#xff0c;对竞争对手进行分析可以帮助决策者和管理员了解竞争对手的发展势头&#xff0c;为企业成略制定…

通用视频模板解决方案,视频生产制作更轻松

对于许多企业来说&#xff0c;视频制作往往面临着技术门槛高、制作周期长、成本投入大等难题。为了解决这些问题&#xff0c;美摄科技凭借其领先的跨平台视频技术和完善的工具链&#xff0c;推出了面向企业的视频通用模板解决方案&#xff0c;为企业视频制作带来了全新的革命性…

【elementui源码解析】如何实现自动渲染md文档-第三篇

目录 1.前言 2.webpack.demo.js 3.markdown文档 4.fence.js 1&#xff09;tokens 2&#xff09;::: 3&#xff09; 5.containers.js 1&#xff09;markdown-it-container 2&#xff09;md.use() 3&#xff09;代码逻辑 4&#xff09;containers小结 6.congfig.js …

5G消息 x 政务 | 新型数智政务服务平台

5G消息 x 政务 | 新型数智政务服务平台 通过 5G 消息&#xff0c;帮助政府部门及公共事业部门优化服务品质、提高服务效能&#xff0c;打造现代政府的展示窗口、便民利企的服务窗口、营商环境的感知窗口&#xff0c;提供多元、透明、高效的线上政务服务。 5G消息 x 政务 —— 优…

转型AI产品经理(11):“损失规避”如何应用在Chatbot产品中

损失规避是行为经济学和心理学中的一个重要概念&#xff0c;它揭示了人们在面对潜在的收益和损失时&#xff0c;表现出对损失的强烈偏好避免&#xff0c;相比于获得同等价值的利益&#xff0c;人们对损失的感受更为强烈。它主要有以下特征&#xff1a; 1、不对称性 损失规避体…

nvm 报错https://npm.taobao.org/mirrors/node/index.json 淘宝镜像更换

文章目录 一、问题背景二、解决问题1. 获取配置文件的位置2. 修改配置文件中的镜像源配置3. 修改 npm 镜像源 一、问题背景 使用nvm的时候报错: Could not retrieve https://npm.taobao.org/mirrors/node/index.json. 由于淘宝的镜像域名更换&#xff0c;npm.taobao.org 域名…

同三维T80004JEH2-4K60 双路4K60 HDMI解码器

输出&#xff1a;2路HDMI2路3.5音频&#xff0c;最高支持1路4K60HDMI输出 可以同源/独立分屏输出两种模式可选&#xff1a;对应两个HDMI输出一样和不一样的信号 同源可以解码36路网络流&#xff08;1/4/9/16/25/36&#xff09;&#xff0c;两个HDMI输出一样的信号&#xff…

运行vue3项目相关报错

1. VSCode打开TSVue3项目很多地方报错 报错内容 几乎所有文件都会出现未知飘红 error Delete CR prettier/prettier报错原因 插件冲突&#xff0c;Windows系统回车换行符与MAC不一致&#xff08;所以这个问题Windows系统才会出现&#xff09; 解决 需要安装Vue - Official…

java的Filter(过滤器),Interceptor(拦截器) 和 Aspect(切面)

文章目录 前言一、过滤器 (Filter) 基本用法二、拦截器 (Interceptor) 基本用法三、Aspect(切面)的基本用法总结 前言 Filter 是servlet层面的&#xff0c;由Servlet容器(如Tomcat)支持&#xff0c;只能在web程序中使用&#xff0c;实现了javax.servlet.Filter接口 Intercept…

NATAPP-内网穿透工具----下载与配置

NATAPP-内网穿透工具 基于ngrok的国内高速内网穿透服务&#xff0c;natapp提供了一种便利的方式&#xff0c;使得开发和测试过程更加高效&#xff0c;尤其是在需要进行远程调试或展示时。无论是进行web开发、微信和支付宝的本地开发调试&#xff0c;还是简单地从外部网络访问家…

Maya 2024 mac/win版:创意无界,设计新生

Maya 2024是一款由Autodesk推出的业界领先的三维计算机图形软件&#xff0c;广泛应用于电影、游戏、广告等创意产业。这款软件以其强大的功能和卓越的性能&#xff0c;为艺术家们提供了一个实现创意梦想的平台。 Maya 2024 mac/win版获取 在建模方面&#xff0c;Maya 2024提供…

作者推荐 | 探索分析从起源到现今的巅峰之旅(MySQL存储模型)

探索分析从起源到现今的巅峰之旅 背景介绍MySQL内部组织与结构MySQL的数据层次和关系InnoDB的数据存储模型数据记录的基本单元 — 行页目录&#xff08;Page Directory&#xff09;文件头&#xff08;File Header&#xff09;决定页面间的关联方式数据页头&#xff08;Page Hea…

大模型应用开发技术:Multi-Agent框架流程、源码及案例实战(一)

LlaMA 3 系列博客 基于 LlaMA 3 LangGraph 在windows本地部署大模型 &#xff08;一&#xff09; 基于 LlaMA 3 LangGraph 在windows本地部署大模型 &#xff08;二&#xff09; 基于 LlaMA 3 LangGraph 在windows本地部署大模型 &#xff08;三&#xff09; 基于 LlaMA…

QT:QT中的默认代码 QT 创建控件的两种方式

目录 QT中的默认代码 新项目的结构 主函数 wiget类的声明文件.h wiget类的定义文件.cpp form file界面文件 .pro文件 QT 创建控件的两种方式 通过ui界面创建控件 通过代码方式创建控件 QT中的默认代码 新项目的结构 主函数 基本概念&#xff1a;Qt 在创建的一个 Wi…

AI在线创作歌曲智能绘画对话三合一源码系统 前后端分离 带完整的安装代码包以及搭建教程

系统概述 在数字化时代背景下&#xff0c;艺术与技术的融合正以前所未有的速度推进&#xff0c;催生出一系列创新应用。为了满足创作者对多元化、高效能创作工具的需求&#xff0c;我们自豪地推出了“AI在线创作歌曲、智能绘画对话三合一源码系统”。这一系统不仅实现了音乐、…

白酒:茅台镇白酒的消费者教育计划与推广活动

云仓酒庄豪迈白酒&#xff0c;作为茅台镇的品牌&#xff0c;一直以来都非常重视消费者教育和推广活动。这些计划和活动的目的在于提高消费者对豪迈白酒的认知度和接受度&#xff0c;同时培养消费者的品鉴能力和酒文化素养。 首先&#xff0c;云仓酒庄豪迈白酒通过开展品鉴活动来…