diff --git a/Dockerfile b/Dockerfile
index 9e7ac57..3b37766 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,7 +1,7 @@
-FROM amazoncorretto:17
+FROM amazoncorretto:21
MAINTAINER xiaowoniu
-ADD ./target/snail-job-example.jar snail-job-example.jar
+ADD ./target/example.jar example.jar
#对外暴漏的端口号
EXPOSE 8018
@@ -9,5 +9,5 @@ EXPOSE 8018
WORKDIR /
#开机启动
-ENTRYPOINT ["sh","-c","java -jar $JAVA_OPTS /snail-job-example.jar $PARAMS"]
+ENTRYPOINT ["sh","-c","java -jar $JAVA_OPTS /example.jar $PARAMS"]
diff --git a/README.md b/README.md
index a6251cf..c2c5d43 100644
--- a/README.md
+++ b/README.md
@@ -10,10 +10,12 @@
# JDK 支持
-| JDK版本 | 对应分支 |
-|-------|-----------------------------------------------------------------------------|
-| ≥ 17 | [master](https://gitee.com/opensnail/snail-job-demo/tree/master/) |
-| 8 | [master-jdk8](https://gitee.com/opensnail/snail-job-demo/tree/master-jdk8/) |
+```shell
+$ java -version ⏎
+java version "21.0.2" 2024-01-16 LTS
+Java(TM) SE Runtime Environment Oracle GraalVM 21.0.2+13.1 (build 21.0.2+13-LTS-jvmci-23.1-b30)
+Java HotSpot(TM) 64-Bit Server VM Oracle GraalVM 21.0.2+13.1 (build 21.0.2+13-LTS-jvmci-23.1-b30, mixed mode, sharing)
+```
# 🌸 简介
diff --git a/build-exclude-test.sh b/build-exclude-test.sh
new file mode 100755
index 0000000..50b86dc
--- /dev/null
+++ b/build-exclude-test.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+CurrentDir=$(dirname $0)
+
+find ${CurrentDir} -name "target" | xargs rm -rf $(xargs)
+mvn clean package -DskipTests=true
+
+
+
diff --git a/build-include-test.sh b/build-include-test.sh
new file mode 100755
index 0000000..67c6a26
--- /dev/null
+++ b/build-include-test.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+CurrentDir=$(dirname $0)
+
+find ${CurrentDir} -name "target" | xargs rm -rf $(xargs)
+mvn clean package
+
+
+
diff --git a/pom.xml b/pom.xml
index 0a06bf7..1a00d4e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,18 +5,18 @@
org.springframework.boot
spring-boot-starter-parent
- 3.3.5
+ 3.4.0
com.example
- example1
+ example
1.0.0
example
- Demo project for Spring Boot
+ Example project for Spring Boot
- 17
+ 21
1.2.0
@@ -24,108 +24,120 @@
org.springframework.boot
spring-boot-starter-web
-
-
- mybatis-spring
- org.mybatis
-
-
org.projectlombok
lombok
- true
-
-
- org.springframework.boot
- spring-boot-starter-test
- test
+ 1.18.36
+
+
com.aizuda
snail-job-client-starter
${snailjob.version}
+
com.aizuda
snail-job-client-retry-core
${snailjob.version}
+
com.aizuda
snail-job-client-job-core
${snailjob.version}
+
com.googlecode.aviator
aviator
- 5.3.3
+ 5.4.3
+
com.alibaba
QLExpress
- 3.3.1
+ 3.3.4
+
- com.baomidou
+ com.baomidou
mybatis-plus-spring-boot3-starter
- 3.5.7
+ 3.5.9
+
com.baomidou
mybatis-plus-generator
- 3.5.1
+ 3.5.9
+
org.freemarker
freemarker
- 2.3.28
+ 2.3.33
+
cn.hutool
hutool-all
- 5.8.19
+ 5.8.34
+
- mysql
- mysql-connector-java
- 8.0.30
+ com.mysql
+ mysql-connector-j
+ 9.1.0
+
org.springdoc
springdoc-openapi-starter-webmvc-ui
- 2.2.0
+ 2.7.0
+
com.alibaba
transmittable-thread-local
- 2.12.0
+ 2.14.5
+
org.awaitility
awaitility
- 4.1.1
+ 4.2.2
+
com.squareup.okhttp3
okhttp
- 4.2.0
+ 4.12.0
+
com.alibaba
easyexcel
- 3.1.3
+ 4.0.3
+
- com.alibaba
- fastjson
- 1.2.83
+ com.alibaba.fastjson2
+ fastjson2
+ 2.0.53
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
- snail-job-example
+ example
org.springframework.boot
@@ -141,5 +153,5 @@
-
+
diff --git a/src/main/java/com/example/ExampleApplication.java b/src/main/java/com/example/ExampleApplication.java
new file mode 100644
index 0000000..f65709d
--- /dev/null
+++ b/src/main/java/com/example/ExampleApplication.java
@@ -0,0 +1,18 @@
+package com.example;
+
+import com.aizuda.snailjob.client.starter.EnableSnailJob;
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+import java.util.TimeZone;
+
+@SpringBootApplication
+@EnableSnailJob
+@MapperScan("com.example.dao")
+public class ExampleApplication {
+ public static void main(String[] args) {
+ TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"));
+ SpringApplication.run(ExampleApplication.class, args);
+ }
+}
diff --git a/src/main/java/com/example/bo/PhoneNumberBo.java b/src/main/java/com/example/bo/PhoneNumberBo.java
new file mode 100644
index 0000000..0254f16
--- /dev/null
+++ b/src/main/java/com/example/bo/PhoneNumberBo.java
@@ -0,0 +1,18 @@
+package com.example.bo;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import lombok.Data;
+
+/**
+ * excel表格手机号BO
+ *
+ * @author JiChenWang
+ * @since 2024/6/27 20:28
+ */
+@Data
+public class PhoneNumberBo {
+
+ @ExcelProperty(value = "手机号码", index = 0)
+ private String phoneNumber;
+
+}
diff --git a/src/main/java/com/example/bo/PhoneNumberCheckBo.java b/src/main/java/com/example/bo/PhoneNumberCheckBo.java
new file mode 100644
index 0000000..d72042d
--- /dev/null
+++ b/src/main/java/com/example/bo/PhoneNumberCheckBo.java
@@ -0,0 +1,39 @@
+package com.example.bo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 手机号检测BO
+ *
+ * @author JiChenWang
+ * @since 2024/6/27 20:50
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class PhoneNumberCheckBo {
+
+ @Schema(description = "检测总条数", accessMode = Schema.AccessMode.READ_WRITE)
+ private Long total = 0L;
+
+ @Schema(description = "检测失败条数", accessMode = Schema.AccessMode.READ_WRITE)
+ private Long error = 0L;
+
+ @Schema(description = "检测成功条数", accessMode = Schema.AccessMode.READ_WRITE)
+ private Long success = 0L;
+
+ @Schema(description = "检测失败临时的数据", accessMode = Schema.AccessMode.READ_WRITE)
+ private List checkErrors = new ArrayList<>();
+
+// @Schema(description = "检测成功临时的数据", accessMode = Schema.AccessMode.READ_WRITE)
+// private List checkSuccessPhoneNumberList = new ArrayList<>();
+
+}
diff --git a/src/main/java/com/example/config/SwaggerConfig.java b/src/main/java/com/example/config/SwaggerConfig.java
new file mode 100644
index 0000000..675d4d0
--- /dev/null
+++ b/src/main/java/com/example/config/SwaggerConfig.java
@@ -0,0 +1,55 @@
+package com.example.config;
+
+import com.aizuda.snailjob.common.core.util.SnailJobVersion;
+import io.swagger.v3.oas.models.ExternalDocumentation;
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.oas.models.info.Info;
+import io.swagger.v3.oas.models.info.License;
+import org.springdoc.core.models.GroupedOpenApi;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+
+/**
+ * @author: www.byteblogs.com
+ * @date : 2023-07-17 18:19
+ * @since 2.1.0
+ */
+@Configuration
+public class SwaggerConfig {
+
+ @Bean
+ public OpenAPI springShopOpenAPI() {
+ return new OpenAPI()
+ .info(new Info()
+ .title("Snail Job Example")
+ .description("SnailJob是一个灵活,可靠和快速的分布式任务重试和分布式任务调度平台
\n" +
+ "官网地址: https://snailjob.opensnail.com/
" +
+ "在线体验地址: https://preview.snailjob.opensnail.com/
" +
+ "源码地址: https://gitee.com/opensnail/snail-job-demo
" +
+ "特别提醒: 🌻在您使用测试案例之前请认真的阅读官网.
")
+ .version(SnailJobVersion.getVersion())
+ .license(new License().name("Apache 2.0").url("https://snailjob.opensnail.com/")))
+ .externalDocs(new ExternalDocumentation()
+ .description("视频教程:以小白视角的SnailJob入门级视频教程")
+ .url("https://www.bilibili.com/video/BV1pvtBerEmV/?vd_source=ec323e2347232ea82321f54aba036b63"))
+ ;
+ }
+
+ @Bean
+ public GroupedOpenApi adminApi() {
+ return GroupedOpenApi.builder()
+ //分组名
+ .group("user")
+ .pathsToMatch("/**")
+ //扫描路径,将路径下有swagger注解的接口解析到文档中
+ .packagesToScan("com.example.controller")
+ .build();
+ }
+}
+
+
+
+
+
+
diff --git a/src/main/java/com/example/controller/JobController.java b/src/main/java/com/example/controller/JobController.java
new file mode 100644
index 0000000..ec8e713
--- /dev/null
+++ b/src/main/java/com/example/controller/JobController.java
@@ -0,0 +1,134 @@
+package com.example.controller;
+
+import com.aizuda.snailjob.client.job.core.dto.JobResponseVO;
+import com.example.handler.TestAddJobHandler;
+import com.example.handler.TestQueryJobHandler;
+import com.example.handler.TestTriggerJobHandler;
+import com.example.handler.TestUpdateJobHandler;
+import com.example.handler.TestUpdateJobStatusHandler;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * @author opensnail
+ * @date 2024-10-19 10:41:25
+ * @since sj_1.2.0-beta2
+ */
+@RestController
+@RequestMapping("/open-api/job")
+@Tag(name = "JobOpenApi", description = "通过OpenApi可以灵活的实现对Job的增、改、查功能")
+@RequiredArgsConstructor
+public class JobController {
+ private final TestAddJobHandler testAddJobHandler;
+ private final TestUpdateJobHandler testUpdateJobHandler;
+ private final TestQueryJobHandler testQueryJobHandler;
+ private final TestTriggerJobHandler testTriggerJobHandler;
+ private final TestUpdateJobStatusHandler testUpdateJobStatusHandler;
+
+ @Operation(
+ description = "添加集群模式的定时任务"
+ )
+ @PostMapping("/custer/add")
+ public Long addClusterJob(@RequestBody String jobName) {
+ return testAddJobHandler.addClusterJob(jobName);
+ }
+
+ @Operation(
+ description = "添加广播模式的定时任务"
+ )
+ @PostMapping("/broadcast/add")
+ public Long addBroadcastJob(@RequestBody String jobName) {
+ return testAddJobHandler.addBroadcastJob(jobName);
+ }
+
+ @Operation(
+ description = "添加静态分片模式的定时任务"
+ )
+ @PostMapping("/sharding/add")
+ public Long addShardingJob(@RequestBody String jobName) {
+ return testAddJobHandler.addShardingJob(jobName);
+ }
+
+ @Operation(
+ description = "添加Map模式的定时任务"
+ )
+ @PostMapping("/map/add")
+ public Long addMapJob(@RequestBody String jobName) {
+ return testAddJobHandler.addMapJob(jobName);
+ }
+
+ @Operation(
+ description = "添加MapReduce模式的定时任务"
+ )
+ @PostMapping("/map-reduce/add")
+ public Long addMapReduceJob(@RequestBody String jobName) {
+ return testAddJobHandler.addMapReduceJob(jobName);
+ }
+
+ @Operation(
+ description = "更新集群模式的定时任务"
+ )
+ @PutMapping("/custer/update")
+ public Boolean updateClusterJob(@RequestBody Long id) {
+ return testUpdateJobHandler.updateClusterJob(id);
+ }
+
+ @Operation(
+ description = "更新广播模式的定时任务"
+ )
+ @PutMapping("/broadcast/update")
+ public Boolean updateBroadcastJob(@RequestBody Long id) {
+ return testUpdateJobHandler.updateBroadcastJob(id);
+ }
+
+ @Operation(
+ description = "更新静态分片模式的定时任务"
+ )
+ @PutMapping("/sharding/update")
+ public Boolean addShardingJob(@RequestBody Long id) {
+ return testUpdateJobHandler.updateShardingJob(id);
+ }
+
+ @Operation(
+ description = "更新Map模式的定时任务"
+ )
+ @PutMapping("/map/update")
+ public Boolean updateMapJob(@RequestBody Long id) {
+ return testUpdateJobHandler.updateMapJob(id);
+ }
+
+ @Operation(
+ description = "更新MapReduce模式的定时任务"
+ )
+ @PutMapping("/map-reduce/update")
+ public Boolean updateMapReduceJob(@RequestBody Long id) {
+ return testUpdateJobHandler.updateMapReduceJob(id);
+ }
+
+ @Operation(
+ description = "通过任务id查询任务的详情"
+ )
+ @GetMapping("/detail/{id}")
+ public JobResponseVO addMapReduceJob(@PathVariable("id") Long id) {
+ return testQueryJobHandler.queryJob(id);
+ }
+
+ @Operation(
+ description = "手动触发任务"
+ )
+ @PostMapping("/trigger/{id}")
+ public Boolean triggerJob(@PathVariable("id") Long id) {
+ return testTriggerJobHandler.triggerJob(id);
+ }
+
+ @Operation(
+ description = "根据id更新任务的状态",
+ summary = "0:关闭 1:开启"
+ )
+ @PutMapping("/update/status/{id}/{status}")
+ public Boolean updateJob(@PathVariable("id") Long id, @PathVariable("status") Long status) {
+ return testUpdateJobStatusHandler.updateJobStatus(id, status);
+ }
+}
diff --git a/src/main/java/com/example/controller/LocalAndRemoteRetryController.java b/src/main/java/com/example/controller/LocalAndRemoteRetryController.java
new file mode 100644
index 0000000..23d9a4a
--- /dev/null
+++ b/src/main/java/com/example/controller/LocalAndRemoteRetryController.java
@@ -0,0 +1,117 @@
+package com.example.controller;
+
+import com.example.service.LocalRemoteService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/local-remote")
+@Tag(name = "模拟先本地再远程重试案例", description = "先本地再远程重试案例【RetryType.LOCAL_REMOTE】")
+public class LocalAndRemoteRetryController {
+
+ @Autowired
+ private LocalRemoteService localRemoteService;
+
+ @GetMapping("/retry")
+ @Operation(description = "一个简单的入门案例")
+ public void localRemote() {
+ localRemoteService.localRemote();
+ }
+
+ @GetMapping("/retryWithLocalRemote")
+ @Operation(
+ summary = "使用同步上报的方式",
+ description = "async = false 代表使用同步上传的方式\n"
+ + "timeout = 1 代表超时时间为1 \n"
+ + "unit = MINUTES 代表超时时间的单位是分钟\n" +
+ "📢查看任务列表: http://preview.easyretry.com/#/retry-task/list"
+ )
+ public void remoteRetryWithLocalRemote(@Parameter(name = "params", description = "测试参数",
+ schema = @Schema(type = "string", description = "测试参数", defaultValue = "test")
+ )
+ @RequestParam("params") String params) {
+ localRemoteService.remoteRetryWithLocalRemote(params);
+ }
+
+ @GetMapping("/localRetryWithTwoRetryMethod")
+ /**
+ *
+ * 方法内部存在两个串行的方法retryMethod1、retryMethod1 如下所属
+ * public boolean localRetryWithTwoRetryMethod(final String params) {
+ * retryHandler.retryMethod1(params);
+ * retryHandler.retryMethod1(params);
+ * return true;
+ * }
+ * params: 1 => 则retryMethod1触发重试
+ * params: 2 => 则retryMethod2触发重试
+ * params: 3 => 则retryMethod1随机触发重试, 若retryMethod1重试成功,则retryMethod2一定触发重试否则只触发retryMethod1重试
+ *
+ */
+ @Operation(
+ summary = "N个串行执行的方法触发重试的场景",
+ description = "方法内部存在两个串行的方法retryMethod1、retryMethod1\n" +
+ "params: 1 => 则retryMethod1触发重试\n" +
+ "params: 2 => 则retryMethod2触发重试\n" +
+ "params: 3 => 则retryMethod1随机触发重试, 若retryMethod1重试成功,则retryMethod2一定触发重试否则只触发retryMethod1重试"
+ )
+ public boolean localRetryWithTwoRetryMethod(@RequestParam("params") String params) {
+ return localRemoteService.localRetryWithTwoRetryMethod(params);
+ }
+
+ /**
+ * 外部方法传播机制为REQUIRED,内部方法传播机制为REQUIRED,
+ * 只执行入口方法重试
+ */
+ @GetMapping("/localRetryWithPropagationRequired")
+ @Operation(
+ description = "外部方法传播机制为REQUIRED,内部方法传播机制为REQUIRED, 只执行入口方法重试",
+ summary = "外部方法传播机制为REQUIRED,内部方法传播机制为REQUIRED, 只执行入口方法重试"
+ )
+ public boolean localRetryWithPropagationRequired(@RequestParam("params") String params) {
+ return localRemoteService.localRetryWithPropagationRequired(params);
+ }
+
+ /**
+ * 外部方法传播机制为REQUIRED,内部方法传播机制为REQUIRED_NEW,
+ * 外部和内部方法都触发重试
+ */
+ @GetMapping("/localRetryWithPropagationRequiredNew")
+ @Operation(
+ description = "外部方法传播机制为REQUIRED,内部方法传播机制为REQUIRED_NEW,外部和内部方法都触发重试",
+ summary = "外部方法传播机制为REQUIRED,内部方法传播机制为REQUIRED_NEW,外部和内部方法都触发重试"
+ )
+ public boolean localRetryWithPropagationRequiredNew(@RequestParam("params") String params) {
+ return localRemoteService.localRetryWithPropagationRequiredNew(params);
+ }
+
+ /**
+ * 内部方法传播机制为REQUIRED_NEW,且异常被try catch捕获,内部方法触发重试,外部方法不会触发重试
+ */
+ @GetMapping("/localRetryWithTryCatch1")
+ @Operation(
+ description = "",
+ summary = "内部方法传播机制为REQUIRED_NEW,且异常被try catch捕获,内部方法触发重试,外部方法不会触发重试"
+ )
+ public boolean localRetryWithTryCatch1(@RequestParam("params") String params) {
+ return localRemoteService.localRetryWithTryCatch1(params);
+ }
+
+ /**
+ * 内部方法传播机制为REQUIRED,且异常被try catch捕获,内部方法不会触发重试,外部方法也不会触发重试
+ */
+ @GetMapping("/localRetryWithTryCatch2")
+ @Operation(
+ description = "",
+ summary = "内部方法传播机制为REQUIRED,且异常被try catch捕获,内部方法不会触发重试,外部方法也不会触发重试"
+ )
+ public boolean localRetryWithTryCatch2(@RequestParam("params") String params) {
+ return localRemoteService.localRetryWithTryCatch2(params);
+ }
+}
diff --git a/src/main/java/com/example/controller/LocalRetryController.java b/src/main/java/com/example/controller/LocalRetryController.java
new file mode 100644
index 0000000..373aef8
--- /dev/null
+++ b/src/main/java/com/example/controller/LocalRetryController.java
@@ -0,0 +1,189 @@
+package com.example.controller;
+
+import com.example.vo.OrderVo;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Parameters;
+import io.swagger.v3.oas.annotations.enums.ParameterIn;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.apache.ibatis.annotations.Param;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.example.service.LocalRetryService;
+
+@RestController
+@RequestMapping("/local")
+@Tag(name = "模拟本地重试", description = "本地重试案例 【RetryType.ONLY_LOCAL】")
+public class LocalRetryController {
+
+ @Autowired
+ private LocalRetryService localRetryService;
+
+ @GetMapping("/retry")
+ @Operation(
+ summary = "一个简单的入门案例",
+ description = "🥇仅仅在本地进行内存重试\n" +
+ "📢任务列表: http://preview.easyretry.com/#/retry-task/list"
+ )
+ public void onlyLocalRetry(@Param(value = "测试参数") @RequestParam("params") String params) {
+ localRetryService.localRetry(params);
+ }
+
+ @GetMapping("/localRetryWithAnnoOnInterface")
+ @Operation(
+ summary = "@Retryable在接口上执行重试"
+ )
+ public void localRetryWithAnnoOnInterface(
+ @Parameter(name = "params", description = "测试参数", in = ParameterIn.QUERY,
+ schema = @Schema(type = "string", description = "测试参数"))
+ @RequestParam("params") String params) {
+ localRetryService.localRetryWithAnnoOnInterface(params);
+ }
+
+ @GetMapping("/withBasicParams")
+ @Operation(
+ summary = "指定基础参数",
+ description = "localTimes 本地重试次数\n" +
+ "localInterval 本地重试间隔时间(默认单位为秒)\n" +
+ "unit 超时时间单位\n" +
+ "本案例设置为本地重试4次,每次重试之间间隔10s"
+ )
+ public void localRetryWithBasicParams(@Parameter(name = "params") String params) {
+ localRetryService.localRetryWithBasicParams(params);
+ }
+
+ @GetMapping("/includeException")
+ @Operation(
+ summary = "指定异常参数",
+ description = "include参数指的是当我们遭遇到指定异常时进行重试\n" +
+ "在这个案例中我们处理两个场景:\n" +
+ "抛出指定异常,例如抛出自定义的ParamException异常,观察是否会重试\n" +
+ "抛出非指定异常,例如我们在这里产生一个异常,观察是否会重试\n" +
+ "注意:如果此时我们在include 中指定参数为BusinessException(ParamException的父类),同样也会进行重试逻辑\n" +
+ "下面参数可以指定:NullPointerException, ParamException"
+ )
+ public void localRetryIncludeException(@Parameter(name = "type", description = "异常类型", in = ParameterIn.QUERY,
+ schema = @Schema(type = "string", description = "异常类型")) @RequestParam("type") String type) {
+ localRetryService.localRetryIncludeException(type);
+ }
+
+ @GetMapping("/excludeException")
+ @Operation(
+ summary = "非指定异常参数进行重试",
+ description = "这个参数的作用和include是相反的\n" +
+ "exclude参数指的是当我们遇到指定异常时则不会进行重试\n" +
+ "比如在下述案例中我们指定了遇到ParamException和ArithmeticException后不进行重试"
+ )
+ @Parameters({
+ @Parameter(name = "type", description = "异常类型", in = ParameterIn.QUERY)
+ })
+ public void localRetryExcludeException(@RequestParam("type") String type) {
+ localRetryService.localRetryExcludeException(type);
+ }
+
+ @GetMapping("/isThrowException")
+ @Operation(
+ summary = "本地重试完成后不抛出异常",
+ description = ""
+ )
+ @Parameters({
+ @Parameter(name = "params", description = "异常类型", in = ParameterIn.QUERY)
+ })
+ public void localRetryIsThrowException(@RequestParam("params") String params) {
+ localRetryService.localRetryIsThrowException(params);
+ }
+
+ @PostMapping("/localRetryWithRetryMethod")
+ /**
+ * 使用自定义的异常处理类 OrderRetryMethod
+ */
+ @Operation(
+ description = "指定自定义的异常处理类",
+ summary = "🥇什么是自定义的异常处理类: https://www.easyretry.com/pages/540554/#%E8%87%AA%E5%AE%9A%E4%B9%89%E6%96%B9%E6%B3%95%E6%89%A7%E8%A1%8C%E5%99%A8"
+ )
+ public boolean localRetryWithRetryMethod(@RequestBody OrderVo orderVo) {
+ return localRetryService.localRetryWithRetryMethod(orderVo);
+ }
+
+ @GetMapping("/localRetryWithTwoRetryMethod")
+ /**
+ *
+ * 方法内部存在两个串行的方法retryMethod1、retryMethod1 如下所属
+ * public boolean localRetryWithTwoRetryMethod(final String params) {
+ * retryHandler.retryMethod1(params);
+ * retryHandler.retryMethod1(params);
+ * return true;
+ * }
+ * params: 1 => 则retryMethod1触发重试
+ * params: 2 => 则retryMethod2触发重试
+ * params: 3 => 则retryMethod1随机触发重试, 若retryMethod1重试成功,则retryMethod2一定触发重试否则只触发retryMethod1重试
+ *
+ */
+ @Operation(
+ summary = "N个串行执行的方法触发重试的场景",
+ description = "方法内部存在两个串行的方法retryMethod1、retryMethod1\n" +
+ "params: 1 => 则retryMethod1触发重试\n" +
+ "params: 2 => 则retryMethod2触发重试\n" +
+ "params: 3 => 则retryMethod1随机触发重试, 若retryMethod1重试成功,则retryMethod2一定触发重试否则只触发retryMethod1重试"
+ )
+ public boolean localRetryWithTwoRetryMethod(@RequestParam("params") String params) {
+ return localRetryService.localRetryWithTwoRetryMethod(params);
+ }
+
+ /**
+ * 外部方法传播机制为REQUIRED,内部方法传播机制为REQUIRED,
+ * 只执行入口方法重试
+ */
+ @GetMapping("/localRetryWithPropagationRequired")
+ @Operation(
+ description = "外部方法传播机制为REQUIRED,内部方法传播机制为REQUIRED, 只执行入口方法重试",
+ summary = "外部方法传播机制为REQUIRED,内部方法传播机制为REQUIRED, 只执行入口方法重试"
+ )
+ public boolean localRetryWithPropagationRequired(@RequestParam("params") String params) {
+ return localRetryService.localRetryWithPropagationRequired(params);
+ }
+
+ /**
+ * 外部方法传播机制为REQUIRED,内部方法传播机制为REQUIRED_NEW,
+ * 外部和内部方法都触发重试
+ */
+ @GetMapping("/localRetryWithPropagationRequiredNew")
+ @Operation(
+ description = "外部方法传播机制为REQUIRED,内部方法传播机制为REQUIRED_NEW,外部和内部方法都触发重试",
+ summary = "外部方法传播机制为REQUIRED,内部方法传播机制为REQUIRED_NEW,外部和内部方法都触发重试"
+ )
+ public boolean localRetryWithPropagationRequiredNew(@RequestParam("params") String params) {
+ return localRetryService.localRetryWithPropagationRequiredNew(params);
+ }
+
+ /**
+ * 内部方法传播机制为REQUIRED_NEW,且异常被try catch捕获,内部方法触发重试,外部方法不会触发重试
+ */
+ @GetMapping("/localRetryWithTryCatch1")
+ @Operation(
+ description = "",
+ summary = "内部方法传播机制为REQUIRED_NEW,且异常被try catch捕获,内部方法触发重试,外部方法不会触发重试"
+ )
+ public boolean localRetryWithTryCatch1(@RequestParam("params") String params) {
+ return localRetryService.localRetryWithTryCatch1(params);
+ }
+
+ /**
+ * 内部方法传播机制为REQUIRED,且异常被try catch捕获,内部方法不会触发重试,外部方法也不会触发重试
+ */
+ @GetMapping("/localRetryWithTryCatch2")
+ @Operation(
+ description = "",
+ summary = "内部方法传播机制为REQUIRED,且异常被try catch捕获,内部方法不会触发重试,外部方法也不会触发重试"
+ )
+ public boolean localRetryWithTryCatch2(@RequestParam("params") String params) {
+ return localRetryService.localRetryWithTryCatch2(params);
+ }
+}
diff --git a/src/main/java/com/example/controller/ManualRetryExecutorController.java b/src/main/java/com/example/controller/ManualRetryExecutorController.java
new file mode 100644
index 0000000..7e3693a
--- /dev/null
+++ b/src/main/java/com/example/controller/ManualRetryExecutorController.java
@@ -0,0 +1,33 @@
+package com.example.controller;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.example.service.ManualRetryExecutorMethodService;
+
+@RestController
+@RequestMapping("/manual")
+@Tag(name = "模拟手动执行重试案例", description = "手动执行重试上报")
+public class ManualRetryExecutorController {
+
+ @Autowired
+ private ManualRetryExecutorMethodService manualRetryExecutorMethodService;
+
+ @Operation(
+ summary = "手动重试",
+ description = "❤️如果不知道这个手动重试的使用场景可以参考: https://www.easyretry.com/pages/406a68/#%E5%8F%91%E9%80%81mq%E5%9C%BA%E6%99%AF \n"
+ + "📢查看任务列表: http://preview.easyretry.com/#/retry-task/list"
+ )
+ @GetMapping("/retry")
+ public void remoteRetryWithCallback(@Parameter(name = "params", description = "测试参数", schema = @Schema(type = "string", description = "测试参数", defaultValue = "test"))
+ @RequestParam("params") String params) {
+ manualRetryExecutorMethodService.myExecutorMethod(params);
+ }
+}
diff --git a/src/main/java/com/example/controller/RemoteRetryController.java b/src/main/java/com/example/controller/RemoteRetryController.java
new file mode 100644
index 0000000..1ed2f28
--- /dev/null
+++ b/src/main/java/com/example/controller/RemoteRetryController.java
@@ -0,0 +1,245 @@
+package com.example.controller;
+
+import java.util.Random;
+import java.util.UUID;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.example.vo.OrderVo;
+import com.example.service.RemoteRetryService;
+
+@RestController
+@RequestMapping("/remote")
+@Tag(name = "模拟远程重试案例", description = "远程重试案例【RetryType.ONLY_REMOTE】")
+public class RemoteRetryController {
+
+ @Autowired
+ private RemoteRetryService remoteRetryService;
+
+ /**
+ * 一个最简单的远程调用案例
+ */
+ @GetMapping("/retry")
+ @Operation(
+ summary = "一个简单的入门案例",
+ description = "🥇不进过本地重试阶段,直接上报到服务端\n" +
+ "📢查看任务列表: http://preview.easyretry.com/#/retry-task/list"
+ )
+ public void remote(@Parameter(name = "params", description = "测试参数",
+ schema = @Schema(defaultValue = "test", type = "String", description = "测试参数"))
+ @RequestParam("params") String params) {
+ remoteRetryService.remoteRetry(params);
+ }
+
+ /**
+ * 一个最简单的远程调用案例
+ * schema = @Schema(type = "String", defaultValue = "test", description = "测试参数", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
+ */
+ @GetMapping("/retry/sync")
+ @Operation(
+ summary = "一个简单的以同步方式远程重试入门案例",
+ description = "🥇不进过本地重试阶段,直接上报到服务端\n" +
+ "📢查看任务列表: http://preview.easyretry.com/#/retry-task/list"
+ )
+ public void remoteSync(@Parameter(name = "params", description = "测试参数",
+ schema = @Schema(type = "string", defaultValue = "test", description = "测试参数"))
+ @RequestParam("params") String params) {
+ remoteRetryService.remoteSync(params);
+ }
+
+ /**
+ * 使用自定义的幂等Id生成规则
+ */
+ @PostMapping("/retryWithIdempotentId")
+ @Operation(
+ summary = "使用自定义的幂等Id生成规则",
+ description =
+ "具体实现类参考: https://gitee.com/zhangyutongxue/easy-retry-demo/blob/master/easy-retry-springboot/src/main/java/com/maluxinyu/easyretry/customized/OrderIdempotentIdGenerate.java\n"
+ + "具体的幂等策略参考: https://www.easyretry.com/pages/97cde9/#%E2%9A%A1%E5%B9%82%E7%AD%89id-idempotentid \n"
+ +
+ "📢查看任务列表: http://preview.easyretry.com/#/retry-task/list"
+ )
+ public void remoteRetryWithIdempotentId(@RequestBody OrderVo orderVo) {
+ remoteRetryService.remoteRetryWithIdempotentId(orderVo);
+ }
+
+ /**
+ * 使用自定义的单参数幂等Id生成规则
+ */
+ @Operation(
+ summary = "使用自定义的单参数幂等Id生成规则",
+ description =
+ "具体实现类参考: https://gitee.com/zhangyutongxue/easy-retry-demo/blob/master/easy-retry-springboot/src/main/java/com/maluxinyu/easyretry/customized/SingleParamIdempotentGenerate.java\n"
+ +
+ "🥇通过对orderId进行md5加密生成幂等ID, 具体的幂等策略参考: https://www.easyretry.com/pages/97cde9/#%E2%9A%A1%E5%B9%82%E7%AD%89id-idempotentid \n"
+ +
+ "📢查看任务列表: http://preview.easyretry.com/#/retry-task/list"
+ )
+ @GetMapping("/retryWithSingleParamIdempotentGenerate")
+ public void retryWithSingleParamIdempotentGenerate(
+ @Parameter(name = "params", description = "测试参数",
+ schema = @Schema(type = "string", description = "测试参数", defaultValue = "test"))
+ @RequestParam("params") String params) {
+ remoteRetryService.retryWithSingleParamIdempotentGenerate(params);
+ }
+
+ /**
+ * 使用自定义的多参数幂等Id生成规则
+ */
+ @PostMapping("/retryWithMulParamIdempotentGenerate")
+ @Operation(
+ summary = "使用自定义的多参数幂等Id生成规则",
+ description =
+ "具体实现类参考: https://gitee.com/zhangyutongxue/easy-retry-demo/blob/master/easy-retry-springboot/src/main/java/com/maluxinyu/easyretry/customized/MultiParamIdempotentGenerate.java\n"
+ +
+ "🥇通过对orderId进行md5加密生成幂等ID, 具体的幂等策略参考: https://www.easyretry.com/pages/97cde9/#%E2%9A%A1%E5%B9%82%E7%AD%89id-idempotentid \n"
+ +
+ "📢查看任务列表: http://preview.easyretry.com/#/retry-task/list"
+ )
+ public void retryWithMulParamIdempotentGenerate(@RequestBody OrderVo orderVo) {
+ Random random = new Random();
+ remoteRetryService.retryWithMulParamIdempotentGenerate(
+ String.valueOf(UUID.randomUUID()),
+ random.nextInt(),
+ random.nextDouble(),
+ 'a',
+ orderVo
+ );
+ }
+
+ /**
+ * 使用自定义的异常处理类 OrderRetryMethod
+ */
+ @Operation(
+ summary = "指定自定义的异常处理类",
+ description =
+ "具体实现类参考: https://gitee.com/zhangyutongxue/easy-retry-demo/blob/master/easy-retry-springboot/src/main/java/com/maluxinyu/easyretry/customized/OrderRetryMethod.java\n"
+ +
+ "🥇什么是自定义的异常处理类: https://www.easyretry.com/pages/540554/#%E8%87%AA%E5%AE%9A%E4%B9%89%E6%96%B9%E6%B3%95%E6%89%A7%E8%A1%8C%E5%99%A8\n"
+ +
+ "📢查看任务列表: http://preview.easyretry.com/#/retry-task/list"
+ )
+ @PostMapping("/retryWithRetryMethod")
+ public void remoteRetryWithRetryMethod(@RequestBody OrderVo orderVo) {
+ remoteRetryService.remoteRetryWithRetryMethod(orderVo);
+ }
+
+ /**
+ * 使用自定义的回调函数
+ */
+ @Operation(
+ summary = "使用自定义的回调函数",
+ description =
+ "具体实现类参考: https://gitee.com/zhangyutongxue/easy-retry-demo/blob/master/easy-retry-springboot/src/main/java/com/maluxinyu/easyretry/customized/OrderCompleteCallback.java\n"
+ +
+ "🥇什么情况下触发回调: https://www.easyretry.com/pages/97cde9/#%E2%9A%A1%E5%9B%9E%E8%B0%83\n"
+ +
+ "📢查看任务列表: http://preview.easyretry.com/#/retry-task/list"
+ )
+ @PostMapping("/retryWithCallback/{scene}")
+ public void remoteRetryWithCallback(@Parameter(name = "scene", description = "场景 FINISH/MAX_COUNT",
+ schema = @Schema(type = "string", description = "测试参数", defaultValue = "FINISH"))
+ @PathVariable("scene") String scene, @RequestBody OrderVo orderVo) {
+ remoteRetryService.remoteRetryWithCompleteCallback(scene, orderVo);
+ }
+
+ /**
+ * 指定bizNo
+ */
+ @Operation(
+ summary = "指定bizNo",
+ description = "🥇什么是bizNo: https://www.easyretry.com/pages/540554/#bizno%E7%94%9F%E6%88%90%E5%99%A8\n"
+ +
+ "📢查看任务列表: http://preview.easyretry.com/#/retry-task/list"
+ )
+ @PostMapping("/remoteRetryWithBizNo")
+ public void remoteRetryWithBizNo(@RequestBody OrderVo orderVo) {
+ remoteRetryService.remoteRetryWithBizNo(orderVo);
+ }
+
+ @GetMapping("/localRetryWithTwoRetryMethod")
+ /**
+ *
+ * 方法内部存在两个串行的方法retryMethod1、retryMethod1 如下所属
+ * public boolean localRetryWithTwoRetryMethod(final String params) {
+ * retryHandler.retryMethod1(params);
+ * retryHandler.retryMethod1(params);
+ * return true;
+ * }
+ * params: 1 => 则retryMethod1触发重试
+ * params: 2 => 则retryMethod2触发重试
+ * params: 3 => 则retryMethod1随机触发重试, 若retryMethod1重试成功,则retryMethod2一定触发重试否则只触发retryMethod1重试
+ *
+ */
+ @Operation(
+ summary = "N个串行执行的方法触发重试的场景",
+ description = "方法内部存在两个串行的方法retryMethod1、retryMethod1\n" +
+ "params: 1 => 则retryMethod1触发重试\n" +
+ "params: 2 => 则retryMethod2触发重试\n" +
+ "params: 3 => 则retryMethod1随机触发重试, 若retryMethod1重试成功,则retryMethod2一定触发重试否则只触发retryMethod1重试"
+ )
+ public boolean localRetryWithTwoRetryMethod(@RequestParam("params") String params) {
+ return remoteRetryService.localRetryWithTwoRetryMethod(params);
+ }
+
+ /**
+ * 外部方法传播机制为REQUIRED,内部方法传播机制为REQUIRED,
+ * 只执行入口方法重试
+ */
+ @GetMapping("/localRetryWithPropagationRequired")
+ @Operation(
+ description = "外部方法传播机制为REQUIRED,内部方法传播机制为REQUIRED, 只执行入口方法重试",
+ summary = "外部方法传播机制为REQUIRED,内部方法传播机制为REQUIRED, 只执行入口方法重试"
+ )
+ public boolean localRetryWithPropagationRequired(@RequestParam("params") String params) {
+ return remoteRetryService.localRetryWithPropagationRequired(params);
+ }
+
+ /**
+ * 外部方法传播机制为REQUIRED,内部方法传播机制为REQUIRED_NEW,
+ * 外部和内部方法都触发重试
+ */
+ @GetMapping("/localRetryWithPropagationRequiredNew")
+ @Operation(
+ description = "外部方法传播机制为REQUIRED,内部方法传播机制为REQUIRED_NEW,外部和内部方法都触发重试",
+ summary = "外部方法传播机制为REQUIRED,内部方法传播机制为REQUIRED_NEW,外部和内部方法都触发重试"
+ )
+ public boolean localRetryWithPropagationRequiredNew(@RequestParam("params") String params) {
+ return remoteRetryService.localRetryWithPropagationRequiredNew(params);
+ }
+
+ /**
+ * 内部方法传播机制为REQUIRED_NEW,且异常被try catch捕获,内部方法触发重试,外部方法不会触发重试
+ */
+ @GetMapping("/localRetryWithTryCatch1")
+ @Operation(
+ description = "",
+ summary = "内部方法传播机制为REQUIRED_NEW,且异常被try catch捕获,内部方法触发重试,外部方法不会触发重试"
+ )
+ public boolean localRetryWithTryCatch1(@RequestParam("params") String params) {
+ return remoteRetryService.localRetryWithTryCatch1(params);
+ }
+
+ /**
+ * 内部方法传播机制为REQUIRED,且异常被try catch捕获,内部方法不会触发重试,外部方法也不会触发重试
+ */
+ @GetMapping("/localRetryWithTryCatch2")
+ @Operation(
+ description = "",
+ summary = "内部方法传播机制为REQUIRED,且异常被try catch捕获,内部方法不会触发重试,外部方法也不会触发重试"
+ )
+ public boolean localRetryWithTryCatch2(@RequestParam("params") String params) {
+ return remoteRetryService.localRetryWithTryCatch2(params);
+ }
+}
diff --git a/src/main/java/com/example/controller/WorkflowCallbackController.java b/src/main/java/com/example/controller/WorkflowCallbackController.java
new file mode 100644
index 0000000..689af74
--- /dev/null
+++ b/src/main/java/com/example/controller/WorkflowCallbackController.java
@@ -0,0 +1,26 @@
+package com.example.controller;
+
+import com.aizuda.snailjob.server.model.dto.CallbackParamsDTO;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpHeaders;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * @author xiaowoniu
+ * @date 2024-01-03 21:09:14
+ * @since 2.6.0
+ */
+@RestController
+@RequestMapping("/workflow/callback")
+@Slf4j
+@Tag(name = "工作流回调", description = "工作流回调")
+public class WorkflowCallbackController {
+
+ @PostMapping
+ public void callback(@RequestBody List object, @RequestHeader HttpHeaders headers) {
+ log.info("callback: {}, secret:{} secret:{}", object, "secret", headers.getFirst("secret"));
+ }
+}
diff --git a/src/main/java/com/example/controller/WorkflowController.java b/src/main/java/com/example/controller/WorkflowController.java
new file mode 100644
index 0000000..3039104
--- /dev/null
+++ b/src/main/java/com/example/controller/WorkflowController.java
@@ -0,0 +1,40 @@
+package com.example.controller;
+
+import com.example.handler.TestTriggerJobHandler;
+import com.example.handler.TestUpdateJobStatusHandler;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * @author opensnail
+ * @date 2024-10-19 10:41:25
+ * @since sj_1.2.0-beta2
+ */
+@RestController
+@RequestMapping("/open-api/workflow")
+@Tag(name = "JobOpenApi", description = "通过OpenApi可以灵活的实现对的Workflow触发和更新状态功能")
+@RequiredArgsConstructor
+public class WorkflowController {
+ private final TestTriggerJobHandler testTriggerJobHandler;
+ private final TestUpdateJobStatusHandler testUpdateJobStatusHandler;
+
+ @Operation(
+ description = "手动触发任务"
+ )
+ @PostMapping("/trigger/{id}")
+ public Boolean triggerJob(@PathVariable("id") Long id) {
+ return testTriggerJobHandler.triggerWorkFlow(id);
+ }
+
+ @Operation(
+ description = "根据id更新任务的状态",
+ summary = "0:关闭 1:开启"
+ )
+ @PutMapping("/update/status/{id}/{status}")
+ public Boolean updateJob(@PathVariable("id") Long id, @PathVariable("status") Long status) {
+ return testUpdateJobStatusHandler.updateWorkFlowStatus(id, status);
+ }
+}
diff --git a/src/main/java/com/example/customized/MultiParamIdempotentGenerate.java b/src/main/java/com/example/customized/MultiParamIdempotentGenerate.java
new file mode 100644
index 0000000..880ebf2
--- /dev/null
+++ b/src/main/java/com/example/customized/MultiParamIdempotentGenerate.java
@@ -0,0 +1,25 @@
+package com.example.customized;
+
+import com.aizuda.snailjob.client.core.IdempotentIdGenerate;
+import com.aizuda.snailjob.common.core.model.IdempotentIdContext;
+import com.example.vo.OrderVo;
+
+import cn.hutool.crypto.SecureUtil;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class MultiParamIdempotentGenerate implements IdempotentIdGenerate {
+
+ @Override
+ public String idGenerate(IdempotentIdContext idempotentIdContext) throws Exception {
+ Object[] args = idempotentIdContext.getArgs();
+ String uuid = (String) args[0];
+ Integer intVal = (Integer) args[1];
+ Double doubleVal = (Double) args[2];
+ Character character = (Character) args[3];
+ OrderVo orderVo = (OrderVo) args[4];
+ log.info("测试多参数解析,String类型:{},Integer类型:{},Double类型:{}," +
+ "Character类型:{},对象类型:{}", uuid, intVal, doubleVal, character, orderVo);
+ return SecureUtil.md5(orderVo.getOrderId());
+ }
+}
diff --git a/src/main/java/com/example/customized/OrderCompleteCallback.java b/src/main/java/com/example/customized/OrderCompleteCallback.java
new file mode 100644
index 0000000..78b1b88
--- /dev/null
+++ b/src/main/java/com/example/customized/OrderCompleteCallback.java
@@ -0,0 +1,57 @@
+package com.example.customized;
+
+import cn.hutool.json.JSONUtil;
+import com.aizuda.snailjob.client.core.callback.RetryCompleteCallback;
+import com.aizuda.snailjob.common.core.util.JsonUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.example.dao.FailOrderBaseMapper;
+import com.example.po.FailOrderPo;
+import com.example.vo.OrderVo;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Slf4j
+@Component
+public class OrderCompleteCallback implements RetryCompleteCallback {
+
+ @Autowired
+ private FailOrderBaseMapper failOrderBaseMapper;
+
+ /**
+ * 重试成功后的回调函数
+ * 参数1-场景名称
+ * 参数2-执行器名称
+ * 参数3-入参信息
+ */
+ @Override
+ public void doSuccessCallback(String sceneName, String executorName, Object[] objects) {
+ // 重试成功后删除失败表中的数据
+ OrderVo orderVo = JsonUtil.parseObject(JsonUtil.toJsonString(objects[1]), OrderVo.class);
+ log.info("远程重试成功,场景{},执行器{},参数信息", sceneName, executorName, JSONUtil.toJsonStr(objects));
+ failOrderBaseMapper.delete(
+ new LambdaQueryWrapper()
+ .eq(FailOrderPo::getOrderId, orderVo.getOrderId())
+ );
+ }
+
+ /**
+ * 重试达到最大次数后的回调函数
+ * 参数1-场景名称
+ * 参数2-执行器名称
+ * 参数3-入参信息
+ */
+ @Override
+ public void doMaxRetryCallback(String sceneName, String executorName, Object[] objects) {
+ OrderVo orderVo = JsonUtil.parseObject(JsonUtil.toJsonString(objects[1]), OrderVo.class);
+ log.info("远程重试达到最大限度,场景{},执行器{},参数信息", sceneName, executorName, JSONUtil.toJsonStr(objects));
+ // 重试失败后插入订单失败信息
+ failOrderBaseMapper.insert(FailOrderPo.builder()
+ .orderId(orderVo.getOrderId())
+ .sourceId(orderVo.getSource())
+ .sceneName(sceneName)
+ .executorName(executorName)
+ .args(JSONUtil.toJsonStr(objects))
+ .build());
+ }
+}
diff --git a/src/main/java/com/example/customized/OrderIdempotentIdGenerate.java b/src/main/java/com/example/customized/OrderIdempotentIdGenerate.java
new file mode 100644
index 0000000..d43366d
--- /dev/null
+++ b/src/main/java/com/example/customized/OrderIdempotentIdGenerate.java
@@ -0,0 +1,19 @@
+package com.example.customized;
+
+import com.aizuda.snailjob.client.core.IdempotentIdGenerate;
+import com.aizuda.snailjob.common.core.model.IdempotentIdContext;
+import com.example.vo.OrderVo;
+
+import cn.hutool.crypto.SecureUtil;
+
+
+public class OrderIdempotentIdGenerate implements IdempotentIdGenerate {
+
+ @Override
+ public String idGenerate(IdempotentIdContext idempotentIdContext) throws Exception {
+ Object[] args = idempotentIdContext.getArgs();
+ OrderVo orderVo = (OrderVo) args[0];
+ return SecureUtil.md5(orderVo.getOrderId());
+ }
+
+}
diff --git a/src/main/java/com/example/customized/OrderRetryMethod.java b/src/main/java/com/example/customized/OrderRetryMethod.java
new file mode 100644
index 0000000..60dfdd8
--- /dev/null
+++ b/src/main/java/com/example/customized/OrderRetryMethod.java
@@ -0,0 +1,22 @@
+package com.example.customized;
+
+import org.springframework.stereotype.Component;
+
+import com.aizuda.snailjob.client.core.strategy.ExecutorMethod;
+import com.example.vo.OrderVo;
+
+import cn.hutool.json.JSONUtil;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Component
+public class OrderRetryMethod implements ExecutorMethod {
+ @Override
+ public Object doExecute(Object params) {
+ // 将特定类型的 Object 对象指定为 Object[]
+ Object[] args = (Object[]) params;
+ OrderVo orderVo = (OrderVo) args[0];
+ log.info("进入指定自定义的异常处理类, 参数信息是{}", JSONUtil.toJsonStr(orderVo));
+ throw new ArithmeticException("自定义的异常处理类处理");
+ }
+}
diff --git a/src/main/java/com/example/customized/SingleParamIdempotentGenerate.java b/src/main/java/com/example/customized/SingleParamIdempotentGenerate.java
new file mode 100644
index 0000000..f3bd60e
--- /dev/null
+++ b/src/main/java/com/example/customized/SingleParamIdempotentGenerate.java
@@ -0,0 +1,16 @@
+package com.example.customized;
+
+import com.aizuda.snailjob.client.core.IdempotentIdGenerate;
+import com.aizuda.snailjob.common.core.model.IdempotentIdContext;
+
+import cn.hutool.crypto.SecureUtil;
+
+public class SingleParamIdempotentGenerate implements IdempotentIdGenerate {
+
+ @Override
+ public String idGenerate(IdempotentIdContext idempotentIdContext) throws Exception {
+ Object[] args = idempotentIdContext.getArgs();
+ String params = (String) args[0];
+ return SecureUtil.md5(params);
+ }
+}
diff --git a/src/main/java/com/example/dao/FailOrderBaseMapper.java b/src/main/java/com/example/dao/FailOrderBaseMapper.java
new file mode 100644
index 0000000..b5fed83
--- /dev/null
+++ b/src/main/java/com/example/dao/FailOrderBaseMapper.java
@@ -0,0 +1,11 @@
+package com.example.dao;
+
+import org.springframework.stereotype.Repository;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.example.po.FailOrderPo;
+
+@Repository
+public interface FailOrderBaseMapper extends BaseMapper {
+
+}
diff --git a/src/main/java/com/example/dao/PhoneNumberBaseMapper.java b/src/main/java/com/example/dao/PhoneNumberBaseMapper.java
new file mode 100644
index 0000000..94f6fa3
--- /dev/null
+++ b/src/main/java/com/example/dao/PhoneNumberBaseMapper.java
@@ -0,0 +1,15 @@
+package com.example.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.example.po.PhoneNumberPo;
+import org.springframework.stereotype.Repository;
+
+/**
+ * 手机号mapper
+ *
+ * @author JiChenWang
+ * @since 2024/6/30 11:55
+ */
+@Repository
+public interface PhoneNumberBaseMapper extends BaseMapper {
+}
diff --git a/src/main/java/com/example/dao/PhoneNumberDao.java b/src/main/java/com/example/dao/PhoneNumberDao.java
new file mode 100644
index 0000000..c26cb72
--- /dev/null
+++ b/src/main/java/com/example/dao/PhoneNumberDao.java
@@ -0,0 +1,34 @@
+package com.example.dao;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.example.po.PhoneNumberPo;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * TODO
+ *
+ * @author JiChenWang
+ * @since 2024/6/30 11:58
+ */
+@Service
+public class PhoneNumberDao extends ServiceImpl {
+
+ @Autowired
+ private PhoneNumberBaseMapper phoneNumberBaseMapper;
+
+ /**
+ * 批量保存手机号信息
+ *
+ * @param phoneNumberPoList 手机号po列表
+ * @return Boolean 保存成功标识:true-成功、false-失败
+ * @author JichenWang
+ * @since 2024/6/30 12:03
+ */
+ public Boolean insertBatch(List phoneNumberPoList) {
+ return this.saveBatch(phoneNumberPoList);
+ }
+
+}
diff --git a/src/main/java/com/example/exception/BusinessException.java b/src/main/java/com/example/exception/BusinessException.java
new file mode 100644
index 0000000..3c14602
--- /dev/null
+++ b/src/main/java/com/example/exception/BusinessException.java
@@ -0,0 +1,12 @@
+package com.example.exception;
+
+/**
+ * 业务异常类
+ */
+public class BusinessException extends RuntimeException {
+
+ public BusinessException(String message) {
+ super(message);
+ }
+
+}
diff --git a/src/main/java/com/example/exception/ParamException.java b/src/main/java/com/example/exception/ParamException.java
new file mode 100644
index 0000000..e129674
--- /dev/null
+++ b/src/main/java/com/example/exception/ParamException.java
@@ -0,0 +1,11 @@
+package com.example.exception;
+
+/**
+ * 参数异常处理类
+ */
+public class ParamException extends BusinessException {
+ public ParamException(String message) {
+ super(message);
+ }
+
+}
diff --git a/src/main/java/com/example/executor/ManualRetryExecutorTask.java b/src/main/java/com/example/executor/ManualRetryExecutorTask.java
new file mode 100644
index 0000000..e6552cc
--- /dev/null
+++ b/src/main/java/com/example/executor/ManualRetryExecutorTask.java
@@ -0,0 +1,26 @@
+package com.example.executor;
+
+import com.aizuda.snailjob.client.core.annotation.ExecutorMethodRegister;
+import com.aizuda.snailjob.client.core.strategy.ExecutorMethod;
+import com.example.vo.OrderVo;
+
+import cn.hutool.json.JSONUtil;
+import lombok.extern.slf4j.Slf4j;
+
+@ExecutorMethodRegister(scene = ManualRetryExecutorTask.SCENE)
+@Slf4j
+public class ManualRetryExecutorTask implements ExecutorMethod {
+ /**
+ * 自定义场景值
+ */
+ public final static String SCENE = "manualRetry";
+
+ @Override
+ public Object doExecute(Object params) {
+ // 将特定类型的 Object 对象指定为 Object[]
+ Object[] args = (Object[]) params;
+ OrderVo orderVo = JSONUtil.toBean(JSONUtil.toJsonStr(args[0]), OrderVo.class);
+ log.info("进入手动重试方法,参数信息是{}", JSONUtil.toJsonStr(orderVo));
+ return true;
+ }
+}
diff --git a/src/main/java/com/example/handler/LocalAndRemoteRetryHandler.java b/src/main/java/com/example/handler/LocalAndRemoteRetryHandler.java
new file mode 100644
index 0000000..e579d65
--- /dev/null
+++ b/src/main/java/com/example/handler/LocalAndRemoteRetryHandler.java
@@ -0,0 +1,59 @@
+package com.example.handler;
+
+import com.aizuda.snailjob.client.core.annotation.Propagation;
+import com.aizuda.snailjob.client.core.annotation.Retryable;
+import com.aizuda.snailjob.client.core.retryer.RetryType;
+import org.springframework.stereotype.Component;
+
+import java.util.Random;
+
+/**
+ * @author: xiaowoniu
+ * @date : 2024-02-05
+ * @since : 3.1.0
+ */
+@Component
+public class LocalAndRemoteRetryHandler {
+
+ @Retryable(scene = "localRetryWithTwoRetryMethod1", retryStrategy = RetryType.LOCAL_REMOTE)
+ public void retryMethod1(String params) {
+ System.out.println("localRetryWithTwoRetryMethod1");
+ if (params.equals("1")) {
+ throw new RuntimeException("抛出异常");
+ }
+
+ if (params.equals("3")) {
+ int random = new Random().nextInt(10);
+ if (random % 3 == 0) {
+ System.out.println("localRetryWithTwoRetryMethod1 is success");
+ return;
+ }
+
+ throw new RuntimeException("抛出异常");
+ }
+ }
+
+ @Retryable(scene = "localRetryWithTwoRetryMethod2", retryStrategy = RetryType.LOCAL_REMOTE)
+ public void retryMethod2(String params) {
+ System.out.println("localRetryWithTwoRetryMethod2");
+ if (params.equals("2")) {
+ throw new RuntimeException("抛出异常");
+ }
+
+ if (params.equals("3")) {
+ throw new RuntimeException("抛出异常");
+ }
+ }
+
+ @Retryable(scene = "localRetryWithRequires", retryStrategy = RetryType.LOCAL_REMOTE)
+ public void localRetryWithRequires(String params) {
+ System.out.println("local retry 方法开始执行");
+ double i = 1 / 0;
+ }
+
+ @Retryable(scene = "localRetryWithRequiresNew", retryStrategy = RetryType.LOCAL_REMOTE, propagation = Propagation.REQUIRES_NEW)
+ public void localRetryWithRequiresNew(String params) {
+ System.out.println("local retry 方法开始执行");
+ double i = 1 / 0;
+ }
+}
diff --git a/src/main/java/com/example/handler/OnlyLocalRetryHandler.java b/src/main/java/com/example/handler/OnlyLocalRetryHandler.java
new file mode 100644
index 0000000..fbbb98d
--- /dev/null
+++ b/src/main/java/com/example/handler/OnlyLocalRetryHandler.java
@@ -0,0 +1,59 @@
+package com.example.handler;
+
+import com.aizuda.snailjob.client.core.annotation.Propagation;
+import com.aizuda.snailjob.client.core.annotation.Retryable;
+import com.aizuda.snailjob.client.core.retryer.RetryType;
+import org.springframework.stereotype.Component;
+
+import java.util.Random;
+
+/**
+ * @author: xiaowoniu
+ * @date : 2024-02-05
+ * @since : 3.1.0
+ */
+@Component
+public class OnlyLocalRetryHandler {
+
+ @Retryable(scene = "localRetryWithTwoRetryMethod1", retryStrategy = RetryType.ONLY_LOCAL)
+ public void retryMethod1(String params) {
+ System.out.println("localRetryWithTwoRetryMethod1");
+ if (params.equals("1")) {
+ throw new RuntimeException("抛出异常");
+ }
+
+ if (params.equals("3")) {
+ int random = new Random().nextInt(10);
+ if (random % 3 == 0) {
+ System.out.println("localRetryWithTwoRetryMethod1 is success");
+ return;
+ }
+
+ throw new RuntimeException("抛出异常");
+ }
+ }
+
+ @Retryable(scene = "localRetryWithTwoRetryMethod2", retryStrategy = RetryType.ONLY_LOCAL)
+ public void retryMethod2(String params) {
+ System.out.println("localRetryWithTwoRetryMethod2");
+ if (params.equals("2")) {
+ throw new RuntimeException("抛出异常");
+ }
+
+ if (params.equals("3")) {
+ throw new RuntimeException("抛出异常");
+ }
+ }
+
+ @Retryable(scene = "localRetry", retryStrategy = RetryType.ONLY_LOCAL)
+ public void localRetry(String params) {
+ System.out.println("local retry 方法开始执行");
+ double i = 1 / 0;
+ }
+
+ @Retryable(scene = "localRetryWithRequiresNew", retryStrategy = RetryType.ONLY_LOCAL, propagation = Propagation.REQUIRES_NEW)
+ public void localRetryWithRequiresNew(String params) {
+ System.out.println("local retry 方法开始执行");
+ double i = 1 / 0;
+ }
+}
diff --git a/src/main/java/com/example/handler/OnlyRemoteRetryHandler.java b/src/main/java/com/example/handler/OnlyRemoteRetryHandler.java
new file mode 100644
index 0000000..c0b7363
--- /dev/null
+++ b/src/main/java/com/example/handler/OnlyRemoteRetryHandler.java
@@ -0,0 +1,59 @@
+package com.example.handler;
+
+import com.aizuda.snailjob.client.core.annotation.Propagation;
+import com.aizuda.snailjob.client.core.annotation.Retryable;
+import com.aizuda.snailjob.client.core.retryer.RetryType;
+import org.springframework.stereotype.Component;
+
+import java.util.Random;
+
+/**
+ * @author: xiaowoniu
+ * @date : 2024-02-05
+ * @since : 3.1.0
+ */
+@Component
+public class OnlyRemoteRetryHandler {
+
+ @Retryable(scene = "localRetryWithTwoRetryMethod1", retryStrategy = RetryType.ONLY_LOCAL)
+ public void retryMethod1(String params) {
+ System.out.println("localRetryWithTwoRetryMethod1");
+ if (params.equals("1")) {
+ throw new RuntimeException("抛出异常");
+ }
+
+ if (params.equals("3")) {
+ int random = new Random().nextInt(10);
+ if (random % 3 == 0) {
+ System.out.println("localRetryWithTwoRetryMethod1 is success");
+ return;
+ }
+
+ throw new RuntimeException("抛出异常");
+ }
+ }
+
+ @Retryable(scene = "localRetryWithTwoRetryMethod2", retryStrategy = RetryType.ONLY_LOCAL)
+ public void retryMethod2(String params) {
+ System.out.println("localRetryWithTwoRetryMethod2");
+ if (params.equals("2")) {
+ throw new RuntimeException("抛出异常");
+ }
+
+ if (params.equals("3")) {
+ throw new RuntimeException("抛出异常");
+ }
+ }
+
+ @Retryable(scene = "localRetryWithRequires", retryStrategy = RetryType.ONLY_LOCAL)
+ public void localRetryWithRequires(String params) {
+ System.out.println("local retry 方法开始执行");
+ double i = 1 / 0;
+ }
+
+ @Retryable(scene = "localRetryWithRequiresNew", retryStrategy = RetryType.ONLY_LOCAL, propagation = Propagation.REQUIRES_NEW)
+ public void localRetryWithRequiresNew(String params) {
+ System.out.println("local retry 方法开始执行");
+ double i = 1 / 0;
+ }
+}
diff --git a/src/main/java/com/example/handler/TestAddJobHandler.java b/src/main/java/com/example/handler/TestAddJobHandler.java
new file mode 100644
index 0000000..385ea96
--- /dev/null
+++ b/src/main/java/com/example/handler/TestAddJobHandler.java
@@ -0,0 +1,126 @@
+package com.example.handler;
+
+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.openapi.SnailJobOpenApi;
+import com.aizuda.snailjob.common.core.enums.BlockStrategyEnum;
+import org.springframework.stereotype.Component;
+
+@Component
+public class TestAddJobHandler {
+
+ /**
+ * 新增集群模式的任务
+ *
+ * @param jobName 任务名称
+ * @return 任务id
+ */
+ public Long addClusterJob(String jobName) {
+ return SnailJobOpenApi.addClusterJob()
+ .setRouteKey(AllocationAlgorithmEnum.RANDOM)
+ .setJobName(jobName)
+ .setExecutorInfo("testJobExecutor")
+ .setExecutorTimeout(30)
+ .setDescription("add")
+ .setBlockStrategy(BlockStrategyEnum.DISCARD)
+ .setMaxRetryTimes(1)
+ .setTriggerType(TriggerTypeEnum.SCHEDULED_TIME)
+ .setTriggerInterval(String.valueOf(60))
+ .addArgsStr("测试数据", 123)
+ .addArgsStr("addArg", "args")
+ .setRetryInterval(3)
+ .execute();
+
+ }
+
+ /**
+ * 新增集群模式的任务
+ *
+ * @param jobName 任务名称
+ * @return 任务id
+ */
+ public Long addBroadcastJob(String jobName) {
+ return SnailJobOpenApi.addBroadcastJob()
+ .setJobName(jobName)
+ .setExecutorInfo("testJobExecutor")
+ .setExecutorTimeout(30)
+ .setDescription("add")
+ .setBlockStrategy(BlockStrategyEnum.DISCARD)
+ .setMaxRetryTimes(1)
+ .setTriggerType(TriggerTypeEnum.CRON)
+ .setTriggerInterval("afas")
+ .addArgsStr("测试数据", 123)
+ .addArgsStr("addArg", "args")
+ .setRetryInterval(3)
+ .execute();
+
+ }
+
+ /**
+ * 新增Sharding模式的任务
+ *
+ * @param jobName 任务名称
+ * @return 任务id
+ */
+ public Long addShardingJob(String jobName) {
+ return SnailJobOpenApi.addShardingJob()
+ .setJobName(jobName)
+ .setExecutorInfo("testJobExecutor")
+ .setExecutorTimeout(30)
+ .setDescription("add")
+ .setBlockStrategy(BlockStrategyEnum.DISCARD)
+ .setMaxRetryTimes(1)
+ .setTriggerType(TriggerTypeEnum.SCHEDULED_TIME)
+ .setTriggerInterval(60)
+ .addShardingArgs("分片1", "分片2", "分片3")
+ .setParallelNum(1)
+ .setRetryInterval(3)
+ .execute();
+
+ }
+
+ /**
+ * 新增MapReduce模式的任务
+ *
+ * @param jobName 任务名称
+ * @return 任务id
+ */
+ public Long addMapJob(String jobName) {
+ return SnailJobOpenApi.addMapJob()
+ .setJobName(jobName)
+ .setExecutorInfo("testJobExecutor")
+ .setExecutorTimeout(30)
+ .setDescription("add")
+ .setBlockStrategy(BlockStrategyEnum.DISCARD)
+ .setMaxRetryTimes(1)
+ .setTriggerType(TriggerTypeEnum.SCHEDULED_TIME)
+ .setTriggerInterval(String.valueOf(60))
+ .setParallelNum(3)
+ .setRetryInterval(3)
+ .execute();
+
+ }
+
+ /**
+ * 新增MapReduce模式的任务
+ *
+ * @param jobName 任务名称
+ * @return 任务id
+ */
+ public Long addMapReduceJob(String jobName) {
+ return SnailJobOpenApi.addMapReduceJob()
+ .setJobName(jobName)
+ .setExecutorInfo("testJobExecutor")
+ .setExecutorTimeout(30)
+ .setDescription("add")
+ .setBlockStrategy(BlockStrategyEnum.DISCARD)
+ .setMaxRetryTimes(1)
+ .setTriggerType(TriggerTypeEnum.SCHEDULED_TIME)
+ .setTriggerInterval(String.valueOf(60))
+ .setParallelNum(3)
+ .setShardNum(2)
+ .setRetryInterval(3)
+ .execute();
+
+ }
+}
diff --git a/src/main/java/com/example/handler/TestQueryJobHandler.java b/src/main/java/com/example/handler/TestQueryJobHandler.java
new file mode 100644
index 0000000..23b8eb0
--- /dev/null
+++ b/src/main/java/com/example/handler/TestQueryJobHandler.java
@@ -0,0 +1,19 @@
+package com.example.handler;
+
+import com.aizuda.snailjob.client.job.core.dto.JobResponseVO;
+import com.aizuda.snailjob.client.job.core.openapi.SnailJobOpenApi;
+import org.springframework.stereotype.Component;
+
+@Component
+public class TestQueryJobHandler {
+
+ /**
+ * 查看任务详情
+ *
+ * @param jobId
+ * @return 任务详情
+ */
+ public JobResponseVO queryJob(Long jobId) {
+ return SnailJobOpenApi.getJobDetail(jobId).execute();
+ }
+}
diff --git a/src/main/java/com/example/handler/TestTriggerJobHandler.java b/src/main/java/com/example/handler/TestTriggerJobHandler.java
new file mode 100644
index 0000000..d93bd4e
--- /dev/null
+++ b/src/main/java/com/example/handler/TestTriggerJobHandler.java
@@ -0,0 +1,28 @@
+package com.example.handler;
+
+import com.aizuda.snailjob.client.job.core.openapi.SnailJobOpenApi;
+import org.springframework.stereotype.Component;
+
+@Component
+public class TestTriggerJobHandler {
+
+ /**
+ * 手动调度任务
+ *
+ * @param jobId 任务ID
+ * @return
+ */
+ public Boolean triggerJob(Long jobId) {
+ return SnailJobOpenApi.triggerJob(jobId).execute();
+ }
+
+ /**
+ * 手动调度工作流任务
+ *
+ * @param workFlowId 工作流任务ID
+ * @return
+ */
+ public Boolean triggerWorkFlow(Long workFlowId) {
+ return SnailJobOpenApi.triggerWorkFlow(workFlowId).execute();
+ }
+}
diff --git a/src/main/java/com/example/handler/TestUpdateJobHandler.java b/src/main/java/com/example/handler/TestUpdateJobHandler.java
new file mode 100644
index 0000000..290ad5b
--- /dev/null
+++ b/src/main/java/com/example/handler/TestUpdateJobHandler.java
@@ -0,0 +1,77 @@
+package com.example.handler;
+
+import com.aizuda.snailjob.client.job.core.enums.TriggerTypeEnum;
+import com.aizuda.snailjob.client.job.core.openapi.SnailJobOpenApi;
+import org.springframework.stereotype.Component;
+
+import java.util.Random;
+
+@Component
+public class TestUpdateJobHandler {
+
+ /**
+ * 新增集群模式的任务
+ *
+ * @return 任务id
+ */
+ public Boolean updateClusterJob(Long jobId) {
+ return SnailJobOpenApi.updateClusterJob(jobId)
+ .setMaxRetryTimes(1)
+ .setTriggerType(TriggerTypeEnum.SCHEDULED_TIME)
+ .setTriggerInterval(String.valueOf(60))
+ .addArgsStr("update测试数据", new Random().nextInt(1000))
+ .addArgsStr("updateArg", "args")
+ .setRetryInterval(3)
+ .execute();
+
+ }
+
+ /**
+ * 新增集群模式的任务
+ *
+ * @return 任务id
+ */
+ public Boolean updateBroadcastJob(Long jobId) {
+ return SnailJobOpenApi.updateBroadcastJob(jobId)
+ .addArgsStr("update测试数据", new Random().nextInt(1000))
+ .execute();
+
+ }
+
+ /**
+ * 新增Sharding模式的任务
+ *
+ * @return 任务id
+ */
+ public Boolean updateShardingJob(Long jobId) {
+ return SnailJobOpenApi.updateShardingJob(jobId)
+ .addShardingArgs("update分片1", "update分片2", "update分片3")
+ .execute();
+
+ }
+
+ /**
+ * 新增MapReduce模式的任务
+ *
+ * @return 任务id
+ */
+ public Boolean updateMapJob(Long jobId) {
+ return SnailJobOpenApi.updateMapJob(jobId)
+ .addArgsStr("update测试数据", new Random().nextInt(1000))
+ .setParallelNum(3)
+ .execute();
+
+ }
+
+ /**
+ * 新增MapReduce模式的任务
+ *
+ * @return 任务id
+ */
+ public Boolean updateMapReduceJob(Long jobId) {
+ return SnailJobOpenApi.updateMapReduceJob(jobId)
+ .addArgsStr("update测试数据", new Random().nextInt(1000))
+ .execute();
+
+ }
+}
diff --git a/src/main/java/com/example/handler/TestUpdateJobStatusHandler.java b/src/main/java/com/example/handler/TestUpdateJobStatusHandler.java
new file mode 100644
index 0000000..9041be2
--- /dev/null
+++ b/src/main/java/com/example/handler/TestUpdateJobStatusHandler.java
@@ -0,0 +1,36 @@
+package com.example.handler;
+
+import com.aizuda.snailjob.client.job.core.openapi.SnailJobOpenApi;
+import com.aizuda.snailjob.common.core.enums.StatusEnum;
+import org.springframework.stereotype.Component;
+
+@Component
+public class TestUpdateJobStatusHandler {
+
+ /**
+ * 更新定时任务状态
+ *
+ * @param jobId 定时任务ID
+ * @return
+ */
+ public Boolean updateJobStatus(Long jobId, Long status) {
+ return SnailJobOpenApi
+ .updateJobStatus(jobId)
+ .setStatus(StatusEnum.YES.getStatus().equals(status.intValue()) ? StatusEnum.YES : StatusEnum.NO)
+ .execute();
+ }
+
+ /**
+ * 更新工作流任务状态
+ *
+ * @param workFlowId 工作流ID
+ * @param status
+ * @return
+ */
+ public Boolean updateWorkFlowStatus(Long workFlowId, Long status) {
+ return SnailJobOpenApi
+ .updateWorkFlowStatus(workFlowId)
+ .setStatus(StatusEnum.YES.getStatus().equals(status.intValue()) ? StatusEnum.YES : StatusEnum.NO)
+ .execute();
+ }
+}
diff --git a/src/main/java/com/example/job/TestAnnoJobExecutor.java b/src/main/java/com/example/job/TestAnnoJobExecutor.java
new file mode 100644
index 0000000..777f599
--- /dev/null
+++ b/src/main/java/com/example/job/TestAnnoJobExecutor.java
@@ -0,0 +1,20 @@
+package com.example.job;
+
+import com.aizuda.snailjob.client.job.core.annotation.JobExecutor;
+import com.aizuda.snailjob.client.job.core.dto.JobArgs;
+import com.aizuda.snailjob.client.model.ExecuteResult;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author www.byteblogs.com
+ * @date 2023-09-28 22:54:07
+ * @since 2.4.0
+ */
+@Component
+@JobExecutor(name = "testJobExecutor")
+public class TestAnnoJobExecutor {
+
+ public ExecuteResult jobExecute(JobArgs jobArgs) {
+ return ExecuteResult.success("测试成功");
+ }
+}
diff --git a/src/main/java/com/example/job/TestAnnoJobExecutorSleep10s.java b/src/main/java/com/example/job/TestAnnoJobExecutorSleep10s.java
new file mode 100644
index 0000000..a9300e2
--- /dev/null
+++ b/src/main/java/com/example/job/TestAnnoJobExecutorSleep10s.java
@@ -0,0 +1,27 @@
+package com.example.job;
+
+import com.aizuda.snailjob.client.job.core.annotation.JobExecutor;
+import com.aizuda.snailjob.client.job.core.dto.JobArgs;
+import com.aizuda.snailjob.client.model.ExecuteResult;
+import com.aizuda.snailjob.common.core.util.JsonUtil;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author www.byteblogs.com
+ * @date 2023-09-28 22:54:07
+ * @since 2.4.0
+ */
+@Component
+@JobExecutor(name = "testAnnoJobExecutorSleep10s")
+public class TestAnnoJobExecutorSleep10s {
+
+ public ExecuteResult jobExecute(JobArgs jobArgs) {
+ System.out.println(JsonUtil.toJsonString(jobArgs));
+ try {
+ Thread.sleep(10 * 1000);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ return ExecuteResult.success("测试成功");
+ }
+}
diff --git a/src/main/java/com/example/job/TestAnnoJobExecutorSleep1s.java b/src/main/java/com/example/job/TestAnnoJobExecutorSleep1s.java
new file mode 100644
index 0000000..51e8f8f
--- /dev/null
+++ b/src/main/java/com/example/job/TestAnnoJobExecutorSleep1s.java
@@ -0,0 +1,27 @@
+package com.example.job;
+
+import com.aizuda.snailjob.client.job.core.annotation.JobExecutor;
+import com.aizuda.snailjob.client.job.core.dto.JobArgs;
+import com.aizuda.snailjob.client.model.ExecuteResult;
+import com.aizuda.snailjob.common.core.util.JsonUtil;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author www.byteblogs.com
+ * @date 2023-09-28 22:54:07
+ * @since 2.4.0
+ */
+@Component
+@JobExecutor(name = "testAnnoJobExecutorSleep1s")
+public class TestAnnoJobExecutorSleep1s {
+
+ public ExecuteResult jobExecute(JobArgs jobArgs) {
+ System.out.println(JsonUtil.toJsonString(jobArgs));
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ return ExecuteResult.success("测试成功");
+ }
+}
diff --git a/src/main/java/com/example/job/TestAnnoJobExecutorSleep30s.java b/src/main/java/com/example/job/TestAnnoJobExecutorSleep30s.java
new file mode 100644
index 0000000..a3e0afb
--- /dev/null
+++ b/src/main/java/com/example/job/TestAnnoJobExecutorSleep30s.java
@@ -0,0 +1,27 @@
+package com.example.job;
+
+import com.aizuda.snailjob.client.job.core.annotation.JobExecutor;
+import com.aizuda.snailjob.client.job.core.dto.JobArgs;
+import com.aizuda.snailjob.client.model.ExecuteResult;
+import com.aizuda.snailjob.common.core.util.JsonUtil;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author www.byteblogs.com
+ * @date 2023-09-28 22:54:07
+ * @since 2.4.0
+ */
+@Component
+@JobExecutor(name = "testAnnoJobExecutorSleep30s")
+public class TestAnnoJobExecutorSleep30s {
+
+ public ExecuteResult jobExecute(JobArgs jobArgs) {
+ System.out.println(JsonUtil.toJsonString(jobArgs));
+ try {
+ Thread.sleep(30 * 1000);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ return ExecuteResult.success("测试成功");
+ }
+}
diff --git a/src/main/java/com/example/job/TestAnnoJobExecutorSleep5s.java b/src/main/java/com/example/job/TestAnnoJobExecutorSleep5s.java
new file mode 100644
index 0000000..0a17c17
--- /dev/null
+++ b/src/main/java/com/example/job/TestAnnoJobExecutorSleep5s.java
@@ -0,0 +1,30 @@
+package com.example.job;
+
+import com.aizuda.snailjob.client.job.core.annotation.JobExecutor;
+import com.aizuda.snailjob.client.job.core.dto.JobArgs;
+import com.aizuda.snailjob.client.model.ExecuteResult;
+import com.aizuda.snailjob.common.core.util.JsonUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author www.byteblogs.com
+ * @date 2023-09-28 22:54:07
+ * @since 2.4.0
+ */
+@Component
+@Slf4j
+@JobExecutor(name = "testAnnoJobExecutorSleep5s")
+public class TestAnnoJobExecutorSleep5s {
+
+ public ExecuteResult jobExecute(JobArgs jobArgs) {
+ log.info("testAnnoJobExecutorSleep5s. jobArgs:{}", JsonUtil.toJsonString(jobArgs));
+
+ try {
+ Thread.sleep(5 * 1000);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ return ExecuteResult.success("测试成功");
+ }
+}
diff --git a/src/main/java/com/example/job/TestAnnoMapJobExecutor.java b/src/main/java/com/example/job/TestAnnoMapJobExecutor.java
new file mode 100644
index 0000000..4f6da29
--- /dev/null
+++ b/src/main/java/com/example/job/TestAnnoMapJobExecutor.java
@@ -0,0 +1,40 @@
+package com.example.job;
+
+import com.aizuda.snailjob.client.job.core.MapHandler;
+import com.aizuda.snailjob.client.job.core.annotation.JobExecutor;
+import com.aizuda.snailjob.client.job.core.annotation.MapExecutor;
+import com.aizuda.snailjob.client.job.core.dto.MapArgs;
+import com.aizuda.snailjob.client.model.ExecuteResult;
+import com.google.common.collect.Lists;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author: opensnail
+ * @date : 2024-06-26
+ */
+@Component
+@JobExecutor(name = "testAnnoMapJobExecutor")
+public class TestAnnoMapJobExecutor {
+
+ @MapExecutor
+ public ExecuteResult rootMapExecute(MapArgs mapArgs, MapHandler mapHandler) {
+ System.out.println(mapArgs);
+ System.out.println(mapArgs.getWfContext());
+ return mapHandler.doMap(Lists.newArrayList("1", "2", "3"), "MONTH_MAP");
+ }
+
+ @MapExecutor(taskName = "MONTH_MAP")
+ public ExecuteResult monthMapExecute(MapArgs mapArgs) {
+ System.out.println(mapArgs);
+ System.out.println(mapArgs.getWfContext());
+ return ExecuteResult.success(123);
+ }
+
+ @MapExecutor(taskName = "LAST_MAP")
+ public ExecuteResult lastMapExecute(MapArgs mapArgs, MapHandler mapHandler) {
+ System.out.println(mapArgs);
+ System.out.println(mapArgs.getWfContext());
+ return ExecuteResult.success();
+ }
+
+}
diff --git a/src/main/java/com/example/job/TestAnnoMapReduceJobExecutor.java b/src/main/java/com/example/job/TestAnnoMapReduceJobExecutor.java
new file mode 100644
index 0000000..c976281
--- /dev/null
+++ b/src/main/java/com/example/job/TestAnnoMapReduceJobExecutor.java
@@ -0,0 +1,69 @@
+package com.example.job;
+
+import com.aizuda.snailjob.client.job.core.MapHandler;
+import com.aizuda.snailjob.client.job.core.annotation.JobExecutor;
+import com.aizuda.snailjob.client.job.core.annotation.MapExecutor;
+import com.aizuda.snailjob.client.job.core.annotation.MergeReduceExecutor;
+import com.aizuda.snailjob.client.job.core.annotation.ReduceExecutor;
+import com.aizuda.snailjob.client.job.core.dto.MapArgs;
+import com.aizuda.snailjob.client.job.core.dto.MergeReduceArgs;
+import com.aizuda.snailjob.client.job.core.dto.ReduceArgs;
+import com.aizuda.snailjob.client.model.ExecuteResult;
+import com.aizuda.snailjob.common.core.util.JsonUtil;
+import com.google.common.collect.Lists;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @author: opensnail
+ * @date : 2024-06-26
+ */
+@Component
+@JobExecutor(name = "testAnnoMapReduceJobExecutor")
+public class TestAnnoMapReduceJobExecutor {
+
+ @MapExecutor
+ public ExecuteResult rootMapExecute(MapArgs mapArgs, MapHandler mapHandler) {
+ System.out.println(mapArgs);
+ System.out.println(mapArgs.getWfContext());
+ mapArgs.appendContext("name", "zsg");
+ return mapHandler.doMap(Lists.newArrayList("1", "2", "3", "4", "5", "6"), "MONTH_MAP");
+ }
+
+ @MapExecutor(taskName = "MONTH_MAP")
+ public ExecuteResult monthMapExecute(MapArgs mapArgs) {
+ System.out.println(mapArgs);
+ System.out.println(mapArgs.getWfContext());
+ mapArgs.appendContext("age", "111");
+ return ExecuteResult.success(Integer.parseInt((String) mapArgs.getMapResult()) * 2);
+ }
+
+ @ReduceExecutor
+ public ExecuteResult reduceExecute(ReduceArgs mapReduceArgs) {
+ System.out.println(mapReduceArgs);
+ System.out.println(mapReduceArgs.getWfContext());
+ return ExecuteResult.success(
+ mapReduceArgs.getMapResult()
+ .stream()
+ .map(String::valueOf)
+ .map(Integer::parseInt)
+ .mapToInt(Integer::intValue).sum());
+ }
+
+ /**
+ * 当只有一个reduce任务时无此执行器
+ */
+ @MergeReduceExecutor
+ public ExecuteResult mergeReduceExecute(MergeReduceArgs mergeReduceArgs) {
+ System.out.println(mergeReduceArgs);
+ System.out.println(mergeReduceArgs.getWfContext());
+ return ExecuteResult.success(
+ mergeReduceArgs.getReduces()
+ .stream()
+ .map(String::valueOf)
+ .map(Integer::parseInt)
+ .mapToInt(Integer::intValue).sum());
+ }
+}
diff --git a/src/main/java/com/example/job/TestBroadcastJobExecutor.java b/src/main/java/com/example/job/TestBroadcastJobExecutor.java
new file mode 100644
index 0000000..faf506e
--- /dev/null
+++ b/src/main/java/com/example/job/TestBroadcastJobExecutor.java
@@ -0,0 +1,30 @@
+package com.example.job;
+
+/**
+ * @author zhengweilin
+ * @version 1.0.0
+ * @date 2024/01/22
+ */
+
+import com.aizuda.snailjob.client.job.core.annotation.JobExecutor;
+import com.aizuda.snailjob.client.job.core.dto.JobArgs;
+import com.aizuda.snailjob.client.model.ExecuteResult;
+import com.aizuda.snailjob.common.core.util.JsonUtil;
+import com.example.po.FailOrderPo;
+import org.springframework.stereotype.Component;
+
+import java.util.Random;
+
+@Component
+@JobExecutor(name = "testBroadcastJobExecutor")
+public class TestBroadcastJobExecutor {
+
+ public ExecuteResult jobExecute(JobArgs jobArgs) {
+ if (new Random().nextInt(9) % 5 == 0) {
+ throw new NullPointerException("广播节点抛异常了" + JsonUtil.toJsonString(jobArgs));
+ }
+ FailOrderPo failOrderPo = new FailOrderPo();
+ failOrderPo.setOrderId("xiaowoniu");
+ return ExecuteResult.success(failOrderPo);
+ }
+}
diff --git a/src/main/java/com/example/job/TestClassJobExecutor.java b/src/main/java/com/example/job/TestClassJobExecutor.java
new file mode 100644
index 0000000..404dcdb
--- /dev/null
+++ b/src/main/java/com/example/job/TestClassJobExecutor.java
@@ -0,0 +1,20 @@
+package com.example.job;
+
+import com.aizuda.snailjob.client.job.core.dto.JobArgs;
+import com.aizuda.snailjob.client.job.core.executor.AbstractJobExecutor;
+import com.aizuda.snailjob.client.model.ExecuteResult;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author www.byteblogs.com
+ * @date 2023-09-28 22:54:07
+ * @since 2.4.0
+ */
+@Component
+public class TestClassJobExecutor extends AbstractJobExecutor {
+
+ @Override
+ protected ExecuteResult doJobExecute(JobArgs jobArgs) {
+ return ExecuteResult.success("TestJobExecutor测试成功");
+ }
+}
diff --git a/src/main/java/com/example/job/TestExcelAnalyseMapJobExecutor.java b/src/main/java/com/example/job/TestExcelAnalyseMapJobExecutor.java
new file mode 100644
index 0000000..a82e5ba
--- /dev/null
+++ b/src/main/java/com/example/job/TestExcelAnalyseMapJobExecutor.java
@@ -0,0 +1,104 @@
+package com.example.job;
+
+import cn.hutool.core.util.ObjectUtil;
+import com.aizuda.snailjob.client.job.core.MapHandler;
+import com.aizuda.snailjob.client.job.core.annotation.JobExecutor;
+import com.aizuda.snailjob.client.job.core.annotation.MapExecutor;
+import com.aizuda.snailjob.client.job.core.dto.MapArgs;
+import com.aizuda.snailjob.client.model.ExecuteResult;
+import com.alibaba.excel.EasyExcel;
+import com.example.bo.PhoneNumberBo;
+import com.example.bo.PhoneNumberCheckBo;
+import com.example.dao.PhoneNumberDao;
+import com.example.listener.PhoneNumberExcelListener;
+import com.example.po.PhoneNumberPo;
+import lombok.Cleanup;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 解析excel文件中的手机号码,并将错误的手机号分片入库
+ *
+ * @author JiChenWang
+ * @since 2024/6/30 10:37
+ */
+@Slf4j
+@Component
+@JobExecutor(name = "testExcelAnalyseMapJobExecutor")
+public class TestExcelAnalyseMapJobExecutor {
+
+ private final Integer BATCH_SIZE = 100;
+
+ @Autowired
+ private PhoneNumberDao phoneNumberDao;
+
+ /**
+ * 读取手机号文件总行数,并进行分组
+ * 比如文档中的手机号总量为307条,每100条一个分组,分组结果为[{0,99}, {100, 199}, {200,299}, {300, 307}]
+ *
+ * @return ExecuteResult
+ * @author JichenWang
+ * @since 2024/6/30 10:48
+ */
+ @MapExecutor
+ public ExecuteResult rootMapExecute(MapArgs mapArgs, MapHandler> mapHandler) {
+ List> ranges = null;
+ // 先获取文件总行数,便于分组
+ try {
+ @Cleanup InputStream numberInputStream = getClass().getClassLoader().getResourceAsStream("doc/number.xlsx");
+ final PhoneNumberCheckBo phoneNumberCheckBo = new PhoneNumberCheckBo();
+ PhoneNumberExcelListener phoneNumberExcelListener = new PhoneNumberExcelListener(phoneNumberCheckBo, true, BATCH_SIZE);
+ EasyExcel.read(numberInputStream, PhoneNumberBo.class, phoneNumberExcelListener).sheet().headRowNumber(1).doReadSync();
+
+ // 设置区间范围
+ ranges = TestMapReduceJobExecutor.doSharding(0L, phoneNumberCheckBo.getTotal(), BATCH_SIZE);
+ } catch (Exception e) {
+ log.error("文件读取异常", e.getMessage());
+ }
+ return mapHandler.doMap(ranges, "TWO_MAP");
+
+ }
+
+ /**
+ * @param mapArgs
+ * @return ExecuteResult
+ * @author JichenWang
+ * @since 2024/6/30 11:05
+ */
+ @MapExecutor(taskName = "TWO_MAP")
+ public ExecuteResult monthMapExecute(MapArgs mapArgs) {
+ // 获取本次要处理的区间
+ final List mapResult = (List) mapArgs.getMapResult();
+ log.info("本次要处理的区间为:{}", mapResult);
+
+ // 按照处理区间,去读取数据
+ final PhoneNumberCheckBo phoneNumberCheckBo = new PhoneNumberCheckBo();
+ try {
+ @Cleanup InputStream numberInputStream = getClass().getClassLoader().getResourceAsStream("doc/number.xlsx");
+ PhoneNumberExcelListener phoneNumberExcelListener = new PhoneNumberExcelListener(phoneNumberCheckBo, false, BATCH_SIZE);
+ EasyExcel.read(numberInputStream, PhoneNumberBo.class, phoneNumberExcelListener).sheet().headRowNumber(mapResult.get(0) + 1).doReadSync();
+ } catch (Exception e) {
+ log.error("文件读取异常:", e.getMessage());
+ }
+
+ // 如果正确手机号不为空,则入库
+ if (ObjectUtil.isNotEmpty(phoneNumberCheckBo.getCheckErrors())) {
+
+ List numberPos = new ArrayList<>();
+ for (String no : phoneNumberCheckBo.getCheckErrors()) {
+ PhoneNumberPo numberPo = new PhoneNumberPo();
+ numberPo.setPhoneNumber(no);
+ numberPos.add(numberPo);
+ }
+ phoneNumberDao.insertBatch(numberPos);
+ }
+
+ return ExecuteResult.success(phoneNumberCheckBo.getError());
+ }
+
+}
diff --git a/src/main/java/com/example/job/TestExcelAnalyseMapReduceJobExecutor.java b/src/main/java/com/example/job/TestExcelAnalyseMapReduceJobExecutor.java
new file mode 100644
index 0000000..a464e9b
--- /dev/null
+++ b/src/main/java/com/example/job/TestExcelAnalyseMapReduceJobExecutor.java
@@ -0,0 +1,132 @@
+package com.example.job;
+
+import com.aizuda.snailjob.client.job.core.MapHandler;
+import com.aizuda.snailjob.client.job.core.annotation.JobExecutor;
+import com.aizuda.snailjob.client.job.core.annotation.MapExecutor;
+import com.aizuda.snailjob.client.job.core.annotation.MergeReduceExecutor;
+import com.aizuda.snailjob.client.job.core.annotation.ReduceExecutor;
+import com.aizuda.snailjob.client.job.core.dto.MapArgs;
+import com.aizuda.snailjob.client.job.core.dto.MergeReduceArgs;
+import com.aizuda.snailjob.client.job.core.dto.ReduceArgs;
+import com.aizuda.snailjob.client.model.ExecuteResult;
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.fastjson2.JSONArray;
+import com.example.bo.PhoneNumberBo;
+import com.example.bo.PhoneNumberCheckBo;
+import com.example.listener.PhoneNumberExcelListener;
+import lombok.Cleanup;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 解析校验Excel中的手机号,统计出错手机号数量,并返回错误手机号详情
+ *
+ * @author JichenWang
+ * @since 2024/6/27 19:52
+ */
+@Slf4j
+@Component
+@JobExecutor(name = "TestExcelAnalyseMapReduceJobExecutor")
+public class TestExcelAnalyseMapReduceJobExecutor {
+
+ private final Integer BATCH_SIZE = 100;
+
+ /**
+ * 处理手机号文件信息,将文档中的手机号进行分组
+ * 比如文档中的手机号总量为307条,每100条一个分组,分组结果为[{0,99}, {100, 199}, {200,299}, {300, 307}]
+ *
+ * @return ExecuteResult
+ * @author JichenWang
+ * @since 2024/6/29 14:03
+ */
+ @MapExecutor
+ public ExecuteResult rootMapExecute(MapArgs mapArgs, MapHandler> mapHandler) {
+ List> ranges = null;
+ // 先获取文件总行数,便于分组
+ try {
+ @Cleanup InputStream numberInputStream = getClass().getClassLoader().getResourceAsStream("doc/number.xlsx");
+ final PhoneNumberCheckBo phoneNumberCheckBo = new PhoneNumberCheckBo();
+ PhoneNumberExcelListener phoneNumberExcelListener = new PhoneNumberExcelListener(phoneNumberCheckBo, true, BATCH_SIZE);
+ EasyExcel.read(numberInputStream, PhoneNumberBo.class, phoneNumberExcelListener).sheet().headRowNumber(1).doReadSync();
+
+ // 设置区间范围
+ ranges = TestMapReduceJobExecutor.doSharding(0L, phoneNumberCheckBo.getTotal(), BATCH_SIZE);
+ } catch (Exception e) {
+ log.error("文件读取异常", e.getMessage());
+ }
+ return mapHandler.doMap(ranges, "TWO_MAP");
+ }
+
+ /**
+ * 处理每个分组内容,如读取{0,99}区间的手机号,并解析
+ *
+ * @return ExecuteResult
+ * @author JichenWang
+ * @since 2024/6/29 14:04
+ */
+ @MapExecutor(taskName = "TWO_MAP")
+ public ExecuteResult monthMapExecute(MapArgs mapArgs) {
+ // 获取本次要处理的区间
+ final List mapResult = (List) mapArgs.getMapResult();
+ log.info("本次要处理的区间为:{}", mapResult);
+
+ // 按照处理区间,去读取数据
+ final PhoneNumberCheckBo phoneNumberCheckBo = new PhoneNumberCheckBo();
+ try {
+ @Cleanup InputStream numberInputStream = getClass().getClassLoader().getResourceAsStream("doc/number.xlsx");
+ PhoneNumberExcelListener phoneNumberExcelListener = new PhoneNumberExcelListener(phoneNumberCheckBo, false, BATCH_SIZE);
+ EasyExcel.read(numberInputStream, PhoneNumberBo.class, phoneNumberExcelListener).sheet().headRowNumber(mapResult.get(0) + 1).doReadSync();
+ } catch (Exception e) {
+ log.error("文件读取异常:", e.getMessage());
+ }
+
+ return ExecuteResult.success(phoneNumberCheckBo);
+ }
+
+
+ @ReduceExecutor
+ public ExecuteResult reduceExecute(ReduceArgs mapReduceArgs) {
+ log.info("WJC Test reduceExecute, 参数为:{}", mapReduceArgs.getMapResult());
+ final PhoneNumberCheckBo phoneNumberCheckBo = this.buildGatherPhoneNumberCheckBo(mapReduceArgs.getMapResult().toString());
+ return ExecuteResult.success(phoneNumberCheckBo);
+ }
+
+ /**
+ * 当只有一个reduce任务时无此执行器
+ */
+ @MergeReduceExecutor
+ public ExecuteResult mergeReduceExecute(MergeReduceArgs mergeReduceArgs) {
+ final PhoneNumberCheckBo phoneNumberCheckBo = this.buildGatherPhoneNumberCheckBo(mergeReduceArgs.getReduces().toString());
+ log.info("WJC 最终检测结果为:{}", phoneNumberCheckBo);
+ return ExecuteResult.success(phoneNumberCheckBo);
+ }
+
+ /**
+ * 构造汇总手机号校验结果BO
+ *
+ * @param phoneNumberCheckBoStr 手机号校验BO字符串
+ * @return PhoneNumberCheckBo 汇总手机号校验结果BO
+ * @author JichenWang
+ * @since 2024/6/29 14:24
+ */
+ private PhoneNumberCheckBo buildGatherPhoneNumberCheckBo(String phoneNumberCheckBoStr) {
+ final List phoneNumberCheckBoList = JSONArray.parseArray(phoneNumberCheckBoStr, PhoneNumberCheckBo.class);
+ // 获取校验总数
+ final long checkTotalNum = phoneNumberCheckBoList.get(0).getTotal();
+ // 汇总校验失败数量
+ final long checkErrorNum = phoneNumberCheckBoList.stream().mapToLong(PhoneNumberCheckBo::getError).sum();
+ // 汇总校验成功数量
+ final long checkSuccessNum = phoneNumberCheckBoList.stream().mapToLong(PhoneNumberCheckBo::getSuccess).sum();
+ // 汇总错误手机号
+ final List errorPhoneNumberList = new ArrayList<>();
+ phoneNumberCheckBoList.forEach(item -> errorPhoneNumberList.addAll(item.getCheckErrors()));
+
+ // 汇总手机号校验结果
+ return PhoneNumberCheckBo.builder().total(checkTotalNum).error(checkErrorNum).success(checkSuccessNum).checkErrors(errorPhoneNumberList).build();
+ }
+
+}
diff --git a/src/main/java/com/example/job/TestMapJobExecutor.java b/src/main/java/com/example/job/TestMapJobExecutor.java
new file mode 100644
index 0000000..2f1cf0f
--- /dev/null
+++ b/src/main/java/com/example/job/TestMapJobExecutor.java
@@ -0,0 +1,162 @@
+package com.example.job;
+
+import com.aizuda.snailjob.client.job.core.MapHandler;
+import com.aizuda.snailjob.client.job.core.dto.MapArgs;
+
+import com.aizuda.snailjob.client.job.core.executor.AbstractMapExecutor;
+import com.aizuda.snailjob.client.model.ExecuteResult;
+import com.aizuda.snailjob.common.core.util.JsonUtil;
+import com.aizuda.snailjob.common.log.SnailJobLog;
+import com.example.job.TestMapReduceJobExecutor.QuarterMap.SubTask;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.google.common.collect.Lists;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Random;
+
+/**
+ * 以下是一个统计某电商公司商家的一年的营业额的计算过程
+ *
+ * @author: opensnail
+ * @date : 2024-06-13
+ * @since : sj_1.1.0
+ */
+@Component
+public class TestMapJobExecutor extends AbstractMapExecutor {
+
+ @Override
+ public ExecuteResult doJobMapExecute(MapArgs mapArgs, MapHandler mapHandler) {
+ MapEnum mapEnum = MapEnum.ofMap(mapArgs.getTaskName());
+ if (Objects.nonNull(mapEnum) && Objects.nonNull(mapEnum.getMap())) {
+ Map map = mapEnum.getMap();
+ MapEnum nextMap = mapEnum.getNextMap();
+ String nextName = null;
+ if (Objects.nonNull(nextMap)) {
+ nextName = nextMap.name();
+ }
+
+ return mapHandler.doMap(map.map(mapArgs), nextName);
+ }
+
+ // 未找到map的任务,则说明当前需要进行处理
+ JsonNode json = JsonUtil.toJson(mapArgs.getMapResult());
+ SnailJobLog.LOCAL.info("LAST_MAP 开始执行 mapResult:{}", json);
+ // 获取最后一次map的信息.
+ SubTask subTask = JsonUtil.parseObject(json.toString(), SubTask.class);
+ // 此处可以统计数据或者做其他的事情
+ // 模拟统计营业额
+ int turnover = new Random().nextInt(1000000);
+ return ExecuteResult.success(turnover);
+
+ }
+
+ @Getter
+ private enum MapEnum {
+ LAST_MAP(null, null),
+ QUARTER_MAP(new QuarterMap(), LAST_MAP),
+ MAP_ROOT(new RootMap(), QUARTER_MAP),
+ ;
+
+ private final Map map;
+ private final MapEnum nextMap;
+
+ MapEnum(Map map, MapEnum nextMap) {
+ this.map = map;
+ this.nextMap = nextMap;
+ }
+
+ public static MapEnum ofMap(String taskName) {
+ for (final MapEnum value : MapEnum.values()) {
+ if (value.name().equals(taskName)) {
+ return value;
+ }
+ }
+
+ return null;
+ }
+
+ }
+
+ private static class RootMap implements Map {
+
+ @Override
+ public List map(MapArgs args) {
+ // 第一层按照商家ID分片
+ // 假设总共有一百万商家 每个分片处理10万商家
+ List> ranges = doSharding(1L, 1000000L, 100000);
+ return ranges;
+ }
+ }
+
+ public static class QuarterMap implements Map {
+
+ @Override
+ public List map(MapArgs args) {
+
+ // 第二层按照月分片
+ // 4个季度
+ JsonNode json = JsonUtil.toJson(args.getMapResult());
+ List list = new ArrayList<>();
+ for (JsonNode jsonNode : json) {
+ long id = jsonNode.asLong();
+ for (int i = 1; i <= 4; i++) {
+ list.add(new TestMapReduceJobExecutor.QuarterMap.SubTask(id, i));
+ }
+ }
+ return list;
+ }
+
+ @Data
+ @AllArgsConstructor
+ @NoArgsConstructor
+ public static class SubTask {
+ // 商家id
+ private Long id;
+
+ // 需要处理的月份
+ private Integer quarter;
+
+ }
+ }
+
+ interface Map {
+ List