diff --git a/snail-job-client/snail-job-client-common/pom.xml b/snail-job-client/snail-job-client-common/pom.xml
index 76d4a030..92ac9300 100644
--- a/snail-job-client/snail-job-client-common/pom.xml
+++ b/snail-job-client/snail-job-client-common/pom.xml
@@ -18,6 +18,7 @@
17
17
UTF-8
+ 8.0.1.Final
@@ -85,6 +86,12 @@
log4j
true
+
+
+ org.hibernate
+ hibernate-validator
+ ${hibernate.verion}
+
diff --git a/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/dto/JobResponseVO.java b/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/dto/JobResponseVO.java
new file mode 100644
index 00000000..60fb45d5
--- /dev/null
+++ b/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/dto/JobResponseVO.java
@@ -0,0 +1,132 @@
+package com.aizuda.snailjob.client.job.core.dto;
+
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * @author opensnail
+ * @date 2023-10-11 22:30:00
+ * @since 2.4.0
+ */
+@Data
+public class JobResponseVO {
+
+ private Long id;
+
+ /**
+ * 组名称
+ */
+ private String groupName;
+
+ /**
+ * 名称
+ */
+ private String jobName;
+
+ /**
+ * 执行方法参数
+ */
+ private String argsStr;
+
+ /**
+ * 参数类型 text/json
+ */
+ private String argsType;
+
+ /**
+ * 扩展字段
+ */
+ private String extAttrs;
+
+ /**
+ * 下次触发时间
+ */
+ private LocalDateTime nextTriggerAt;
+
+ /**
+ * 重试状态 0、关闭、1、开启
+ */
+ private Integer jobStatus;
+
+ /**
+ * 执行器路由策略
+ */
+ private Integer routeKey;
+
+ /**
+ * 执行器类型 1、Java
+ */
+ private Integer executorType;
+
+ /**
+ * 执行器名称
+ */
+ private String executorInfo;
+
+ /**
+ * 触发类型 1.CRON 表达式 2. 固定时间
+ */
+ private Integer triggerType;
+
+ /**
+ * 间隔时长
+ */
+ private String triggerInterval;
+
+ /**
+ * 阻塞策略 1、丢弃 2、覆盖 3、并行
+ */
+ private Integer blockStrategy;
+
+ /**
+ * 任务执行超时时间,单位秒
+ */
+ private Integer executorTimeout;
+
+ /**
+ * 最大重试次数
+ */
+ private Integer maxRetryTimes;
+
+ /**
+ * 重试间隔(s)
+ */
+ private Integer retryInterval;
+
+ /**
+ * 任务类型
+ */
+ private Integer taskType;
+
+ /**
+ * 并行数
+ */
+ private Integer parallelNum;
+
+ /**
+ * bucket
+ */
+ private Integer bucketIndex;
+
+ /**
+ * 描述
+ */
+ private String description;
+
+ /**
+ * 创建时间
+ */
+ private LocalDateTime createDt;
+
+ /**
+ * 修改时间
+ */
+ private LocalDateTime updateDt;
+
+ /**
+ * 逻辑删除 1、删除
+ */
+ private Integer deleted;
+
+}
diff --git a/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/dto/RequestAddJobDTO.java b/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/dto/RequestAddJobDTO.java
new file mode 100644
index 00000000..9406af30
--- /dev/null
+++ b/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/dto/RequestAddJobDTO.java
@@ -0,0 +1,107 @@
+package com.aizuda.snailjob.client.job.core.dto;
+
+import com.aizuda.snailjob.common.core.enums.ExecutorTypeEnum;
+import com.aizuda.snailjob.common.core.enums.JobTaskTypeEnum;
+import com.aizuda.snailjob.common.core.enums.StatusEnum;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Data
+public class RequestAddJobDTO{
+ /**
+ * 名称
+ */
+ @NotBlank(message = "jobName 不能为空")
+ private String jobName;
+
+ /**
+ * 重试状态 0、关闭、1、开启
+ * {@link StatusEnum}
+ */
+ @NotNull(message = "jobStatus 不能为空")
+ private Integer jobStatus;
+
+ /**
+ * 执行方法参数
+ */
+ private String argsStr;
+
+ /**
+ * 参数类型 text/json
+ */
+ private Integer argsType;
+
+ /**
+ * 执行器路由策略
+ */
+ @NotNull(message = "routeKey 不能为空")
+ private Integer routeKey;
+
+ /**
+ * 执行器类型
+ * {@link ExecutorTypeEnum}
+ */
+ @NotNull(message = "executorType 不能为空")
+ private Integer executorType;
+
+ /**
+ * 执行器名称
+ */
+ @NotBlank(message = "executorInfo 不能为空")
+ private String executorInfo;
+
+ /**
+ * 触发类型 2. 固定时间 3.CRON 表达式 99.工作流
+ */
+ @NotNull(message = "triggerType 不能为空")
+ private Integer triggerType;
+
+ /**
+ * 间隔时长
+ */
+ @NotNull(message = "triggerInterval 不能为空")
+ private String triggerInterval;
+
+ /**
+ * 阻塞策略 1、丢弃 2、覆盖 3、并行
+ */
+ @NotNull(message = "blockStrategy 不能为空")
+ private Integer blockStrategy;
+
+ /**
+ * 任务执行超时时间,单位秒
+ */
+ @NotNull(message = "executorTimeout 不能为空")
+ private Integer executorTimeout;
+
+ /**
+ * 最大重试次数
+ */
+ @NotNull(message = "maxRetryTimes 不能为空")
+ private Integer maxRetryTimes;
+
+ /**
+ * 重试间隔(s)
+ */
+ @NotNull(message = "retryInterval 不能为空")
+ private Integer retryInterval;
+
+ /**
+ * 任务类型
+ * {@link JobTaskTypeEnum}
+ */
+ @NotNull(message = "taskType 不能为空")
+ private Integer taskType;
+
+ /**
+ * 并行数
+ */
+ @NotNull(message = "parallelNum 不能为空")
+ private Integer parallelNum;
+
+ /**
+ * 描述
+ */
+ private String description;
+}
diff --git a/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/dto/RequestUpdateJobDTO.java b/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/dto/RequestUpdateJobDTO.java
new file mode 100644
index 00000000..c6babc06
--- /dev/null
+++ b/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/dto/RequestUpdateJobDTO.java
@@ -0,0 +1,102 @@
+package com.aizuda.snailjob.client.job.core.dto;
+
+import com.aizuda.snailjob.common.core.enums.ExecutorTypeEnum;
+import com.aizuda.snailjob.common.core.enums.JobTaskTypeEnum;
+import com.aizuda.snailjob.common.core.enums.StatusEnum;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+/**
+ * @author opensnail
+ * @date 2023-10-11 22:37:55
+ * @since 2.4.0
+ */
+@Data
+public class RequestUpdateJobDTO {
+ @NotNull(message = "id 不能为空")
+ private Long id;
+
+ /**
+ * 名称
+ */
+ private String jobName;
+
+ /**
+ * 重试状态 0、关闭、1、开启
+ * {@link StatusEnum}
+ */
+ private Integer jobStatus;
+
+ /**
+ * 执行方法参数
+ */
+ private String argsStr;
+
+ /**
+ * 参数类型 text/json
+ */
+ private Integer argsType;
+
+ /**
+ * 执行器路由策略
+ */
+ private Integer routeKey;
+
+ /**
+ * 执行器类型
+ * {@link ExecutorTypeEnum}
+ */
+ private Integer executorType;
+
+ /**
+ * 执行器名称
+ */
+ private String executorInfo;
+
+ /**
+ * 触发类型 2. 固定时间 3.CRON 表达式 99.工作流
+ */
+ private Integer triggerType;
+
+ /**
+ * 间隔时长
+ */
+ private String triggerInterval;
+
+ /**
+ * 阻塞策略 1、丢弃 2、覆盖 3、并行
+ */
+ private Integer blockStrategy;
+
+ /**
+ * 任务执行超时时间,单位秒
+ */
+ private Integer executorTimeout;
+
+ /**
+ * 最大重试次数
+ */
+ private Integer maxRetryTimes;
+
+ /**
+ * 重试间隔(s)
+ */
+ private Integer retryInterval;
+
+ /**
+ * 任务类型
+ * {@link JobTaskTypeEnum}
+ */
+ private Integer taskType;
+
+ /**
+ * 并行数
+ */
+ private Integer parallelNum;
+
+ /**
+ * 描述
+ */
+ private String description;
+
+}
diff --git a/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/dto/RequestUpdateStatusDTO.java b/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/dto/RequestUpdateStatusDTO.java
new file mode 100644
index 00000000..eeda6f46
--- /dev/null
+++ b/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/dto/RequestUpdateStatusDTO.java
@@ -0,0 +1,20 @@
+package com.aizuda.snailjob.client.job.core.dto;
+
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+/**
+ * @author opensnail
+ * @date 2023-10-15 16:06:20
+ * @since 2.4.0
+ */
+@Data
+public class RequestUpdateStatusDTO {
+
+ @NotNull(message = "id 不能为空")
+ private Long id;
+
+ @NotNull(message = "jobStatus 不能为空")
+ private Integer jobStatus;
+
+}
diff --git a/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/enums/AllocationAlgorithmEnum.java b/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/enums/AllocationAlgorithmEnum.java
new file mode 100644
index 00000000..5806b663
--- /dev/null
+++ b/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/enums/AllocationAlgorithmEnum.java
@@ -0,0 +1,24 @@
+package com.aizuda.snailjob.client.job.core.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@AllArgsConstructor
+@Getter
+public enum AllocationAlgorithmEnum {
+ // Hash
+ CONSISTENT_HASH(1),
+ // 随机
+ RANDOM(2),
+ // LRU
+ LRU(3),
+ // 轮询
+ ROUND(4),
+ // 匹配第一个
+ FIRST(5),
+ // 匹配最后一个
+ LAST(6);
+
+ private final int type;
+
+}
\ No newline at end of file
diff --git a/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/enums/TriggerTypeEnum.java b/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/enums/TriggerTypeEnum.java
new file mode 100644
index 00000000..1e4b7438
--- /dev/null
+++ b/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/enums/TriggerTypeEnum.java
@@ -0,0 +1,14 @@
+package com.aizuda.snailjob.client.job.core.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@AllArgsConstructor
+@Getter
+public enum TriggerTypeEnum {
+ SCHEDULED_TIME(2),
+ CRON(3),
+ WORK_FLOW(99);
+
+ private final int type;
+}
diff --git a/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/handler/AbstractRequestHandler.java b/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/handler/AbstractRequestHandler.java
new file mode 100644
index 00000000..9c87e636
--- /dev/null
+++ b/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/handler/AbstractRequestHandler.java
@@ -0,0 +1,28 @@
+package com.aizuda.snailjob.client.job.core.handler;
+
+import com.aizuda.snailjob.client.common.exception.SnailJobClientException;
+
+/**
+ * @author opensnail
+ * @date 2024-09-29 20:40:10
+ * @since sj_1.1.0
+ */
+public abstract class AbstractRequestHandler implements RequestHandler {
+
+ /**
+ * 具体调用
+ * @return
+ */
+ @Override
+ public R execute() {
+ if (checkRequest()) {
+ return doExecute();
+ } else {
+ throw new SnailJobClientException("snail job openapi check error");
+ }
+ }
+
+ protected abstract R doExecute();
+
+ protected abstract boolean checkRequest();
+}
diff --git a/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/handler/RequestAddHandler.java b/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/handler/RequestAddHandler.java
new file mode 100644
index 00000000..e480d214
--- /dev/null
+++ b/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/handler/RequestAddHandler.java
@@ -0,0 +1,242 @@
+package com.aizuda.snailjob.client.job.core.handler;
+
+import cn.hutool.core.util.StrUtil;
+import com.aizuda.snailjob.client.common.exception.SnailJobClientException;
+import com.aizuda.snailjob.client.job.core.dto.RequestAddJobDTO;
+import com.aizuda.snailjob.client.job.core.enums.AllocationAlgorithmEnum;
+import com.aizuda.snailjob.client.job.core.enums.TriggerTypeEnum;
+import com.aizuda.snailjob.client.job.core.util.ValidatorUtils;
+import com.aizuda.snailjob.common.core.enums.BlockStrategyEnum;
+import com.aizuda.snailjob.common.core.enums.ExecutorTypeEnum;
+import com.aizuda.snailjob.common.core.enums.JobTaskTypeEnum;
+import com.aizuda.snailjob.common.core.enums.StatusEnum;
+import com.aizuda.snailjob.common.core.util.JsonUtil;
+import com.aizuda.snailjob.common.log.SnailJobLog;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class RequestAddHandler extends AbstractRequestHandler {
+ private RequestAddJobDTO requestAddJobDTO;
+
+ public RequestAddHandler(JobTaskTypeEnum taskType, Integer shardNum) {
+ this.requestAddJobDTO = new RequestAddJobDTO();
+ // 默认创建就开启
+ requestAddJobDTO.setJobStatus(StatusEnum.YES.getStatus());
+ // 设置任务类型
+ requestAddJobDTO.setTaskType(taskType.getType());
+ // 默认java
+ requestAddJobDTO.setExecutorType(ExecutorTypeEnum.JAVA.getType());
+ // 设置分片
+ if (shardNum != null){
+ Map map = new HashMap<>(1);
+ map.put("shardNum", shardNum);
+ requestAddJobDTO.setArgsStr(JsonUtil.toJsonString(map));
+ }
+ }
+
+
+ @Override
+ protected Long doExecute() {
+ String data = JsonUtil.toJsonString(client.addJob(requestAddJobDTO).getData());
+ return Long.valueOf(data);
+ }
+
+ @Override
+ protected boolean checkRequest() {
+ boolean validated = ValidatorUtils.validateEntity(requestAddJobDTO);
+ // 如果校验正确,则正对进行相关筛选
+ if (validated) {
+ if (requestAddJobDTO.getTaskType() == JobTaskTypeEnum.CLUSTER.getType()){
+ // 集群模式只允许并发为 1
+ setParallelNum(1);
+ }
+ if (requestAddJobDTO.getTriggerType() == TriggerTypeEnum.WORK_FLOW.getType()){
+ // 工作流没有调度时间
+ setTriggerInterval("*");
+ }
+ }
+ return validated;
+ }
+
+ /**
+ * 设置任务名
+ * @param jobName 任务名
+ * @return
+ */
+ public RequestAddHandler setJobName(String jobName) {
+ requestAddJobDTO.setJobName(jobName);
+ return this;
+ }
+
+ /**
+ * 设置参数
+ * @param argsStr
+ * @return
+ */
+ private RequestAddHandler setArgsStr(Map argsStr) {
+ Map args = new HashMap<>();
+ if (StrUtil.isNotBlank(requestAddJobDTO.getArgsStr())){
+ args = JsonUtil.parseHashMap(requestAddJobDTO.getArgsStr());
+ }
+ args.putAll(argsStr);
+ requestAddJobDTO.setArgsStr(JsonUtil.toJsonString(args));
+ requestAddJobDTO.setArgsType(2);
+ return this;
+ }
+
+ /**
+ * 添加参数,可支持多次添加
+ * 静态分片不可使用该方法
+ * @param argsKey 参数名
+ * @param argsValue 参数值
+ * @return
+ */
+ public RequestAddHandler addArgsStr(String argsKey, Object argsValue) {
+ if (requestAddJobDTO.getTaskType().equals(JobTaskTypeEnum.SHARDING.getType())){
+ SnailJobLog.LOCAL.warn("静态分片任务,不可使用该方法添加相关任务参数,请使用addShardingArgs");
+ return this;
+ }
+ Map map = new HashMap<>();
+ if (StrUtil.isNotBlank(requestAddJobDTO.getArgsStr())){
+ map = JsonUtil.parseHashMap(requestAddJobDTO.getArgsStr());
+ }
+ map.put(argsKey, argsValue);
+ requestAddJobDTO.setArgsStr(JsonUtil.toJsonString(map));
+ requestAddJobDTO.setArgsType(2);
+ return this;
+ }
+
+ /**
+ * 添加静态分片相关参数
+ * @param shardingValue
+ * @return
+ */
+ public RequestAddHandler addShardingArgs(String[] shardingValue){
+ if (!requestAddJobDTO.getTaskType().equals(JobTaskTypeEnum.SHARDING.getType())){
+ SnailJobLog.LOCAL.warn("非静态分片任务,不可使用该方法添加相关任务参数,请使用addArgsStr");
+ return this;
+ }
+ requestAddJobDTO.setArgsStr(JsonUtil.toJsonString(shardingValue));
+ requestAddJobDTO.setArgsType(1);
+ return this;
+ }
+
+ /**
+ * 设置路由
+ * @param algorithmEnum 路由算法
+ * @return
+ */
+ public RequestAddHandler setRouteKey(AllocationAlgorithmEnum algorithmEnum) {
+ // 非集群模式 路由策略只能为轮询
+ if (requestAddJobDTO.getTaskType() != JobTaskTypeEnum.CLUSTER.getType()){
+ setRouteKey(AllocationAlgorithmEnum.ROUND);
+ SnailJobLog.LOCAL.warn("非集群模式 路由策略只能为轮询");
+ return this;
+ }
+ requestAddJobDTO.setRouteKey(algorithmEnum.getType());
+ return this;
+ }
+
+ /**
+ * 设置执行器信息
+ * @param executorInfo
+ * @return
+ */
+ public RequestAddHandler setExecutorInfo(String executorInfo) {
+ requestAddJobDTO.setExecutorInfo(executorInfo);
+ return this;
+ }
+
+ /**
+ * 设置调度类型
+ * @param triggerType
+ * @return
+ */
+ public RequestAddHandler setTriggerType(TriggerTypeEnum triggerType) {
+ requestAddJobDTO.setTriggerType(triggerType.getType());
+ if (requestAddJobDTO.getTriggerType() == TriggerTypeEnum.WORK_FLOW.getType()){
+ // 工作流没有调度时间
+ setTriggerInterval("*");
+ }
+ return this;
+ }
+
+ /**
+ * 设置触发间隔;
+ * 单位:秒
+ * 工作流无需配置
+ * @param triggerInterval
+ * @return
+ */
+ public RequestAddHandler setTriggerInterval(String triggerInterval) {
+ requestAddJobDTO.setTriggerInterval(triggerInterval);
+ return this;
+ }
+
+ /**
+ * 设置阻塞策略
+ * @param blockStrategy
+ * @return
+ */
+ public RequestAddHandler setBlockStrategy(BlockStrategyEnum blockStrategy) {
+ // 非集群模式 路由策略只能为轮询
+ if (requestAddJobDTO.getTaskType() == JobTaskTypeEnum.CLUSTER.getType()
+ && blockStrategy.getBlockStrategy() == BlockStrategyEnum.CONCURRENCY.getBlockStrategy()){
+ throw new SnailJobClientException("集群模式不能使用并行阻塞策略");
+ }
+ requestAddJobDTO.setBlockStrategy(blockStrategy.getBlockStrategy());
+ return this;
+ }
+
+ /**
+ * 设置执行器超时时间
+ * @param executorTimeout
+ * @return
+ */
+ public RequestAddHandler setExecutorTimeout(Integer executorTimeout) {
+ requestAddJobDTO.setExecutorTimeout(executorTimeout);
+ return this;
+ }
+
+ /**
+ * 设置任务最大重试次数
+ * @param maxRetryTimes
+ * @return
+ */
+ public RequestAddHandler setMaxRetryTimes(Integer maxRetryTimes) {
+ requestAddJobDTO.setMaxRetryTimes(maxRetryTimes);
+ return this;
+ }
+
+ /**
+ * 设置重试间隔
+ * @param retryInterval
+ * @return
+ */
+ public RequestAddHandler setRetryInterval(Integer retryInterval) {
+ requestAddJobDTO.setRetryInterval(retryInterval);
+ return this;
+ }
+
+ /**
+ * 设置并发数量
+ * @param parallelNum
+ * @return
+ */
+ public RequestAddHandler setParallelNum(Integer parallelNum) {
+ requestAddJobDTO.setParallelNum(parallelNum);
+ return this;
+ }
+
+ /**
+ * 设置定时任务描述
+ * @param description
+ * @return
+ */
+ public RequestAddHandler setDescription(String description) {
+ requestAddJobDTO.setDescription(description);
+ return this;
+ }
+
+}
diff --git a/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/handler/RequestHandler.java b/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/handler/RequestHandler.java
new file mode 100644
index 00000000..62df2d24
--- /dev/null
+++ b/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/handler/RequestHandler.java
@@ -0,0 +1,16 @@
+package com.aizuda.snailjob.client.job.core.handler;
+
+import com.aizuda.snailjob.client.common.rpc.client.RequestBuilder;
+import com.aizuda.snailjob.client.job.core.openapi.OpenApiClient;
+import com.aizuda.snailjob.common.core.model.NettyResult;
+
+public interface RequestHandler {
+
+ OpenApiClient client = RequestBuilder.newBuilder()
+ .client(OpenApiClient.class)
+ .async(false)
+ .build();
+
+ R execute();
+
+}
\ No newline at end of file
diff --git a/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/handler/RequestQueryHandler.java b/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/handler/RequestQueryHandler.java
new file mode 100644
index 00000000..84a7fb17
--- /dev/null
+++ b/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/handler/RequestQueryHandler.java
@@ -0,0 +1,30 @@
+package com.aizuda.snailjob.client.job.core.handler;
+
+
+import cn.hutool.core.lang.Assert;
+import com.aizuda.snailjob.client.common.exception.SnailJobClientException;
+import com.aizuda.snailjob.client.job.core.dto.JobResponseVO;
+import com.aizuda.snailjob.common.core.util.JsonUtil;
+
+import java.util.Objects;
+
+public class RequestQueryHandler extends AbstractRequestHandler {
+ private Long queryJobId;
+
+ public RequestQueryHandler(Long queryJobId) {
+ this.queryJobId = queryJobId;
+ }
+
+ @Override
+ protected JobResponseVO doExecute() {
+ Object data = client.getJobDetail(queryJobId).getData();
+ Assert.isTrue(Objects.nonNull(data),()-> new SnailJobClientException("获取[{}]任务详情失败", queryJobId));
+ return JsonUtil.parseObject(JsonUtil.toJsonString(data), JobResponseVO.class);
+ }
+
+ @Override
+ protected boolean checkRequest() {
+ return queryJobId != null && ! Long.valueOf(0).equals(queryJobId);
+ }
+
+}
diff --git a/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/handler/RequestTriggerJobHandler.java b/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/handler/RequestTriggerJobHandler.java
new file mode 100644
index 00000000..924e5ca4
--- /dev/null
+++ b/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/handler/RequestTriggerJobHandler.java
@@ -0,0 +1,30 @@
+package com.aizuda.snailjob.client.job.core.handler;
+
+import com.aizuda.snailjob.client.common.exception.SnailJobClientException;
+
+public class RequestTriggerJobHandler extends AbstractRequestHandler{
+ private Long triggerJobId;
+ // 1: job; 2: workflow
+ private int triggerType;
+
+ public RequestTriggerJobHandler(Long tiggerJobId, int triggerType) {
+ this.triggerJobId = tiggerJobId;
+ this.triggerType = triggerType;
+ }
+
+ @Override
+ protected Boolean doExecute() {
+ if (triggerType == 1) {
+ return (Boolean) client.triggerJob(triggerJobId).getData();
+ }
+ if (triggerType == 2) {
+ return (Boolean) client.triggerWorkFlow(triggerJobId).getData();
+ }
+ throw new SnailJobClientException("snail job openapi check error");
+ }
+
+ @Override
+ protected boolean checkRequest() {
+ return triggerJobId != null && !Long.valueOf(0).equals(triggerJobId);
+ }
+}
diff --git a/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/handler/RequestUpdateHandler.java b/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/handler/RequestUpdateHandler.java
new file mode 100644
index 00000000..5981e0fa
--- /dev/null
+++ b/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/handler/RequestUpdateHandler.java
@@ -0,0 +1,249 @@
+package com.aizuda.snailjob.client.job.core.handler;
+
+import cn.hutool.core.util.StrUtil;
+import com.aizuda.snailjob.client.common.exception.SnailJobClientException;
+import com.aizuda.snailjob.client.job.core.dto.RequestUpdateJobDTO;
+import com.aizuda.snailjob.client.job.core.enums.AllocationAlgorithmEnum;
+import com.aizuda.snailjob.client.job.core.enums.TriggerTypeEnum;
+import com.aizuda.snailjob.client.job.core.util.ValidatorUtils;
+import com.aizuda.snailjob.common.core.enums.BlockStrategyEnum;
+import com.aizuda.snailjob.common.core.enums.ExecutorTypeEnum;
+import com.aizuda.snailjob.common.core.enums.JobTaskTypeEnum;
+import com.aizuda.snailjob.common.core.util.JsonUtil;
+import com.aizuda.snailjob.common.log.SnailJobLog;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class RequestUpdateHandler extends AbstractRequestHandler {
+ private RequestUpdateJobDTO requestUpdateJobDTO;
+
+ public RequestUpdateHandler(Long jobId) {
+ this.requestUpdateJobDTO = new RequestUpdateJobDTO();
+ // 更新必须要id
+ requestUpdateJobDTO.setId(jobId);
+ // 默认java
+ requestUpdateJobDTO.setExecutorType(ExecutorTypeEnum.JAVA.getType());
+ }
+
+ @Override
+ protected Boolean doExecute() {
+ return (Boolean) client.updateJob(requestUpdateJobDTO).getData();
+ }
+
+ @Override
+ protected boolean checkRequest() {
+ boolean validated = ValidatorUtils.validateEntity(requestUpdateJobDTO);
+ // 如果校验正确,则正对进行相关筛选
+ if (validated) {
+ if (requestUpdateJobDTO.getTaskType() != null
+ && requestUpdateJobDTO.getTaskType() == JobTaskTypeEnum.CLUSTER.getType()){
+ // 集群模式只允许并发为 1
+ setParallelNum(1);
+ }
+ // 非集群模式 路由策略只能为轮询
+ if (requestUpdateJobDTO.getTaskType() != null
+ && requestUpdateJobDTO.getTaskType() != JobTaskTypeEnum.CLUSTER.getType()){
+ setRouteKey(AllocationAlgorithmEnum.ROUND);
+ }
+ if (requestUpdateJobDTO.getTriggerType() != null
+ && requestUpdateJobDTO.getTriggerType() == TriggerTypeEnum.WORK_FLOW.getType()){
+ // 工作流没有调度时间
+ setTriggerInterval("*");
+ }
+ }
+ return validated;
+ }
+
+ /**
+ * 修改Reduce的分片数
+ * 只允许MAP_REDUCE设置
+ * @param shardNum
+ * @return
+ */
+ public RequestUpdateHandler setShardNum(Integer shardNum){
+ Integer taskType = requestUpdateJobDTO.getTaskType();
+ if (taskType != null && taskType.equals(JobTaskTypeEnum.MAP_REDUCE.getType())){
+ // 设置分片
+ if (shardNum != null){
+ Map map = new HashMap<>(1);
+ map.put("shardNum", shardNum);
+ requestUpdateJobDTO.setArgsStr(JsonUtil.toJsonString(map));
+ }
+ }else {
+ throw new SnailJobClientException("非MapReduce模式不能设置分片数");
+ }
+ return this;
+ }
+
+ /**
+ * 修改任务名称
+ * @param jobName
+ * @return
+ */
+ public RequestUpdateHandler setJobName(String jobName) {
+ requestUpdateJobDTO.setJobName(jobName);
+ return this;
+ }
+
+ /**
+ * 修改时会直接覆盖之前的任务参数
+ * 修改参数
+ * @param argsStr
+ * @return
+ */
+ private RequestUpdateHandler setArgsStr(Map argsStr) {
+ Map args = new HashMap<>();
+ if (StrUtil.isNotBlank(requestUpdateJobDTO.getArgsStr())){
+ args = JsonUtil.parseHashMap(requestUpdateJobDTO.getArgsStr());
+ }
+ args.putAll(argsStr);
+ requestUpdateJobDTO.setArgsStr(JsonUtil.toJsonString(args));
+ requestUpdateJobDTO.setArgsType(2);
+ return this;
+ }
+
+ /**
+ * 修改时会直接覆盖之前的任务参数
+ * 添加参数,可支持多次添加
+ * 静态分片不可使用该方法
+ * @param argsKey 参数名
+ * @param argsValue 参数值
+ * @return
+ */
+ public RequestUpdateHandler addArgsStr(String argsKey, Object argsValue) {
+ if (requestUpdateJobDTO.getTaskType() != null
+ && requestUpdateJobDTO.getTaskType().equals(JobTaskTypeEnum.SHARDING.getType())){
+ SnailJobLog.LOCAL.warn("静态分片任务,不可使用该方法添加相关任务参数,请使用addShardingArgs");
+ return this;
+ }
+ Map map = new HashMap<>();
+ if (StrUtil.isNotBlank(requestUpdateJobDTO.getArgsStr())){
+ map = JsonUtil.parseHashMap(requestUpdateJobDTO.getArgsStr());
+ }
+ map.put(argsKey, argsValue);
+ requestUpdateJobDTO.setArgsStr(JsonUtil.toJsonString(map));
+ requestUpdateJobDTO.setArgsType(2);
+ return this;
+ }
+
+ /**
+ * 添加静态分片相关参数
+ * @param shardingValue
+ * @return
+ */
+ public RequestUpdateHandler addShardingArgs(String[] shardingValue){
+ if (requestUpdateJobDTO.getTaskType() != null
+ && !requestUpdateJobDTO.getTaskType().equals(JobTaskTypeEnum.SHARDING.getType())){
+ SnailJobLog.LOCAL.warn("非静态分片任务,不可使用该方法添加相关任务参数,请使用addArgsStr");
+ return this;
+ }
+ requestUpdateJobDTO.setArgsStr(JsonUtil.toJsonString(shardingValue));
+ requestUpdateJobDTO.setArgsType(1);
+ return this;
+ }
+
+ /**
+ * 修改路由
+ * @param algorithmEnum
+ * @return
+ */
+ public RequestUpdateHandler setRouteKey(AllocationAlgorithmEnum algorithmEnum) {
+ requestUpdateJobDTO.setRouteKey(algorithmEnum.getType());
+ return this;
+ }
+
+ /**
+ * 修改相关执行器
+ * @param executorInfo
+ * @return
+ */
+ public RequestUpdateHandler setExecutorInfo(String executorInfo) {
+ requestUpdateJobDTO.setExecutorInfo(executorInfo);
+ return this;
+ }
+
+ /**
+ * 修改调度类型
+ * @param triggerType
+ * @return
+ */
+ public RequestUpdateHandler setTriggerType(TriggerTypeEnum triggerType) {
+ requestUpdateJobDTO.setTriggerType(triggerType.getType());
+ return this;
+ }
+
+ /**
+ * 修改调度时间
+ * 单位:秒
+ * 工作流无需配置
+ * @param triggerInterval
+ * @return
+ */
+ public RequestUpdateHandler setTriggerInterval(String triggerInterval) {
+ requestUpdateJobDTO.setTriggerInterval(triggerInterval);
+ return this;
+ }
+
+ /**
+ * 修改阻塞策略
+ * @param blockStrategy
+ * @return
+ */
+ public RequestUpdateHandler setBlockStrategy(BlockStrategyEnum blockStrategy) {
+ requestUpdateJobDTO.setBlockStrategy(blockStrategy.getBlockStrategy());
+ return this;
+ }
+
+ /**
+ * 修改执行器超时时间
+ * @param executorTimeout
+ * @return
+ */
+ public RequestUpdateHandler setExecutorTimeout(Integer executorTimeout) {
+ requestUpdateJobDTO.setExecutorTimeout(executorTimeout);
+ return this;
+ }
+
+ /**
+ * 修改任务最大重试次数
+ * @param maxRetryTimes
+ * @return
+ */
+ public RequestUpdateHandler setMaxRetryTimes(Integer maxRetryTimes) {
+ requestUpdateJobDTO.setMaxRetryTimes(maxRetryTimes);
+ return this;
+ }
+
+ /**
+ * 修改重试间隔
+ * @param retryInterval
+ * @return
+ */
+ public RequestUpdateHandler setRetryInterval(Integer retryInterval) {
+ requestUpdateJobDTO.setRetryInterval(retryInterval);
+ return this;
+ }
+
+ /**
+ * 修改并发数量
+ * @param parallelNum
+ * @return
+ */
+ public RequestUpdateHandler setParallelNum(Integer parallelNum) {
+ requestUpdateJobDTO.setParallelNum(parallelNum);
+ return this;
+ }
+
+ /**
+ * 修改定时任务描述
+ * @param description
+ * @return
+ */
+ public RequestUpdateHandler setDescription(String description) {
+ requestUpdateJobDTO.setDescription(description);
+ return this;
+ }
+
+
+}
diff --git a/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/handler/RequestUpdateStatusHandler.java b/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/handler/RequestUpdateStatusHandler.java
new file mode 100644
index 00000000..adbf3a41
--- /dev/null
+++ b/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/handler/RequestUpdateStatusHandler.java
@@ -0,0 +1,55 @@
+package com.aizuda.snailjob.client.job.core.handler;
+
+import com.aizuda.snailjob.client.common.exception.SnailJobClientException;
+import com.aizuda.snailjob.client.job.core.dto.RequestUpdateStatusDTO;
+import com.aizuda.snailjob.client.job.core.util.ValidatorUtils;
+import com.aizuda.snailjob.common.core.enums.StatusEnum;
+
+
+public class RequestUpdateStatusHandler extends AbstractRequestHandler{
+ private RequestUpdateStatusDTO statusDTO;
+ // 1: job; 2: workflow
+ private int type;
+
+ public RequestUpdateStatusHandler(Long id, int type) {
+ this.statusDTO = new RequestUpdateStatusDTO();
+ this.type = type;
+ setId(id);
+ }
+
+ @Override
+ protected Boolean doExecute() {
+ if (type == 1){
+ return (Boolean) client.updateJobStatus(statusDTO).getData();
+ }
+ if (type == 2){
+ return (Boolean) client.updateWorkFlowStatus(statusDTO).getData();
+ }
+ throw new SnailJobClientException("snail job openapi check error");
+ }
+
+ @Override
+ protected boolean checkRequest() {
+ return ValidatorUtils.validateEntity(statusDTO);
+ }
+
+ /**
+ * 设置任务/工作流ID
+ * @param id
+ * @return
+ */
+ private RequestUpdateStatusHandler setId(Long id){
+ this.statusDTO.setId(id);
+ return this;
+ }
+
+ /**
+ * 设置状态
+ * @param status
+ * @return
+ */
+ public RequestUpdateStatusHandler setStatus(StatusEnum status){
+ this.statusDTO.setJobStatus(status.getStatus());
+ return this;
+ }
+}
diff --git a/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/openapi/OpenApiClient.java b/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/openapi/OpenApiClient.java
new file mode 100644
index 00000000..b386cdcd
--- /dev/null
+++ b/snail-job-client/snail-job-client-job-core/src/main/java/com/aizuda/snailjob/client/job/core/openapi/OpenApiClient.java
@@ -0,0 +1,31 @@
+package com.aizuda.snailjob.client.job.core.openapi;
+
+import com.aizuda.snailjob.client.common.annotation.Mapping;
+import com.aizuda.snailjob.client.common.rpc.client.RequestMethod;
+import com.aizuda.snailjob.client.job.core.dto.RequestAddJobDTO;
+import com.aizuda.snailjob.client.job.core.dto.RequestUpdateJobDTO;
+import com.aizuda.snailjob.client.job.core.dto.RequestUpdateStatusDTO;
+import com.aizuda.snailjob.common.core.model.Result;
+
+public interface OpenApiClient {
+ @Mapping(method = RequestMethod.POST, path = "/api/job/add")
+ Result