From 7ef4152750051ced3b485ff557b7e0c52bce247f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=B0=91=E5=B9=B4=E6=A2=A6=E4=B8=8E=E7=A0=96?=
<2847127106@qq.com>
Date: Mon, 9 Sep 2024 16:57:22 +0800
Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.gitignore | 36 ++
Dockerfile | 15 +
muyu-quest-client/pom.xml | 26 ++
muyu-quest-common/pom.xml | 28 ++
.../main/java/com/muyu/quest/domain/Node.java | 80 ++++
.../muyu/quest/domain/NodeDisposition.java | 72 ++++
.../java/com/muyu/quest/domain/NodeType.java | 48 +++
.../main/java/com/muyu/quest/domain/Task.java | 72 ++++
.../com/muyu/quest/domain/TaskExport.java | 72 ++++
.../java/com/muyu/quest/model/DataModel.java | 39 ++
.../com/muyu/quest/model/DataValueModel.java | 30 ++
.../main/java/com/muyu/quest/req/NodeReq.java | 51 +++
.../main/java/com/muyu/quest/req/TaskReq.java | 50 +++
.../java/com/muyu/quest/resp/NodeResp.java | 68 ++++
.../java/com/muyu/quest/resp/TaskResp.java | 64 ++++
muyu-quest-remote/pom.xml | 27 ++
...ot.autoconfigure.AutoConfiguration.imports | 1 +
muyu-quest-server/pom.xml | 150 ++++++++
.../com/muyu/quest/MyTaskApplication.java | 31 ++
.../com/muyu/quest/advice/TaskAdvice.java | 33 ++
.../muyu/quest/controller/NodeController.java | 157 ++++++++
.../controller/NodeDispositionController.java | 133 +++++++
.../muyu/quest/controller/TaskController.java | 141 +++++++
.../controller/TaskExportController.java | 122 +++++++
.../muyu/quest/exception/TaskException.java | 43 +++
.../main/java/com/muyu/quest/job/TaskJob.java | 81 +++++
.../com/muyu/quest/manager/TaskManager.java | 182 ++++++++++
.../quest/mapper/NodeDispositionMapper.java | 16 +
.../com/muyu/quest/mapper/NodeMapper.java | 17 +
.../muyu/quest/mapper/TaskExportMapper.java | 17 +
.../com/muyu/quest/mapper/TaskMapper.java | 22 ++
.../service/INodeDispositionService.java | 40 ++
.../com/muyu/quest/service/INodeService.java | 41 +++
.../quest/service/ITaskExportService.java | 44 +++
.../com/muyu/quest/service/TaskService.java | 52 +++
.../impl/NodeDispositionServiceImpl.java | 92 +++++
.../quest/service/impl/NodeServiceImpl.java | 98 +++++
.../service/impl/TaskExportServiceImpl.java | 93 +++++
.../quest/service/impl/TaskServiceImpl.java | 318 ++++++++++++++++
.../java/com/muyu/quest/utils/DispUtils.java | 97 +++++
.../java/com/muyu/quest/utils/NodeUtils.java | 343 ++++++++++++++++++
.../src/main/resources/banner.txt | 2 +
.../src/main/resources/bootstrap.yml | 55 +++
.../src/main/resources/logback/dev.xml | 74 ++++
.../src/main/resources/logback/prod.xml | 81 +++++
.../src/main/resources/logback/test.xml | 81 +++++
.../src/main/resources/mapper/TaskMapper.xml | 14 +
.../mapper/quest/TaskExportMapper.xml | 73 ++++
pom.xml | 37 ++
49 files changed, 3559 insertions(+)
create mode 100644 .gitignore
create mode 100644 Dockerfile
create mode 100644 muyu-quest-client/pom.xml
create mode 100644 muyu-quest-common/pom.xml
create mode 100644 muyu-quest-common/src/main/java/com/muyu/quest/domain/Node.java
create mode 100644 muyu-quest-common/src/main/java/com/muyu/quest/domain/NodeDisposition.java
create mode 100644 muyu-quest-common/src/main/java/com/muyu/quest/domain/NodeType.java
create mode 100644 muyu-quest-common/src/main/java/com/muyu/quest/domain/Task.java
create mode 100644 muyu-quest-common/src/main/java/com/muyu/quest/domain/TaskExport.java
create mode 100644 muyu-quest-common/src/main/java/com/muyu/quest/model/DataModel.java
create mode 100644 muyu-quest-common/src/main/java/com/muyu/quest/model/DataValueModel.java
create mode 100644 muyu-quest-common/src/main/java/com/muyu/quest/req/NodeReq.java
create mode 100644 muyu-quest-common/src/main/java/com/muyu/quest/req/TaskReq.java
create mode 100644 muyu-quest-common/src/main/java/com/muyu/quest/resp/NodeResp.java
create mode 100644 muyu-quest-common/src/main/java/com/muyu/quest/resp/TaskResp.java
create mode 100644 muyu-quest-remote/pom.xml
create mode 100644 muyu-quest-remote/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
create mode 100644 muyu-quest-server/pom.xml
create mode 100644 muyu-quest-server/src/main/java/com/muyu/quest/MyTaskApplication.java
create mode 100644 muyu-quest-server/src/main/java/com/muyu/quest/advice/TaskAdvice.java
create mode 100644 muyu-quest-server/src/main/java/com/muyu/quest/controller/NodeController.java
create mode 100644 muyu-quest-server/src/main/java/com/muyu/quest/controller/NodeDispositionController.java
create mode 100644 muyu-quest-server/src/main/java/com/muyu/quest/controller/TaskController.java
create mode 100644 muyu-quest-server/src/main/java/com/muyu/quest/controller/TaskExportController.java
create mode 100644 muyu-quest-server/src/main/java/com/muyu/quest/exception/TaskException.java
create mode 100644 muyu-quest-server/src/main/java/com/muyu/quest/job/TaskJob.java
create mode 100644 muyu-quest-server/src/main/java/com/muyu/quest/manager/TaskManager.java
create mode 100644 muyu-quest-server/src/main/java/com/muyu/quest/mapper/NodeDispositionMapper.java
create mode 100644 muyu-quest-server/src/main/java/com/muyu/quest/mapper/NodeMapper.java
create mode 100644 muyu-quest-server/src/main/java/com/muyu/quest/mapper/TaskExportMapper.java
create mode 100644 muyu-quest-server/src/main/java/com/muyu/quest/mapper/TaskMapper.java
create mode 100644 muyu-quest-server/src/main/java/com/muyu/quest/service/INodeDispositionService.java
create mode 100644 muyu-quest-server/src/main/java/com/muyu/quest/service/INodeService.java
create mode 100644 muyu-quest-server/src/main/java/com/muyu/quest/service/ITaskExportService.java
create mode 100644 muyu-quest-server/src/main/java/com/muyu/quest/service/TaskService.java
create mode 100644 muyu-quest-server/src/main/java/com/muyu/quest/service/impl/NodeDispositionServiceImpl.java
create mode 100644 muyu-quest-server/src/main/java/com/muyu/quest/service/impl/NodeServiceImpl.java
create mode 100644 muyu-quest-server/src/main/java/com/muyu/quest/service/impl/TaskExportServiceImpl.java
create mode 100644 muyu-quest-server/src/main/java/com/muyu/quest/service/impl/TaskServiceImpl.java
create mode 100644 muyu-quest-server/src/main/java/com/muyu/quest/utils/DispUtils.java
create mode 100644 muyu-quest-server/src/main/java/com/muyu/quest/utils/NodeUtils.java
create mode 100644 muyu-quest-server/src/main/resources/banner.txt
create mode 100644 muyu-quest-server/src/main/resources/bootstrap.yml
create mode 100644 muyu-quest-server/src/main/resources/logback/dev.xml
create mode 100644 muyu-quest-server/src/main/resources/logback/prod.xml
create mode 100644 muyu-quest-server/src/main/resources/logback/test.xml
create mode 100644 muyu-quest-server/src/main/resources/mapper/TaskMapper.xml
create mode 100644 muyu-quest-server/src/main/resources/mapper/quest/TaskExportMapper.xml
create mode 100644 pom.xml
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..6d0ca4f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,36 @@
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### IntelliJ IDEA ###
+.idea
+logs
+*.iws
+*.iml
+*.ipr
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..f32152d
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,15 @@
+#指定构建镜像的起始镜像
+FROM anolis-registry.cn-zhangjiakou.cr.aliyuncs.com/openanolis/dragonwell:17.0.4.0.4.8-standard-ga-8.6
+#定义时区参数
+ENV TZ=Asia/Shanghai
+#设置时区
+RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo '$TZ' > /etc/timezone
+
+#挂载目录
+VOLUME ["/home/logs/muyu-quest"]
+
+#拷贝执行jar报
+COPY ./muyu-quest-server/target/muyu-quest.jar /home/app.jar
+
+ENTRYPOINT ["java","-Dfile.encoding=utf-8","-jar"]
+CMD ["/home/app.jar"]
diff --git a/muyu-quest-client/pom.xml b/muyu-quest-client/pom.xml
new file mode 100644
index 0000000..8d4119d
--- /dev/null
+++ b/muyu-quest-client/pom.xml
@@ -0,0 +1,26 @@
+
+
+ 4.0.0
+
+ com.muyu
+ muyu-quest
+ 3.6.5
+
+
+ muyu-quest-client
+
+
+ 17
+ 17
+ UTF-8
+
+
+
+ com.muyu
+ muyu-quest-common
+
+
+
+
diff --git a/muyu-quest-common/pom.xml b/muyu-quest-common/pom.xml
new file mode 100644
index 0000000..d7acd4c
--- /dev/null
+++ b/muyu-quest-common/pom.xml
@@ -0,0 +1,28 @@
+
+
+ 4.0.0
+
+ com.muyu
+ muyu-quest
+ 3.6.5
+
+
+ muyu-quest-common
+ 3.6.5
+
+
+
+ 17
+ 17
+ UTF-8
+
+
+
+
+ com.muyu
+ cloud-etl-common
+
+
+
diff --git a/muyu-quest-common/src/main/java/com/muyu/quest/domain/Node.java b/muyu-quest-common/src/main/java/com/muyu/quest/domain/Node.java
new file mode 100644
index 0000000..f04d73c
--- /dev/null
+++ b/muyu-quest-common/src/main/java/com/muyu/quest/domain/Node.java
@@ -0,0 +1,80 @@
+package com.muyu.quest.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.muyu.common.core.annotation.Excel;
+import com.muyu.common.core.validation.custom.IsSysNodeType;
+import com.muyu.common.core.validation.custom.IsSysYesNo;
+import com.muyu.common.core.web.domain.BaseEntity;
+import lombok.*;
+import lombok.experimental.SuperBuilder;
+
+/**
+ * 节点管理对象 node_source
+ *
+ * @Author: 胡杨
+ * @date 2024-08-23
+ */
+
+@Data
+@Setter
+@Getter
+@SuperBuilder
+@NoArgsConstructor
+@AllArgsConstructor
+@TableName("node_info")
+public class Node extends BaseEntity{
+ private static final long serialVersionUID = 1L;
+
+ /** 自增主键 */
+ @TableId( type = IdType.AUTO)
+ private Long id;
+
+ /** 节点编码 */
+ @Excel(name = "节点编码")
+ private String nodeCode;
+
+ /** 任务编码 */
+ @Excel(name = "任务编码")
+ private String taskCode;
+
+ /** 节点类型 */
+ @Excel(name = "节点类型")
+ @IsSysNodeType
+ private String nodeType;
+
+ /** 节点名称 */
+ @Excel(name = "节点名称")
+ private String nodeName;
+
+ /** 节点位置Y */
+ @Excel(name = "节点位置Y")
+ private String nodePositionTop;
+
+ /** 节点位置X */
+ @Excel(name = "节点位置X")
+ private String nodePositionLeft;
+
+ /** 节点上一级 */
+ @Excel(name = "节点上一级")
+ private String nodePreCode;
+
+ /** 节点下一级 */
+ @Excel(name = "节点下一级")
+ private String nodeNextCode;
+
+ /** 启用状态 */
+ @Excel(name = "启用状态")
+ @IsSysYesNo
+ private String state;
+
+
+
+ @Override
+ public String toString() {
+ return "编码:"+this.getNodeCode() +
+ ",名称:"+this.getNodeName() +
+ ",类型:"+this.getNodeType();
+ }
+}
diff --git a/muyu-quest-common/src/main/java/com/muyu/quest/domain/NodeDisposition.java b/muyu-quest-common/src/main/java/com/muyu/quest/domain/NodeDisposition.java
new file mode 100644
index 0000000..74344fc
--- /dev/null
+++ b/muyu-quest-common/src/main/java/com/muyu/quest/domain/NodeDisposition.java
@@ -0,0 +1,72 @@
+package com.muyu.quest.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.muyu.common.core.annotation.Excel;
+import com.muyu.common.core.validation.custom.IsSysNodeType;
+import lombok.*;
+import lombok.experimental.SuperBuilder;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+/**
+ * 节点配置对象 node_disposition
+ *
+ * @Author: 胡杨
+ * @date 2024-08-29
+ */
+
+@Data
+@Setter
+@Getter
+@SuperBuilder
+@NoArgsConstructor
+@AllArgsConstructor
+@TableName("node_disposition")
+public class NodeDisposition {
+ private static final long serialVersionUID = 1L;
+
+ /** 自增主键 */
+ @TableId( type = IdType.AUTO)
+ private Long id;
+
+ /** 节点编码 */
+ @Excel(name = "节点编码")
+ private String nodeCode;
+
+ /** 配置编码 */
+ @Excel(name = "配置编码")
+ private String dispKey;
+
+ /** 配置名称 */
+ @Excel(name = "配置名称")
+ @IsSysNodeType
+ private String dispLabel;
+
+ /** 配置内容 */
+ @Excel(name = "配置内容")
+ private Object dispValue;
+
+ /** 配置类型 */
+ @Excel(name = "配置类型")
+ private String dispType;
+
+ /** 其他信息 */
+ private String dispDesc;
+
+
+ @Override
+ public String toString() {
+ return "配置编码:'" + dispKey + '\'' +
+ ", 配置名称:'" + dispLabel + '\'' +
+ ", 配置内容:" + dispValue +
+ ", 配置类型:'" + dispType + '\'' +
+ ", 其他信息:'" + dispDesc ;
+ }
+
+ public NodeDisposition buildNodeCode(String nodeCode) {
+ this.nodeCode = nodeCode;
+ return this;
+ }
+}
diff --git a/muyu-quest-common/src/main/java/com/muyu/quest/domain/NodeType.java b/muyu-quest-common/src/main/java/com/muyu/quest/domain/NodeType.java
new file mode 100644
index 0000000..5d5e6fd
--- /dev/null
+++ b/muyu-quest-common/src/main/java/com/muyu/quest/domain/NodeType.java
@@ -0,0 +1,48 @@
+package com.muyu.quest.domain;
+
+import lombok.*;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @Author: 胡杨
+ * @Name: NodeType
+ * @Description: 节点类型
+ * @CreatedDate: 2024/9/1 下午10:01
+ * @FilePath: com.muyu.quest.domain
+ */
+
+@Data
+@Builder
+@ToString
+@NoArgsConstructor
+@AllArgsConstructor
+public class NodeType {
+
+ /** 节点类型主键 */
+ private String id;
+
+ /** 节点类型编码 */
+ private String nodeTypeCode;
+
+ /** 节点类型名称 */
+ private String nodeTypeName;
+
+ /** 节点存储格式编码集 */
+ private String nodeTypeData;
+
+ private String nextNodeTypeCode;
+
+ // 节点类型数量限制
+ private Integer nodeMaxNum;
+ private Integer nodeMinNum;
+
+ /** 下级节点类型编码集 */
+ private List nextNodeTypeCodeList;
+
+ public void setNextNodeTypeCode(String nextNodeTypeCode) {
+ this.nextNodeTypeCode = nextNodeTypeCode;
+ this.nextNodeTypeCodeList = Arrays.stream(nextNodeTypeCode.split(",")).toList();
+ }
+}
diff --git a/muyu-quest-common/src/main/java/com/muyu/quest/domain/Task.java b/muyu-quest-common/src/main/java/com/muyu/quest/domain/Task.java
new file mode 100644
index 0000000..5263f3a
--- /dev/null
+++ b/muyu-quest-common/src/main/java/com/muyu/quest/domain/Task.java
@@ -0,0 +1,72 @@
+package com.muyu.quest.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.muyu.common.core.annotation.Excel;
+import com.muyu.common.core.validation.custom.IsSysYesNo;
+import com.muyu.common.core.web.domain.BaseEntity;
+import jakarta.validation.constraints.NotEmpty;
+import lombok.*;
+import lombok.experimental.SuperBuilder;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+
+/**
+ * 任务对象 task_source
+ *
+ * @Author: 胡杨
+ * @date 2024-08-22
+ */
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+@SuperBuilder
+@NoArgsConstructor
+@AllArgsConstructor
+@TableName("task_source")
+public class Task extends BaseEntity {
+ private static final long serialVersionUID = 1L;
+
+ /** 自增主键 */
+ @TableId( type = IdType.AUTO)
+ private Long id;
+
+ /** 任务编码 */
+ @Excel(name = "任务编码")
+ private String taskCode;
+
+ /** 任务名称 */
+ @Excel(name = "任务名称")
+ private String taskName;
+
+ private String taskData;
+
+
+ /** 任务类型 */
+ @Excel(name = "任务类型")
+ private String taskType;
+
+ /** 启用状态 */
+ @Excel(name = "启用状态")
+ @IsSysYesNo
+ private String state;
+
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+ .append("id", getId())
+ .append("taskCode", getTaskCode())
+ .append("taskName", getTaskName())
+ .append("taskType", getTaskType())
+ .append("state", getState())
+ .append("createBy", getCreateBy())
+ .append("createTime", getCreateTime())
+ .append("updateBy", getUpdateBy())
+ .append("updateTime", getUpdateTime())
+ .append("remark", getRemark())
+ .toString();
+ }
+}
diff --git a/muyu-quest-common/src/main/java/com/muyu/quest/domain/TaskExport.java b/muyu-quest-common/src/main/java/com/muyu/quest/domain/TaskExport.java
new file mode 100644
index 0000000..7d9d0b8
--- /dev/null
+++ b/muyu-quest-common/src/main/java/com/muyu/quest/domain/TaskExport.java
@@ -0,0 +1,72 @@
+package com.muyu.quest.domain;
+
+import com.muyu.common.core.annotation.Excel;
+import com.muyu.common.core.web.domain.BaseEntity;
+import lombok.*;
+import lombok.experimental.SuperBuilder;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.IdType;
+
+import java.io.Serializable;
+
+/**
+ * 任务执行记录对象 task_export
+ *
+ * @Author: 胡杨
+ * @date 2024-09-06
+ */
+
+@Data
+@Setter
+@Getter
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@TableName("task_export")
+public class TaskExport implements Serializable {
+
+ /** ID */
+ @TableId( type = IdType.AUTO)
+ private Long id;
+
+ /** 任务编码 */
+ @Excel(name = "任务编码")
+ private String taskCode;
+ /** 执行编码 */
+ @Excel(name = "执行编码")
+ private String exportCode;
+
+ /** 执行SQL */
+ @Excel(name = "执行SQL")
+ private Object addSql;
+
+ /** 执行状态 */
+ @Excel(name = "执行状态")
+ private Integer start;
+
+ /** 失败原因 */
+ @Excel(name = "失败原因")
+ private Object error;
+
+ public TaskExport(String taskCode,String exportCode, String addSql, Integer start, String error) {
+ this.taskCode = taskCode;
+ this.exportCode = exportCode;
+ this.addSql = addSql;
+ this.start = start;
+ this.error = error;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+ .append("id", getId())
+ .append("taskCode", getTaskCode())
+ .append("addSql", getAddSql())
+ .append("start", getStart())
+ .append("error", getError())
+ .toString();
+ }
+}
diff --git a/muyu-quest-common/src/main/java/com/muyu/quest/model/DataModel.java b/muyu-quest-common/src/main/java/com/muyu/quest/model/DataModel.java
new file mode 100644
index 0000000..ce9c594
--- /dev/null
+++ b/muyu-quest-common/src/main/java/com/muyu/quest/model/DataModel.java
@@ -0,0 +1,39 @@
+package com.muyu.quest.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.SuperBuilder;
+
+/**
+ * @Author: 胡杨
+ * @ Tool:IntelliJ IDEA
+ * @ Author: 胡杨
+ * @ Date:2024-08-23-9:41
+ * @ Version:1.0
+ * @ Description:数据转换对象
+ */
+@Data
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+public class DataModel {
+ /**
+ * 键
+ */
+ private String key;
+ /**
+ * 标签
+ */
+ private String label;
+ /**
+ * 类型
+ */
+ private String type;
+ /**
+ * 值
+ */
+ private Object value;
+
+
+}
diff --git a/muyu-quest-common/src/main/java/com/muyu/quest/model/DataValueModel.java b/muyu-quest-common/src/main/java/com/muyu/quest/model/DataValueModel.java
new file mode 100644
index 0000000..c7eeacf
--- /dev/null
+++ b/muyu-quest-common/src/main/java/com/muyu/quest/model/DataValueModel.java
@@ -0,0 +1,30 @@
+package com.muyu.quest.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.SuperBuilder;
+
+/**
+ * @Author: 胡杨
+ * @ Tool:IntelliJ IDEA
+ * @ Author: 胡杨
+ * @ Date:2024-09-03-22:14
+ * @ Version:1.0
+ * @ Description:任务表查询时用到
+ */
+@Data
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+public class DataValueModel {
+ /**
+ * 数据接入的ID
+ */
+ private Long basicId;
+ /**
+ * 拼写的sql语句
+ */
+ private String sql;
+
+}
diff --git a/muyu-quest-common/src/main/java/com/muyu/quest/req/NodeReq.java b/muyu-quest-common/src/main/java/com/muyu/quest/req/NodeReq.java
new file mode 100644
index 0000000..e4e34b5
--- /dev/null
+++ b/muyu-quest-common/src/main/java/com/muyu/quest/req/NodeReq.java
@@ -0,0 +1,51 @@
+package com.muyu.quest.req;
+
+import com.muyu.common.core.validation.custom.IsSysYesNo;
+import lombok.*;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+/**
+ * 节点管理对象 node_source
+ *
+ * @Author: 胡杨
+ * @date 2024-08-23
+ */
+
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class NodeReq{
+
+ /** 任务编码 */
+ private String taskCode;
+
+ /** 节点名称 */
+ private String nodeName;
+
+ /** 上一级节点 */
+ private String nodePreCode;
+
+ /** 下一级节点 */
+ private String nodeNextCode;
+
+ /** 启用状态 */
+ @IsSysYesNo
+ private String state;
+
+ public NodeReq buildTaskCode(String taskCode){
+ this.taskCode = taskCode;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+ .append("nodeName", getNodeName())
+ .append("nodePreCode", getNodePreCode())
+ .append("nodeNextCode", getNodeNextCode())
+ .append("state", getState())
+ .toString();
+ }
+}
diff --git a/muyu-quest-common/src/main/java/com/muyu/quest/req/TaskReq.java b/muyu-quest-common/src/main/java/com/muyu/quest/req/TaskReq.java
new file mode 100644
index 0000000..2a25106
--- /dev/null
+++ b/muyu-quest-common/src/main/java/com/muyu/quest/req/TaskReq.java
@@ -0,0 +1,50 @@
+package com.muyu.quest.req;
+
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.muyu.common.core.annotation.Excel;
+import com.muyu.common.core.validation.custom.IsSysYesNo;
+import com.muyu.common.core.web.domain.BaseEntity;
+import lombok.*;
+import lombok.experimental.SuperBuilder;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+
+/**
+ * 任务对象 task_source
+ *
+ * @Author: 胡杨
+ * @date 2024-08-22
+ */
+
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class TaskReq{
+ private static final long serialVersionUID = 1L;
+
+
+ /** 任务名称 */
+ private String taskName;
+
+ /** 任务类型 */
+ private String taskType;
+
+ /** 启用状态 */
+ @IsSysYesNo
+ private String state;
+
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+ .append("taskName", getTaskName())
+ .append("taskType", getTaskType())
+ .append("state", getState())
+ .toString();
+ }
+}
diff --git a/muyu-quest-common/src/main/java/com/muyu/quest/resp/NodeResp.java b/muyu-quest-common/src/main/java/com/muyu/quest/resp/NodeResp.java
new file mode 100644
index 0000000..02d771a
--- /dev/null
+++ b/muyu-quest-common/src/main/java/com/muyu/quest/resp/NodeResp.java
@@ -0,0 +1,68 @@
+package com.muyu.quest.resp;
+
+import com.muyu.common.core.annotation.Excel;
+import com.muyu.common.core.enums.SysYesNo;
+import com.muyu.quest.domain.Node;
+import lombok.*;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+/**
+ * 节点管理对象 node_source
+ *
+ * @Author: 胡杨
+ * @date 2024-08-23
+ */
+
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class NodeResp{
+
+ /** 自增主键 */
+ private Long id;
+
+ /** 节点编码 */
+ private String nodeCode;
+
+ /** 任务编码 */
+ private String taskCode;
+
+ /** 节点名称 */
+ private String nodeName;
+
+ /** 节点类型 */
+ private String nodeType;
+
+ /** 节点位置Y */
+ private String nodePositionTop;
+
+ /** 节点位置X */
+ private String nodePositionLeft;
+
+ /** 上一级节点 */
+ private String nodePreCode;
+
+ /** 下一级节点 */
+ private String nodeNextCode;
+
+ /** 启用状态 */
+ private String state;
+
+
+ public static NodeResp build(Node node) {
+ return NodeResp.builder()
+ .id(node.getId())
+ .nodeCode(node.getNodeCode())
+ .taskCode(node.getTaskCode())
+ .nodeName(node.getNodeName())
+ .nodeType(node.getNodeType())
+ .nodePositionTop(node.getNodePositionTop())
+ .nodePositionLeft(node.getNodePositionLeft())
+ .nodePreCode(node.getNodePreCode())
+ .nodeNextCode(node.getNodeNextCode())
+ .state(SysYesNo.getInfoByCode(node.getState()))
+ .build();
+ }
+}
diff --git a/muyu-quest-common/src/main/java/com/muyu/quest/resp/TaskResp.java b/muyu-quest-common/src/main/java/com/muyu/quest/resp/TaskResp.java
new file mode 100644
index 0000000..a801249
--- /dev/null
+++ b/muyu-quest-common/src/main/java/com/muyu/quest/resp/TaskResp.java
@@ -0,0 +1,64 @@
+package com.muyu.quest.resp;
+
+
+import com.muyu.common.core.enums.SysYesNo;
+import com.muyu.quest.domain.Task;
+import lombok.*;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+
+/**
+ * 任务对象 task_source
+ *
+ * @Author: 胡杨
+ * @date 2024-08-22
+ */
+
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class TaskResp {
+
+ /** 自增主键 */
+ private Long id;
+
+ /** 任务编码 */
+ private String taskCode;
+
+ /** 任务名称 */
+ private String taskName;
+
+ /** 任务描述 */
+ private String taskData;
+
+ /** 任务类型 */
+ private String taskType;
+
+ /** 启用状态 */
+ private String state;
+
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+ .append("id", getId())
+ .append("taskCode", getTaskCode())
+ .append("taskName", getTaskName())
+ .append("taskType", getTaskType())
+ .append("state", getState())
+ .toString();
+ }
+
+ public static TaskResp build(Task task) {
+ return TaskResp.builder()
+ .id(task.getId())
+ .taskCode(task.getTaskCode())
+ .taskName(task.getTaskName())
+ .taskData(task.getTaskData())
+ .taskType(task.getTaskType())
+ .state(SysYesNo.getInfoByCode(task.getState()))
+ .build();
+ }
+}
diff --git a/muyu-quest-remote/pom.xml b/muyu-quest-remote/pom.xml
new file mode 100644
index 0000000..a2bdf38
--- /dev/null
+++ b/muyu-quest-remote/pom.xml
@@ -0,0 +1,27 @@
+
+
+ 4.0.0
+
+ com.muyu
+ muyu-quest
+ 3.6.5
+
+
+ muyu-quest-remote
+ 3.6.5
+
+
+ 17
+ 17
+ UTF-8
+
+
+
+ com.muyu
+ muyu-quest-common
+
+
+
+
diff --git a/muyu-quest-remote/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/muyu-quest-remote/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/muyu-quest-remote/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -0,0 +1 @@
+
diff --git a/muyu-quest-server/pom.xml b/muyu-quest-server/pom.xml
new file mode 100644
index 0000000..8d83998
--- /dev/null
+++ b/muyu-quest-server/pom.xml
@@ -0,0 +1,150 @@
+
+
+ 4.0.0
+
+ com.muyu
+ muyu-quest
+ 3.6.5
+
+
+ muyu-quest-server
+
+
+
+ 17
+ 17
+ UTF-8
+
+
+
+ com.muyu
+ etl-datasource-remote
+ 3.6.5
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-discovery
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-config
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-sentinel
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+
+
+ com.mysql
+ mysql-connector-j
+
+
+
+
+ com.muyu
+ cloud-common-datasource
+
+
+
+
+ com.muyu
+ cloud-common-datascope
+
+
+
+
+
+
+
+
+
+
+ com.muyu
+ cloud-common-api-doc
+
+
+
+
+ com.muyu
+ cloud-common-xxl
+
+
+
+
+
+
+
+
+
+ com.muyu
+ muyu-quest-common
+
+
+
+ com.muyu
+ muyu-quest-remote
+
+
+ com.muyu
+ etl-rule-remote
+ 3.6.5
+
+
+ com.muyu
+ etl-datasource-client
+ 3.6.5
+
+
+
+ muyu-quest
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ repackage
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+
+ true
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ 3.8.1
+
+ 16
+ 16
+
+ -parameters
+
+
+
+
+
+
diff --git a/muyu-quest-server/src/main/java/com/muyu/quest/MyTaskApplication.java b/muyu-quest-server/src/main/java/com/muyu/quest/MyTaskApplication.java
new file mode 100644
index 0000000..8b308e2
--- /dev/null
+++ b/muyu-quest-server/src/main/java/com/muyu/quest/MyTaskApplication.java
@@ -0,0 +1,31 @@
+package com.muyu.quest;
+
+import com.muyu.common.security.annotation.EnableCustomConfig;
+import com.muyu.common.security.annotation.EnableMyFeignClients;
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.ComponentScan;
+
+/**
+ * @Author: 胡杨
+ * @Name: MyTask
+ * @Description: 任务模块启动器
+ * @CreatedDate: 2024/8/22 下午6:23
+ * @FilePath: com.muyu.quest
+ */
+
+@EnableCustomConfig
+//@EnableCustomSwagger2
+@EnableMyFeignClients
+@SpringBootApplication
+public class MyTaskApplication {
+ public static void main(String[] args) {
+ try {
+ SpringApplication.run(MyTaskApplication.class, args);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ System.out.println("MyTask 模块启动成功!");
+ }
+}
diff --git a/muyu-quest-server/src/main/java/com/muyu/quest/advice/TaskAdvice.java b/muyu-quest-server/src/main/java/com/muyu/quest/advice/TaskAdvice.java
new file mode 100644
index 0000000..8a52f47
--- /dev/null
+++ b/muyu-quest-server/src/main/java/com/muyu/quest/advice/TaskAdvice.java
@@ -0,0 +1,33 @@
+package com.muyu.quest.advice;
+
+/**
+ * @Author: 胡杨
+ * @Name: TaskAdvice
+ * @Description: 任务统一异常处理
+ * @CreatedDate: 2024/9/1 下午4:12
+ * @FilePath: com.muyu.quest.advice
+ */
+
+
+import com.alibaba.nacos.api.model.v2.Result;
+import com.muyu.quest.exception.TaskException;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+
+/**
+ * @Author: 胡杨
+ * @Name: TaskAdvice
+ * @Description: 任务统一异常处理
+ * @CreatedDate: 2024/9/1 下午4:12
+ * @FilePath: com.muyu.quest.advice
+ */
+@Slf4j
+@Component
+public class TaskAdvice {
+ @ExceptionHandler(TaskException.class)
+ public Result commonException(TaskException e) {
+ log.error("任务异常处理,异常信息:[{}]",e.toString());
+ return Result.failure(e.getMessage());
+ }
+}
diff --git a/muyu-quest-server/src/main/java/com/muyu/quest/controller/NodeController.java b/muyu-quest-server/src/main/java/com/muyu/quest/controller/NodeController.java
new file mode 100644
index 0000000..76eb07d
--- /dev/null
+++ b/muyu-quest-server/src/main/java/com/muyu/quest/controller/NodeController.java
@@ -0,0 +1,157 @@
+package com.muyu.quest.controller;
+
+import java.util.*;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.muyu.common.security.annotation.RequiresPermissions;
+import com.muyu.quest.req.NodeReq;
+import com.muyu.quest.resp.NodeResp;
+import jakarta.servlet.http.HttpServletResponse;
+import javax.annotation.Resource;
+
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.muyu.quest.domain.Node;
+import com.muyu.quest.service.INodeService;
+import com.muyu.common.core.web.controller.BaseController;
+import com.muyu.common.core.domain.Result;
+import com.muyu.common.core.utils.poi.ExcelUtil;
+import com.muyu.common.security.utils.SecurityUtils;
+import org.springframework.validation.annotation.Validated;
+import com.muyu.common.core.web.page.TableDataInfo;
+
+/**
+ * 节点管理Controller
+ *
+ * @Author: 胡杨
+ * @date 2024-08-23
+ */
+@RestController
+@RequestMapping("/node")
+public class NodeController extends BaseController {
+ @Resource
+ private INodeService nodeService;
+
+ /**
+ * 查询节点管理列表
+ */
+// @RequiresPermissions("quest:node:list")
+ @GetMapping("/list")
+ public Result> list(NodeReq nodeReq) {
+ startPage();
+ List list = nodeService.selectNodeList(nodeReq);
+ return getDataTable(list
+ .stream()
+ .map(NodeResp::build)
+ .toList());
+ }
+
+ /**
+ * 导出节点管理列表
+ */
+// @RequiresPermissions("quest:node:export")
+ @PostMapping("/export")
+ public void export(HttpServletResponse response, NodeReq nodeReq) {
+ List list = nodeService.selectNodeList(nodeReq);
+ ExcelUtil util = new ExcelUtil(Node.class);
+ util.exportExcel(response, list, "节点管理数据");
+ }
+
+ /**
+ * 获取节点管理详细信息
+ */
+// @RequiresPermissions("quest:node:query")
+ @GetMapping(value = "/{id}")
+ public Result> getInfo(@PathVariable("id") Long id) {
+ return success(nodeService.selectNodeById(id));
+ }
+
+ /**
+ * 新增节点管理
+ */
+// @RequiresPermissions("quest:node:add")
+ @PostMapping
+ @Transactional
+ public Result add(
+ @Validated @RequestBody Node node) {
+ if (nodeService.checkIdUnique(node)) {
+ return error("新增 节点管理 '" + node + "'失败,节点已存在");
+ }
+ String code = UUID.randomUUID().toString().replace("-", "");
+ node.setNodeCode(code);
+ node.setCreateBy(SecurityUtils.getUsername());
+ boolean save = nodeService.save(node);
+// if (save){
+// // 新增任务节点中间表信息
+// middleService.save(new TaskNodeMiddle(null,taskCode,code));
+// }
+ return toAjax(save);
+ }
+
+ /**
+ * 修改节点管理
+ */
+ @RequiresPermissions("quest:node:edit")
+ @PutMapping
+ public Result edit(
+ @Validated @RequestBody Node node) {
+ if (!nodeService.checkIdUnique(node)) {
+ return error("修改 节点管理 '" + node + "'失败,节点不存在");
+ }
+ node.setUpdateBy(SecurityUtils.getUsername());
+ return toAjax(nodeService.updateById(node));
+ }
+
+ /**
+ * 删除节点管理
+ */
+// @RequiresPermissions("quest:node:remove")
+ @DeleteMapping("/{ids}")
+ @Transactional
+ public Result removeByIds(@PathVariable("ids") Long[] ids) {
+ nodeService.removeBatchByIds(Arrays.asList(ids));
+ return success();
+ }
+
+ /**
+ *
+ * @param nodeCodes
+ * @param taskCode
+ * @return
+ */
+ @DeleteMapping("/{taskCode}/{nodeCodes}")
+ @Transactional
+ public Result removeByTaskCode(@PathVariable("nodeCodes") String[] nodeCodes, @PathVariable("taskCode") String taskCode) {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
+ queryWrapper.eq(Node::getTaskCode,taskCode);
+ if (!Arrays.asList(nodeCodes).isEmpty()){
+ queryWrapper.eq(Node::getNodeCode,nodeCodes);
+ }
+ nodeService.remove(queryWrapper);
+ return success();
+ }
+
+ /**
+ * 节点批量删除再批量新增
+ */
+ @PostMapping("/batch/{taskCode}")
+ @Transactional
+ public Result batch(@Validated @RequestBody ArrayList nodeList,@PathVariable("taskCode") String[] taskCode) {
+ // 批量删除
+ nodeService.batchDelect(taskCode);
+
+ if (nodeList.isEmpty()){
+ return Result.success();
+ }
+ // 批量添加
+ boolean save = nodeService.saveBatch(nodeList);
+ return toAjax(save);
+ }
+}
diff --git a/muyu-quest-server/src/main/java/com/muyu/quest/controller/NodeDispositionController.java b/muyu-quest-server/src/main/java/com/muyu/quest/controller/NodeDispositionController.java
new file mode 100644
index 0000000..b06c4a4
--- /dev/null
+++ b/muyu-quest-server/src/main/java/com/muyu/quest/controller/NodeDispositionController.java
@@ -0,0 +1,133 @@
+package com.muyu.quest.controller;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.muyu.quest.domain.Node;
+import jakarta.servlet.http.HttpServletResponse;
+import javax.annotation.Resource;
+
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.muyu.common.security.annotation.RequiresPermissions;
+import com.muyu.quest.domain.NodeDisposition;
+import com.muyu.quest.service.INodeDispositionService;
+import com.muyu.common.core.web.controller.BaseController;
+import com.muyu.common.core.domain.Result;
+import com.muyu.common.core.utils.poi.ExcelUtil;
+import com.muyu.common.security.utils.SecurityUtils;
+import org.springframework.validation.annotation.Validated;
+import com.muyu.common.core.web.page.TableDataInfo;
+
+/**
+ * 节点配置Controller
+ *
+ * @Author: 胡杨
+ * @date 2024-08-29
+ */
+@RestController
+@RequestMapping("/disposition")
+public class NodeDispositionController extends BaseController
+{
+ @Resource
+ private INodeDispositionService nodeDispositionService;
+
+ /**
+ * 查询节点配置列表
+ */
+ @RequiresPermissions("quest:disposition:list")
+ @GetMapping("/list")
+ public Result> list(NodeDisposition nodeDisposition)
+ {
+ startPage();
+ List list = nodeDispositionService.selectNodeDispositionList(nodeDisposition);
+ return getDataTable(list);
+ }
+
+ /**
+ * 导出节点配置列表
+ */
+ @RequiresPermissions("quest:disposition:export")
+ @PostMapping("/export")
+ public void export(HttpServletResponse response, NodeDisposition nodeDisposition)
+ {
+ List list = nodeDispositionService.selectNodeDispositionList(nodeDisposition);
+ ExcelUtil util = new ExcelUtil(NodeDisposition.class);
+ util.exportExcel(response, list, "节点配置数据");
+ }
+
+ /**
+ * 获取节点配置详细信息
+ */
+ @RequiresPermissions("quest:disposition:query")
+ @GetMapping(value = "/{id}")
+ public Result> getInfo(@PathVariable("id") Long id)
+ {
+ return success(nodeDispositionService.selectNodeDispositionById(id));
+ }
+
+ /**
+ * 新增节点配置
+ */
+ @RequiresPermissions("quest:disposition:add")
+ @PostMapping
+ public Result add(
+ @Validated @RequestBody NodeDisposition nodeDisposition)
+ {
+ if (nodeDispositionService.checkIdUnique(nodeDisposition)) {
+ return error("新增 节点配置 '" + nodeDisposition + "'失败,节点配置已存在");
+ }
+ return toAjax(nodeDispositionService.save(nodeDisposition));
+ }
+
+ /**
+ * 修改节点配置
+ */
+ @RequiresPermissions("quest:disposition:edit")
+ @PutMapping
+ public Result edit(
+ @Validated @RequestBody NodeDisposition nodeDisposition)
+ {
+ if (!nodeDispositionService.checkIdUnique(nodeDisposition)) {
+ nodeDispositionService.save(nodeDisposition);
+ }
+ return toAjax(nodeDispositionService.updateById(nodeDisposition));
+ }
+
+ /**
+ * 删除节点配置
+ */
+ @RequiresPermissions("quest:disposition:remove")
+ @DeleteMapping("/{ids}")
+ public Result remove(@PathVariable("ids") Long[] ids)
+ {
+ nodeDispositionService.removeBatchByIds(Arrays.asList(ids));
+ return success();
+ }
+
+ /**
+ * 批量删除与新增
+ */
+ @PostMapping("/batch/{nodeCode}")
+ @Transactional
+ public Result batch(
+ @Validated @RequestBody ArrayList dispList,
+ @PathVariable("nodeCode") String[] nodeCode){
+ // 根据节点编码删除
+ nodeDispositionService.batchDelect(nodeCode);
+ // 批量新增
+ if (!dispList.isEmpty()) {
+ nodeDispositionService.saveBatch(dispList);
+ }
+ return success();
+ }
+}
diff --git a/muyu-quest-server/src/main/java/com/muyu/quest/controller/TaskController.java b/muyu-quest-server/src/main/java/com/muyu/quest/controller/TaskController.java
new file mode 100644
index 0000000..907648e
--- /dev/null
+++ b/muyu-quest-server/src/main/java/com/muyu/quest/controller/TaskController.java
@@ -0,0 +1,141 @@
+package com.muyu.quest.controller;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.UUID;
+
+import com.muyu.common.security.utils.SecurityUtils;
+import com.muyu.quest.domain.Node;
+import com.muyu.quest.domain.Task;
+
+import javax.annotation.Resource;
+
+import com.muyu.quest.job.TaskJob;
+import com.muyu.quest.req.TaskReq;
+import com.muyu.quest.resp.TaskResp;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.muyu.quest.service.TaskService;
+import com.muyu.common.core.web.controller.BaseController;
+import com.muyu.common.core.domain.Result;
+import com.muyu.common.core.web.page.TableDataInfo;
+
+/**
+ * 任务Controller
+ *
+ * @Author: 胡杨
+ * @date 2024-08-22
+ */
+@RestController
+@RequestMapping("/task")
+public class TaskController extends BaseController
+{
+ @Resource
+ private TaskService taskService;
+ @Resource
+ private TaskJob taskJob;
+
+ /**
+ * 查询任务列表
+ */
+// @RequiresPermissions("quest:quest:list")
+ @GetMapping("/list")
+ public Result> list(TaskReq taskReq)
+ {
+ startPage();
+ List list = taskService.selectTaskList(taskReq);
+ return getDataTable(list);
+ }
+
+ /**
+ * 获取任务详细信息
+ */
+// @RequiresPermissions("quest:quest:query")
+ @GetMapping(value = "/selectTaskByTaskCode/{taskCode}")
+ public Result getInfo(@PathVariable("taskCode") String taskCode) {
+ return success(taskService.selectTaskByTaskCode(taskCode));
+ }
+
+ /**
+ * 获取任务详细信息
+ */
+// @RequiresPermissions("quest:quest:query")
+ @GetMapping(value = "/selectTaskById/{id}")
+ public Result getInfo(@PathVariable("id") Long id) {
+ return success(taskService.selectTaskById(id));
+ }
+
+ /**
+ * 新增任务
+ */
+// @RequiresPermissions("quest:quest:quest")
+ @PostMapping
+ public Result add (@Validated @RequestBody Task task) {
+ if (taskService.checkTaskCodeUnique(task)) {
+ return error("新增参数'" + task.getTaskName() + "'失败,任务已存在");
+ }
+ String code = UUID.randomUUID().toString().replace("-", "");
+ task.setTaskCode(code);
+ task.setCreateBy(SecurityUtils.getUsername());
+ return toAjax(taskService.save(task));
+ }
+
+ /**
+ * 修改参数配置
+ */
+// @RequiresPermissions("quest:quest:quest")
+ @PutMapping
+ public Result edit (@Validated @RequestBody Task task) {
+ if (!taskService.checkTaskCodeUnique(task)) {
+ return error("修改参数'" + task.getTaskName() + "'失败,任务不存在");
+ }
+ task.setUpdateBy(SecurityUtils.getUsername());
+ return toAjax(taskService.updateById(task));
+ }
+
+ /**
+ * 删除参数配置
+ */
+// @RequiresPermissions("system:task:remove")
+ @DeleteMapping("/{taskIds}")
+ public Result remove (@PathVariable("taskIds") Long[] taskIds) {
+ taskService.removeBatch(Arrays.asList(taskIds));
+ return success();
+ }
+
+ /**
+ * 测试任务
+ */
+ @PostMapping("/testExecute")
+ public Result testExecute(@RequestBody List nodeList) {
+ return success(taskService.testExecute(nodeList));
+ }
+
+ /**
+ * 执行任务
+ */
+ @PostMapping("/execute/{taskCode}")
+ public Result execute(@PathVariable("taskCode") String taskCode) {
+ return success(taskService.execute(taskCode));
+ }
+
+ /**
+ * 手动清理无用任务数据
+ */
+ @PostMapping("/clearTask")
+ public Result clearTask() {
+ taskJob.clearTask();
+ return success();
+ }
+
+
+
+}
diff --git a/muyu-quest-server/src/main/java/com/muyu/quest/controller/TaskExportController.java b/muyu-quest-server/src/main/java/com/muyu/quest/controller/TaskExportController.java
new file mode 100644
index 0000000..d2e2d68
--- /dev/null
+++ b/muyu-quest-server/src/main/java/com/muyu/quest/controller/TaskExportController.java
@@ -0,0 +1,122 @@
+package com.muyu.quest.controller;
+
+import java.util.Arrays;
+import java.util.List;
+import jakarta.servlet.http.HttpServletResponse;
+import javax.annotation.Resource;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.muyu.common.security.annotation.RequiresPermissions;
+import com.muyu.quest.domain.TaskExport;
+import com.muyu.quest.service.ITaskExportService;
+import com.muyu.common.core.web.controller.BaseController;
+import com.muyu.common.core.domain.Result;
+import com.muyu.common.core.utils.poi.ExcelUtil;
+import com.muyu.common.security.utils.SecurityUtils;
+import org.springframework.validation.annotation.Validated;
+import com.muyu.common.core.web.page.TableDataInfo;
+
+/**
+ * 任务执行记录Controller
+ *
+ * @Author: 胡杨
+ * @date 2024-09-06
+ */
+@RestController
+@RequestMapping("/export")
+public class TaskExportController extends BaseController
+{
+ @Resource
+ private ITaskExportService taskExportService;
+
+ /**
+ * 查询任务执行记录列表
+ */
+ @RequiresPermissions("quest:export:list")
+ @GetMapping("/list")
+ public Result> list(TaskExport taskExport)
+ {
+ startPage();
+ List list = taskExportService.selectTaskExportList(taskExport);
+ return getDataTable(list);
+ }
+
+ /**
+ * 导出任务执行记录列表
+ */
+ @RequiresPermissions("quest:export:export")
+ @PostMapping("/export")
+ public void export(HttpServletResponse response, TaskExport taskExport)
+ {
+ List list = taskExportService.selectTaskExportList(taskExport);
+ ExcelUtil util = new ExcelUtil(TaskExport.class);
+ util.exportExcel(response, list, "任务执行记录数据");
+ }
+
+ /**
+ * 获取任务执行记录详细信息
+ */
+ @RequiresPermissions("quest:export:query")
+ @GetMapping(value = "/{id}")
+ public Result> getInfo(@PathVariable("id") Long id)
+ {
+ return success(taskExportService.selectTaskExportById(id));
+ }
+
+ /**
+ * 新增任务执行记录
+ */
+ @RequiresPermissions("quest:export:add")
+ @PostMapping
+ public Result add(
+ @Validated @RequestBody TaskExport taskExport)
+ {
+ if (taskExportService.checkIdUnique(taskExport)) {
+ return error("新增 任务执行记录 '" + taskExport + "'失败,任务执行记录已存在");
+ }
+ return toAjax(taskExportService.save(taskExport));
+ }
+
+ /**
+ * 修改任务执行记录
+ */
+ @RequiresPermissions("quest:export:edit")
+ @PutMapping
+ public Result edit(
+ @Validated @RequestBody TaskExport taskExport)
+ {
+ if (!taskExportService.checkIdUnique(taskExport)) {
+ return error("修改 任务执行记录 '" + taskExport + "'失败,任务执行记录不存在");
+ }
+ return toAjax(taskExportService.updateById(taskExport));
+ }
+
+ /**
+ * 删除任务执行记录
+ */
+ @RequiresPermissions("quest:export:remove")
+ @DeleteMapping("/{ids}")
+ public Result remove(@PathVariable("ids") Long[] ids)
+ {
+ taskExportService.removeBatchByIds(Arrays.asList(ids));
+ return success();
+ }
+
+
+ @PostMapping("/selTaskStart")
+ public Result selTaskStart(String taskCode){
+ TaskExport taskExport = new TaskExport();
+ taskExport.setTaskCode(taskCode);
+ List taskExports = taskExportService.selectTaskExportList(taskExport);
+ int num = taskExports.size();
+ int yNum = taskExports.stream().filter(taskExport1 -> taskExport1.getStart() == 1).toArray().length;
+ int nNum = num - yNum;
+ return success("共执行线程"+num+"个,完成"+yNum+"个,未完成"+nNum+"个");
+ }
+}
diff --git a/muyu-quest-server/src/main/java/com/muyu/quest/exception/TaskException.java b/muyu-quest-server/src/main/java/com/muyu/quest/exception/TaskException.java
new file mode 100644
index 0000000..2b51861
--- /dev/null
+++ b/muyu-quest-server/src/main/java/com/muyu/quest/exception/TaskException.java
@@ -0,0 +1,43 @@
+package com.muyu.quest.exception;
+
+/**
+ * @Author: 胡杨
+ * @Name: TaskException
+ * @Description: 任务统一异常
+ * @CreatedDate: 2024/9/1 下午4:11
+ * @FilePath: com.muyu.quest.exception
+ */
+
+
+import org.springframework.stereotype.Component;
+
+/**
+ * @Author: 胡杨
+ * @Name: TaskException
+ * @Description: 任务统一异常
+ * @CreatedDate: 2024/9/1 下午4:11
+ * @FilePath: com.muyu.quest.exception
+ */
+
+@Component
+public class TaskException extends RuntimeException {
+ public TaskException() {
+ super();
+ }
+
+ public TaskException(String message) {
+ super(message);
+ }
+
+ public TaskException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public TaskException(Throwable cause) {
+ super(cause);
+ }
+
+ protected TaskException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+}
diff --git a/muyu-quest-server/src/main/java/com/muyu/quest/job/TaskJob.java b/muyu-quest-server/src/main/java/com/muyu/quest/job/TaskJob.java
new file mode 100644
index 0000000..def6c61
--- /dev/null
+++ b/muyu-quest-server/src/main/java/com/muyu/quest/job/TaskJob.java
@@ -0,0 +1,81 @@
+package com.muyu.quest.job;
+
+/**
+ * @Author: 胡杨
+ * @Name: TaskJob
+ * @Description: 任务定时任务
+ * @CreatedDate: 2024/9/4 下午2:37
+ * @FilePath: com.muyu.quest.job
+ */
+
+
+import com.muyu.quest.domain.Node;
+import com.muyu.quest.domain.NodeDisposition;
+import com.muyu.quest.req.NodeReq;
+import com.muyu.quest.req.TaskReq;
+import com.muyu.quest.resp.TaskResp;
+import com.muyu.quest.service.INodeDispositionService;
+import com.muyu.quest.service.INodeService;
+import com.muyu.quest.service.TaskService;
+import com.xxl.job.core.handler.annotation.XxlJob;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * @Author: 胡杨
+ * @Name: TaskJob
+ * @Description: 任务定时任务
+ * @CreatedDate: 2024/9/4 下午2:37
+ * @FilePath: com.muyu.quest.job
+ */
+
+@Component
+public class TaskJob {
+
+ @Resource
+ private TaskService taskService;
+ @Resource
+ private INodeService nodeService;
+ @Resource
+ private INodeDispositionService nodeDispositionService;
+
+ // 定时清理过期任务与其相关数据
+ @XxlJob(value = "clearTask")
+ public void clearTask() {
+ List tasks = taskService.selectTaskList(new TaskReq());
+ List nodes = nodeService.selectNodeList(new NodeReq());
+ List dispositions = nodeDispositionService.selectNodeDispositionList(new NodeDisposition());
+
+ // 查询节点表存在但任务表不存在的任务编码
+ Set notContainsTaskCodeList = nodes.
+ stream().
+ map(Node::getTaskCode).
+ filter(nodeTaskCode -> !tasks.
+ stream().
+ map(TaskResp::getTaskCode).
+ toList().contains(nodeTaskCode)).
+ collect(Collectors.toSet());
+ // 删除相关节点表数据
+ if (!notContainsTaskCodeList.isEmpty()) {
+ nodeService.batchDelect(notContainsTaskCodeList.toArray(new String[0]));
+ }
+ // 查询配置表存在但节点表不存在的节点编码
+ Set notContainsNodeCodeList = dispositions.
+ stream().
+ map(NodeDisposition::getNodeCode).
+ filter(nodeDispositionNodeCode -> !nodes.
+ stream().
+ map(Node::getNodeCode).
+ toList().contains(nodeDispositionNodeCode)).
+ collect(Collectors.toSet());
+ // 删除相关配置表数据
+ if (!notContainsNodeCodeList.isEmpty()) {
+ nodeDispositionService.batchDelect(notContainsNodeCodeList.toArray(new String[0]));
+ }
+ }
+
+}
diff --git a/muyu-quest-server/src/main/java/com/muyu/quest/manager/TaskManager.java b/muyu-quest-server/src/main/java/com/muyu/quest/manager/TaskManager.java
new file mode 100644
index 0000000..d469dae
--- /dev/null
+++ b/muyu-quest-server/src/main/java/com/muyu/quest/manager/TaskManager.java
@@ -0,0 +1,182 @@
+package com.muyu.quest.manager;
+
+/**
+ * @Author: 胡杨
+ * @Name: TaskManager
+ * @Description: 任务管理器
+ * @CreatedDate: 2024/9/4 下午7:44
+ * @FilePath: com.muyu.quest.manager
+ */
+
+
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @Author: 胡杨
+ * @Name: TaskManager
+ * @Description: 任务线程管理器
+ * @CreatedDate: 2024/9/4 下午7:44
+ * @FilePath: com.muyu.quest.manager
+ */
+@Slf4j
+public final class TaskManager {
+ // 线程池中默认线程的个数为5
+ private static int workerNum = 9;
+ // 工作线程
+ private final WorkThread[] workThrads;
+ // 未处理的任务
+ private static volatile int finishedTask = 0;
+
+ // 任务队列
+ private final List taskQueue = new LinkedList();
+ private static TaskManager taskManager;
+
+ private long startTime;
+ private long endTime;
+
+ // 创建具有默认线程个数的线程池
+ private TaskManager() {
+ this(9);
+ }
+
+ // 创建线程池,workerNum为线程池中工作线程的个数
+ private TaskManager(int workerNum) {
+ taskManager.workerNum = workerNum;
+ workThrads = new WorkThread[workerNum];
+ for (int i = 0; i < workerNum; i++) {
+ workThrads[i] = new WorkThread();
+ workThrads[i].start();// 开启线程池中的线程
+ }
+ startTime = System.currentTimeMillis();
+ }
+
+ // 单态模式,获得一个默认线程个数的线程池
+ public static TaskManager getTaskManager() {
+ return getTaskManager(TaskManager.workerNum);
+ }
+
+ // 单态模式,获得一个指定线程个数的线程池,workerNum(>0)为线程池中工作线程的个数
+ // workerNum<=0创建默认的工作线程个数
+ public static TaskManager getTaskManager(int workerNum1) {
+ if (workerNum1 <= 0) {
+ workerNum1 = TaskManager.workerNum;
+ }
+ if (taskManager == null) {
+ taskManager = new TaskManager(workerNum1);
+ }
+ return taskManager;
+ }
+
+ // 把任务加入任务队列
+ public void execute(List task) {
+ execute(task.toArray(new Runnable[0]));
+ }
+
+ // 把任务加入任务队列
+ public void execute(Runnable... task) {
+ synchronized (taskQueue) {
+ Collections.addAll(taskQueue, task);
+ taskQueue.notify();
+ }
+ }
+
+
+
+ // 销毁线程池,该方法保证在所有任务都完成的情况下才销毁所有线程,否则等待任务完成才销毁
+ public void destroy() {
+ while (!taskQueue.isEmpty()) {// 如果还有任务没执行完成,就先睡会吧
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ // 工作线程停止工作,且置为null
+ for (int i = 0; i < workerNum; i++) {
+ workThrads[i].stopWorker();
+ workThrads[i] = null;
+ }
+ taskManager=null;
+ taskQueue.clear();// 清空任务队列
+ }
+
+ // 销毁线程池,该方法保证在所有任务都完成的情况下才销毁所有线程,否则等待任务完成才销毁
+ public void close() {
+ while (!taskQueue.isEmpty()) {// 如果还有任务没执行完成,就先睡会吧
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ taskManager=null;
+ endTime = System.currentTimeMillis();
+ log.info("任务执行完成,任务执行情况: {},共计耗时: {}ms",this.toString(),endTime-startTime);
+ }
+
+ // 返回工作线程的个数
+ public int getWorkThreadNumber() {
+ return workerNum;
+ }
+
+ // 返回已完成任务的个数,这里的已完成是只出了任务队列的任务个数,可能该任务并没有实际执行完成
+ public int getFinishedTasknumber() {
+ return finishedTask;
+ }
+
+ // 返回任务队列的长度,即还没处理的任务个数
+ public int getWaitTasknumber() {
+ return taskQueue.size();
+ }
+
+ // 覆盖toString方法,返回线程池信息:工作线程个数和已完成任务个数
+ @Override
+ public String toString() {
+ return "工作任务数:" + workerNum + ",已完成任务数:"
+ + finishedTask + ",等待任务数:" + getWaitTasknumber();
+ }
+
+ /**
+ * 内部类,工作线程
+ */
+ private class WorkThread extends Thread {
+ // 该工作线程是否有效,用于结束该工作线程
+ private boolean isRunning = true;
+
+ /*
+ * 关键所在,如果任务队列不空,则取出任务执行,若任务队列空,则等待
+ */
+ @Override
+ public void run() {
+ Runnable r = null;
+ while (isRunning) {// 若线程无效则自然结束run方法,该线程就没用了
+ synchronized (taskQueue) {
+ while (isRunning && taskQueue.isEmpty()) {// 队列为空
+ try {
+ taskQueue.wait(50);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ if (!taskQueue.isEmpty()) {
+ r = taskQueue.remove(0);// 取出任务
+ }
+ }
+ if (r != null) {
+ r.run();// 执行任务
+ }
+ finishedTask++;
+ r = null;
+ }
+ }
+
+ // 停止工作,让该线程自然执行完run方法,自然结束
+ public void stopWorker() {
+ isRunning = false;
+ }
+ }
+}
diff --git a/muyu-quest-server/src/main/java/com/muyu/quest/mapper/NodeDispositionMapper.java b/muyu-quest-server/src/main/java/com/muyu/quest/mapper/NodeDispositionMapper.java
new file mode 100644
index 0000000..41d8711
--- /dev/null
+++ b/muyu-quest-server/src/main/java/com/muyu/quest/mapper/NodeDispositionMapper.java
@@ -0,0 +1,16 @@
+package com.muyu.quest.mapper;
+
+import com.muyu.quest.domain.NodeDisposition;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 节点配置Mapper接口
+ *
+ * @Author: 胡杨
+ * @date 2024-08-29
+ */
+@Mapper
+public interface NodeDispositionMapper extends BaseMapper{
+
+}
diff --git a/muyu-quest-server/src/main/java/com/muyu/quest/mapper/NodeMapper.java b/muyu-quest-server/src/main/java/com/muyu/quest/mapper/NodeMapper.java
new file mode 100644
index 0000000..f8b2ceb
--- /dev/null
+++ b/muyu-quest-server/src/main/java/com/muyu/quest/mapper/NodeMapper.java
@@ -0,0 +1,17 @@
+package com.muyu.quest.mapper;
+
+import java.util.List;
+import com.muyu.quest.domain.Node;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 节点管理Mapper接口
+ *
+ * @Author: 胡杨
+ * @date 2024-08-23
+ */
+@Mapper
+public interface NodeMapper extends BaseMapper{
+
+}
diff --git a/muyu-quest-server/src/main/java/com/muyu/quest/mapper/TaskExportMapper.java b/muyu-quest-server/src/main/java/com/muyu/quest/mapper/TaskExportMapper.java
new file mode 100644
index 0000000..aefaaba
--- /dev/null
+++ b/muyu-quest-server/src/main/java/com/muyu/quest/mapper/TaskExportMapper.java
@@ -0,0 +1,17 @@
+package com.muyu.quest.mapper;
+
+import java.util.List;
+import com.muyu.quest.domain.TaskExport;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 任务执行记录Mapper接口
+ *
+ * @Author: 胡杨
+ * @date 2024-09-06
+ */
+@Mapper
+public interface TaskExportMapper extends BaseMapper{
+
+}
diff --git a/muyu-quest-server/src/main/java/com/muyu/quest/mapper/TaskMapper.java b/muyu-quest-server/src/main/java/com/muyu/quest/mapper/TaskMapper.java
new file mode 100644
index 0000000..b9d9c6b
--- /dev/null
+++ b/muyu-quest-server/src/main/java/com/muyu/quest/mapper/TaskMapper.java
@@ -0,0 +1,22 @@
+package com.muyu.quest.mapper;
+
+import java.util.List;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.muyu.quest.domain.NodeType;
+import com.muyu.quest.domain.Task;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 任务Mapper接口
+ *
+ * @Author: 胡杨
+ * @date 2024-08-22
+ */
+@Mapper
+public interface TaskMapper extends BaseMapper {
+
+ List selectNodeTypeList();
+
+
+}
diff --git a/muyu-quest-server/src/main/java/com/muyu/quest/service/INodeDispositionService.java b/muyu-quest-server/src/main/java/com/muyu/quest/service/INodeDispositionService.java
new file mode 100644
index 0000000..baab747
--- /dev/null
+++ b/muyu-quest-server/src/main/java/com/muyu/quest/service/INodeDispositionService.java
@@ -0,0 +1,40 @@
+package com.muyu.quest.service;
+
+import java.util.List;
+import com.muyu.quest.domain.NodeDisposition;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * 节点配置Service接口
+ *
+ * @Author: 胡杨
+ * @date 2024-08-29
+ */
+public interface INodeDispositionService extends IService {
+ /**
+ * 精确查询节点配置
+ *
+ * @param id 节点配置主键
+ * @return 节点配置
+ */
+ public NodeDisposition selectNodeDispositionById(Long id);
+
+ /**
+ * 查询节点配置列表
+ *
+ * @param nodeDisposition 节点配置
+ * @return 节点配置集合
+ */
+ public List selectNodeDispositionList(NodeDisposition nodeDisposition);
+
+ /**
+ * 判断 节点配置 id是否唯一
+ * @param nodeDisposition 节点配置
+ * @return 结果
+ */
+ Boolean checkIdUnique(NodeDisposition nodeDisposition);
+
+ Boolean checkDispUnique(NodeDisposition disp);
+
+ void batchDelect(String[] nodeCode);
+}
diff --git a/muyu-quest-server/src/main/java/com/muyu/quest/service/INodeService.java b/muyu-quest-server/src/main/java/com/muyu/quest/service/INodeService.java
new file mode 100644
index 0000000..e2aac22
--- /dev/null
+++ b/muyu-quest-server/src/main/java/com/muyu/quest/service/INodeService.java
@@ -0,0 +1,41 @@
+package com.muyu.quest.service;
+
+import java.util.List;
+import com.muyu.quest.domain.Node;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.muyu.quest.req.NodeReq;
+
+/**
+ * 节点管理Service接口
+ *
+ * @Author: 胡杨
+ * @date 2024-08-23
+ */
+public interface INodeService extends IService {
+ /**
+ * 精确查询节点管理
+ *
+ * @param id 节点管理主键
+ * @return 节点管理
+ */
+ public Node selectNodeById(Long id);
+
+ /**
+ * 查询节点管理列表
+ *
+ * @param nodeReq 节点管理
+ * @return 节点管理集合
+ */
+ public List selectNodeList(NodeReq nodeReq);
+
+ /**
+ * 判断 节点管理 id是否唯一
+ * @param node 节点管理
+ * @return 结果
+ */
+ Boolean checkIdUnique(Node node);
+
+ List selectNodeByIds(Long[] ids);
+
+ void batchDelect(String[] taskCode);
+}
diff --git a/muyu-quest-server/src/main/java/com/muyu/quest/service/ITaskExportService.java b/muyu-quest-server/src/main/java/com/muyu/quest/service/ITaskExportService.java
new file mode 100644
index 0000000..64132fd
--- /dev/null
+++ b/muyu-quest-server/src/main/java/com/muyu/quest/service/ITaskExportService.java
@@ -0,0 +1,44 @@
+package com.muyu.quest.service;
+
+import java.util.List;
+import com.muyu.quest.domain.TaskExport;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * 任务执行记录Service接口
+ *
+ * @Author: 胡杨
+ * @date 2024-09-06
+ */
+public interface ITaskExportService extends IService {
+ /**
+ * 精确查询任务执行记录
+ *
+ * @param id 任务执行记录主键
+ * @return 任务执行记录
+ */
+ public TaskExport selectTaskExportById(Long id);
+
+ /**
+ * 查询任务执行记录列表
+ *
+ * @param taskExport 任务执行记录
+ * @return 任务执行记录集合
+ */
+ public List selectTaskExportList(TaskExport taskExport);
+
+ /**
+ * 判断 任务执行记录 id是否唯一
+ * @param taskExport 任务执行记录
+ * @return 结果
+ */
+ Boolean checkIdUnique(TaskExport taskExport);
+
+
+ /**
+ * 条件精确查询
+ */
+ TaskExport selectTaskExport(TaskExport taskExport);
+
+ void updateByExportCode(TaskExport entity);
+}
diff --git a/muyu-quest-server/src/main/java/com/muyu/quest/service/TaskService.java b/muyu-quest-server/src/main/java/com/muyu/quest/service/TaskService.java
new file mode 100644
index 0000000..7c99745
--- /dev/null
+++ b/muyu-quest-server/src/main/java/com/muyu/quest/service/TaskService.java
@@ -0,0 +1,52 @@
+package com.muyu.quest.service;
+
+import java.util.List;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.muyu.quest.domain.Node;
+import com.muyu.quest.domain.Task;
+import com.muyu.quest.req.TaskReq;
+import com.muyu.quest.resp.TaskResp;
+
+/**
+ * 任务Service接口
+ *
+ * @Author: 胡杨
+ * @date 2024-08-22
+ */
+public interface TaskService extends IService {
+ /**
+ * 查询任务
+ *
+ * @param taskCode 任务编码
+ * @return 任务
+ */
+ public Task selectTaskByTaskCode(String taskCode);
+
+ /**
+ * 查询任务列表
+ *
+ * @param taskReq 任务
+ * @return 任务集合
+ */
+ public List selectTaskList(TaskReq taskReq);
+
+
+ Task selectTaskById(Long id);
+
+ Boolean checkTaskCodeUnique(Task task);
+
+ /**
+ * 判断id是否唯一
+ * @param task 实体
+ * @return 结果
+ */
+ Boolean checkIdUnique(Task task);
+
+ void removeBatch(List list);
+
+ String execute(String taskCode);
+
+ String testExecute(List nodeList);
+
+}
diff --git a/muyu-quest-server/src/main/java/com/muyu/quest/service/impl/NodeDispositionServiceImpl.java b/muyu-quest-server/src/main/java/com/muyu/quest/service/impl/NodeDispositionServiceImpl.java
new file mode 100644
index 0000000..9092552
--- /dev/null
+++ b/muyu-quest-server/src/main/java/com/muyu/quest/service/impl/NodeDispositionServiceImpl.java
@@ -0,0 +1,92 @@
+package com.muyu.quest.service.impl;
+
+import java.util.List;
+import org.springframework.stereotype.Service;
+import com.muyu.quest.mapper.NodeDispositionMapper;
+import com.muyu.quest.domain.NodeDisposition;
+import com.muyu.quest.service.INodeDispositionService;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.muyu.common.core.utils.StringUtils;
+import org.springframework.util.Assert;
+
+/**
+ * 节点配置Service业务层处理
+ *
+ * @Author: 胡杨
+ * @date 2024-08-29
+ */
+@Service
+public class NodeDispositionServiceImpl
+ extends ServiceImpl
+ implements INodeDispositionService {
+
+ /**
+ * 精确查询节点配置
+ *
+ * @param id 节点配置主键
+ * @return 节点配置
+ */
+ @Override
+ public NodeDisposition selectNodeDispositionById(Long id)
+ {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
+ Assert.notNull(id, "id不可为空");
+ queryWrapper.eq(NodeDisposition::getId, id);
+ return this.getOne(queryWrapper);
+ }
+
+
+ /**
+ * 查询节点配置列表
+ *
+ * @param nodeDisposition 节点配置
+ * @return 节点配置
+ */
+ @Override
+ public List selectNodeDispositionList(NodeDisposition nodeDisposition)
+ {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
+ if (StringUtils.isNotEmpty(nodeDisposition.getNodeCode())){
+ queryWrapper.eq(NodeDisposition::getNodeCode, nodeDisposition.getNodeCode());
+ }
+ if (StringUtils.isNotEmpty(nodeDisposition.getDispKey())){
+ queryWrapper.eq(NodeDisposition::getDispKey, nodeDisposition.getDispKey());
+ }
+ if (StringUtils.isNotEmpty(nodeDisposition.getDispLabel())){
+ queryWrapper.eq(NodeDisposition::getDispLabel, nodeDisposition.getDispLabel());
+ }
+ if (StringUtils.isNotEmpty(nodeDisposition.getDispType())){
+ queryWrapper.eq(NodeDisposition::getDispType, nodeDisposition.getDispType());
+ }
+ return this.list(queryWrapper);
+ }
+
+ /**
+ * 唯一 判断
+ * @param nodeDisposition 节点配置
+ * @return 节点配置
+ */
+ @Override
+ public Boolean checkIdUnique(NodeDisposition nodeDisposition) {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
+ queryWrapper.eq(NodeDisposition::getId, nodeDisposition.getId());
+ return this.count(queryWrapper) > 0;
+ }
+
+ @Override
+ public Boolean checkDispUnique(NodeDisposition disp) {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
+ queryWrapper.eq(NodeDisposition::getId, disp.getId());
+ queryWrapper.eq(NodeDisposition::getNodeCode, disp.getNodeCode());
+ return this.exists(queryWrapper);
+ }
+
+ @Override
+ public void batchDelect(String[] nodeCode) {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
+ queryWrapper.in(NodeDisposition::getNodeCode, nodeCode);
+ this.remove(queryWrapper);
+ }
+
+}
diff --git a/muyu-quest-server/src/main/java/com/muyu/quest/service/impl/NodeServiceImpl.java b/muyu-quest-server/src/main/java/com/muyu/quest/service/impl/NodeServiceImpl.java
new file mode 100644
index 0000000..c8d5097
--- /dev/null
+++ b/muyu-quest-server/src/main/java/com/muyu/quest/service/impl/NodeServiceImpl.java
@@ -0,0 +1,98 @@
+package com.muyu.quest.service.impl;
+
+import java.util.Arrays;
+import java.util.List;
+
+import com.muyu.quest.req.NodeReq;
+import org.springframework.stereotype.Service;
+import com.muyu.quest.mapper.NodeMapper;
+import com.muyu.quest.domain.Node;
+import com.muyu.quest.service.INodeService;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.muyu.common.core.utils.StringUtils;
+import org.springframework.util.Assert;
+
+/**
+ * 节点管理Service业务层处理
+ *
+ * @Author: 胡杨
+ * @date 2024-08-23
+ */
+@Service
+public class NodeServiceImpl
+ extends ServiceImpl
+ implements INodeService {
+
+ /**
+ * 精确查询节点管理
+ *
+ * @param id 节点管理主键
+ * @return 节点管理
+ */
+ @Override
+ public Node selectNodeById(Long id)
+ {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
+ Assert.notNull(id, "id不可为空");
+ queryWrapper.eq(Node::getId, id);
+ return this.getOne(queryWrapper);
+ }
+
+
+ /**
+ * 查询节点管理列表
+ *
+ * @param nodeReq 节点管理
+ * @return 节点管理
+ */
+ @Override
+ public List selectNodeList(NodeReq nodeReq)
+ {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
+ if (StringUtils.isNotEmpty(nodeReq.getTaskCode())){
+ queryWrapper.eq(Node::getTaskCode, nodeReq.getTaskCode());
+ }
+ if (StringUtils.isNotEmpty(nodeReq.getNodeName())){
+ queryWrapper.like(Node::getNodeName, nodeReq.getNodeName());
+ }
+ if (StringUtils.isNotEmpty(nodeReq.getNodePreCode())){
+ queryWrapper.eq(Node::getNodePreCode, nodeReq.getNodePreCode());
+ }
+ if (StringUtils.isNotEmpty(nodeReq.getNodeNextCode())){
+ queryWrapper.eq(Node::getNodeNextCode, nodeReq.getNodeNextCode());
+ }
+ if (StringUtils.isNotEmpty(nodeReq.getState())){
+ queryWrapper.eq(Node::getState, nodeReq.getState());
+ }
+ return this.list(queryWrapper);
+ }
+
+ /**
+ * 唯一 判断
+ * @param node 节点管理
+ * @return 节点管理
+ */
+ @Override
+ public Boolean checkIdUnique(Node node) {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
+ queryWrapper.eq(Node::getId, node.getId());
+ return this.exists(queryWrapper);
+ }
+
+ @Override
+ public List selectNodeByIds(Long[] ids) {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
+ queryWrapper.in(Node::getId, Arrays.asList(ids));
+ return this.list(queryWrapper);
+ }
+
+ @Override
+ public void batchDelect(String[] taskCode) {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
+ queryWrapper.in(Node::getTaskCode,taskCode);
+ this.remove(queryWrapper);
+ }
+
+
+}
diff --git a/muyu-quest-server/src/main/java/com/muyu/quest/service/impl/TaskExportServiceImpl.java b/muyu-quest-server/src/main/java/com/muyu/quest/service/impl/TaskExportServiceImpl.java
new file mode 100644
index 0000000..6c73919
--- /dev/null
+++ b/muyu-quest-server/src/main/java/com/muyu/quest/service/impl/TaskExportServiceImpl.java
@@ -0,0 +1,93 @@
+package com.muyu.quest.service.impl;
+
+import java.util.List;
+
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import org.springframework.stereotype.Service;
+import com.muyu.quest.mapper.TaskExportMapper;
+import com.muyu.quest.domain.TaskExport;
+import com.muyu.quest.service.ITaskExportService;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.muyu.common.core.utils.StringUtils;
+import org.springframework.util.Assert;
+
+/**
+ * 任务执行记录Service业务层处理
+ *
+ * @Author: 胡杨
+ * @date 2024-09-06
+ */
+@Service
+public class TaskExportServiceImpl
+ extends ServiceImpl
+ implements ITaskExportService {
+
+ /**
+ * 精确查询任务执行记录
+ *
+ * @param id 任务执行记录主键
+ * @return 任务执行记录
+ */
+ @Override
+ public TaskExport selectTaskExportById(Long id)
+ {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
+ Assert.notNull(id, "id不可为空");
+ queryWrapper.eq(TaskExport::getId, id);
+ return this.getOne(queryWrapper);
+ }
+
+
+ /**
+ * 查询任务执行记录列表
+ *
+ * @param taskExport 任务执行记录
+ * @return 任务执行记录
+ */
+ @Override
+ public List selectTaskExportList(TaskExport taskExport)
+ {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
+ if (StringUtils.isNotEmpty(taskExport.getTaskCode())){
+ queryWrapper.eq(TaskExport::getTaskCode, taskExport.getTaskCode());
+ }
+
+ return this.list(queryWrapper);
+ }
+
+ /**
+ * 唯一 判断
+ * @param taskExport 任务执行记录
+ * @return 任务执行记录
+ */
+ @Override
+ public Boolean checkIdUnique(TaskExport taskExport) {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
+ queryWrapper.eq(TaskExport::getId, taskExport.getId());
+ return this.exists(queryWrapper);
+ }
+
+ @Override
+ public TaskExport selectTaskExport(TaskExport taskExport) {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
+ if (StringUtils.isNotEmpty(taskExport.getTaskCode())){
+ queryWrapper.eq(TaskExport::getTaskCode, taskExport.getTaskCode());
+ }
+ if (taskExport.getStart() != null){
+ queryWrapper.eq(TaskExport::getStart, taskExport.getStart());
+ }
+ queryWrapper.last("limit 1");
+ return this.getOne(queryWrapper);
+ }
+
+ @Override
+ public void updateByExportCode(TaskExport taskExport) {
+ LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>();
+ updateWrapper.eq(TaskExport::getExportCode, taskExport.getExportCode())
+ .set(TaskExport::getError, taskExport.getError())
+ .set(TaskExport::getStart, taskExport.getStart());
+ this.update(null, updateWrapper);
+ }
+
+}
diff --git a/muyu-quest-server/src/main/java/com/muyu/quest/service/impl/TaskServiceImpl.java b/muyu-quest-server/src/main/java/com/muyu/quest/service/impl/TaskServiceImpl.java
new file mode 100644
index 0000000..b6edc35
--- /dev/null
+++ b/muyu-quest-server/src/main/java/com/muyu/quest/service/impl/TaskServiceImpl.java
@@ -0,0 +1,318 @@
+package com.muyu.quest.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.muyu.common.core.domain.Result;
+import com.muyu.common.core.utils.StringUtils;
+import com.muyu.etl.data.access.data.access.client.mysql.MySqlDataSource;
+import com.muyu.etl.data.access.data.access.client.mysql.MySqlQuery;
+import com.muyu.etl.data.access.domain.req.EtlDataSqlReq;
+import com.muyu.etl.data.access.remote.RemoteEtlDataService;
+import com.muyu.etl.domain.DataStructure;
+import com.muyu.quest.domain.*;
+import com.muyu.quest.exception.TaskException;
+import com.muyu.quest.manager.TaskManager;
+import com.muyu.quest.mapper.TaskMapper;
+import com.muyu.quest.model.DataValueModel;
+import com.muyu.quest.req.NodeReq;
+import com.muyu.quest.req.TaskReq;
+import com.muyu.quest.resp.TaskResp;
+import com.muyu.quest.service.INodeDispositionService;
+import com.muyu.quest.service.INodeService;
+import com.muyu.quest.service.ITaskExportService;
+import com.muyu.quest.service.TaskService;
+import com.muyu.quest.utils.NodeUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.jdbc.object.SqlQuery;
+import org.springframework.stereotype.Service;
+import org.springframework.util.Assert;
+
+import javax.annotation.Resource;
+import java.util.HashMap;
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * 任务Service业务层处理
+ *
+ * @Author: 胡杨
+ * @date 2024-08-22
+ */
+@Slf4j
+@Service
+public class TaskServiceImpl extends ServiceImpl
+ implements TaskService {
+
+ @Resource
+ private INodeService nodeService;
+ @Resource
+ private INodeDispositionService dispositionService;
+ @Resource
+ private TaskMapper taskMapper;
+ @Resource
+ private RemoteEtlDataService remoteEtlDataService;
+ @Resource
+ private ITaskExportService taskExportService;
+ @Resource
+ private MySqlDataSource mySqlDataSource;
+
+ private static TaskManager taskManager = null;
+
+ /**
+ * 查询任务
+ *
+ * @param taskCode 任务编码
+ * @return 任务
+ */
+ @Override
+ public Task selectTaskByTaskCode(String taskCode)
+ {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
+ Assert.notNull(taskCode, "taskCode不可为空");
+ queryWrapper.eq(Task::getTaskCode, taskCode);
+ return this.getOne(queryWrapper);
+ }
+
+ /**
+ * 查询任务
+ *
+ * @param id 任务主键
+ * @return 任务
+ */
+ @Override
+ public Task selectTaskById(Long id)
+ {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
+ Assert.notNull(id, "id不可为空");
+ queryWrapper.eq(Task::getId, id);
+ return this.getOne(queryWrapper);
+ }
+
+ /**
+ * 唯一编码判断
+ * @param task
+ * @return
+ */
+ @Override
+ public Boolean checkTaskCodeUnique(Task task) {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
+ queryWrapper.eq(Task::getTaskCode, task.getTaskCode());
+ return this.exists(queryWrapper);
+ }
+
+ @Override
+ public Boolean checkIdUnique(Task task) {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
+ queryWrapper.eq(Task::getId, task.getId());
+ return this.exists(queryWrapper);
+ }
+
+ @Override
+ public void removeBatch(List list) {
+ this.removeBatchByIds(list);
+ }
+
+ /**
+ * 查询任务列表
+ *
+ * @param taskReq 任务
+ * @return 任务
+ */
+ @Override
+ public List selectTaskList(TaskReq taskReq)
+ {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
+ if (StringUtils.isNotEmpty(taskReq.getTaskName())){
+ queryWrapper.like(Task::getTaskName, taskReq.getTaskName());
+ }
+ if (StringUtils.isNotEmpty(taskReq.getState())){
+ queryWrapper.eq(Task::getState, taskReq.getState());
+ }
+ if (StringUtils.isNotEmpty(taskReq.getTaskType())){
+ queryWrapper.eq(Task::getTaskType, taskReq.getTaskType());
+ }
+ return this.list(queryWrapper)
+ .stream()
+ .map(TaskResp::build)
+ .toList();
+ }
+
+ // 查询节点所有类型
+ public List selectNodeTypeList(){
+ return taskMapper.selectNodeTypeList();
+ }
+
+ /**
+ * //TODO 执行任务
+ * 获取任务所有节点配置信息 拼接sql语句
+ * @param taskCode 任务编码
+ * @return 成功与否
+ */
+ @Override
+ public String execute(String taskCode) {
+ log.info("任务编码 {} 开始执行......",taskCode);
+ // 查询任务所有节点
+ List nodeListAll = nodeService.selectNodeList(new NodeReq().buildTaskCode(taskCode));
+ // 节点初始化
+ HashMap> nodeMap = nodeCheckNorm(nodeListAll);
+ // 获取查询SQL
+ String findSql = getFindSql(nodeMap);
+ // 获取查询条数
+ int count = getFindCount(findSql, nodeMap);
+ // 划分线程 每次查询10000条
+ int pageSize = 100000;
+ if (count < 500000){
+ pageSize = 10000;
+ }
+ int threadNum = count / pageSize + 1;
+ if (taskManager == null){
+ taskManager = TaskManager.getTaskManager();
+ }
+ log.info("任务 {} 总共需要 {} 条数据, 划分为线程{}条",taskCode,count,threadNum);
+ for (int i = 0; i < threadNum; i++) {
+ int index = i+1;
+ int limitNum = pageSize;
+ // 添加进入任务队列
+ taskManager.execute(() -> {
+ String exportCode = UUID.randomUUID().toString().replace("-","");
+ // 获取新SQL 并执行
+ StringBuilder newAddSql = new StringBuilder(findSql);
+ newAddSql.append(" LIMIT ")
+ .append(limitNum)
+ .append(" OFFSET ")
+ .append((index-1)*limitNum);
+ String addSql = getAddSql(nodeMap, newAddSql.toString());
+ TaskExport entity = new TaskExport(taskCode,exportCode, newAddSql.toString(), 0, "");
+ taskExportService.save(entity);
+ mySqlDataSource.setQuery(
+ MySqlQuery.builder()
+ .dataSourceId(14L)
+ .sql(addSql)
+ .build()
+ );
+ Result addResult = mySqlDataSource.addTargetDatabase();
+ log.info("任务 {} 第 {} 线程执行结果 {}",taskCode,index,addResult.getMsg());
+ if (addResult.getCode() != 200){
+ int errorMaxLength = Math.min(addResult.getMsg().length(), 30000);
+ entity.setError(addResult.getMsg().substring(0,errorMaxLength));
+ entity.setStart(2);
+ }else {
+ entity.setStart(1);
+ }
+ taskExportService.updateByExportCode(entity);
+ });
+ }
+ new Thread(() ->{
+ taskManager.close();
+ taskManager = null;
+ });
+ return "执行成功";
+ }
+
+
+
+ @Override
+ public String testExecute(List nodeListAll) {
+ log.info("开始测试执行......");
+ // 查询节点类型与其规范
+ List nodeTypeList = selectNodeTypeList();
+ /* 节点组成校验 */
+ NodeUtils.nodeCheckMakeUp(nodeListAll, nodeTypeList);
+ /* 节点连接规范校验 */
+ NodeUtils.nodeCheckNorm(nodeListAll, nodeTypeList);
+
+ return "测试成功,无异常";
+ }
+
+
+ /**
+ * 执行任务第一步: 校验节点规范 并返回初始化节点map
+ */
+ private HashMap> nodeCheckNorm(List nodeListAll) {
+ // 查询节点类型与其校验相关配置
+ List nodeTypeList = selectNodeTypeList();
+ /* 节点组成校验 */
+ NodeUtils.nodeCheckMakeUp(nodeListAll, nodeTypeList);
+ /* 节点连接规范校验 */
+ NodeUtils.nodeCheckNorm(nodeListAll, nodeTypeList);
+
+ return NodeUtils.nodeInit(nodeListAll);
+ }
+
+ /**
+ * 执行任务第二步: 拼接查询SQL
+ */
+ private String getFindSql(HashMap> nodeMap) {
+ String findSql = "";
+ List uniteNodes = nodeMap.get("unite");
+ if (uniteNodes==null || uniteNodes.isEmpty()){
+ Node tableNode = nodeMap.get("table").get(0);
+ List dispList = getNodeDisp(tableNode);
+ findSql = NodeUtils.tableNode(dispList);
+ }else{
+ Node uniteNode = uniteNodes.get(0);
+ List dispList = getNodeDisp(uniteNode);
+ findSql = NodeUtils.nodeDispUnite(dispList);
+ }
+ return findSql;
+ }
+
+ /**
+ * 执行任务第三步: 获取查询条数
+ */
+ private int getFindCount(String findSql, HashMap> nodeMap) {
+ System.out.println(findSql);
+ String findCountSql = "";
+ List uniteNodes = nodeMap.get("unite");
+ findSql = findSql.replace(" "," ");
+ String[] s1 = findSql.split(" ");
+ if (uniteNodes!=null && !uniteNodes.isEmpty()){
+ s1[1] = "COUNT(" + s1[1].split(",")[0]+ ")";
+ findCountSql = StringUtils.join(s1," ");
+ }else {
+
+ String[] split = s1[s1.length-1].split("\\.");
+ findCountSql = "SELECT TABLE_ROWS " +
+ "FROM INFORMATION_SCHEMA.TABLES " +
+ "WHERE TABLE_SCHEMA = "+split[0]+" AND TABLE_NAME = '"+split[1]+"';";
+ findCountSql = findCountSql.replace("`","'");
+ }
+ System.out.println(findCountSql);
+ mySqlDataSource.setQuery(
+ MySqlQuery.builder()
+ .dataSourceId(14L)
+ .sql(findCountSql)
+ .build()
+ );
+ Result tableValue = mySqlDataSource.getCount();
+ System.out.println(tableValue);
+ return (int) tableValue.getData();
+ }
+
+ /**
+ * 获取新增SQL
+ */
+ private String getAddSql(HashMap> nodeMap, String findSql) {
+ mySqlDataSource.setQuery(
+ MySqlQuery.builder()
+ .dataSourceId(14L)
+ .sql(findSql)
+ .build()
+ );
+ DataStructure[][] rows = mySqlDataSource.getRows();
+ List dispList = getNodeDisp(nodeMap.get("exportation").get(0));
+ return NodeUtils.nodeDispExportation(dispList, rows);
+
+ }
+
+ // 查询节点配置信息
+ public List getNodeDisp(Node node){
+ List dispList = dispositionService
+ .selectNodeDispositionList(new NodeDisposition().buildNodeCode(node.getNodeCode()));
+ if (dispList.isEmpty()){
+ throw new TaskException("节点 "+node+" 配置为空");
+ }
+ return dispList;
+ }
+
+}
diff --git a/muyu-quest-server/src/main/java/com/muyu/quest/utils/DispUtils.java b/muyu-quest-server/src/main/java/com/muyu/quest/utils/DispUtils.java
new file mode 100644
index 0000000..2ad2115
--- /dev/null
+++ b/muyu-quest-server/src/main/java/com/muyu/quest/utils/DispUtils.java
@@ -0,0 +1,97 @@
+package com.muyu.quest.utils;
+
+/**
+ * @Author: 胡杨
+ * @Name: DispUtils
+ * @Description: 节点配置工具
+ * @CreatedDate: 2024/9/1 下午4:27
+ * @FilePath: com.muyu.quest.utils
+ */
+
+
+import com.muyu.common.core.utils.StringUtils;
+import com.muyu.quest.domain.NodeDisposition;
+import com.muyu.quest.domain.NodeType;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * @Author: 胡杨
+ * @Name: DispUtils
+ * @Description: 节点配置工具
+ * @CreatedDate: 2024/9/1 下午4:27
+ * @FilePath: com.muyu.quest.utils
+ */
+
+public class DispUtils {
+
+ /**
+ * 筛选节点配置信息
+ *
+ * @param nodeType 节点类型
+ * @param nodeTypeList 节点类型集合
+ * @return 配置key数组
+ */
+ public static String[] getDispKeys(String nodeType, List nodeTypeList) {
+ return nodeTypeList
+ .stream()
+ .filter(type -> StringUtils.equals(type.getNodeTypeCode(), nodeType))
+ .map(NodeType::getNodeTypeData)
+ .findFirst()
+ .orElse("-")
+ .split(",");
+ }
+
+ /**
+ * 整理节点配置信息
+ *
+ * @param nodeType 节点类型
+ * @param nodeTypeList 节点类型集合
+ * @param dispList 节点配置集合
+ * @return map集合
+ */
+ public static Map> getDispMap(
+ String nodeType,
+ List nodeTypeList,
+ List dispList) {
+ return getDispMap(getDispKeys(nodeType, nodeTypeList), dispList);
+ }
+
+ /**
+ * 整理节点配置信息
+ *
+ * @param dispKeys 配置key
+ * @param dispList 配置集合
+ * @return map集合
+ */
+ public static Map> getDispMap(String[] dispKeys, List dispList) {
+ HashMap> dispMap = new HashMap<>();
+ for (String nodeTypeName : dispKeys) {
+ dispMap.put(nodeTypeName, dispList.stream()
+ .filter(disp -> StringUtils.equals(disp.getDispKey(), nodeTypeName))
+ .toList());
+ }
+ return dispMap;
+ }
+
+ /**
+ * 整理节点配置信息
+ *
+ * @param dispList 节点配置集合
+ * @return map集合
+ */
+ public static Map> getDispMap(List dispList) {
+ HashMap> map = new HashMap<>();
+ dispList.forEach(disp -> {
+ List dispositions = map.get(disp.getDispKey());
+ if (dispositions == null || dispositions.isEmpty()) {
+ dispositions = new ArrayList<>();
+ }
+ dispositions.add(disp);
+ map.put(disp.getDispKey(), dispositions);
+ });
+ return map;
+ }
+
+}
diff --git a/muyu-quest-server/src/main/java/com/muyu/quest/utils/NodeUtils.java b/muyu-quest-server/src/main/java/com/muyu/quest/utils/NodeUtils.java
new file mode 100644
index 0000000..ee25812
--- /dev/null
+++ b/muyu-quest-server/src/main/java/com/muyu/quest/utils/NodeUtils.java
@@ -0,0 +1,343 @@
+package com.muyu.quest.utils;
+
+/**
+ * @Author: 胡杨
+ * @Name: NodeUtils
+ * @Description: 节点处理工具
+ * @CreatedDate: 2024/9/1 下午4:27
+ * @FilePath: com.muyu.quest.utils
+ */
+
+
+import com.muyu.common.core.domain.Result;
+import com.muyu.common.core.utils.StringUtils;
+import com.muyu.etl.domain.DataStructure;
+import com.muyu.etl.rule.remote.RemoteRuleVersion;
+import com.muyu.quest.domain.Node;
+import com.muyu.quest.domain.NodeDisposition;
+import com.muyu.quest.domain.NodeType;
+import com.muyu.quest.exception.TaskException;
+import com.muyu.quest.model.DataModel;
+import org.springframework.stereotype.Component;
+
+
+import javax.annotation.Resource;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * @Author: 胡杨
+ * @Name: NodeUtils
+ * @Description: 节点处理工具
+ * @CreatedDate: 2024/9/1 下午4:27
+ * @FilePath: com.muyu.quest.utils
+ */
+@Component
+public class NodeUtils {
+ private static HashMap sqlMap = new HashMap<>();
+ /**
+ * 节点初始化
+ * 将所有节点按节点类型分类
+ * 并判断任务流程是否完整
+ * @param nodes 节点列表
+ * @return 节点Map
+ */
+ public static HashMap> nodeInit(List nodes){
+ if (nodes == null || nodes.isEmpty()){
+ throw new TaskException("节点列表为空");
+ }
+ HashMap> nodeMap = new HashMap<>();
+ // 整理所有节点
+ nodes.forEach(node -> {
+ List nodeList = nodeMap.get(node.getNodeType());
+ if (nodeList == null || nodeList.isEmpty()) {
+ nodeList = new ArrayList<>();
+ }
+ nodeList.add(node);
+ nodeMap.put(node.getNodeType(), nodeList);
+ });
+ return nodeMap;
+ }
+
+ /**
+ * 获取上级节点
+ * @param node 当前节点
+ * @param nodes 所有节点
+ * @return 下级节点列表
+ */
+ public static Node getPreNode(Node node, List nodes){
+ if (nodes == null || nodes.isEmpty()){
+ throw new TaskException("节点列表为空");
+ }else if (node == null){
+ throw new TaskException("当前节点为空");
+ }
+ return nodes.stream()
+ .filter(nodeIndex -> StringUtils.equals(node.getNodePreCode() ,nodeIndex.getNodeCode()))
+ .toList().get(0);
+ }
+
+ /**
+ * 获取下级节点
+ * @param node 当前节点
+ * @param nodes 所有节点
+ * @return 下级节点列表
+ */
+ public static List getNextNode(Node node, List nodes) {
+ if (nodes == null || nodes.isEmpty()) {
+ throw new TaskException("节点列表为空");
+ } else if (node == null) {
+ throw new TaskException("当前节点为空");
+ }
+ return nodes.stream()
+ .filter(nodeIndex ->
+ StringUtils.equals(node.getNodeCode(), nodeIndex.getNodePreCode()) ||
+ StringUtils.equals(node.getNodeNextCode(), nodeIndex.getNodeCode()))
+ .toList();
+ }
+
+ /**
+ * 任务节点组成检查
+ *
+ * @param nodeListAll 所有节点
+ * @param nodeTypeList 所有节点类型
+ */
+ public static void nodeCheckMakeUp(List nodeListAll, List nodeTypeList) {
+ HashMap> nodeMapAll = nodeInit(nodeListAll);
+ nodeTypeList.forEach(nodeType -> {
+ // 根据节点类型查询对应类型节点
+ List nodes = nodeMapAll.get(nodeType.getNodeTypeCode());
+
+ Integer maxNum = nodeType.getNodeMaxNum();
+ Integer minNum = nodeType.getNodeMinNum();
+ if (nodes != null && !nodes.isEmpty()){
+ int num = nodes.size();
+ if (num < minNum){
+ throw new TaskException("节点 " + nodeType.getNodeTypeName() + " 数量不足,至少需要 " + minNum + " 个");
+ }else if (maxNum != -1 && num > maxNum){
+ throw new TaskException("节点 " + nodeType.getNodeTypeName() + " 数量超出范围,最多允许 " + maxNum + " 个");
+ }
+ }else if (minNum > 0){
+ throw new TaskException("节点 " + nodeType.getNodeTypeName() + " 数量不足,至少需要 " + minNum + " 个");
+ }
+
+ });
+ }
+
+
+ /**
+ * 校验任务流程是否符合节点规范
+ *
+ * @param nodeListAll 所有节点
+ * @param nodeTypeList 节点类型规范
+ */
+ public static void nodeCheckNorm(List nodeListAll, List nodeTypeList) {
+ HashMap> nodeMapAll = nodeInit(nodeListAll);
+ nodeTypeList.forEach(nodeType -> {
+ // 根据节点类型查询对应类型节点
+ List nodes = nodeMapAll.get(nodeType.getNodeTypeCode());
+ if (nodes != null) {
+ nodes.forEach(node -> {
+ if (StringUtils.equals(node.getNodeType(), "exportation")){
+ Node nextNode = getPreNode(node, nodeListAll);
+ if (!StringUtils.equals(nextNode.getNodeType(), "table") &&
+ !StringUtils.equals(nextNode.getNodeType(), "unite")) {
+ throw new TaskException("数据输出节点必须紧跟在数据输入/操作节点之后");
+ }
+ }
+ // 获取该节点的下级节点
+ List nextNodeList = getNextNode(node, nodeListAll);
+ if (nextNodeList != null && !nextNodeList.isEmpty()){
+ for (Node nextNode : nextNodeList) {
+ // 判断其是否是允许的下级节点
+ if (!StringUtils.matches(nextNode.getNodeType(), nodeType.getNextNodeTypeCodeList())){
+ throw new TaskException("节点 " + node + " 连接不符合规范,该节点可有下级节点为: [" + nodeType.getNextNodeTypeCodeList() + "]");
+ }
+ }
+ }else if (!StringUtils.equals(node.getNodeType(), "end")){
+ throw new TaskException("节点 "+node+" 无下级节点.");
+ }
+ });
+ }
+ });
+
+ }
+
+ /**
+ * 查询节点的下一级节点
+ */
+ public static Node nodeCheckNorm(Node node, List nodes) {
+ List nextNode = NodeUtils.getNextNode(node, nodes);
+ Set nextNodeTypes = nextNode.stream().map(Node::getNodeType).collect(Collectors.toSet());
+ if (nextNode.isEmpty()){
+ throw new TaskException("任务执行失败,节点 "+node+" 无后续节点");
+ }else if (nextNodeTypes.size()>1){
+ throw new TaskException("同级节点 "+nextNode+" 类型不同");
+ }else if (nextNode.stream().map(Node::getNodeNextCode).collect(Collectors.toSet()).size()>1){
+ throw new TaskException("同级节点 "+nextNode+" 下级节点不同");
+ }
+ return nextNode.get(0);
+ }
+
+
+ /**
+ * 联合查询节点处理
+ * @param dispList 节点全部配置信息
+ * @return 查询sql
+ */
+ public static String nodeDispUnite(List dispList) {
+ return nodeDispUnite(DispUtils.getDispMap(dispList));
+ }
+
+
+ /**
+ * 联合查询节点处理
+ * @param dispMap 节点整理后的Map配置信息
+ * @return 查询sql
+ */
+ public static String nodeDispUnite(Map> dispMap) {
+ List dbList = dispMap.get("db");
+ List tableList = dispMap.get("table");
+ List fieldList = dispMap.get("fields");
+ NodeDisposition join = dispMap.get("join").get(0);
+ NodeDisposition joinDataForm = dispMap.get("joinDataForm").get(0);
+ NodeDisposition joinDataTo = dispMap.get("joinDataTo").get(0);
+ // 查询表
+ Object[] array = tableList.stream().map(NodeDisposition::getDispDesc).toArray();
+ StringBuilder table = new StringBuilder(array[0].toString())
+ .append(" ")
+ .append(join.getDispValue())
+ .append(" ")
+ .append(array[1].toString())
+ .append(" ON ")
+ .append(joinDataForm.getDispDesc())
+ .append(".")
+ .append(joinDataForm.getDispValue())
+ .append(" = ")
+ .append(joinDataTo.getDispDesc())
+ .append(".")
+ .append(joinDataTo.getDispValue());
+
+ // 查询列
+ String field = StringUtils.join(fieldList
+ .stream()
+ .map(fields -> fields.getDispDesc()+"."+fields.getDispValue())
+ .toArray(),
+ ",");
+ Set