diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d9350ab
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,101 @@
+#======================================================================================================================
+#========================================= gitignore的基础用法 =========================================
+#======================================================================================================================
+# 一.基础语法
+# .gitignore配置文件的一些通用技巧 [参考:https://git-scm.com/docs/gitignore]
+# 1.空白行不匹配任何文件,所以可以作为可读性的分隔符,同时两端的空格将会被忽略.
+# 2.使用[#]开头,将会注释掉整行,使其不进行匹配操作,如果需要匹配#开头,可以使用转义字符[\].
+# 3.1匹配模式以[/]结尾,表示想要匹配一个目录及其子文件.(比如[foo/]会匹配foo目录及其下面的路径.)
+# 3.2匹配模式不包含[/],将会全局匹配该文件.
+# 4.通配符
+# [*]: 匹配除[/]以外的任何内容,也就意味着[*]不能跨目录.
+# [?]: 匹配除[/]和[[]以及[]]以外的任何一个字符.
+# [**]: 匹配所有的内容,或者说匹配任意目录下的内容.
+# 示例:
+# 1.[**/foo/bar] 将会匹配所有直接在foo目录下的bar,无论foo处在何处.
+# 2.[foo/**]则表示匹配foo目录下的所有文件和目录.
+# 3.[a/**/b]则可以匹配a/b, a/c/b, a/c/d/b,即此处的[**]可以表示0个或多个.
+# !!! 需要注意的是,除上面示例的用法外,剩余的[**]都是无效的..
+# 5.可以通过前缀[!]来表示不忽略某些文件,比如可以通过[!a]来确保文件a不会被忽略,即时前面已经声明了忽略其父目录,该模式优先级高于普通忽略模式.
+# 二.常用命令
+# 1.git -rm [https://git-scm.com/docs/git-rm]
+# 删除文件索引,或者同时删除文件索引和物理文件.可以使用通配符.
+# 2.git-check-ignore [https://git-scm.com/docs/git-check-ignore]
+# 调试.gitignore文件
+# 三.注意事项
+# 1.如果文件已经被git管理,那么后续添加的忽略模式将不会生效,具体解决方法,参考<<二.常用命令>>.
+
+#=======================================================================================================================
+#============================== java忽略文件 =====================================
+#===================== https://github.com/github/gitignore/blob/master/Java.gitignore ==================
+#=======================================================================================================================
+# 编译后的class文件,忽略所有以[.class]结尾的文件
+*.class
+
+# 日志文件,忽略所有以[.log]结尾的文件.
+*.log
+
+# BlueJ 文件,忽略所有以[.ctxt]结尾的文件.
+*.ctxt
+
+# Mobile Tools for Java (J2ME),忽略[.mtj.tmp/]目录及其子文件.
+.mtj.tmp/
+
+# 打包文件,忽略所有以[.jar]或[.war]或[.nar]或[.ear]或[.zip]或[.tar.gz]或[rar]结尾的文件.
+*.jar
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# 虚拟机崩溃日志,忽略所有以[hs_err_pid]开头的文件.[see http://www.java.com/en/download/help/error_hotspot.xml]
+hs_err_pid*
+#=======================================================================================================================
+#============================== maven忽略文件 ===================================
+#===================== https://github.com/github/gitignore/blob/master/Maven.gitignore ==================
+#=======================================================================================================================
+target/
+pom.xml.tag
+pom.xml.releaseBackup
+pom.xml.versionsBackup
+pom.xml.next
+release.properties
+dependency-reduced-pom.xml
+buildNumber.properties
+.mvn/timing.properties
+# Avoid ignoring Maven wrapper jar file (.jar files are usually ignored)
+!/.mvn/wrapper/maven-wrapper.jar
+#=======================================================================================================================
+#============================== IDE环境忽略文件 ==================================
+#===================== https://github.com/github/gitignore/blob/master/Maven.gitignore ==================
+#=======================================================================================================================
+#----------------IDEA-------------
+.idea/*
+.idea/compiler.xml
+.idea/encodings.xml
+.idea/modules.xml
+*.iml
+#=======================================================================================================================
+#============================== other环境忽略文件 ================================
+#===================== https://github.com/github/gitignore/blob/master/Maven.gitignore ==================
+#=======================================================================================================================
+*.sw?
+.#*
+*#
+*~
+.classpath
+.project
+.settings/
+bin
+build
+target
+dependency-reduced-pom.xml
+*.sublime-*
+/scratch
+.gradle
+Guardfile
+README.html
+*.iml
+.idea
\ No newline at end of file
diff --git a/README b/README
deleted file mode 100644
index e69de29..0000000
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..b1ec99a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,45 @@
+> 特别说明:源码、JDK、数据库、Redis等安装或存放路径禁止包含中文、空格、特殊字符等
+
+## 一 环境要求
+
+| 类目 | 版本说明或建议 |
+| --- |--------------------------------------------------|
+| 硬件 | 开发电脑建议使用I3及以上CPU,16G及以上内存 |
+| 操作系统 | Windows 10/11,MacOS |
+| JDK | 默认使用JDK 21,如需要切换JDK 8/11/17版本请参考文档调整代码,推荐使用 `OpenJDK`,如 `Liberica JDK`、`Eclipse Temurin`、`Alibaba Dragonwell`、`BiSheng`等发行版;|
+| Maven | 依赖管理工具,推荐使用 `3.6.3` 及以上版本 |
+| IDE | 代码集成开发环境,推荐使用 `IDEA2024` 及以上版本,兼容 `Eclipse`、 `Spring Tool Suite` 等IDE工具 |
+
+## 二 关联项目
+> 为以下项目提供基础依赖
+
+| 项目 | 分支 | 说明 |
+|---------------|---------------|------------|
+| yunzhupaas-workflow | v1.0.0-stable | 流程引擎后端项目源码 |
+
+## 三 选择是否加密
+
+> 是否加密将会影响 `yunzhupaas-workflow` 项目的启动方式
+> 如果此项目选择加密 `yunzhupaas-workflow` 项目也需要选择加密
+
+### 3.1 不使用加密
+
+在IDEA中, 展开右侧 `Maven` 中 `Profiles` 去除勾选 `encrypted` 选项, 再点击Maven `刷新` 图标刷新Maven
+
+### 3.2 使用加密
+
+在IDEA中, 展开右侧 `Maven` 中 `Profiles` 勾选 `encrypted` 选项, 再点击Maven `刷新` 图标刷新Maven
+
+#### 3.2.1 安装加密插件
+
+在IDEA中,双击右侧 `Maven` 中 `yunzhupaas-workflow-core` > `clean` 将会自动安装加密打包插件
+
+## 四 使用方式
+
+### 4.1 本地安装
+
+在IDEA中,双击右侧 `Maven` 中 `yunzhupaas-workflow-core` > `Lifecycle` > `install`,将`yunzhupaas-workflow-core`包安装至本地
+
+### 4.2 发布到私服
+
+在IDEA中,双击右侧 `Maven` 中 `yunzhupaas-workflow-core` > `Lifecycle` > `deploy` 发布至私服。
diff --git a/allatori/allatori.xml b/allatori/allatori.xml
new file mode 100644
index 0000000..d251dbb
--- /dev/null
+++ b/allatori/allatori.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/allatori/class-winter-maven-plugin-pom.xml b/allatori/class-winter-maven-plugin-pom.xml
new file mode 100644
index 0000000..b8cef5e
--- /dev/null
+++ b/allatori/class-winter-maven-plugin-pom.xml
@@ -0,0 +1,37 @@
+
+
+ 4.0.0
+
+ com.idea-aedi
+ class-winter-core
+ enhance-2.9.4
+
+
+
+ com.idea-aedi
+ class-winter-core
+ ${project.version}
+
+
+
+ org.apache.maven
+ maven-plugin-api
+ 3.8.1
+
+
+
+ org.apache.maven.plugin-tools
+ maven-plugin-annotations
+ 3.6.1
+ provided
+
+
+
+ org.apache.maven
+ maven-project
+ 2.2.1
+
+
+
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..afa583b
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,196 @@
+
+
+ 4.0.0
+
+ com.yunzhupaas
+ yunzhupaas-workflow-core
+ pom
+ 1.0.0-RELEASE
+
+
+ yunzhupaas-workflow-common
+ yunzhupaas-workflow-flowable
+
+
+
+
+ UTF-8
+ UTF-8
+ UTF-8
+
+ 3.13.0
+ 3.1.1
+ 2.5.2
+ 3.5.0
+ 2.6
+ 1.2.1
+ enhance-2.9.4
+
+ 5.8.27
+ 2.3.0
+ 7.1.0.M4
+
+ 4.5.0
+
+ 4.27.0
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ ${spring-boot.version}
+ pom
+ import
+
+
+
+ org.liquibase
+ liquibase-core
+ ${liqui.version}
+
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${maven-compiler-plugin.version}
+
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+ ${maven-deploy-plugin.version}
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ ${spring-boot.version}
+
+
+ org.apache.maven.plugins
+ maven-install-plugin
+ ${maven-install-plugin.version}
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+ ${maven-dependency-plugin.version}
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+ ${maven-resources-plugin.version}
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ ${exec-maven-plugin.version}
+
+
+ com.idea-aedi
+ class-winter-maven-plugin
+ ${class-winter-maven-plugin.version}
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ true
+ ${maven.compiler.source}
+ ${maven.compiler.target}
+ ${project.build.sourceEncoding}
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+
+
+
+
+
+
+ boot3
+
+ [17,)
+
+
+
+ 21
+
+ 1.8
+
+ 17
+
+ 3.3.2
+
+
+ 7.0.1
+
+
+
+
+ com.github.xiaoymin
+ knife4j-openapi3-jakarta-spring-boot-starter
+ ${knife4j.version}
+
+
+
+
+
+ boot2
+
+ (,17)
+
+
+
+ 1.8
+
+ 1.8
+
+ 1.8
+
+ 2.7.18
+
+
+ 6.8.1
+
+
+
+
+ com.github.xiaoymin
+ knife4j-openapi3-spring-boot-starter
+ ${knife4j.version}
+
+
+
+
+
+
+
+
+
+ maven-releases
+ maven-releases
+ https://xadev.szlecheng.cn/nexus/repository/maven-public/
+
+
+
+
diff --git a/yunzhupaas-workflow-activiti/pom.xml b/yunzhupaas-workflow-activiti/pom.xml
new file mode 100644
index 0000000..1e2e72a
--- /dev/null
+++ b/yunzhupaas-workflow-activiti/pom.xml
@@ -0,0 +1,32 @@
+
+
+ 4.0.0
+
+ com.yunzhupaas
+ yunzhupaas-workflow-core
+ 1.0.0-RELEASE
+
+
+ yunzhupaas-workflow-activiti
+
+
+
+ org.activiti
+ activiti-spring-boot-starter
+ ${activiti.version}
+
+
+ mybatis
+ org.mybatis
+
+
+
+
+ com.yunzhupaas
+ yunzhupaas-workflow-common
+ ${project.version}
+
+
+
diff --git a/yunzhupaas-workflow-activiti/src/main/java/com/yunzhupaas/workflow/activiti/cmd/JumpCmd.java b/yunzhupaas-workflow-activiti/src/main/java/com/yunzhupaas/workflow/activiti/cmd/JumpCmd.java
new file mode 100644
index 0000000..c0e2023
--- /dev/null
+++ b/yunzhupaas-workflow-activiti/src/main/java/com/yunzhupaas/workflow/activiti/cmd/JumpCmd.java
@@ -0,0 +1,144 @@
+package com.yunzhupaas.workflow.activiti.cmd;
+
+import cn.hutool.core.collection.CollectionUtil;
+import org.activiti.bpmn.model.BpmnModel;
+import org.activiti.bpmn.model.UserTask;
+import org.activiti.engine.RuntimeService;
+import org.activiti.engine.history.HistoricActivityInstance;
+import org.activiti.engine.history.HistoricTaskInstance;
+import org.activiti.engine.impl.HistoricActivityInstanceQueryImpl;
+import org.activiti.engine.impl.HistoricTaskInstanceQueryImpl;
+import org.activiti.engine.impl.Page;
+import org.activiti.engine.impl.interceptor.Command;
+import org.activiti.engine.impl.interceptor.CommandContext;
+import org.activiti.engine.impl.persistence.entity.*;
+import org.activiti.engine.runtime.Execution;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 跳转命令类
+ *
+ * @author yanghuixing
+ * @author YMPaaS CloudYUNZHUPAAS开发组
+ * @version 0.2.0
+ * @since 2023/8/22 17:56
+ */
+public class JumpCmd implements Command {
+ private final String processInstanceId;
+
+ private List sourceTaskDefIdList;
+ private List targetFlowNodeIdList;
+
+ private String deleteReason;
+
+ private final BpmnModel bpmnModel;
+ private final RuntimeService runtimeService;
+
+ /**
+ * 保存撤回节点的变量map
+ */
+ private Map> varMap = new ConcurrentHashMap<>();
+
+ public JumpCmd(String processInstanceId, List sourceTaskDefIdList, List targetFlowNodeIdList,
+ String deleteReason, BpmnModel bpmnModel, RuntimeService runtimeService) {
+ this.processInstanceId = processInstanceId;
+ this.sourceTaskDefIdList = sourceTaskDefIdList;
+ this.deleteReason = deleteReason;
+ this.targetFlowNodeIdList = targetFlowNodeIdList;
+ this.bpmnModel = bpmnModel;
+ this.runtimeService = runtimeService;
+ }
+
+ @Override
+ public Void execute(CommandContext commandContext) {
+ ExecutionEntityManager executionEntityManager = commandContext.getExecutionEntityManager();
+ // 处理act_ru_execution
+ handleExecution(commandContext);
+ // 处理act_hi_actinst
+ handleActInst(commandContext);
+
+ targetFlowNodeIdList.forEach(targetId -> {
+ UserTask userTask = (UserTask) bpmnModel.getFlowElement(targetId);
+ // 创建子执行流,开启任务
+ ExecutionEntity processExecution = executionEntityManager.findById(processInstanceId);
+ ExecutionEntity childExecution = executionEntityManager.createChildExecution(processExecution);
+ childExecution.setCurrentFlowElement(userTask);
+
+ // 设置执行变量
+ VariableInstanceEntityManager variableManage = commandContext.getVariableInstanceEntityManager();
+ List variableInstanceEntities = varMap.get(userTask.getId());
+ if (CollectionUtil.isNotEmpty(variableInstanceEntities)) {
+ variableInstanceEntities.forEach(var -> {
+ var.setExecutionId(childExecution.getId());
+ variableManage.insert(var);
+ });
+ }
+ executionEntityManager.insert(childExecution);
+ // 交给引擎流转
+ commandContext.getAgenda().planContinueProcessOperation(childExecution);
+ });
+ return null;
+ }
+
+ private void handleActInst(CommandContext commandContext) {
+ for (String str : sourceTaskDefIdList) {
+ HistoricActivityInstanceQueryImpl query = new HistoricActivityInstanceQueryImpl().activityId(str)
+ .processInstanceId(processInstanceId).unfinished();
+ List activityInstances = commandContext.getHistoricActivityInstanceEntityManager()
+ .findHistoricActivityInstancesByQueryCriteria(query, new Page(0, Integer.MAX_VALUE));
+ for (HistoricActivityInstance activity : activityInstances) {
+ HistoricActivityInstanceEntity activityEntity = (HistoricActivityInstanceEntity) activity;
+ // 修改act_hi_actinst表
+ activityEntity.setDeleted(true);
+ activityEntity.setDeleteReason(deleteReason);
+ commandContext.getHistoricActivityInstanceEntityManager().update(activityEntity);
+ }
+ }
+ }
+
+ private void handleExecution(CommandContext commandContext) {
+ ExecutionEntityManager executionEntityManager = commandContext.getExecutionEntityManager();
+ HistoricTaskInstanceEntityManager historicTaskManager = commandContext.getHistoricTaskInstanceEntityManager();
+ VariableInstanceEntityManager variableManager = commandContext.getVariableInstanceEntityManager();
+ for (String str : sourceTaskDefIdList) {
+ List executionEntities = runtimeService.createExecutionQuery()
+ .processInstanceId(processInstanceId).activityId(str).list();
+ for (Execution parentExecution : executionEntities) {
+ // 关闭未完成的任务执行流
+ // 获取子级Executions,如子流程节点等需要处理
+ List childExecutions = executionEntityManager
+ .findChildExecutionsByParentExecutionId(parentExecution.getId());
+ for (ExecutionEntity childExecution : childExecutions) {
+ // 因为外键约束,首先要删除variable表中的execution相关数据
+ List variableInstances = variableManager
+ .findVariableInstancesByExecutionId(childExecution.getId());
+ varMap.put(parentExecution.getActivityId(), variableInstances);
+ variableInstances.forEach(variableManager::delete);
+ executionEntityManager.deleteExecutionAndRelatedData(childExecution, deleteReason, false);
+ // 修改历史实例
+ HistoricTaskInstanceQueryImpl query = new HistoricTaskInstanceQueryImpl()
+ .executionId(childExecution.getId()).processInstanceId(processInstanceId);
+ List HistoricTaskInstances = historicTaskManager
+ .findHistoricTaskInstancesByQueryCriteria(query);
+ if (CollectionUtil.isNotEmpty(HistoricTaskInstances)) {
+ for (HistoricTaskInstance HistoricTaskInstance : HistoricTaskInstances) {
+ HistoricTaskInstanceEntity entity = (HistoricTaskInstanceEntity) HistoricTaskInstance;
+ entity.setDeleteReason(deleteReason);
+ commandContext.getHistoricTaskInstanceEntityManager().update(entity);
+ }
+ }
+ }
+ // 父执行流关闭
+ List variableInstances = variableManager
+ .findVariableInstancesByExecutionId(parentExecution.getId());
+ varMap.put(parentExecution.getActivityId(), variableInstances);
+ variableInstances.forEach(variableManager::delete);
+ ExecutionEntity parentExecution1 = (ExecutionEntity) parentExecution;
+ executionEntityManager.deleteExecutionAndRelatedData(parentExecution1, deleteReason, false);
+ }
+ }
+ }
+}
diff --git a/yunzhupaas-workflow-activiti/src/main/java/com/yunzhupaas/workflow/activiti/util/ActivitiUtil.java b/yunzhupaas-workflow-activiti/src/main/java/com/yunzhupaas/workflow/activiti/util/ActivitiUtil.java
new file mode 100644
index 0000000..ef2fb57
--- /dev/null
+++ b/yunzhupaas-workflow-activiti/src/main/java/com/yunzhupaas/workflow/activiti/util/ActivitiUtil.java
@@ -0,0 +1,289 @@
+package com.yunzhupaas.workflow.activiti.util;
+
+import org.activiti.bpmn.model.*;
+
+import java.util.*;
+
+/**
+ * activiti工具类
+ *
+ * @author yanghuixing
+ * @author YMPaaS CloudYUNZHUPAAS开发组
+ * @version 0.2.0
+ * @since 2023/8/22 14:12
+ */
+public class ActivitiUtil {
+ /**
+ * 获取全部节点元素
+ *
+ * @param flowElements {@link Collection}
+ * @param allElements {@link Collection}
+ * @return {@link Collection}
+ * @author yanghuixing
+ * @since 2023/8/22 16:08
+ **/
+ public static Collection getAllElements(Collection flowElements,
+ Collection allElements) {
+ allElements = allElements == null ? new ArrayList<>() : allElements;
+ for (FlowElement flowElement : flowElements) {
+ allElements.add(flowElement);
+ if (flowElement instanceof SubProcess) {
+ // 获取子流程元素
+ allElements = getAllElements(((SubProcess) flowElement).getFlowElements(), allElements);
+ }
+ }
+ return allElements;
+ }
+
+ /**
+ * 获取节点的入口连线
+ *
+ * @param element {@link FlowElement}
+ * @return {@link List}
+ * @author yanghuixing
+ * @since 2023/8/22 16:09
+ **/
+ public static List getElementIncomingFlows(FlowElement element) {
+ List sequenceFlows = null;
+ if (element instanceof FlowNode) {
+ sequenceFlows = ((FlowNode) element).getIncomingFlows();
+ }
+ return sequenceFlows;
+ }
+
+ /**
+ * 获取节点的入口连线
+ *
+ * @param element {@link FlowElement}
+ * @return {@link List}
+ * @author yanghuixing
+ * @since 2023/8/22 16:10
+ **/
+ public static List getElementOutgoingFlows(FlowElement element) {
+ List sequenceFlows = null;
+ if (element instanceof FlowNode) {
+ sequenceFlows = ((FlowNode) element).getOutgoingFlows();
+ }
+ return sequenceFlows;
+ }
+
+ /**
+ * 获取可回退的节点(用户任务、子流程)
+ *
+ * @param source {@link FlowElement}
+ * @param passFlows {@link Set}
+ * @param passActs {@link List}
+ * @return {@link List}
+ * @author yanghuixing
+ * @since 2023/8/22 16:11
+ **/
+ public static List getPassActs(FlowElement source, Set passFlows, List passActs) {
+ passFlows = passFlows == null ? new HashSet<>() : passFlows;
+ passActs = passActs == null ? new ArrayList<>() : passActs;
+
+ List sequenceFlows = getElementIncomingFlows(source);
+ if (null != sequenceFlows && sequenceFlows.size() > 0) {
+ for (SequenceFlow sequenceFlow : sequenceFlows) {
+ // 连线重复
+ if (passFlows.contains(sequenceFlow.getId())) {
+ continue;
+ }
+ // 添加经过的连线
+ passFlows.add(sequenceFlow.getId());
+ // 添加经过的用户任务、子流程
+ if (sequenceFlow.getSourceFlowElement() instanceof UserTask) {
+ passActs.add((UserTask) sequenceFlow.getSourceFlowElement());
+ }
+ if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) {
+ passActs.add((SubProcess) sequenceFlow.getSourceFlowElement());
+ }
+ // 迭代
+ getPassActs(sequenceFlow.getSourceFlowElement(), passFlows, passActs);
+ }
+ }
+ return passActs;
+ }
+
+ /**
+ * 获取上一级节点(用户任务、子流程)
+ *
+ * @param source {@link FlowElement}
+ * @param passFlows {@link Set}
+ * @param parentActs {@link List}
+ * @return {@link List}
+ * @author yanghuixing
+ * @since 2023/8/22 16:13
+ **/
+ public static List getParentActs(FlowElement source, Set passFlows, List parentActs) {
+ passFlows = passFlows == null ? new HashSet<>() : passFlows;
+ parentActs = parentActs == null ? new ArrayList<>() : parentActs;
+
+ List sequenceFlows = getElementIncomingFlows(source);
+ if (null != sequenceFlows && sequenceFlows.size() > 0) {
+ for (SequenceFlow sequenceFlow : sequenceFlows) {
+ // 连线重复
+ if (passFlows.contains(sequenceFlow.getId())) {
+ continue;
+ }
+ // 添加经过的连线
+ passFlows.add(sequenceFlow.getId());
+ // 添加用户任务、子流程
+ if (sequenceFlow.getSourceFlowElement() instanceof UserTask) {
+ parentActs.add((UserTask) sequenceFlow.getSourceFlowElement());
+ continue;
+ }
+ if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) {
+ parentActs.add((SubProcess) sequenceFlow.getSourceFlowElement());
+ continue;
+ }
+ // 迭代
+ getParentActs(sequenceFlow.getSourceFlowElement(), passFlows, parentActs);
+ }
+ }
+ return parentActs;
+ }
+
+ /**
+ * 获取需要撤回的节点
+ *
+ * @param source {@link FlowElement}
+ * @param runTaskKeyList {@link List}
+ * @param passFlows {@link Set}
+ * @param userTasks {@link List}
+ * @return {@link List}
+ * @author yanghuixing
+ * @since 2023/8/22 16:13
+ **/
+ public static List getChildUserTasks(FlowElement source, List runTaskKeyList,
+ Set passFlows, List userTasks) {
+ passFlows = passFlows == null ? new HashSet<>() : passFlows;
+ userTasks = userTasks == null ? new ArrayList<>() : userTasks;
+ List sequenceFlows = getElementOutgoingFlows(source);
+ if (null != sequenceFlows && sequenceFlows.size() > 0) {
+ for (SequenceFlow sequenceFlow : sequenceFlows) {
+ // 连线重复
+ if (passFlows.contains(sequenceFlow.getId())) {
+ continue;
+ }
+ // 添加经过的连线
+ passFlows.add(sequenceFlow.getId());
+ // 用户任务
+ if (sequenceFlow.getTargetFlowElement() instanceof UserTask
+ && runTaskKeyList.contains(sequenceFlow.getTargetFlowElement().getId())) {
+ userTasks.add((UserTask) sequenceFlow.getTargetFlowElement());
+ continue;
+ }
+ // 子流程,从第一个节点开始获取
+ if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) {
+ FlowElement flowElement = (FlowElement) ((SubProcess) sequenceFlow.getTargetFlowElement())
+ .getFlowElements().toArray()[0];
+ List tasks = getChildUserTasks(flowElement, runTaskKeyList, passFlows, null);
+ // 找到用户任务,不继续向下找
+ if (tasks.size() > 0) {
+ userTasks.addAll(tasks);
+ continue;
+ }
+ }
+ // 迭代
+ getChildUserTasks(sequenceFlow.getTargetFlowElement(), runTaskKeyList, passFlows, userTasks);
+ }
+ }
+ return userTasks;
+ }
+
+ /**
+ * 获取下一级的用户任务
+ *
+ * @param source {@link FlowElement}
+ * @param hasSequenceFlow {@link Set}
+ * @param userTaskList {@link List}
+ * @return {@link List}
+ * @author yanghuixing
+ * @since 2023/8/22 16:15
+ **/
+ public static List getNextUserTasks(FlowElement source, Set hasSequenceFlow,
+ List userTaskList) {
+ hasSequenceFlow = Optional.ofNullable(hasSequenceFlow).orElse(new HashSet<>());
+ userTaskList = Optional.ofNullable(userTaskList).orElse(new ArrayList<>());
+ // 获取出口连线
+ List sequenceFlows = getElementOutgoingFlows(source);
+ if (null != sequenceFlows) {
+ for (SequenceFlow sequenceFlow : sequenceFlows) {
+ // 如果发现连线重复,说明循环了,跳过这个循环
+ if (hasSequenceFlow.contains(sequenceFlow.getId())) {
+ continue;
+ }
+ // 添加已经走过的连线
+ hasSequenceFlow.add(sequenceFlow.getId());
+ FlowElement targetFlowElement = sequenceFlow.getTargetFlowElement();
+ if (targetFlowElement instanceof UserTask) {
+ // 若节点为用户任务,加入到结果列表中
+ userTaskList.add((UserTask) targetFlowElement);
+ } else {
+ // 若节点非用户任务,继续递归查找下一个节点
+ getNextUserTasks(targetFlowElement, hasSequenceFlow, userTaskList);
+ }
+ }
+ }
+ return userTaskList;
+ }
+
+ /**
+ * 判断某个节点的出口是否是网关,获取网关的出口连线
+ *
+ * @param source {@link FlowElement}
+ * @return {@link List}
+ * @author yanghuixing
+ * @since 2023/8/22 16:15
+ **/
+ public static List getOutFlowsOfGateway(FlowElement source) {
+ List flows = new ArrayList<>();
+ // 获取出口连线
+ List sequenceFlows = getElementOutgoingFlows(source);
+ if (null != sequenceFlows) {
+ for (SequenceFlow sequenceFlow : sequenceFlows) {
+ FlowElement targetFlowElement = sequenceFlow.getTargetFlowElement();
+ if (targetFlowElement instanceof Gateway) {
+ List outgoingFlows = ((Gateway) targetFlowElement).getOutgoingFlows();
+ flows.addAll(outgoingFlows);
+ }
+ }
+ return flows;
+ }
+ return null;
+ }
+
+ /**
+ * 获取之前的节点
+ *
+ * @param source {@link FlowElement}
+ * @param passFlows {@link Set}
+ * @param keys {@link List}
+ * @return {@link List}
+ * @author yanghuixing
+ * @since 2023/8/22 16:16
+ **/
+ public static List getBefore(FlowElement source, Set passFlows, List keys) {
+ passFlows = passFlows == null ? new HashSet<>() : passFlows;
+ keys = keys == null ? new ArrayList<>() : keys;
+ List sequenceFlows = getElementIncomingFlows(source);
+ if (null != sequenceFlows && sequenceFlows.size() > 0) {
+ for (SequenceFlow sequenceFlow : sequenceFlows) {
+ // 连线重复
+ if (passFlows.contains(sequenceFlow.getId())) {
+ continue;
+ }
+ // 添加经过的连线
+ passFlows.add(sequenceFlow.getId());
+ // 添加节点Key
+ keys.add(sequenceFlow.getSourceFlowElement().getId());
+ if (sequenceFlow.getSourceFlowElement() instanceof StartEvent) {
+ continue;
+ }
+ // 迭代
+ getBefore(sequenceFlow.getSourceFlowElement(), passFlows, keys);
+ }
+ }
+ return keys;
+ }
+}
diff --git a/yunzhupaas-workflow-common/pom.xml b/yunzhupaas-workflow-common/pom.xml
new file mode 100644
index 0000000..6b4139a
--- /dev/null
+++ b/yunzhupaas-workflow-common/pom.xml
@@ -0,0 +1,65 @@
+
+
+ 4.0.0
+
+ yunzhupaas-workflow-common
+
+
+ com.yunzhupaas
+ yunzhupaas-workflow-core
+ 1.0.0-RELEASE
+
+
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+ cn.hutool
+ hutool-all
+ ${hutool.version}
+
+
+
+
+
+
+
+
+
+ boot3
+
+ [17,)
+
+
+
+ com.github.xiaoymin
+ knife4j-openapi3-jakarta-spring-boot-starter
+
+
+
+
+ boot2
+
+ (,17)
+
+
+
+
+ com.github.xiaoymin
+ knife4j-openapi3-spring-boot-starter
+
+
+
+
+
+
diff --git a/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/exception/BizException.java b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/exception/BizException.java
new file mode 100644
index 0000000..4794fc0
--- /dev/null
+++ b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/exception/BizException.java
@@ -0,0 +1,35 @@
+package com.yunzhupaas.workflow.common.exception;
+
+import lombok.Getter;
+
+/**
+ * 业务异常类
+ *
+ * @author YUNZHUPAAS FlowableYUNZHUPAAS开发组
+ * @version 1.0.0
+ * @since 2024/4/3 15:31
+ */
+@Getter
+public class BizException extends RuntimeException {
+ public ResultCode resultCode;
+
+ public BizException(ResultCode errorCode) {
+ super(errorCode.getMsg());
+ this.resultCode = errorCode;
+ }
+
+ public BizException(String message) {
+ super(message);
+ this.resultCode = ResultCode.SYSTEM_EXECUTION_ERROR;
+ }
+
+ public BizException(String message, Throwable cause) {
+ super(message, cause);
+ this.resultCode = ResultCode.SYSTEM_EXECUTION_ERROR;
+ }
+
+ public BizException(Throwable cause) {
+ super(cause);
+ this.resultCode = ResultCode.SYSTEM_EXECUTION_ERROR;
+ }
+}
diff --git a/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/exception/ResultCode.java b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/exception/ResultCode.java
new file mode 100644
index 0000000..c2d3323
--- /dev/null
+++ b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/exception/ResultCode.java
@@ -0,0 +1,48 @@
+package com.yunzhupaas.workflow.common.exception;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 统一返回结果枚举类
+ *
+ * @author YUNZHUPAAS FlowableYUNZHUPAAS开发组
+ * @version 1.0.0
+ * @since 2024/4/3 15:08
+ */
+@AllArgsConstructor
+@NoArgsConstructor
+@Getter
+public enum ResultCode implements Serializable {
+
+ SUCCESS("200", "请求成功"),
+ FAILURE("999", "请求失败"),
+
+ DELETE_SUCCESS("1101", "删除成功"),
+ DELETE_FAILURE("1102", "删除失败"),
+ DEPLOY_FAILURE("1103", "部署失败"),
+ START_FAILURE("1104", "启动失败"),
+ COMPLETE_SUCCESS("1105", "任务完成成功"),
+ COMPLETE_FAILURE("1106", "任务完成失败"),
+ RETRACT_SUCCESS("1107", "撤回成功"),
+ RETRACT_FAILURE("1108", "撤回失败"),
+ JUMP_SUCCESS("1109", "跳转成功"),
+ JUMP_FAILURE("1110", "跳转失败"),
+
+ DEPLOY_ERROR("9001", "部署错误,请检查XML格式、内容等是否有误"),
+ DEFINITION_NOT_EXIST("9002", "找不到流程模板,请重新发布该流程"),
+ INSTANCE_NOT_EXIST("9003", "实例不存在"),
+ TASK_NOT_EXIST("9004", "任务不存在"),
+ TASK_COMPLETE_ERROR("9005", "任务完成错误"),
+ TASK_JUMP_ERROR("9006", "节点跳转错误"),
+
+ SYSTEM_EXECUTION_ERROR("9901", "系统执行出错"),
+ REQUEST_PARAM_IS_NULL("9701", "请求必填参数为空");
+
+ private String code;
+ private String msg;
+
+}
diff --git a/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/CompensateFo.java b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/CompensateFo.java
new file mode 100644
index 0000000..2a3c7c4
--- /dev/null
+++ b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/CompensateFo.java
@@ -0,0 +1,24 @@
+package com.yunzhupaas.workflow.common.model.fo;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 类的描述
+ *
+ * @author YUNZHUPAASYUNZHUPAAS开发组
+ * @version 5.0.x
+ * @since 2024/6/4 11:31
+ */
+@Data
+public class CompensateFo {
+ /**
+ * 实例主键
+ */
+ private String instanceId;
+ /**
+ * 原先的节点
+ */
+ private List source;
+}
diff --git a/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/DefinitionDeleteFo.java b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/DefinitionDeleteFo.java
new file mode 100644
index 0000000..93eb4ca
--- /dev/null
+++ b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/DefinitionDeleteFo.java
@@ -0,0 +1,29 @@
+package com.yunzhupaas.workflow.common.model.fo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 流程定义删除参数类
+ *
+ * @author YUNZHUPAAS FlowableYUNZHUPAAS开发组
+ * @version 1.0.0
+ * @since 2024/4/7 11:37
+ */
+@Data
+public class DefinitionDeleteFo implements Serializable {
+ /**
+ * 引擎部署ID
+ */
+ @NotBlank(message = "引擎部署ID不能为空")
+ @Schema(name = "deploymentId", description = "引擎部署ID")
+ private String deploymentId;
+ /**
+ * 是否级联删除流程定义下的流程实例等
+ */
+ @Schema(name = "cascade", description = "是否级联删除流程定义下的流程实例等")
+ private Boolean cascade;
+}
diff --git a/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/DefinitionDeployFo.java b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/DefinitionDeployFo.java
new file mode 100644
index 0000000..06ff720
--- /dev/null
+++ b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/DefinitionDeployFo.java
@@ -0,0 +1,34 @@
+package com.yunzhupaas.workflow.common.model.fo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 流程定义部署参数类
+ *
+ * @author YUNZHUPAAS FlowableYUNZHUPAAS开发组
+ * @version 1.0.0
+ * @since 2024/4/3 11:45
+ */
+@Data
+public class DefinitionDeployFo implements Serializable {
+ /**
+ * bpmn xml字符串
+ */
+ @NotBlank(message = "bpmn xml字符串不能为空")
+ @Schema(name = "bpmnXml", description = "bpmn xml字符串")
+ private String bpmnXml;
+ /**
+ * 业务名称
+ */
+ @Schema(name = "name", description = "业务名称")
+ private String name;
+ /**
+ * 业务Key
+ */
+ @Schema(name = "key", description = "业务Key")
+ private String key;
+}
diff --git a/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/FlowTargetTaskFo.java b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/FlowTargetTaskFo.java
new file mode 100644
index 0000000..a9087ed
--- /dev/null
+++ b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/FlowTargetTaskFo.java
@@ -0,0 +1,27 @@
+package com.yunzhupaas.workflow.common.model.fo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 连接线目标任务
+ *
+ * @author YUNZHUPAASYUNZHUPAAS开发组
+ * @version 5.0.x
+ * @since 2024/4/17 17:36
+ */
+@Data
+public class FlowTargetTaskFo implements Serializable {
+ /**
+ * 部署ID
+ */
+ @Schema(name = "deploymentId", description = "部署ID")
+ private String deploymentId;
+ /**
+ * 线的Key
+ */
+ @Schema(name = "flowKey", description = "线的Key")
+ private String flowKey;
+}
diff --git a/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/InfoModel.java b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/InfoModel.java
new file mode 100644
index 0000000..6408c59
--- /dev/null
+++ b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/InfoModel.java
@@ -0,0 +1,27 @@
+package com.yunzhupaas.workflow.common.model.fo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 类的描述
+ *
+ * @author YUNZHUPAASYUNZHUPAAS开发组
+ * @version 5.0.x
+ * @since 2024/9/30 15:09
+ */
+@Data
+public class InfoModel implements Serializable {
+ /**
+ * 部署ID
+ */
+ @Schema(name = "deploymentId", description = "部署ID")
+ private String deploymentId;
+ /**
+ * 节点Key
+ */
+ @Schema(name = "key", description = "节点Key")
+ private String key;
+}
diff --git a/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/InstanceDeleteFo.java b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/InstanceDeleteFo.java
new file mode 100644
index 0000000..25ff231
--- /dev/null
+++ b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/InstanceDeleteFo.java
@@ -0,0 +1,29 @@
+package com.yunzhupaas.workflow.common.model.fo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 流程实例删除参数类
+ *
+ * @author YUNZHUPAAS FlowableYUNZHUPAAS开发组
+ * @version 1.0.0
+ * @since 2024/4/7 15:55
+ */
+@Data
+public class InstanceDeleteFo implements Serializable {
+ /**
+ * 实例ID
+ */
+ @NotBlank(message = "实例ID不能为空")
+ @Schema(name = "instanceId", description = "实例ID")
+ private String instanceId;
+ /**
+ * 删除原因
+ */
+ @Schema(name = "deleteReason", description = "删除原因")
+ private String deleteReason;
+}
diff --git a/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/InstanceStartFo.java b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/InstanceStartFo.java
new file mode 100644
index 0000000..3f1fd28
--- /dev/null
+++ b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/InstanceStartFo.java
@@ -0,0 +1,30 @@
+package com.yunzhupaas.workflow.common.model.fo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Map;
+
+/**
+ * 流程实例启动参数类
+ *
+ * @author YUNZHUPAAS FlowableYUNZHUPAAS开发组
+ * @version 1.0.0
+ * @since 2024/4/7 15:14
+ */
+@Data
+public class InstanceStartFo implements Serializable {
+ /**
+ * 部署ID
+ */
+ @NotBlank(message = "部署ID不能为空")
+ @Schema(name = "deploymentId", description = "部署ID")
+ private String deploymentId;
+ /**
+ * 变量
+ */
+ @Schema(name = "variables", description = "变量")
+ private Map variables;
+}
diff --git a/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/JumpFo.java b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/JumpFo.java
new file mode 100644
index 0000000..539642c
--- /dev/null
+++ b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/JumpFo.java
@@ -0,0 +1,35 @@
+package com.yunzhupaas.workflow.common.model.fo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 跳转参数类
+ *
+ * @author YUNZHUPAAS FlowableYUNZHUPAAS开发组
+ * @version 1.0.0
+ * @since 2024/4/10 11:18
+ */
+@Data
+public class JumpFo implements Serializable {
+ /**
+ * 实例ID
+ */
+ @NotBlank(message = "实例ID不能为空")
+ @Schema(name = "instanceId", description = "实例ID")
+ private String instanceId;
+ /**
+ * 源节点集合
+ */
+ @Schema(name = "source", description = "源节点集合")
+ private List source;
+ /**
+ * 目标节点集合
+ */
+ @Schema(name = "target", description = "目标节点集合")
+ private List target;
+}
diff --git a/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/MoveMultiToSingleFo.java b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/MoveMultiToSingleFo.java
new file mode 100644
index 0000000..feddda9
--- /dev/null
+++ b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/MoveMultiToSingleFo.java
@@ -0,0 +1,38 @@
+package com.yunzhupaas.workflow.common.model.fo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 多节点跳转单节点参数类
+ *
+ * @author YUNZHUPAAS FlowableYUNZHUPAAS开发组
+ * @version 1.0.0
+ * @since 2024/4/9 14:23
+ */
+@Data
+public class MoveMultiToSingleFo implements Serializable {
+ /**
+ * 实例ID
+ */
+ @NotBlank(message = "实例ID不能为空")
+ @Schema(name = "instanceId", description = "实例ID")
+ private String instanceId;
+ /**
+ * 当前节点集合
+ */
+ @NotNull(message = "当前节点集合不能为空")
+ @Schema(name = "sourceKeys", description = "当前节点集合")
+ private List sourceKeys;
+ /**
+ * 目标节点
+ */
+ @NotBlank(message = "目标节点不能为空")
+ @Schema(name = "targetKey", description = "目标节点")
+ private String targetKey;
+}
diff --git a/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/MoveSingleToMultiFo.java b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/MoveSingleToMultiFo.java
new file mode 100644
index 0000000..5ffb445
--- /dev/null
+++ b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/MoveSingleToMultiFo.java
@@ -0,0 +1,38 @@
+package com.yunzhupaas.workflow.common.model.fo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 单节点跳转多节点参数类
+ *
+ * @author YUNZHUPAAS FlowableYUNZHUPAAS开发组
+ * @version 1.0.0
+ * @since 2024/4/8 16:36
+ */
+@Data
+public class MoveSingleToMultiFo implements Serializable {
+ /**
+ * 实例ID
+ */
+ @NotBlank(message = "实例ID不能为空")
+ @Schema(name = "instanceId", description = "实例ID")
+ private String instanceId;
+ /**
+ * 当前节点
+ */
+ @NotBlank(message = "当前节点不能为空")
+ @Schema(name = "sourceKey", description = "当前节点")
+ private String sourceKey;
+ /**
+ * 目标节点集合
+ */
+ @NotNull(message = "目标节点集合不能为空")
+ @Schema(name = "targetKeys", description = "目标节点集合")
+ private List targetKeys;
+}
diff --git a/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/TaskAfterFo.java b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/TaskAfterFo.java
new file mode 100644
index 0000000..eb4267e
--- /dev/null
+++ b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/TaskAfterFo.java
@@ -0,0 +1,29 @@
+package com.yunzhupaas.workflow.common.model.fo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 类的描述
+ *
+ * @author YUNZHUPAASYUNZHUPAAS开发组
+ * @version 5.0.x
+ * @since 2024/5/8 20:03
+ */
+@Data
+public class TaskAfterFo implements Serializable {
+ /**
+ * 部署ID
+ */
+ @Schema(name = "deploymentId", description = "部署ID")
+ private String deploymentId;
+ /**
+ * 节点Key
+ */
+ @Schema(name = "taskKeys", description = "节点Key")
+ private List taskKeys = new ArrayList<>();
+}
diff --git a/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/TaskBackFo.java b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/TaskBackFo.java
new file mode 100644
index 0000000..4ee086b
--- /dev/null
+++ b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/TaskBackFo.java
@@ -0,0 +1,29 @@
+package com.yunzhupaas.workflow.common.model.fo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 任务退回参数类
+ *
+ * @author YUNZHUPAAS FlowableYUNZHUPAAS开发组
+ * @version 1.0.0
+ * @since 2024/4/8 16:05
+ */
+@Data
+public class TaskBackFo implements Serializable {
+ /**
+ * 任务ID
+ */
+ @NotBlank(message = "任务ID不能为空")
+ @Schema(name = "taskId", description = "任务ID")
+ private String taskId;
+ /**
+ * 目标节点ID
+ */
+ @Schema(name = "targetKey", description = "目标节点ID")
+ private String targetKey;
+}
diff --git a/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/TaskCompleteFo.java b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/TaskCompleteFo.java
new file mode 100644
index 0000000..91f239a
--- /dev/null
+++ b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/TaskCompleteFo.java
@@ -0,0 +1,30 @@
+package com.yunzhupaas.workflow.common.model.fo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Map;
+
+/**
+ * 任务完成参数类
+ *
+ * @author YUNZHUPAAS FlowableYUNZHUPAAS开发组
+ * @version 1.0.0
+ * @since 2024/4/8 13:57
+ */
+@Data
+public class TaskCompleteFo implements Serializable {
+ /**
+ * 任务ID
+ */
+ @NotBlank(message = "任务ID不能为空")
+ @Schema(name = "taskId", description = "任务ID")
+ private String taskId;
+ /**
+ * 变量
+ */
+ @Schema(name = "variables", description = "变量")
+ private Map variables;
+}
diff --git a/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/TaskNextFo.java b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/TaskNextFo.java
new file mode 100644
index 0000000..514a502
--- /dev/null
+++ b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/TaskNextFo.java
@@ -0,0 +1,32 @@
+package com.yunzhupaas.workflow.common.model.fo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 下一级任务参数类
+ *
+ * @author YUNZHUPAAS FlowableYUNZHUPAAS开发组
+ * @version 1.0.0
+ * @since 2024/4/8 17:03
+ */
+@Data
+public class TaskNextFo implements Serializable {
+ /**
+ * 部署ID
+ */
+ @Schema(name = "deploymentId", description = "部署ID")
+ private String deploymentId;
+ /**
+ * 节点Key
+ */
+ @Schema(name = "taskKey", description = "节点Key")
+ private String taskKey;
+ /**
+ * 任务ID
+ */
+ @Schema(name = "taskId", description = "任务ID")
+ private String taskId;
+}
diff --git a/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/TaskOutgoingFo.java b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/TaskOutgoingFo.java
new file mode 100644
index 0000000..46d4309
--- /dev/null
+++ b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/TaskOutgoingFo.java
@@ -0,0 +1,32 @@
+package com.yunzhupaas.workflow.common.model.fo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 查询参数类
+ *
+ * @author YUNZHUPAAS FlowableYUNZHUPAAS开发组
+ * @version 1.0.0
+ * @since 2024/4/9 9:53
+ */
+@Data
+public class TaskOutgoingFo implements Serializable {
+ /**
+ * 部署ID
+ */
+ @Schema(name = "deploymentId", description = "部署ID")
+ private String deploymentId;
+ /**
+ * 节点Key
+ */
+ @Schema(name = "taskKey", description = "节点Key")
+ private String taskKey;
+ /**
+ * 任务ID
+ */
+ @Schema(name = "taskId", description = "任务ID")
+ private String taskId;
+}
diff --git a/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/TaskPrevFo.java b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/TaskPrevFo.java
new file mode 100644
index 0000000..a6b1423
--- /dev/null
+++ b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/fo/TaskPrevFo.java
@@ -0,0 +1,32 @@
+package com.yunzhupaas.workflow.common.model.fo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 上一级任务参数类
+ *
+ * @author YUNZHUPAASYUNZHUPAAS开发组
+ * @version 5.0.x
+ * @since 2024/4/23 10:38
+ */
+@Data
+public class TaskPrevFo implements Serializable {
+ /**
+ * 部署ID
+ */
+ @Schema(name = "deploymentId", description = "部署ID")
+ private String deploymentId;
+ /**
+ * 节点Key
+ */
+ @Schema(name = "taskKey", description = "节点Key")
+ private String taskKey;
+ /**
+ * 任务ID
+ */
+ @Schema(name = "taskId", description = "任务ID")
+ private String taskId;
+}
diff --git a/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/vo/DefinitionVo.java b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/vo/DefinitionVo.java
new file mode 100644
index 0000000..255e4db
--- /dev/null
+++ b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/vo/DefinitionVo.java
@@ -0,0 +1,42 @@
+package com.yunzhupaas.workflow.common.model.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 流程定义VO
+ *
+ * @author YUNZHUPAAS FlowableYUNZHUPAAS开发组
+ * @version 1.0.0
+ * @since 2024/4/3 14:26
+ */
+@Data
+public class DefinitionVo implements Serializable {
+ /**
+ * 定义ID
+ */
+ @Schema(name = "id", description = "定义ID")
+ String definitionId;
+ /**
+ * 定义名称
+ */
+ @Schema(name = "name", description = "定义名称")
+ String definitionName;
+ /**
+ * 定义Key
+ */
+ @Schema(name = "key", description = "定义Key")
+ String definitionKey;
+ /**
+ * 定义版本
+ */
+ @Schema(name = "version", description = "定义版本")
+ Integer definitionVersion;
+ /**
+ * 定义部署ID
+ */
+ @Schema(name = "deploymentId", description = "定义部署ID")
+ String deploymentId;
+}
diff --git a/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/vo/DeploymentVo.java b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/vo/DeploymentVo.java
new file mode 100644
index 0000000..72b0d41
--- /dev/null
+++ b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/vo/DeploymentVo.java
@@ -0,0 +1,22 @@
+package com.yunzhupaas.workflow.common.model.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 定义部署Vo
+ *
+ * @author YUNZHUPAAS FlowableYUNZHUPAAS开发组
+ * @version 1.0.0
+ * @since 2024/4/7 9:35
+ */
+@Data
+public class DeploymentVo implements Serializable {
+ /**
+ * 部署ID
+ */
+ @Schema(name = "deploymentId", description = "部署ID")
+ String deploymentId;
+}
diff --git a/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/vo/FlowElementVo.java b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/vo/FlowElementVo.java
new file mode 100644
index 0000000..6c17d92
--- /dev/null
+++ b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/vo/FlowElementVo.java
@@ -0,0 +1,34 @@
+package com.yunzhupaas.workflow.common.model.vo;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 类的描述
+ *
+ * @author YUNZHUPAASYUNZHUPAAS开发组
+ * @version 5.0.x
+ * @since 2024/6/11 10:33
+ */
+@Data
+public class FlowElementVo {
+ private String id;
+ private String name;
+ /**
+ * 线的源
+ */
+ private String sourceRef;
+ /**
+ * 线的目标
+ */
+ private String targetRef;
+ /**
+ * 节点进线
+ */
+ private List incomingList;
+ /**
+ * 节点出线
+ */
+ private List outgoingList;
+}
diff --git a/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/vo/FlowVo.java b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/vo/FlowVo.java
new file mode 100644
index 0000000..1b971e1
--- /dev/null
+++ b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/vo/FlowVo.java
@@ -0,0 +1,19 @@
+package com.yunzhupaas.workflow.common.model.vo;
+
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 类的描述
+ *
+ * @author YUNZHUPAASYUNZHUPAAS开发组
+ * @version 5.0.x
+ * @since 2024/8/13 13:31
+ */
+@Data
+public class FlowVo {
+ private String key;
+ private List children = new ArrayList<>();
+}
diff --git a/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/vo/HistoricInstanceVo.java b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/vo/HistoricInstanceVo.java
new file mode 100644
index 0000000..c4fd761
--- /dev/null
+++ b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/vo/HistoricInstanceVo.java
@@ -0,0 +1,46 @@
+package com.yunzhupaas.workflow.common.model.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * 历史流程实例Vo
+ *
+ * @author YUNZHUPAAS FlowableYUNZHUPAAS开发组
+ * @version 1.0.0
+ * @since 2024/4/7 17:04
+ */
+@Data
+public class HistoricInstanceVo implements Serializable {
+ /**
+ * 实例ID
+ */
+ @Schema(name = "instanceId", description = "实例ID")
+ private String instanceId;
+ /**
+ * 开始时间
+ */
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ @Schema(name = "startTime", description = "开始时间")
+ private LocalDateTime startTime;
+ /**
+ * 结束时间
+ */
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ @Schema(name = "endTime", description = "结束时间")
+ private LocalDateTime endTime;
+ /**
+ * 耗时
+ */
+ @Schema(name = "durationInMillis", description = "耗时")
+ private Long durationInMillis;
+ /**
+ * 删除原因
+ */
+ @Schema(name = "deleteReason", description = "删除原因")
+ private String deleteReason;
+}
diff --git a/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/vo/HistoricNodeVo.java b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/vo/HistoricNodeVo.java
new file mode 100644
index 0000000..836b7d1
--- /dev/null
+++ b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/vo/HistoricNodeVo.java
@@ -0,0 +1,28 @@
+package com.yunzhupaas.workflow.common.model.vo;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 类的描述
+ *
+ * @author YUNZHUPAASYUNZHUPAAS开发组
+ * @version 5.0.x
+ * @since 2024/6/17 16:06
+ */
+@Data
+public class HistoricNodeVo implements Serializable {
+ /**
+ * 任务ID
+ */
+ private String taskId;
+ /**
+ * 节点编码
+ */
+ private String code;
+ /**
+ * 开始时间
+ */
+ private Long startTime;
+}
diff --git a/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/vo/InstanceVo.java b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/vo/InstanceVo.java
new file mode 100644
index 0000000..67b9e29
--- /dev/null
+++ b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/vo/InstanceVo.java
@@ -0,0 +1,22 @@
+package com.yunzhupaas.workflow.common.model.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 流程实例Vo
+ *
+ * @author YUNZHUPAAS FlowableYUNZHUPAAS开发组
+ * @version 1.0.0
+ * @since 2024/4/7 14:42
+ */
+@Data
+public class InstanceVo implements Serializable {
+ /**
+ * 实例ID
+ */
+ @Schema(name = "instanceId", description = "实例ID")
+ private String instanceId;
+}
diff --git a/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/vo/NodeElementVo.java b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/vo/NodeElementVo.java
new file mode 100644
index 0000000..f31e24b
--- /dev/null
+++ b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/vo/NodeElementVo.java
@@ -0,0 +1,38 @@
+package com.yunzhupaas.workflow.common.model.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 元素Vo
+ *
+ * @author YUNZHUPAAS FlowableYUNZHUPAAS开发组
+ * @version 1.0.0
+ * @since 2024/4/8 17:48
+ */
+@Data
+public class NodeElementVo implements Serializable {
+ /**
+ * 元素ID
+ */
+ @Schema(name = "id", description = "元素ID")
+ private String id;
+ /**
+ * 元素名称
+ */
+ @Schema(name = "name", description = "元素名称")
+ private String name;
+ /**
+ * 进线ID
+ */
+ @Schema(name = "incoming", description = "进线ID")
+ private List incomingList;
+ /**
+ * 出线ID
+ */
+ @Schema(name = "outgoingList", description = "出线ID")
+ private List outgoingList;
+}
diff --git a/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/vo/TaskVo.java b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/vo/TaskVo.java
new file mode 100644
index 0000000..c75597c
--- /dev/null
+++ b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/model/vo/TaskVo.java
@@ -0,0 +1,37 @@
+package com.yunzhupaas.workflow.common.model.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 流程任务VO
+ *
+ * @author YUNZHUPAAS FlowableYUNZHUPAAS开发组
+ * @version 1.0.0
+ * @since 2024/4/8 11:22
+ */
+@Data
+public class TaskVo implements Serializable {
+ /**
+ * 任务ID
+ */
+ @Schema(name = "taskId", description = "任务ID")
+ private String taskId;
+ /**
+ * 任务名称
+ */
+ @Schema(name = "taskName", description = "任务名称")
+ private String taskName;
+ /**
+ * 任务Key
+ */
+ @Schema(name = "taskKey", description = "任务Key")
+ private String taskKey;
+ /**
+ * 实例ID
+ */
+ @Schema(name = "instanceId", description = "实例ID")
+ private String instanceId;
+}
diff --git a/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/service/IDefinitionService.java b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/service/IDefinitionService.java
new file mode 100644
index 0000000..f0872f0
--- /dev/null
+++ b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/service/IDefinitionService.java
@@ -0,0 +1,51 @@
+package com.yunzhupaas.workflow.common.service;
+
+import com.yunzhupaas.workflow.common.model.fo.DefinitionDeleteFo;
+import com.yunzhupaas.workflow.common.model.fo.DefinitionDeployFo;
+import com.yunzhupaas.workflow.common.model.vo.DefinitionVo;
+import com.yunzhupaas.workflow.common.model.vo.DeploymentVo;
+import com.yunzhupaas.workflow.common.model.vo.FlowElementVo;
+
+import java.util.List;
+
+/**
+ * 流程定义服务接口
+ *
+ * @author YUNZHUPAAS FlowableYUNZHUPAAS开发组
+ * @version 1.0.0
+ * @since 2024/4/3 11:36
+ */
+public interface IDefinitionService {
+ /**
+ * 部署流程定义
+ *
+ * @param fo {@link DefinitionDeployFo}
+ * @return {@link DeploymentVo}
+ * @since 2024/4/7 10:51
+ **/
+ DeploymentVo deployDefinition(DefinitionDeployFo fo);
+
+ /**
+ * 列表查询流程定义
+ *
+ * @return {@link List}
+ * @since 2024/4/7 11:23
+ **/
+ List listDefinition();
+
+ /**
+ * 删除流程定义
+ *
+ * @param fo {@link DefinitionDeleteFo}
+ * @return {@link boolean}
+ * @since 2024/4/7 13:51
+ **/
+ boolean deleteDefinition(DefinitionDeleteFo fo);
+
+ /**
+ * 获取流程元素
+ *
+ * @param deploymentId 部署ID
+ */
+ List getStructure(String deploymentId);
+}
diff --git a/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/service/IInstanceService.java b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/service/IInstanceService.java
new file mode 100644
index 0000000..cb2365b
--- /dev/null
+++ b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/service/IInstanceService.java
@@ -0,0 +1,42 @@
+package com.yunzhupaas.workflow.common.service;
+
+import com.yunzhupaas.workflow.common.model.fo.InstanceDeleteFo;
+import com.yunzhupaas.workflow.common.model.fo.InstanceStartFo;
+import com.yunzhupaas.workflow.common.model.vo.HistoricInstanceVo;
+import com.yunzhupaas.workflow.common.model.vo.InstanceVo;
+
+/**
+ * 流程实例服务接口
+ *
+ * @author YUNZHUPAAS FlowableYUNZHUPAAS开发组
+ * @version 1.0.0
+ * @since 2024/4/7 14:31
+ */
+public interface IInstanceService {
+ /**
+ * 根据ID启动实例
+ *
+ * @param fo {@link InstanceStartFo}
+ * @return {@link InstanceVo}
+ * @since 2024/4/7 15:44
+ **/
+ InstanceVo startById(InstanceStartFo fo);
+
+ /**
+ * 获取历史流程实例
+ *
+ * @param processInstanceId {@link String}
+ * @return {@link HistoricInstanceVo}
+ * @since 2024/4/7 17:30
+ **/
+ HistoricInstanceVo getHistoricProcessInstance(String processInstanceId);
+
+ /**
+ * 删除流程实例
+ *
+ * @param fo {@link InstanceDeleteFo}
+ * @return {@link boolean}
+ * @since 2024/4/7 16:07
+ **/
+ boolean deleteInstance(InstanceDeleteFo fo);
+}
diff --git a/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/service/ITaskService.java b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/service/ITaskService.java
new file mode 100644
index 0000000..9d2c22a
--- /dev/null
+++ b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/service/ITaskService.java
@@ -0,0 +1,196 @@
+package com.yunzhupaas.workflow.common.service;
+
+import com.yunzhupaas.workflow.common.model.fo.*;
+import com.yunzhupaas.workflow.common.model.vo.FlowVo;
+import com.yunzhupaas.workflow.common.model.vo.HistoricNodeVo;
+import com.yunzhupaas.workflow.common.model.vo.NodeElementVo;
+import com.yunzhupaas.workflow.common.model.vo.TaskVo;
+
+import java.util.List;
+
+/**
+ * 流程任务服务接口
+ *
+ * @author YUNZHUPAAS FlowableYUNZHUPAAS开发组
+ * @version 1.0.0
+ * @since 2024/4/8 11:11
+ */
+public interface ITaskService {
+
+ /**
+ * 根据流程实例ID获取任务
+ *
+ * @param instanceId {@link String}
+ * @return {@link List}
+ * @since 2024/4/8 14:15
+ **/
+ List getTask(String instanceId);
+
+ /**
+ * 完成任务
+ *
+ * @param fo {@link TaskCompleteFo}
+ * @return {@link boolean}
+ * @since 2024/4/8 14:21
+ **/
+ boolean complete(TaskCompleteFo fo);
+
+ /**
+ * 单节点跳转多节点
+ *
+ * @param fo {@link MoveSingleToMultiFo}
+ * @return {@link boolean}
+ * @since 2024/4/9 14:48
+ **/
+ boolean moveSingleToMulti(MoveSingleToMultiFo fo);
+
+ /**
+ * 多节点跳转单节点
+ *
+ * @param fo {@link MoveMultiToSingleFo}
+ * @return {@link boolean}
+ * @since 2024/4/9 14:48
+ **/
+ boolean moveMultiToSingle(MoveMultiToSingleFo fo);
+
+ /**
+ * 节点跳转
+ *
+ * @param fo {@link JumpFo}
+ * @return {@link boolean}
+ * @since 2024/4/10 11:35
+ **/
+ boolean jump(JumpFo fo);
+
+ /**
+ * 获取可回退的节点ID
+ *
+ * @param taskId {@link String}
+ * @return {@link List}
+ * @since 2024/4/8 15:39
+ **/
+ List getFallbacks(String taskId);
+
+ /**
+ * 回退目标节点
+ *
+ * @param fo {@link TaskBackFo}
+ * @return {@link List}
+ * @since 2024/4/8 16:11
+ **/
+ List back(TaskBackFo fo);
+
+ /**
+ * 获取上一级任务节点ID集合,用于自动处置的相邻选项
+ *
+ * @param fo {@link TaskPrevFo}
+ * @return {@link List}
+ * @since 2024/4/8 16:30
+ **/
+ List getPrevUserTask(TaskPrevFo fo);
+
+ /**
+ * 获取下一级任务节点集合
+ *
+ * @param fo {@link TaskNextFo}
+ * @return {@link List< NodeElementVo >}
+ * @since 2024/4/9 9:20
+ **/
+ List getNextUserTask(TaskNextFo fo);
+
+ /**
+ * 获取线之后的任务节点
+ *
+ * @param fo {@link FlowTargetTaskFo}
+ * @return {@link String}
+ * @since 2024/4/17 17:45
+ **/
+ List getTaskKeyAfterFlow(FlowTargetTaskFo fo);
+
+ /**
+ * 撤回
+ *
+ * @param taskId {@link String}
+ * @return {@link boolean}
+ * @since 2024/4/9 9:30
+ **/
+ boolean retract(String taskId);
+
+ /**
+ * 获取出线Key集合(若出线的出口为网关,则一并获取网关的出线)
+ *
+ * @param fo {@link TaskOutgoingFo}
+ * @return {@link List}
+ * @since 2024/4/9 10:54
+ **/
+ List getOutgoingFlows(TaskOutgoingFo fo);
+
+ /**
+ * 获取出线
+ *
+ * @param fo 参数
+ */
+ List getOutgoing(TaskOutgoingFo fo);
+
+ /**
+ * 获取完成的节点Key
+ *
+ * @param instanceId {@link String}
+ * @return {@link List}
+ * @since 2024/4/9 13:51
+ **/
+ List getKeysOfFinished(String instanceId);
+
+ /**
+ * 获取进线的Key
+ *
+ * @param taskId {@link String}
+ * @return {@link List}
+ * @since 2024/4/9 13:56
+ **/
+ List getIncomingFlows(String taskId);
+
+ /**
+ * 获取未经过的节点
+ *
+ * @param instanceId {@link String}
+ * @return {@link List}
+ * @since 2024/4/29 10:01
+ **/
+ List getToBePass(String instanceId);
+
+ /**
+ * 获取节点的后续节点
+ *
+ * @param fo 参数
+ */
+ List getAfter(TaskAfterFo fo);
+
+ /**
+ * 异常补偿
+ *
+ * @param fo 参数
+ */
+ List compensate(CompensateFo fo);
+
+ /**
+ * 获取历史节点
+ *
+ * @param instanceId 实例主键
+ */
+ List getHistoric(String instanceId);
+
+ /**
+ * 获取历史结束节点
+ *
+ * @param instanceId 实例主键
+ */
+ List getHistoricEnd(String instanceId);
+
+ /**
+ * 获取元素信息
+ *
+ * @param model 参数
+ */
+ NodeElementVo getElementInfo(InfoModel model);
+}
diff --git a/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/util/FlowUtil.java b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/util/FlowUtil.java
new file mode 100644
index 0000000..d598477
--- /dev/null
+++ b/yunzhupaas-workflow-common/src/main/java/com/yunzhupaas/workflow/common/util/FlowUtil.java
@@ -0,0 +1,8 @@
+package com.yunzhupaas.workflow.common.util;
+
+public class FlowUtil {
+
+ public static boolean isDM(String url) {
+ return url.startsWith("jdbc:dm");
+ }
+}
diff --git a/yunzhupaas-workflow-flowable/pom.xml b/yunzhupaas-workflow-flowable/pom.xml
new file mode 100644
index 0000000..79ec41e
--- /dev/null
+++ b/yunzhupaas-workflow-flowable/pom.xml
@@ -0,0 +1,31 @@
+
+
+ 4.0.0
+
+ com.yunzhupaas
+ yunzhupaas-workflow-core
+ 1.0.0-RELEASE
+
+
+ yunzhupaas-workflow-flowable
+
+
+
+ org.flowable
+ flowable-spring-boot-starter
+ ${flowable.version}
+
+
+ com.yunzhupaas
+ yunzhupaas-workflow-common
+ ${project.version}
+
+
+
+
+
+
+
+
diff --git a/yunzhupaas-workflow-flowable/src/main/java/com/yunzhupaas/workflow/flowable/cmd/JumpCmd.java b/yunzhupaas-workflow-flowable/src/main/java/com/yunzhupaas/workflow/flowable/cmd/JumpCmd.java
new file mode 100644
index 0000000..f5f96d8
--- /dev/null
+++ b/yunzhupaas-workflow-flowable/src/main/java/com/yunzhupaas/workflow/flowable/cmd/JumpCmd.java
@@ -0,0 +1,151 @@
+package com.yunzhupaas.workflow.flowable.cmd;
+
+import cn.hutool.core.collection.CollectionUtil;
+import org.flowable.bpmn.model.BpmnModel;
+import org.flowable.bpmn.model.FlowNode;
+import org.flowable.common.engine.impl.interceptor.Command;
+import org.flowable.common.engine.impl.interceptor.CommandContext;
+import org.flowable.engine.RuntimeService;
+import org.flowable.engine.history.HistoricActivityInstance;
+import org.flowable.engine.impl.HistoricActivityInstanceQueryImpl;
+import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
+import org.flowable.engine.impl.persistence.entity.ExecutionEntityManager;
+import org.flowable.engine.impl.persistence.entity.HistoricActivityInstanceEntity;
+import org.flowable.engine.impl.util.CommandContextUtil;
+import org.flowable.engine.runtime.Execution;
+import org.flowable.task.api.history.HistoricTaskInstance;
+import org.flowable.task.service.HistoricTaskService;
+import org.flowable.task.service.impl.HistoricTaskInstanceQueryImpl;
+import org.flowable.task.service.impl.persistence.entity.HistoricTaskInstanceEntity;
+import org.flowable.variable.service.VariableService;
+import org.flowable.variable.service.impl.persistence.entity.VariableInstanceEntity;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 跳转命令类
+ * 参考:https://blog.csdn.net/zhsp419/article/details/114264451
+ *
+ * @author YUNZHUPAAS FlowableYUNZHUPAAS开发组
+ * @version 1.0.0
+ * @since 2024/4/9 17:45
+ */
+public class JumpCmd implements Command {
+ private final String processInstanceId;
+
+ private final List sourceTaskDefIdList;
+ private final List targetFlowNodeIdList;
+
+ private final String deleteReason;
+
+ private final BpmnModel bpmnModel;
+ private final RuntimeService runtimeService;
+
+ /**
+ * 保存撤回节点的变量map
+ */
+ private final Map> varMap = new ConcurrentHashMap<>();
+
+ public JumpCmd(String processInstanceId, List sourceTaskDefIdList, List targetFlowNodeIdList,
+ String deleteReason, BpmnModel bpmnModel, RuntimeService runtimeService) {
+ this.processInstanceId = processInstanceId;
+ this.sourceTaskDefIdList = sourceTaskDefIdList;
+ this.deleteReason = deleteReason;
+ this.targetFlowNodeIdList = targetFlowNodeIdList;
+ this.bpmnModel = bpmnModel;
+ this.runtimeService = runtimeService;
+ }
+
+ @Override
+ public Void execute(CommandContext commandContext) {
+ ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager();
+ // 处理act_ru_execution
+ handleExecution(commandContext);
+ // 处理act_hi_actinst
+ handleActInst(commandContext);
+
+ targetFlowNodeIdList.forEach(targetId -> {
+ FlowNode flowNode = (FlowNode) bpmnModel.getFlowElement(targetId);
+ // 创建子执行流,开启任务
+ ExecutionEntity processExecution = executionEntityManager.findById(processInstanceId);
+ ExecutionEntity childExecution = executionEntityManager.createChildExecution(processExecution);
+ childExecution.setCurrentFlowElement(flowNode);
+
+ // 设置执行变量
+ VariableService variableService = CommandContextUtil.getVariableService();
+ List variableInstanceEntities = varMap.get(flowNode.getId());
+ if (CollectionUtil.isNotEmpty(variableInstanceEntities)) {
+ variableInstanceEntities.forEach(var -> {
+ var.setExecutionId(childExecution.getId());
+ variableService.insertVariableInstance(var);
+ });
+ }
+ executionEntityManager.insert(childExecution);
+ // 交给引擎流转
+ CommandContextUtil.getAgenda().planContinueProcessOperation(childExecution);
+ });
+ return null;
+ }
+
+ private void handleActInst(CommandContext commandContext) {
+ for (String str : sourceTaskDefIdList) {
+ HistoricActivityInstanceQueryImpl query = new HistoricActivityInstanceQueryImpl()
+ .activityId(str).processInstanceId(processInstanceId).unfinished();
+ List activityInstances = CommandContextUtil
+ .getHistoricActivityInstanceEntityManager()
+ .findHistoricActivityInstancesByQueryCriteria(query);
+ for (HistoricActivityInstance activity : activityInstances) {
+ HistoricActivityInstanceEntity activityEntity = (HistoricActivityInstanceEntity) activity;
+ // 修改act_hi_actinst表
+ activityEntity.setDeleted(true);
+ activityEntity.setDeleteReason(deleteReason);
+ CommandContextUtil.getHistoricActivityInstanceEntityManager().update(activityEntity);
+ }
+ }
+ }
+
+ private void handleExecution(CommandContext commandContext) {
+ ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager();
+ HistoricTaskService historicTaskService = CommandContextUtil.getHistoricTaskService();
+ VariableService variableService = CommandContextUtil.getVariableService();
+ for (String str : sourceTaskDefIdList) {
+ List executionEntities = runtimeService.createExecutionQuery()
+ .processInstanceId(processInstanceId).activityId(str).list();
+ for (Execution parentExecution : executionEntities) {
+ // 关闭未完成的任务执行流
+ // 获取子级Executions,如子流程节点等需要处理
+ List childExecutions = executionEntityManager
+ .findChildExecutionsByParentExecutionId(parentExecution.getId());
+ for (ExecutionEntity childExecution : childExecutions) {
+ // 因为外键约束,首先要删除variable表中的execution相关数据
+ List variableInstances = variableService
+ .findVariableInstancesByExecutionId(childExecution.getId());
+ varMap.put(parentExecution.getActivityId(), variableInstances);
+ variableInstances.forEach(variableService::deleteVariableInstance);
+ executionEntityManager.deleteExecutionAndRelatedData(childExecution, deleteReason, false);
+ // 修改历史实例
+ HistoricTaskInstanceQueryImpl query = new HistoricTaskInstanceQueryImpl()
+ .executionId(childExecution.getId()).processInstanceId(processInstanceId);
+ List HistoricTaskInstances = historicTaskService
+ .findHistoricTaskInstancesByQueryCriteria(query);
+ if (CollectionUtil.isNotEmpty(HistoricTaskInstances)) {
+ for (HistoricTaskInstance HistoricTaskInstance : HistoricTaskInstances) {
+ HistoricTaskInstanceEntity entity = (HistoricTaskInstanceEntity) HistoricTaskInstance;
+ entity.setDeleteReason(deleteReason);
+ historicTaskService.updateHistoricTask(entity, true);
+ }
+ }
+ }
+ // 父执行流关闭
+ List variableInstances = variableService
+ .findVariableInstancesByExecutionId(parentExecution.getId());
+ varMap.put(parentExecution.getActivityId(), variableInstances);
+ variableInstances.forEach(variableService::deleteVariableInstance);
+ ExecutionEntity parentExecution1 = (ExecutionEntity) parentExecution;
+ executionEntityManager.deleteExecutionAndRelatedData(parentExecution1, deleteReason, false);
+ }
+ }
+ }
+}
diff --git a/yunzhupaas-workflow-flowable/src/main/java/com/yunzhupaas/workflow/flowable/service/DefinitionServiceImpl.java b/yunzhupaas-workflow-flowable/src/main/java/com/yunzhupaas/workflow/flowable/service/DefinitionServiceImpl.java
new file mode 100644
index 0000000..8f028f2
--- /dev/null
+++ b/yunzhupaas-workflow-flowable/src/main/java/com/yunzhupaas/workflow/flowable/service/DefinitionServiceImpl.java
@@ -0,0 +1,139 @@
+package com.yunzhupaas.workflow.flowable.service;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.util.IdUtil;
+import cn.hutool.core.util.StrUtil;
+import com.yunzhupaas.workflow.common.exception.BizException;
+import com.yunzhupaas.workflow.common.exception.ResultCode;
+import com.yunzhupaas.workflow.common.model.fo.DefinitionDeleteFo;
+import com.yunzhupaas.workflow.common.model.fo.DefinitionDeployFo;
+import com.yunzhupaas.workflow.common.model.vo.DefinitionVo;
+import com.yunzhupaas.workflow.common.model.vo.DeploymentVo;
+import com.yunzhupaas.workflow.common.model.vo.FlowElementVo;
+import com.yunzhupaas.workflow.common.service.IDefinitionService;
+import com.yunzhupaas.workflow.flowable.util.FlowableUtil;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.flowable.bpmn.model.Process;
+import org.flowable.bpmn.model.*;
+import org.flowable.engine.RepositoryService;
+import org.flowable.engine.repository.Deployment;
+import org.flowable.engine.repository.ProcessDefinition;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 流程定义实现层
+ *
+ * @author YUNZHUPAAS FlowableYUNZHUPAAS开发组
+ * @version 1.0.0
+ * @since 2024/4/3 11:36
+ */
+@Slf4j
+@Service
+@AllArgsConstructor
+public class DefinitionServiceImpl implements IDefinitionService {
+ private final RepositoryService repositoryService;
+
+ @Override
+ public DeploymentVo deployDefinition(DefinitionDeployFo fo) {
+ Deployment deployment;
+ try {
+ String resourceName;
+ if (StrUtil.isNotBlank(fo.getKey())) {
+ resourceName = fo.getKey();
+ } else {
+ resourceName = IdUtil.getSnowflakeNextIdStr();
+ }
+ deployment = repositoryService
+ .createDeployment()
+ .name(fo.getName())
+ .key(fo.getKey())
+ .addString(resourceName + ".bpmn20.xml", fo.getBpmnXml())
+ .disableSchemaValidation()
+ .deploy();
+ } catch (Exception e) {
+ throw new BizException(ResultCode.DEPLOY_ERROR.getMsg(), e);
+ }
+ DeploymentVo vo = new DeploymentVo();
+ vo.setDeploymentId(deployment.getId());
+ return vo;
+ }
+
+ @Override
+ public List listDefinition() {
+ List definitions = repositoryService.createProcessDefinitionQuery().list();
+ List list = new ArrayList<>();
+ if (CollectionUtil.isNotEmpty(definitions)) {
+ for (ProcessDefinition definition : definitions) {
+ DefinitionVo vo = new DefinitionVo();
+ vo.setDefinitionId(definition.getId());
+ vo.setDefinitionName(definition.getName());
+ vo.setDefinitionKey(definition.getKey());
+ vo.setDefinitionVersion(definition.getVersion());
+ vo.setDeploymentId(definition.getDeploymentId());
+ list.add(vo);
+ }
+ }
+ return list;
+ }
+
+ @Override
+ public boolean deleteDefinition(DefinitionDeleteFo fo) {
+ if (null == fo.getCascade()) {
+ fo.setCascade(true);
+ }
+ try {
+ // 根据部署ID删除,并级联删除当前流程定义下的所有流程实例、job
+ repositoryService.deleteDeployment(fo.getDeploymentId(), fo.getCascade());
+ return true;
+ } catch (Exception e) {
+ log.error(ResultCode.DELETE_FAILURE.getMsg(), e);
+ }
+ return false;
+ }
+
+ @Override
+ public List getStructure(String deploymentId) {
+ List vos = new ArrayList<>();
+
+ ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId)
+ .singleResult();
+ if (null != definition) {
+ Process process = repositoryService.getBpmnModel(definition.getId()).getProcesses().get(0);
+ Collection elements = FlowableUtil.getAllElements(process.getFlowElements(), null);
+ for (FlowElement element : elements) {
+ FlowElementVo vo = BeanUtil.copyProperties(element, FlowElementVo.class);
+ if (element instanceof Event) {
+ Event el = (Event) element;
+ vo.setIncomingList(
+ el.getIncomingFlows().stream().map(SequenceFlow::getId).collect(Collectors.toList()));
+ vo.setOutgoingList(
+ el.getOutgoingFlows().stream().map(SequenceFlow::getId).collect(Collectors.toList()));
+ }
+ if (element instanceof Activity) {
+ Activity el = (Activity) element;
+ vo.setIncomingList(
+ el.getIncomingFlows().stream().map(SequenceFlow::getId).collect(Collectors.toList()));
+ vo.setOutgoingList(
+ el.getOutgoingFlows().stream().map(SequenceFlow::getId).collect(Collectors.toList()));
+ }
+ if (element instanceof Gateway) {
+ Gateway el = (Gateway) element;
+ vo.setIncomingList(
+ el.getIncomingFlows().stream().map(SequenceFlow::getId).collect(Collectors.toList()));
+ vo.setOutgoingList(
+ el.getOutgoingFlows().stream().map(SequenceFlow::getId).collect(Collectors.toList()));
+ }
+ vos.add(vo);
+ }
+ }
+
+ return vos;
+ }
+}
diff --git a/yunzhupaas-workflow-flowable/src/main/java/com/yunzhupaas/workflow/flowable/service/InstanceServiceImpl.java b/yunzhupaas-workflow-flowable/src/main/java/com/yunzhupaas/workflow/flowable/service/InstanceServiceImpl.java
new file mode 100644
index 0000000..ba6280a
--- /dev/null
+++ b/yunzhupaas-workflow-flowable/src/main/java/com/yunzhupaas/workflow/flowable/service/InstanceServiceImpl.java
@@ -0,0 +1,88 @@
+package com.yunzhupaas.workflow.flowable.service;
+
+import cn.hutool.core.collection.CollectionUtil;
+import com.yunzhupaas.workflow.common.exception.BizException;
+import com.yunzhupaas.workflow.common.exception.ResultCode;
+import com.yunzhupaas.workflow.common.model.fo.InstanceDeleteFo;
+import com.yunzhupaas.workflow.common.model.fo.InstanceStartFo;
+import com.yunzhupaas.workflow.common.model.vo.HistoricInstanceVo;
+import com.yunzhupaas.workflow.common.model.vo.InstanceVo;
+import com.yunzhupaas.workflow.common.service.IInstanceService;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.flowable.engine.HistoryService;
+import org.flowable.engine.RepositoryService;
+import org.flowable.engine.RuntimeService;
+import org.flowable.engine.history.HistoricProcessInstance;
+import org.flowable.engine.repository.ProcessDefinition;
+import org.flowable.engine.runtime.ProcessInstance;
+import org.springframework.stereotype.Service;
+
+import java.time.ZoneId;
+
+/**
+ * 流程实例实现层
+ *
+ * @author YUNZHUPAAS FlowableYUNZHUPAAS开发组
+ * @version 1.0.0
+ * @since 2024/4/7 14:34
+ */
+@Slf4j
+@Service
+@AllArgsConstructor
+public class InstanceServiceImpl implements IInstanceService {
+ private final RepositoryService repositoryService;
+ private final RuntimeService runtimeService;
+ private final HistoryService historyService;
+
+ @Override
+ public InstanceVo startById(InstanceStartFo fo) {
+ ProcessDefinition definition = repositoryService
+ .createProcessDefinitionQuery()
+ .deploymentId(fo.getDeploymentId()).singleResult();
+ if (null == definition) {
+ throw new BizException(ResultCode.DEFINITION_NOT_EXIST);
+ }
+ InstanceVo vo = new InstanceVo();
+ ProcessInstance instance;
+ if (CollectionUtil.isNotEmpty(fo.getVariables())) {
+ instance = runtimeService.startProcessInstanceById(definition.getId(), fo.getVariables());
+ } else {
+ instance = runtimeService.startProcessInstanceById(definition.getId());
+ }
+ if (null != instance) {
+ vo.setInstanceId(instance.getId());
+ }
+ return vo;
+ }
+
+ @Override
+ public HistoricInstanceVo getHistoricProcessInstance(String processInstanceId) {
+ HistoricProcessInstance historicInstance = historyService
+ .createHistoricProcessInstanceQuery()
+ .processInstanceId(processInstanceId)
+ .singleResult();
+ if (null == historicInstance) {
+ throw new BizException(ResultCode.INSTANCE_NOT_EXIST);
+ }
+ HistoricInstanceVo vo = new HistoricInstanceVo();
+ vo.setInstanceId(historicInstance.getId());
+ vo.setStartTime(historicInstance.getStartTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime());
+ vo.setEndTime(historicInstance.getEndTime() == null ? null
+ : historicInstance.getEndTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime());
+ vo.setDurationInMillis(historicInstance.getDurationInMillis());
+ vo.setDeleteReason(historicInstance.getDeleteReason());
+ return vo;
+ }
+
+ @Override
+ public boolean deleteInstance(InstanceDeleteFo fo) {
+ try {
+ runtimeService.deleteProcessInstance(fo.getInstanceId(), fo.getDeleteReason());
+ return true;
+ } catch (Exception e) {
+ log.error(ResultCode.DELETE_FAILURE.getMsg(), e);
+ }
+ return false;
+ }
+}
diff --git a/yunzhupaas-workflow-flowable/src/main/java/com/yunzhupaas/workflow/flowable/service/TaskServiceImpl.java b/yunzhupaas-workflow-flowable/src/main/java/com/yunzhupaas/workflow/flowable/service/TaskServiceImpl.java
new file mode 100644
index 0000000..b2bfffc
--- /dev/null
+++ b/yunzhupaas-workflow-flowable/src/main/java/com/yunzhupaas/workflow/flowable/service/TaskServiceImpl.java
@@ -0,0 +1,619 @@
+package com.yunzhupaas.workflow.flowable.service;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.util.StrUtil;
+import com.yunzhupaas.workflow.common.exception.BizException;
+import com.yunzhupaas.workflow.common.exception.ResultCode;
+import com.yunzhupaas.workflow.common.model.fo.*;
+import com.yunzhupaas.workflow.common.model.vo.*;
+import com.yunzhupaas.workflow.common.service.IInstanceService;
+import com.yunzhupaas.workflow.common.service.ITaskService;
+import com.yunzhupaas.workflow.flowable.cmd.JumpCmd;
+import com.yunzhupaas.workflow.flowable.util.FlowableUtil;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.flowable.bpmn.constants.BpmnXMLConstants;
+import org.flowable.bpmn.model.Process;
+import org.flowable.bpmn.model.*;
+import org.flowable.engine.*;
+import org.flowable.engine.history.HistoricActivityInstance;
+import org.flowable.engine.history.HistoricProcessInstance;
+import org.flowable.engine.repository.ProcessDefinition;
+import org.flowable.engine.runtime.ProcessInstance;
+import org.flowable.task.api.Task;
+import org.flowable.task.api.TaskInfo;
+import org.flowable.task.api.history.HistoricTaskInstance;
+import org.flowable.variable.api.history.HistoricVariableInstance;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 流程任务实现层
+ *
+ * @author YUNZHUPAAS FlowableYUNZHUPAAS开发组
+ * @version 1.0.0
+ * @since 2024/4/8 11:12
+ */
+@Slf4j
+@Service
+@AllArgsConstructor
+public class TaskServiceImpl implements ITaskService {
+ private final TaskService taskService;
+ private final HistoryService historyService;
+ private final RuntimeService runtimeService;
+ private final RepositoryService repositoryService;
+ private final ManagementService managementService;
+ private final IInstanceService instanceService;
+
+ @Override
+ public List getTask(String instanceId) {
+ List list = taskService.createTaskQuery().processInstanceId(instanceId).list();
+ List vos = new ArrayList<>();
+ if (CollectionUtil.isNotEmpty(list)) {
+ for (Task task : list) {
+ TaskVo vo = new TaskVo();
+ vo.setTaskId(task.getId());
+ vo.setTaskName(task.getName());
+ vo.setTaskKey(task.getTaskDefinitionKey());
+ vo.setInstanceId(task.getProcessInstanceId());
+ vos.add(vo);
+ }
+ }
+ return vos;
+ }
+
+ @Override
+ public boolean complete(TaskCompleteFo fo) {
+ Task task = taskService.createTaskQuery().taskId(fo.getTaskId()).singleResult();
+ if (null == task) {
+ throw new BizException(ResultCode.TASK_NOT_EXIST);
+ }
+ try {
+ if (CollectionUtil.isNotEmpty(fo.getVariables())) {
+ taskService.complete(fo.getTaskId(), fo.getVariables());
+ } else {
+ taskService.complete(fo.getTaskId());
+ }
+ return true;
+ } catch (Exception e) {
+ log.error(ResultCode.TASK_COMPLETE_ERROR.getMsg(), e);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean moveSingleToMulti(MoveSingleToMultiFo fo) {
+ ProcessInstance instance = runtimeService.createProcessInstanceQuery().processInstanceId(fo.getInstanceId())
+ .singleResult();
+ if (null == instance) {
+ throw new BizException(ResultCode.INSTANCE_NOT_EXIST);
+ }
+ try {
+ this.moveSingleActivityIdToActivityIds(fo.getInstanceId(), fo.getSourceKey(), fo.getTargetKeys());
+ return true;
+ } catch (Exception e) {
+ log.error(ResultCode.TASK_JUMP_ERROR.getMsg(), e);
+ }
+ return false;
+ }
+
+ /**
+ * 节点跳转
+ * Set the activity id that should be changed to multiple activity ids
+ */
+ public void moveSingleActivityIdToActivityIds(String processInstanceId, String activityId,
+ List activityIds) {
+ runtimeService.createChangeActivityStateBuilder()
+ .processInstanceId(processInstanceId)
+ .moveSingleActivityIdToActivityIds(activityId, activityIds).changeState();
+ }
+
+ @Override
+ public boolean moveMultiToSingle(MoveMultiToSingleFo fo) {
+ ProcessInstance instance = runtimeService.createProcessInstanceQuery().processInstanceId(fo.getInstanceId())
+ .singleResult();
+ if (null == instance) {
+ throw new BizException(ResultCode.INSTANCE_NOT_EXIST);
+ }
+ try {
+ this.moveActivityIdsToSingleActivityId(fo.getInstanceId(), fo.getSourceKeys(), fo.getTargetKey());
+ return true;
+ } catch (Exception e) {
+ log.error(ResultCode.TASK_JUMP_ERROR.getMsg(), e);
+ }
+ return false;
+ }
+
+ /**
+ * 节点跳转
+ * Set the activity ids that should be changed to a single activity id
+ */
+ public void moveActivityIdsToSingleActivityId(String processInstanceId, List activityIds,
+ String activityId) {
+ runtimeService.createChangeActivityStateBuilder()
+ .processInstanceId(processInstanceId)
+ .moveActivityIdsToSingleActivityId(activityIds, activityId).changeState();
+ }
+
+ @Override
+ public boolean jump(JumpFo fo) {
+ ProcessInstance instance = runtimeService.createProcessInstanceQuery().processInstanceId(fo.getInstanceId())
+ .singleResult();
+ if (null == instance) {
+ throw new BizException(ResultCode.INSTANCE_NOT_EXIST);
+ }
+ try {
+ BpmnModel bpmnModel = repositoryService.getBpmnModel(instance.getProcessDefinitionId());
+ JumpCmd jumpCmd = new JumpCmd(fo.getInstanceId(), fo.getSource(), fo.getTarget(), "custom jump", bpmnModel,
+ runtimeService);
+ managementService.executeCommand(jumpCmd);
+ return true;
+ } catch (Exception e) {
+ log.error(ResultCode.TASK_JUMP_ERROR.getMsg(), e);
+ }
+ return false;
+ }
+
+ @Override
+ public List getFallbacks(String taskId) {
+ Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
+ if (null == task) {
+ throw new BizException(ResultCode.TASK_NOT_EXIST);
+ }
+ FlowElement source = getFlowElement(task.getProcessDefinitionId(), task.getTaskDefinitionKey());
+ List list = FlowableUtil.getPassActs(source, null, null);
+ return list.stream().distinct().collect(Collectors.toList());
+ }
+
+ /**
+ * 根据流程定义ID和任务KEY 获取节点元素
+ */
+ public FlowElement getFlowElement(String processDefinitionId, String taskDefinitionKey) {
+ ProcessDefinition definition = repositoryService.createProcessDefinitionQuery()
+ .processDefinitionId(processDefinitionId).singleResult();
+ if (null == definition) {
+ throw new BizException(ResultCode.DEFINITION_NOT_EXIST);
+ }
+ Process process = repositoryService.getBpmnModel(definition.getId()).getProcesses().get(0);
+ Collection elements = FlowableUtil.getAllElements(process.getFlowElements(), null);
+ FlowElement source = null;
+ if (null != elements && !elements.isEmpty()) {
+ for (FlowElement element : elements) {
+ if (element.getId().equals(taskDefinitionKey)) {
+ source = element;
+ }
+ }
+ }
+ return source;
+ }
+
+ @Override
+ public List back(TaskBackFo fo) {
+ Task task = taskService.createTaskQuery().taskId(fo.getTaskId()).singleResult();
+ String definitionId;
+ String instanceId;
+ if (null == task) {
+ HistoricTaskInstance historicTask = historyService.createHistoricTaskInstanceQuery().taskId(fo.getTaskId())
+ .singleResult();
+ definitionId = historicTask.getProcessDefinitionId();
+ instanceId = historicTask.getProcessInstanceId();
+ } else {
+ definitionId = task.getProcessDefinitionId();
+ instanceId = task.getProcessInstanceId();
+ }
+ if (StrUtil.isNotBlank(fo.getTargetKey())) {
+ String[] split = fo.getTargetKey().split(",");
+ if (split.length == 0) {
+ throw new BizException("目标节点编码不能为空");
+ }
+ List list = Arrays.asList(split);
+ return this.back(definitionId, instanceId, list);
+ }
+ return null;
+ }
+
+ public List back(String definitionId, String instanceId, List targetList) {
+ List currentIds = new ArrayList<>();
+ for (String targetKey : targetList) {
+ FlowElement target = getFlowElement(definitionId, targetKey);
+ // 获取所有正常进行的任务节点Key,用于找出需要撤回的任务
+ List runTaskList = taskService.createTaskQuery().processInstanceId(instanceId).list();
+ List runTaskKeyList = runTaskList.stream().map(Task::getTaskDefinitionKey)
+ .collect(Collectors.toList());
+ // 需驳回的任务列表
+ List userTaskList = FlowableUtil.getChildUserTasks(target, runTaskKeyList, null, null);
+ List collect = userTaskList.stream().map(UserTask::getId).collect(Collectors.toList());
+ currentIds.addAll(collect);
+ }
+ currentIds = currentIds.stream().distinct().collect(Collectors.toList());
+
+ JumpFo jumpFo = new JumpFo();
+ jumpFo.setInstanceId(instanceId);
+ jumpFo.setSource(currentIds);
+ jumpFo.setTarget(targetList);
+ this.jump(jumpFo);
+
+ return currentIds;
+ }
+
+ @Override
+ public List getPrevUserTask(TaskPrevFo fo) {
+ List list = new ArrayList<>();
+ if (StrUtil.isNotBlank(fo.getDeploymentId())) {
+ ProcessDefinition definition = repositoryService.createProcessDefinitionQuery()
+ .deploymentId(fo.getDeploymentId()).singleResult();
+ if (null == definition) {
+ throw new BizException(ResultCode.DEFINITION_NOT_EXIST);
+ }
+ // 获取当前节点
+ FlowElement source = getFlowElement(definition.getId(), fo.getTaskKey());
+ // 获取下一级用户任务
+ list = FlowableUtil.getParentActs(source, null, null);
+ } else {
+ Task task = taskService.createTaskQuery().taskId(fo.getTaskId()).singleResult();
+ if (null == task) {
+ throw new BizException(ResultCode.TASK_NOT_EXIST);
+ }
+ FlowElement source = getFlowElement(task.getProcessDefinitionId(), task.getTaskDefinitionKey());
+ list = FlowableUtil.getParentActs(source, null, null);
+ }
+ return list;
+ }
+
+ @Override
+ public List getNextUserTask(TaskNextFo fo) {
+ List nextUserTasks = new ArrayList<>();
+ if (StrUtil.isNotBlank(fo.getDeploymentId())) {
+ ProcessDefinition definition = repositoryService.createProcessDefinitionQuery()
+ .deploymentId(fo.getDeploymentId()).singleResult();
+ if (null == definition) {
+ throw new BizException(ResultCode.DEFINITION_NOT_EXIST);
+ }
+ // 获取当前节点
+ FlowElement source = getFlowElement(definition.getId(), fo.getTaskKey());
+ // 获取下一级用户任务
+ nextUserTasks = FlowableUtil.getNextUserTasks(source, null, null);
+ } else {
+ HistoricTaskInstance taskInst = historyService.createHistoricTaskInstanceQuery().taskId(fo.getTaskId())
+ .singleResult();
+ if (null == taskInst) {
+ throw new BizException(ResultCode.TASK_NOT_EXIST);
+ }
+ FlowElement source = getFlowElement(taskInst.getProcessDefinitionId(), taskInst.getTaskDefinitionKey());
+ // 获取下一级用户任务
+ nextUserTasks = FlowableUtil.getNextUserTasks(source, null, null);
+ }
+ List vos = new ArrayList<>();
+ if (CollectionUtil.isNotEmpty(nextUserTasks)) {
+ for (UserTask task : nextUserTasks) {
+ NodeElementVo vo = new NodeElementVo();
+ vo.setId(task.getId());
+ vo.setName(task.getName());
+ vo.setIncomingList(
+ task.getIncomingFlows().stream().map(SequenceFlow::getId).collect(Collectors.toList()));
+ vo.setOutgoingList(
+ task.getOutgoingFlows().stream().map(SequenceFlow::getId).collect(Collectors.toList()));
+ vos.add(vo);
+ }
+ }
+ return vos;
+ }
+
+ @Override
+ public List getTaskKeyAfterFlow(FlowTargetTaskFo fo) {
+ ProcessDefinition definition = repositoryService.createProcessDefinitionQuery()
+ .deploymentId(fo.getDeploymentId()).singleResult();
+ if (null == definition) {
+ throw new BizException(ResultCode.DEFINITION_NOT_EXIST);
+ }
+ List list = new ArrayList<>();
+ // 获取当前节点
+ FlowElement source = getFlowElement(definition.getId(), fo.getFlowKey());
+ String taskKey = FlowableUtil.getTaskKeyAfterFlow(source);
+ list.add(taskKey);
+ return list;
+ }
+
+ @Override
+ public boolean retract(String taskId) {
+ // 需要撤回的任务实例
+ HistoricTaskInstance taskInst = historyService.createHistoricTaskInstanceQuery().taskId(taskId).singleResult();
+ if (null != taskInst) {
+ ProcessInstance procInst = runtimeService.createProcessInstanceQuery()
+ .processInstanceId(taskInst.getProcessInstanceId())
+ .active().singleResult();
+ if (null != procInst) {
+ // 获取当前节点
+ FlowElement source = getFlowElement(taskInst.getProcessDefinitionId(), taskInst.getTaskDefinitionKey());
+ // 获取下一级用户任务
+ List nextUserTasks = FlowableUtil.getNextUserTasks(source, null, null);
+ List nextUserTaskKeys = nextUserTasks.stream().map(UserTask::getId)
+ .collect(Collectors.toList());
+ // 获取所有运行的任务节点,找到需要撤回的任务
+ List activateTasks = taskService.createTaskQuery()
+ .processInstanceId(taskInst.getProcessInstanceId()).list();
+ List currentIds = new ArrayList<>();
+ for (Task task : activateTasks) {
+ // 检查激活的任务节点是否存在下一级中,如果存在,则加入到需要撤回的节点
+ if (CollUtil.contains(nextUserTaskKeys, task.getTaskDefinitionKey())) {
+ currentIds.add(task.getTaskDefinitionKey());
+ }
+ }
+ this.moveActivityIdsToSingleActivityId(taskInst.getProcessInstanceId(), currentIds,
+ taskInst.getTaskDefinitionKey());
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public List getOutgoingFlows(TaskOutgoingFo fo) {
+ if (StrUtil.isNotBlank(fo.getDeploymentId())) {
+ ProcessDefinition definition = repositoryService.createProcessDefinitionQuery()
+ .deploymentId(fo.getDeploymentId()).singleResult();
+ if (null == definition) {
+ throw new BizException(ResultCode.DEFINITION_NOT_EXIST);
+ }
+ return this.getOutgoingFlows(definition.getId(), fo.getTaskKey());
+ } else {
+ Task task = taskService.createTaskQuery().taskId(fo.getTaskId()).singleResult();
+ if (null == task) {
+ throw new BizException(ResultCode.TASK_NOT_EXIST);
+ }
+ return this.getOutgoingFlows(task.getProcessDefinitionId(), task.getTaskDefinitionKey());
+ }
+ }
+
+ /**
+ * 获取出线Key集合(若出线的出口为网关,则一并获取网关的出线)
+ */
+ public List getOutgoingFlows(String processDefinitionId, String taskDefinitionKey) {
+ FlowElement source = getFlowElement(processDefinitionId, taskDefinitionKey);
+ List flows = new ArrayList<>();
+ flows = FlowableUtil.getOutFlowsWithGateway(source, flows);
+ List list = new ArrayList<>();
+ if (!flows.isEmpty()) {
+ for (SequenceFlow flow : flows) {
+ list.add(flow.getId());
+ }
+ }
+ return list.stream().distinct().collect(Collectors.toList());
+ }
+
+ @Override
+ public List getOutgoing(TaskOutgoingFo fo) {
+ if (StrUtil.isNotBlank(fo.getDeploymentId())) {
+ ProcessDefinition definition = repositoryService.createProcessDefinitionQuery()
+ .deploymentId(fo.getDeploymentId()).singleResult();
+ if (null == definition) {
+ throw new BizException(ResultCode.DEFINITION_NOT_EXIST);
+ }
+ return this.getOutgoing(definition.getId(), fo.getTaskKey());
+ } else {
+ Task task = taskService.createTaskQuery().taskId(fo.getTaskId()).singleResult();
+ if (null == task) {
+ throw new BizException(ResultCode.TASK_NOT_EXIST);
+ }
+ return this.getOutgoing(task.getProcessDefinitionId(), task.getTaskDefinitionKey());
+ }
+ }
+
+ public List getOutgoing(String processDefinitionId, String taskDefinitionKey) {
+ FlowElement source = getFlowElement(processDefinitionId, taskDefinitionKey);
+ return FlowableUtil.getOutFlows(source, null);
+ }
+
+ @Override
+ public List getKeysOfFinished(String instanceId) {
+ ProcessInstance instance = runtimeService.createProcessInstanceQuery().processInstanceId(instanceId)
+ .singleResult();
+ if (null == instance) {
+ throw new BizException(ResultCode.INSTANCE_NOT_EXIST);
+ }
+ // 获取当前节点之前的节点
+ List keysOfBefore = getKeysOfBefore(instance);
+ // 获取流程实例下完成的历史活动
+ List list = historyService.createHistoricActivityInstanceQuery()
+ .processInstanceId(instanceId).finished().list();
+ if (CollectionUtil.isNotEmpty(list)) {
+ // 去除线,并去重
+ List keysOfFinished = list.stream()
+ .filter(e -> !BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW.equals(e.getActivityType()))
+ .sorted(Comparator.comparing(HistoricActivityInstance::getStartTime))
+ .map(HistoricActivityInstance::getActivityId)
+ .distinct().collect(Collectors.toList());
+ if (CollectionUtil.isNotEmpty(keysOfBefore)) {
+ keysOfFinished.retainAll(keysOfBefore);
+ }
+ return keysOfFinished;
+ }
+ return null;
+ }
+
+ /**
+ * 遍历当前节点,获取之前的节点
+ */
+ public List getKeysOfBefore(ProcessInstance instance) {
+ List res = new ArrayList<>();
+ // 获取实例下的当前任务
+ List taskList = taskService.createTaskQuery().processInstanceId(instance.getId()).list();
+ if (CollectionUtil.isNotEmpty(taskList)) {
+ List keys = taskList.stream().map(TaskInfo::getTaskDefinitionKey).collect(Collectors.toList());
+ for (String key : keys) {
+ FlowElement source = getFlowElement(instance.getProcessDefinitionId(), key);
+ List list = FlowableUtil.getBefore(source, null, null);
+ res.addAll(list);
+ }
+ }
+ return res.stream().distinct().collect(Collectors.toList());
+ }
+
+ @Override
+ public List getIncomingFlows(String taskId) {
+ HistoricTaskInstance taskInst = historyService.createHistoricTaskInstanceQuery().taskId(taskId).singleResult();
+ if (null != taskInst) {
+ FlowElement source = getFlowElement(taskInst.getProcessDefinitionId(), taskInst.getTaskDefinitionKey());
+ List flows = FlowableUtil.getElementIncomingFlows(source);
+ return flows.stream().map(BaseElement::getId).collect(Collectors.toList());
+ }
+ return null;
+ }
+
+ // 获取未经过的节点
+ @Override
+ public List getToBePass(String instanceId) {
+ List list = new ArrayList<>();
+ List currentList = taskService.createTaskQuery().processInstanceId(instanceId).list();
+ if (CollectionUtil.isNotEmpty(currentList)) {
+ List collect = currentList.stream().map(Task::getTaskDefinitionKey).collect(Collectors.toList());
+ // 根据当前的节点 递归后续的所有节点
+ for (Task task : currentList) {
+ FlowElement source = getFlowElement(task.getProcessDefinitionId(), task.getTaskDefinitionKey());
+ List after = FlowableUtil.getAfter(source, null, null);
+ list.addAll(after);
+ }
+ list = list.stream().filter(e -> !collect.contains(e)).collect(Collectors.toList());
+ }
+ return list.stream().distinct().collect(Collectors.toList());
+ }
+
+ @Override
+ public List getAfter(TaskAfterFo fo) {
+ String deploymentId = fo.getDeploymentId();
+ List taskKeys = fo.getTaskKeys();
+ List list = new ArrayList<>();
+ ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId)
+ .singleResult();
+ if (null == definition) {
+ throw new BizException(ResultCode.DEFINITION_NOT_EXIST);
+ }
+ for (String taskKey : taskKeys) {
+ FlowElement source = getFlowElement(definition.getId(), taskKey);
+ List after = FlowableUtil.getAfter(source, null, null);
+ list.addAll(after);
+ }
+ return list.stream().distinct().collect(Collectors.toList());
+ }
+
+ // complete 异常 补偿
+ @Override
+ public List compensate(CompensateFo fo) {
+ String instanceId = fo.getInstanceId();
+ ProcessInstance instance = runtimeService.createProcessInstanceQuery().processInstanceId(instanceId)
+ .singleResult();
+ if (null == instance) {
+ HistoricProcessInstance historicInstance = historyService.createHistoricProcessInstanceQuery()
+ .processInstanceId(instanceId).singleResult();
+ Map variables = new HashMap<>();
+ List list = historyService.createHistoricVariableInstanceQuery()
+ .processInstanceId(instanceId).list();
+ for (HistoricVariableInstance var : list) {
+ variables.put(var.getVariableName(), var.getValue());
+ }
+ String deploymentId = historicInstance.getDeploymentId();
+
+ InstanceStartFo startFo = new InstanceStartFo();
+ startFo.setDeploymentId(deploymentId);
+ startFo.setVariables(variables);
+ InstanceVo instanceVo = instanceService.startById(startFo);
+ instanceId = instanceVo.getInstanceId();
+ }
+ List sourceList = fo.getSource().stream().sorted().collect(Collectors.toList());
+
+ List taskVoList = this.getTask(instanceId);
+ List currentList = taskVoList.stream().map(TaskVo::getTaskKey).sorted().collect(Collectors.toList());
+
+ // if (ObjectUtil.equals(sourceList, currentList)) {
+ // return null == instance ? taskVoList : new ArrayList<>();
+ // }
+
+ // 获取需要跳转的节点集合、目标节点集合
+ List createList = sourceList.stream().filter(e -> !currentList.contains(e))
+ .collect(Collectors.toList());
+ List deleteList = currentList.stream().filter(e -> !sourceList.contains(e))
+ .collect(Collectors.toList());
+
+ JumpFo jumpFo = new JumpFo();
+ jumpFo.setInstanceId(instanceId);
+ jumpFo.setSource(deleteList);
+ jumpFo.setTarget(createList);
+ this.jump(jumpFo);
+
+ List vos = this.getTask(instanceId);
+ if (null != instance) {
+ vos.forEach(e -> e.setInstanceId(null));
+ }
+ return vos;
+ }
+
+ @Override
+ public List getHistoric(String instanceId) {
+ List vos;
+
+ Set set = new HashSet<>();
+ set.add("userTask");
+ set.add("startEvent");
+
+ vos = this.getHistoricVos(instanceId, set);
+
+ return vos.stream().sorted(Comparator.comparing(HistoricNodeVo::getStartTime)).collect(Collectors.toList());
+ }
+
+ // 获取历史结束节点
+ @Override
+ public List getHistoricEnd(String instanceId) {
+ List list = new ArrayList<>();
+ List vos = this.getHistoricVos(instanceId, null);
+ if (CollectionUtil.isNotEmpty(vos)) {
+ list = vos.stream().map(HistoricNodeVo::getCode).distinct().collect(Collectors.toList());
+ }
+ return list;
+ }
+
+ public List getHistoricVos(String instanceId, Set set) {
+ List vos = new ArrayList<>();
+ if (CollectionUtil.isEmpty(set)) {
+ set = new HashSet<>();
+ set.add("endEvent");
+ }
+ List list = historyService.createHistoricActivityInstanceQuery()
+ .activityTypes(set)
+ .processInstanceId(instanceId).list();
+ if (CollectionUtil.isNotEmpty(list)) {
+ for (HistoricActivityInstance act : list) {
+ HistoricNodeVo vo = new HistoricNodeVo();
+ vo.setCode(act.getActivityId());
+ vo.setTaskId(act.getTaskId());
+ vo.setStartTime(act.getStartTime().getTime());
+ vos.add(vo);
+ }
+ }
+ return vos;
+ }
+
+ @Override
+ public NodeElementVo getElementInfo(InfoModel model) {
+ String deploymentId = model.getDeploymentId();
+ ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId)
+ .singleResult();
+ if (null == definition) {
+ throw new BizException(ResultCode.DEFINITION_NOT_EXIST);
+ }
+ String definitionId = definition.getId();
+ String key = model.getKey();
+ NodeElementVo vo = new NodeElementVo();
+ FlowElement source = getFlowElement(definitionId, key);
+ if (null != source) {
+ vo.setId(source.getId());
+ List outgoingFlows = FlowableUtil.getElementOutgoingFlows(source);
+ vo.setOutgoingList(outgoingFlows.stream().map(SequenceFlow::getId).collect(Collectors.toList()));
+ List incomingFlows = FlowableUtil.getElementIncomingFlows(source);
+ vo.setIncomingList(incomingFlows.stream().map(SequenceFlow::getId).collect(Collectors.toList()));
+ }
+ return vo;
+ }
+}
diff --git a/yunzhupaas-workflow-flowable/src/main/java/com/yunzhupaas/workflow/flowable/util/FlowableUtil.java b/yunzhupaas-workflow-flowable/src/main/java/com/yunzhupaas/workflow/flowable/util/FlowableUtil.java
new file mode 100644
index 0000000..9092ea9
--- /dev/null
+++ b/yunzhupaas-workflow-flowable/src/main/java/com/yunzhupaas/workflow/flowable/util/FlowableUtil.java
@@ -0,0 +1,374 @@
+package com.yunzhupaas.workflow.flowable.util;
+
+import cn.hutool.core.collection.CollectionUtil;
+import com.yunzhupaas.workflow.common.model.vo.FlowVo;
+import org.flowable.bpmn.model.*;
+
+import java.util.*;
+
+/**
+ * flowable工具类
+ *
+ * @author YUNZHUPAAS FlowableYUNZHUPAAS开发组
+ * @version 1.0.0
+ * @since 2024/4/8 11:06
+ */
+public class FlowableUtil {
+ /**
+ * 获取全部节点元素
+ *
+ * @param flowElements {@link Collection}
+ * @param allElements {@link Collection}
+ * @return {@link Collection}
+ * @since 2024/4/8 11:07
+ **/
+ public static Collection getAllElements(Collection flowElements,
+ Collection allElements) {
+ allElements = allElements == null ? new ArrayList<>() : allElements;
+ for (FlowElement flowElement : flowElements) {
+ allElements.add(flowElement);
+ if (flowElement instanceof SubProcess) {
+ // 获取子流程元素
+ allElements = getAllElements(((SubProcess) flowElement).getFlowElements(), allElements);
+ }
+ }
+ return allElements;
+ }
+
+ /**
+ * 获取节点的入口连线
+ *
+ * @param element {@link FlowElement}
+ * @return {@link List}
+ * @since 2024/4/8 11:10
+ **/
+ public static List getElementIncomingFlows(FlowElement element) {
+ List flows = null;
+ if (element instanceof FlowNode) {
+ flows = ((FlowNode) element).getIncomingFlows();
+ }
+ return flows;
+ }
+
+ /**
+ * 获取节点的出口连线
+ *
+ * @param element {@link FlowElement}
+ * @return {@link List}
+ * @since 2024/4/8 11:10
+ **/
+ public static List getElementOutgoingFlows(FlowElement element) {
+ List flows = null;
+ if (element instanceof FlowNode) {
+ flows = ((FlowNode) element).getOutgoingFlows();
+ }
+ return flows;
+ }
+
+ /**
+ * 获取可回退的节点(仅用户任务)
+ *
+ * @param source {@link FlowElement}
+ * @param passFlows {@link Set}
+ * @param passActs {@link List}
+ * @return {@link List}
+ * @since 2024/4/8 15:27
+ **/
+ public static List getPassActs(FlowElement source, Set passFlows, List passActs) {
+ passFlows = passFlows == null ? new HashSet<>() : passFlows;
+ passActs = passActs == null ? new ArrayList<>() : passActs;
+
+ List sequenceFlows = getElementIncomingFlows(source);
+ if (null != sequenceFlows && !sequenceFlows.isEmpty()) {
+ for (SequenceFlow sequenceFlow : sequenceFlows) {
+ // 连线重复
+ if (passFlows.contains(sequenceFlow.getId())) {
+ continue;
+ }
+ // 添加经过的连线
+ passFlows.add(sequenceFlow.getId());
+ // 添加经过的用户任务
+ if (sequenceFlow.getSourceFlowElement() instanceof UserTask) {
+ passActs.add(sequenceFlow.getSourceFlowElement().getId());
+ }
+ if (sequenceFlow.getSourceFlowElement() instanceof StartEvent) {
+ passActs.add(sequenceFlow.getSourceFlowElement().getId());
+ continue;
+ }
+ // 迭代
+ getPassActs(sequenceFlow.getSourceFlowElement(), passFlows, passActs);
+ }
+ }
+ return passActs;
+ }
+
+ /**
+ * 获取需要撤回的节点
+ *
+ * @param source {@link FlowElement}
+ * @param runTaskKeyList {@link List}
+ * @param passFlows {@link Set}
+ * @param userTasks {@link List}
+ * @return {@link List}
+ * @since 2024/4/8 15:42
+ **/
+ public static List getChildUserTasks(FlowElement source, List runTaskKeyList,
+ Set passFlows, List userTasks) {
+ passFlows = passFlows == null ? new HashSet<>() : passFlows;
+ userTasks = userTasks == null ? new ArrayList<>() : userTasks;
+ List sequenceFlows = getElementOutgoingFlows(source);
+ if (null != sequenceFlows && !sequenceFlows.isEmpty()) {
+ for (SequenceFlow sequenceFlow : sequenceFlows) {
+ // 连线重复
+ if (passFlows.contains(sequenceFlow.getId())) {
+ continue;
+ }
+ // 添加经过的连线
+ passFlows.add(sequenceFlow.getId());
+ // 用户任务
+ if (sequenceFlow.getTargetFlowElement() instanceof UserTask
+ && runTaskKeyList.contains(sequenceFlow.getTargetFlowElement().getId())) {
+ userTasks.add((UserTask) sequenceFlow.getTargetFlowElement());
+ continue;
+ }
+ // 子流程,从第一个节点开始获取
+ if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) {
+ FlowElement flowElement = (FlowElement) ((SubProcess) sequenceFlow.getTargetFlowElement())
+ .getFlowElements().toArray()[0];
+ List tasks = getChildUserTasks(flowElement, runTaskKeyList, passFlows, null);
+ // 找到用户任务,不继续向下找
+ if (!tasks.isEmpty()) {
+ userTasks.addAll(tasks);
+ continue;
+ }
+ }
+ // 迭代
+ getChildUserTasks(sequenceFlow.getTargetFlowElement(), runTaskKeyList, passFlows, userTasks);
+ }
+ }
+ return userTasks;
+ }
+
+ /**
+ * 获取上一级节点
+ *
+ * @param source {@link FlowElement}
+ * @param passFlows {@link Set}
+ * @param parentActs {@link List}
+ * @return {@link List}
+ * @since 2024/4/8 15:53
+ **/
+ public static List getParentActs(FlowElement source, Set passFlows, List parentActs) {
+ passFlows = passFlows == null ? new HashSet<>() : passFlows;
+ parentActs = parentActs == null ? new ArrayList<>() : parentActs;
+
+ List sequenceFlows = getElementIncomingFlows(source);
+ if (null != sequenceFlows && !sequenceFlows.isEmpty()) {
+ for (SequenceFlow sequenceFlow : sequenceFlows) {
+ // 连线重复
+ if (passFlows.contains(sequenceFlow.getId())) {
+ continue;
+ }
+ // 添加经过的连线
+ passFlows.add(sequenceFlow.getId());
+ // 添加用户任务、子流程
+ if (sequenceFlow.getSourceFlowElement() instanceof UserTask) {
+ parentActs.add(sequenceFlow.getSourceFlowElement().getId());
+ continue;
+ }
+ if (sequenceFlow.getSourceFlowElement() instanceof StartEvent) {
+ parentActs.add(sequenceFlow.getSourceFlowElement().getId());
+ continue;
+ }
+ // 迭代
+ getParentActs(sequenceFlow.getSourceFlowElement(), passFlows, parentActs);
+ }
+ }
+ return parentActs;
+ }
+
+ /**
+ * 获取下一级的用户任务
+ *
+ * @param source {@link FlowElement}
+ * @param hasSequenceFlow {@link Set}
+ * @param userTaskList {@link List}
+ * @return {@link List}
+ * @since 2024/4/8 16:34
+ **/
+ public static List getNextUserTasks(FlowElement source, Set hasSequenceFlow,
+ List userTaskList) {
+ hasSequenceFlow = Optional.ofNullable(hasSequenceFlow).orElse(new HashSet<>());
+ userTaskList = Optional.ofNullable(userTaskList).orElse(new ArrayList<>());
+ // 获取出口连线
+ List sequenceFlows = getElementOutgoingFlows(source);
+ if (null != sequenceFlows) {
+ for (SequenceFlow sequenceFlow : sequenceFlows) {
+ // 如果发现连线重复,说明循环了,跳过这个循环
+ if (hasSequenceFlow.contains(sequenceFlow.getId())) {
+ continue;
+ }
+ // 添加已经走过的连线
+ hasSequenceFlow.add(sequenceFlow.getId());
+ FlowElement targetFlowElement = sequenceFlow.getTargetFlowElement();
+ if (targetFlowElement instanceof UserTask) {
+ // 若节点为用户任务,加入到结果列表中
+ userTaskList.add((UserTask) targetFlowElement);
+ } else {
+ // 若节点非用户任务,继续递归查找下一个节点
+ getNextUserTasks(targetFlowElement, hasSequenceFlow, userTaskList);
+ }
+ }
+ }
+ return userTaskList;
+ }
+
+ /**
+ * 获取元素之后的所有节点
+ *
+ * @param source {@link FlowElement}
+ * @param hasSequenceFlow {@link Set}
+ * @param list {@link List}
+ * @return {@link List}
+ * @since 2024/4/29 16:00
+ **/
+ public static List getAfter(FlowElement source, Set hasSequenceFlow, List list) {
+ hasSequenceFlow = Optional.ofNullable(hasSequenceFlow).orElse(new HashSet<>());
+ list = Optional.ofNullable(list).orElse(new ArrayList<>());
+ // 获取出口连线
+ List sequenceFlows = getElementOutgoingFlows(source);
+ if (null != sequenceFlows) {
+ for (SequenceFlow sequenceFlow : sequenceFlows) {
+ // 如果发现连线重复,说明循环了,跳过这个循环
+ if (hasSequenceFlow.contains(sequenceFlow.getId())) {
+ continue;
+ }
+ // 添加已经走过的连线
+ hasSequenceFlow.add(sequenceFlow.getId());
+ FlowElement targetFlowElement = sequenceFlow.getTargetFlowElement();
+ if (targetFlowElement instanceof UserTask) {
+ // 若节点为用户任务,加入到结果列表中
+ list.add(targetFlowElement.getId());
+ } else if (targetFlowElement instanceof EndEvent) {
+ list.add(targetFlowElement.getId());
+ continue;
+ }
+ // 继续递归查找下一个节点
+ getAfter(targetFlowElement, hasSequenceFlow, list);
+ }
+ }
+ return list;
+ }
+
+ /**
+ * 获取节点的出口连线,若出线的出口是网关,则一并获取网关的出线
+ *
+ * @param source {@link FlowElement}
+ * @return {@link List}
+ * @since 2024/4/9 9:58
+ **/
+ public static List getOutFlowsWithGateway(FlowElement source, List flows) {
+ flows = flows == null ? new ArrayList<>() : flows;
+ // 获取出口连线
+ List