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 dbNameSet = dbList.stream().map(NodeDisposition::getDispValue).collect(Collectors.toSet()); + String dbTable = table.toString(); + for (Object o : dbNameSet) { + dbTable = StringUtils.replace(dbTable, o.toString()+".", "`" + o.toString() + "`."); + field = StringUtils.replace(field, o.toString()+".", "`" + o.toString() + "`."); + } + return new StringBuilder("SELECT ").append(field).append(" FORM ").append(dbTable).toString(); + } + + + /** + * 表结构节点处理 + * @param dispList 节点全部配置信息 + * @return 查询sql + */ + public static String tableNode(List dispList) { + return tableNode(DispUtils.getDispMap(dispList)); + } + + + /** + * 表结构节点处理 + * @param dispMap 节点整理后的Map配置信息 + * @return 查询sql + */ + private static String tableNode(Map> dispMap) { + NodeDisposition db = dispMap.get("db").get(0); + NodeDisposition table = dispMap.get("table").get(0); + List fieldList = dispMap.get("fields"); + StringBuilder findSql = new StringBuilder("SELECT "); + StringBuilder stringBuilder = new StringBuilder("`"); + stringBuilder.append(db.getDispValue()) + .append("`.") + .append(table.getDispValue()); + findSql.append(StringUtils.join(fieldList.stream(). + map(field -> stringBuilder.toString() + "." + field.getDispValue()). + toArray(), + ",")) + .append(" FROM ") + .append(stringBuilder); + return findSql.toString(); + } + + /** + * 查询sql拼接 + * @param table 表名 + * @param fields 字段 + * @return sql语句 + */ + private static String findSqlSplice(String table, String fields) { + return "SELECT " + fields + " FROM " + table; + } + + /** + * 输出节点 节点处理 + * + * @param dispList 节点全部配置信息 + * @param data 查询到的数据 + * @return 新增sql + */ + public static String nodeDispExportation(List dispList, DataStructure[][] data) { + if (data == null){ + throw new TaskException("查询数据为空"); + } + return nodeDispExportation(DispUtils.getDispMap(dispList), data); + } + + + /** + * 输出节点 节点处理 + * + * @param dispMap 节点整理后的Map配置信息 + * @param data 查询到的数据 + * @return 新增sql + */ + private static String nodeDispExportation(Map> dispMap, DataStructure[][] data) { + // 拼接新增表 + NodeDisposition db = dispMap.get("toDb").get(0); + StringBuilder insSql = new StringBuilder("INSERT INTO "); + insSql.append("`").append(db.getDispDesc()).append("`.").append(db.getDispValue()); + // 根据表结构拼接新增字段 + List fieldList = dispMap.get("toFields"); + // 拼接新增语句的表与字段 + String join = StringUtils.join(fieldList.stream().map(NodeDisposition::getDispValue).toArray(), ","); + insSql.append("( ").append(join).append(" ) VALUES "); + // 整理需新增数据 + List> dataList1 = new ArrayList<>(); + for (DataStructure[] datum : data) { + HashMap dataMap = new HashMap<>(); + for (DataStructure dataModel : datum) { + // 检查 getValue 是否为空 + String value = dataModel.getValue() != null ? dataModel.getValue().toString() : null; + // 规则执行 非法字符转换 单引号'和` => ‘ + if (value!=null){ + value = StringUtils.replace(value, "'", "‘"); + value = StringUtils.replace(value, "`", "‘"); + value = StringUtils.replace(value, "\\", "‘"); + value = StringUtils.replace(value, " ", "‘"); + } + dataMap.put(dataModel.getKey(), value); + } + + dataList1.add(dataMap); + } + // 拼接新增语句的值 + dataList1.forEach(map -> insSql.append("( "). + append(StringUtils.join( + fieldList. + stream(). + map(field -> map.get(field.getDispDesc())). + map(field -> "'" + field + "'"). + toArray(), + ",")).append(" ),")); + return insSql.deleteCharAt(insSql.length() - 1).toString(); + } + + +} diff --git a/muyu-quest-server/src/main/resources/banner.txt b/muyu-quest-server/src/main/resources/banner.txt new file mode 100644 index 0000000..0dd5eee --- /dev/null +++ b/muyu-quest-server/src/main/resources/banner.txt @@ -0,0 +1,2 @@ +Spring Boot Version: ${spring-boot.version} +Spring Application Name: ${spring.application.name} diff --git a/muyu-quest-server/src/main/resources/bootstrap.yml b/muyu-quest-server/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..540446d --- /dev/null +++ b/muyu-quest-server/src/main/resources/bootstrap.yml @@ -0,0 +1,55 @@ +# Tomcat +server: + port: 19501 + +# nacos线上地址 +nacos: + addr: 10.0.1.97:8848 + user-name: nacos + password: nacos + namespace: wu_zu_cloud + + +# Spring +spring: + main: + allow-bean-definition-overriding: true + application: + # 应用名称 + name: muyu-quest + profiles: + # 环境配置 + active: dev + cloud: + nacos: + discovery: + # 服务注册地址 + server-addr: ${nacos.addr} + # nacos用户名 + username: ${nacos.user-name} + # nacos密码 + password: ${nacos.password} + # 命名空间 + namespace: ${nacos.namespace} + config: + # 服务注册地址 + server-addr: ${nacos.addr} + # nacos用户名 + username: ${nacos.user-name} + # nacos密码 + password: ${nacos.password} + # 命名空间 + namespace: ${nacos.namespace} + # 配置文件格式 + file-extension: yml + # 共享配置 + shared-configs: + # 系统共享配置 + - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} + # 系统环境Config共享配置 + - application-config-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} + # xxl-job 配置文件 + - application-xxl-config-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} + # rabbit 配置文件 +# - application-rabbit-config-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} + diff --git a/muyu-quest-server/src/main/resources/logback/dev.xml b/muyu-quest-server/src/main/resources/logback/dev.xml new file mode 100644 index 0000000..e979886 --- /dev/null +++ b/muyu-quest-server/src/main/resources/logback/dev.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + + + + + + + + + + + diff --git a/muyu-quest-server/src/main/resources/logback/prod.xml b/muyu-quest-server/src/main/resources/logback/prod.xml new file mode 100644 index 0000000..499b043 --- /dev/null +++ b/muyu-quest-server/src/main/resources/logback/prod.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + ${log.sky.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + + + ERROR + + ACCEPT + + DENY + + + + + + + + ${log.sky.pattern} + + + + + + + + + + + + + + + + + + + + diff --git a/muyu-quest-server/src/main/resources/logback/test.xml b/muyu-quest-server/src/main/resources/logback/test.xml new file mode 100644 index 0000000..499b043 --- /dev/null +++ b/muyu-quest-server/src/main/resources/logback/test.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + ${log.sky.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + + + ERROR + + ACCEPT + + DENY + + + + + + + + ${log.sky.pattern} + + + + + + + + + + + + + + + + + + + + diff --git a/muyu-quest-server/src/main/resources/mapper/TaskMapper.xml b/muyu-quest-server/src/main/resources/mapper/TaskMapper.xml new file mode 100644 index 0000000..38dcb8e --- /dev/null +++ b/muyu-quest-server/src/main/resources/mapper/TaskMapper.xml @@ -0,0 +1,14 @@ + + + + + + diff --git a/muyu-quest-server/src/main/resources/mapper/quest/TaskExportMapper.xml b/muyu-quest-server/src/main/resources/mapper/quest/TaskExportMapper.xml new file mode 100644 index 0000000..aecc326 --- /dev/null +++ b/muyu-quest-server/src/main/resources/mapper/quest/TaskExportMapper.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + select id, taskCode, sql, start, error from task_export + + + + + + + + insert into task_export + + id, + taskCode, + sql, + start, + error, + + + #{id}, + #{taskCode}, + #{sql}, + #{start}, + #{error}, + + + + + update task_export + + taskCode = #{taskCode}, + sql = #{sql}, + start = #{start}, + error = #{error}, + + where id = #{id} + + + + delete from task_export where id = #{id} + + + + delete from task_export where id in + + #{id} + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..aabe19b --- /dev/null +++ b/pom.xml @@ -0,0 +1,37 @@ + + + 4.0.0 + + + com.muyu + cloud-modules + 3.6.5 + + + muyu-quest + 3.6.5 + pom + + + muyu-quest-remote + muyu-quest-server + muyu-quest-client + muyu-quest-common + + + + 17 + 17 + UTF-8 + + + + + yun-releases + yun-releases + http://10.0.1.3:8081/repository/maven-releases/ + + +