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 map(MapArgs args); + } + + public static List> doSharding(Long min, Long max, int interval) { + + if (max.equals(min)) { + return new ArrayList<>(); + } + + // 总数量 + long total = max - min + 1; + + // 计算分页总页数 + Long totalPages = total / interval; + if (total % interval != 0) { + totalPages++; + } + + List> partitions = new ArrayList<>(); + for (Long index = 0L; index < totalPages; index++) { + + // 计算起始点 因为是从min开始所以每次需要加上一个min + Long start = index * interval + min; + + // 结算结束点 若最后一个 start + interval - 1 > max 取max + // 减一是保证 [start, end] 都是闭区间 + Long end = Math.min(start + interval - 1, max); + partitions.add(Lists.newArrayList(start, end)); + } + + return partitions; + } +} diff --git a/src/main/java/com/example/job/TestMapReduceJobExecutor.java b/src/main/java/com/example/job/TestMapReduceJobExecutor.java new file mode 100644 index 0000000..a3515c6 --- /dev/null +++ b/src/main/java/com/example/job/TestMapReduceJobExecutor.java @@ -0,0 +1,176 @@ +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.dto.MergeReduceArgs; +import com.aizuda.snailjob.client.job.core.dto.ReduceArgs; +import com.aizuda.snailjob.client.job.core.executor.AbstractMapReduceExecutor; +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 TestMapReduceJobExecutor extends AbstractMapReduceExecutor { + + @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); + + } + + @Override + protected ExecuteResult doReduceExecute(ReduceArgs reduceArgs) { + return ExecuteResult.success(reduceArgs.getMapResult()); + } + + @Override + protected ExecuteResult doMergeReduceExecute(MergeReduceArgs reduceArgs) { + List reduces = reduceArgs.getReduces(); + SnailJobLog.LOCAL.info("merge reduce {}", reduces); + return ExecuteResult.success(111); + } + + @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 SubTask(id, i)); + } + } + return list; + } + + @Data + @AllArgsConstructor + @NoArgsConstructor + public static class SubTask { + // 商家id + private Long id; + + // 需要处理的月份 + private Integer quarter; + + } + } + + interface Map { + List map(MapArgs args); + } + + public static List> doSharding(Long min, Long max, int interval) { + + if (max.equals(min)) { + return new ArrayList<>(); + } + + // 总数量 + long total = max - min + 1; + + // 计算分页总页数 + Long totalPages = total / interval; + if (total % interval != 0) { + totalPages++; + } + + List> partitions = new ArrayList<>(); + for (Long index = 0L; index < totalPages; index++) { + + // 计算起始点 因为是从min开始所以每次需要加上一个min + Long start = index * interval + min; + + // 结算结束点 若最后一个 start + interval - 1 > max 取max + // 减一是保证 [start, end] 都是闭区间 + Long end = Math.min(start + interval - 1, max); + partitions.add(Lists.newArrayList(start, end)); + } + + return partitions; + } +} diff --git a/src/main/java/com/example/job/TestMyMapExecutor.java b/src/main/java/com/example/job/TestMyMapExecutor.java new file mode 100644 index 0000000..ddf1cc9 --- /dev/null +++ b/src/main/java/com/example/job/TestMyMapExecutor.java @@ -0,0 +1,33 @@ +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.dto.MergeReduceArgs; +import com.aizuda.snailjob.client.job.core.dto.ReduceArgs; +import com.aizuda.snailjob.client.job.core.executor.AbstractMapExecutor; +import com.aizuda.snailjob.client.job.core.executor.AbstractMapReduceExecutor; +import com.aizuda.snailjob.client.model.ExecuteResult; +import com.aizuda.snailjob.common.core.constant.SystemConstants; +import com.google.common.collect.Lists; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * @author opensnail + * @date 2024-07-07 12:06:57 + * @since sj_1.1.0 + */ +@Component +public class TestMyMapExecutor extends AbstractMapExecutor { + + @Override + public ExecuteResult doJobMapExecute(MapArgs mapArgs, MapHandler mapHandler) { + if (SystemConstants.ROOT_MAP.equals(mapArgs.getTaskName())) { + return mapHandler.doMap(Lists.newArrayList("1", "2", "3", "4"), "TWO_MAP"); + } + + return ExecuteResult.success(mapArgs.getMapResult()); + } + +} diff --git a/src/main/java/com/example/job/TestMyMapReduceExecutor.java b/src/main/java/com/example/job/TestMyMapReduceExecutor.java new file mode 100644 index 0000000..ebba70c --- /dev/null +++ b/src/main/java/com/example/job/TestMyMapReduceExecutor.java @@ -0,0 +1,45 @@ +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.dto.MergeReduceArgs; +import com.aizuda.snailjob.client.job.core.dto.ReduceArgs; +import com.aizuda.snailjob.client.job.core.executor.AbstractMapReduceExecutor; +import com.aizuda.snailjob.client.model.ExecuteResult; +import com.aizuda.snailjob.common.core.constant.SystemConstants; +import com.google.common.collect.Lists; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * @author opensnail + * @date 2024-07-07 12:06:57 + * @since sj_1.1.0 + */ +@Component +public class TestMyMapReduceExecutor extends AbstractMapReduceExecutor { + + @Override + public ExecuteResult doJobMapExecute(MapArgs mapArgs, MapHandler mapHandler) { + if (SystemConstants.ROOT_MAP.equals(mapArgs.getTaskName())) { + return mapHandler.doMap(Lists.newArrayList("1", "2", "3", "4"), "TWO_MAP"); + } + + return ExecuteResult.success(mapArgs.getMapResult()); + } + + @Override + protected ExecuteResult doReduceExecute(ReduceArgs reduceArgs) { + List mapResult = (List) reduceArgs.getMapResult(); + return ExecuteResult.success(mapResult.stream().mapToInt(Integer::parseInt).sum()); + } + + @Override + protected ExecuteResult doMergeReduceExecute(MergeReduceArgs mergeReduceArgs) { + List reduces = (List) mergeReduceArgs.getReduces(); + return ExecuteResult.success(reduces.stream().mapToInt(Integer::parseInt).sum()); + } + + +} diff --git a/src/main/java/com/example/job/TestPartitionJobExecutor.java b/src/main/java/com/example/job/TestPartitionJobExecutor.java new file mode 100644 index 0000000..e7e2097 --- /dev/null +++ b/src/main/java/com/example/job/TestPartitionJobExecutor.java @@ -0,0 +1,27 @@ +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.example.po.FailOrderPo; +import org.springframework.stereotype.Component; + +@Component +@JobExecutor(name = "testPartitionJobExecutor") +public class TestPartitionJobExecutor { + + public ExecuteResult jobExecute(JobArgs jobArgs) { + if (jobArgs.getJobParams().equals("1")) { + throw new NullPointerException("分区空指针抛异常了"); + } + FailOrderPo failOrderPo = new FailOrderPo(); + failOrderPo.setOrderId("xiaowoniu"); + return ExecuteResult.success(failOrderPo); + } +} diff --git a/src/main/java/com/example/job/TestSyncLogPrintExecutor.java b/src/main/java/com/example/job/TestSyncLogPrintExecutor.java new file mode 100644 index 0000000..c157592 --- /dev/null +++ b/src/main/java/com/example/job/TestSyncLogPrintExecutor.java @@ -0,0 +1,52 @@ +package com.example.job; + +import com.aizuda.snailjob.client.common.log.report.LogMeta; +import com.aizuda.snailjob.client.common.log.support.SnailJobLogManager; +import com.aizuda.snailjob.client.job.core.annotation.JobExecutor; +import com.aizuda.snailjob.client.job.core.dto.JobArgs; +import com.aizuda.snailjob.client.job.core.executor.JobContextManager; +import com.aizuda.snailjob.client.model.ExecuteResult; +import com.aizuda.snailjob.common.core.model.JobContext; +import com.aizuda.snailjob.common.log.SnailJobLog; +import com.aizuda.snailjob.common.log.enums.LogTypeEnum; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.concurrent.TimeUnit; + +/** + * @author PT + * @date 2024-09-02 上午10:50 + * @Description 线程异步情况下日志打印,场景:定时调用接口更新数据, + * 由于数据量每天大小不一致,无法界定执行时间,所以需要异步执行,但是异步执行时日志也需要进行打印 + */ +@Slf4j +@Component +public class TestSyncLogPrintExecutor { + + @JobExecutor(name = "TestSyncLogPrintExecutor") + public ExecuteResult jobExecute(JobArgs jobArgs) { + JobContext jobContext = JobContextManager.getJobContext(); + LogMeta logMeta = SnailJobLogManager.getLogMeta(); + LogTypeEnum logType = SnailJobLogManager.getLogType(); + + //此处简单模拟开启新线程执行,实际生产建议使用线程池执行 + new Thread(() -> { + JobContextManager.setJobContext(jobContext); + SnailJobLogManager.setLogMeta(logMeta); + SnailJobLogManager.setLogType(logType); + //执行业务 + doSomething(); + }); + return ExecuteResult.success(); + } + + private void doSomething() { + try { + TimeUnit.SECONDS.sleep(10); + SnailJobLog.REMOTE.info("测试异步打印"); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/main/java/com/example/job/TestWorkflowAnnoJobExecutor.java b/src/main/java/com/example/job/TestWorkflowAnnoJobExecutor.java new file mode 100644 index 0000000..f92ba24 --- /dev/null +++ b/src/main/java/com/example/job/TestWorkflowAnnoJobExecutor.java @@ -0,0 +1,33 @@ +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 com.aizuda.snailjob.common.log.SnailJobLog; +import com.example.po.FailOrderPo; +import org.springframework.stereotype.Component; + +import java.util.Random; + +/** + * @author www.byteblogs.com + * @date 2023-09-28 22:54:07 + * @since 2.6.0 + */ +@Component +@JobExecutor(name = "testWorkflowAnnoJobExecutor") +public class TestWorkflowAnnoJobExecutor { + + public ExecuteResult jobExecute(JobArgs jobArgs) throws InterruptedException { + FailOrderPo failOrderPo = new FailOrderPo(); + failOrderPo.setOrderId("xiaowoniu"); + // 测试上下文传递 + int i = new Random().nextInt(1000); + jobArgs.appendContext("name" + i, "小蜗牛" + i); + jobArgs.appendContext("age", i); + SnailJobLog.REMOTE.info("上下文: {}", jobArgs.getWfContext()); + return ExecuteResult.success(failOrderPo); + } + +} diff --git a/src/main/java/com/example/listener/MySnailJobListener.java b/src/main/java/com/example/listener/MySnailJobListener.java new file mode 100644 index 0000000..87a0933 --- /dev/null +++ b/src/main/java/com/example/listener/MySnailJobListener.java @@ -0,0 +1,26 @@ +package com.example.listener; + +import com.aizuda.snailjob.client.core.event.SnailJobListener; + +/** + * @author opensnail + * @date 2024-05-19 17:58:25 + * @since sj_1.0.0 + */ +public class MySnailJobListener implements SnailJobListener { + + @Override + public void beforeRetry(String sceneName, String executorClassName, Object[] params) { + + } + + @Override + public void successOnRetry(Object result, String sceneName, String executorClassName) { + + } + + @Override + public void failureOnRetry(String sceneName, String executorClassName, Throwable e) { + + } +} diff --git a/src/main/java/com/example/listener/PhoneNumberExcelListener.java b/src/main/java/com/example/listener/PhoneNumberExcelListener.java new file mode 100644 index 0000000..9f5343e --- /dev/null +++ b/src/main/java/com/example/listener/PhoneNumberExcelListener.java @@ -0,0 +1,84 @@ +package com.example.listener; + +import cn.hutool.core.lang.Validator; +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.example.bo.PhoneNumberBo; +import com.example.bo.PhoneNumberCheckBo; +import lombok.extern.slf4j.Slf4j; + +import java.util.Map; + +/** + * 手机号excel解析 Listener + * + * @author JiChenWang + * @since 2024/6/27 20:38 + */ +@Slf4j +public class PhoneNumberExcelListener extends AnalysisEventListener { + + /** + * 手机号校验BO + **/ + private PhoneNumberCheckBo phoneNumberCheckBo; + + /** + * 是否第一次读取Excel + **/ + private Boolean firstReadStatus = false; + + /** + * 读取批次大小 + **/ + private Integer batchSize = 100; + + /** + * 已读取的数据数量 + **/ + private Integer cacheSize = 0; + + @Override + public void invokeHeadMap(Map headMap, AnalysisContext context) { + this.phoneNumberCheckBo.setTotal(Long.parseLong(String.valueOf(context.readSheetHolder().getApproximateTotalRowNumber() - 1))); + } + + @Override + public void invoke(PhoneNumberBo phoneNumberBo, AnalysisContext context) { + // 如果是第一次读该文件,已读取的数量已超过读取批次大小,直接返回 + if (firstReadStatus || cacheSize >= batchSize) { + return; + } + + cacheSize++; + + if (ObjectUtil.isEmpty(phoneNumberBo.getPhoneNumber())) { + return; + } + + // 校验手机号 + log.info("本次校验的手机号为: {}", phoneNumberBo.getPhoneNumber()); + Boolean validateStatus = Validator.isMobile(phoneNumberBo.getPhoneNumber()); + if (validateStatus) { + this.phoneNumberCheckBo.setSuccess(this.phoneNumberCheckBo.getSuccess() + 1); +// final PhoneNumberPo phoneNumberPo = PhoneNumberPo.builder().phoneNumber(phoneNumberBo.getPhoneNumber()).createTime(LocalDateTime.now()).build(); +// this.phoneNumberCheckBo.getCheckSuccessPhoneNumberList().add(phoneNumberPo); + } else { + this.phoneNumberCheckBo.setError(this.phoneNumberCheckBo.getError() + 1); + this.phoneNumberCheckBo.getCheckErrors().add(phoneNumberBo.getPhoneNumber()); + } + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + + } + + public PhoneNumberExcelListener(PhoneNumberCheckBo phoneNumberCheckBo, Boolean firstReadStatus, Integer batchSize) { + this.phoneNumberCheckBo = phoneNumberCheckBo; + this.firstReadStatus = firstReadStatus; + this.batchSize = batchSize; + } + +} diff --git a/src/main/java/com/example/listener/SnailJobChannelReconnectListener.java b/src/main/java/com/example/listener/SnailJobChannelReconnectListener.java new file mode 100644 index 0000000..7415981 --- /dev/null +++ b/src/main/java/com/example/listener/SnailJobChannelReconnectListener.java @@ -0,0 +1,19 @@ +package com.example.listener; + +import com.aizuda.snailjob.client.common.event.SnailChannelReconnectEvent; +import com.aizuda.snailjob.common.log.SnailJobLog; +import org.springframework.context.ApplicationListener; +import org.springframework.stereotype.Component; + +/** + * @author xiaowoniu + * @date 2024-03-14 21:58:18 + * @since 3.1.0 + */ +@Component +public class SnailJobChannelReconnectListener implements ApplicationListener { + @Override + public void onApplicationEvent(SnailChannelReconnectEvent event) { + SnailJobLog.LOCAL.info("这个一个重连事件"); + } +} diff --git a/src/main/java/com/example/listener/SnailJobClosedListener.java b/src/main/java/com/example/listener/SnailJobClosedListener.java new file mode 100644 index 0000000..74f51e0 --- /dev/null +++ b/src/main/java/com/example/listener/SnailJobClosedListener.java @@ -0,0 +1,19 @@ +package com.example.listener; + +import com.aizuda.snailjob.client.common.event.SnailClientClosedEvent; +import com.aizuda.snailjob.common.log.SnailJobLog; +import org.springframework.context.ApplicationListener; +import org.springframework.stereotype.Component; + +/** + * @author xiaowoniu + * @date 2024-03-14 22:00:58 + * @since 3.1.0 + */ +@Component +public class SnailJobClosedListener implements ApplicationListener { + @Override + public void onApplicationEvent(SnailClientClosedEvent event) { + SnailJobLog.LOCAL.info("这是一个SnailJob关闭完成事件"); + } +} diff --git a/src/main/java/com/example/listener/SnailJobClosingListener.java b/src/main/java/com/example/listener/SnailJobClosingListener.java new file mode 100644 index 0000000..4f4df88 --- /dev/null +++ b/src/main/java/com/example/listener/SnailJobClosingListener.java @@ -0,0 +1,19 @@ +package com.example.listener; + +import com.aizuda.snailjob.client.common.event.SnailClientClosingEvent; +import com.aizuda.snailjob.common.log.SnailJobLog; +import org.springframework.context.ApplicationListener; +import org.springframework.stereotype.Component; + +/** + * @author xiaowoniu + * @date 2024-03-14 22:00:58 + * @since 3.1.0 + */ +@Component +public class SnailJobClosingListener implements ApplicationListener { + @Override + public void onApplicationEvent(SnailClientClosingEvent event) { + SnailJobLog.LOCAL.info("这是一个SnailJob开始关闭事件"); + } +} diff --git a/src/main/java/com/example/listener/SnailJobStartedListener.java b/src/main/java/com/example/listener/SnailJobStartedListener.java new file mode 100644 index 0000000..d1e5ab7 --- /dev/null +++ b/src/main/java/com/example/listener/SnailJobStartedListener.java @@ -0,0 +1,19 @@ +package com.example.listener; + +import com.aizuda.snailjob.client.common.event.SnailClientStartedEvent; +import com.aizuda.snailjob.common.log.SnailJobLog; +import org.springframework.context.ApplicationListener; +import org.springframework.stereotype.Component; + +/** + * @author xiaowoniu + * @date 2024-03-14 22:00:58 + * @since 3.1.0 + */ +@Component +public class SnailJobStartedListener implements ApplicationListener { + @Override + public void onApplicationEvent(SnailClientStartedEvent event) { + SnailJobLog.LOCAL.info("这是一个SnailJob启动完成事件"); + } +} diff --git a/src/main/java/com/example/listener/SnailJobStartingListener.java b/src/main/java/com/example/listener/SnailJobStartingListener.java new file mode 100644 index 0000000..d50e7e5 --- /dev/null +++ b/src/main/java/com/example/listener/SnailJobStartingListener.java @@ -0,0 +1,19 @@ +package com.example.listener; + +import com.aizuda.snailjob.client.common.event.SnailClientStartingEvent; +import com.aizuda.snailjob.common.log.SnailJobLog; +import org.springframework.context.ApplicationListener; +import org.springframework.stereotype.Component; + +/** + * @author xiaowoniu + * @date 2024-03-14 22:00:58 + * @since 3.1.0 + */ +@Component +public class SnailJobStartingListener implements ApplicationListener { + @Override + public void onApplicationEvent(SnailClientStartingEvent event) { + SnailJobLog.LOCAL.info("这是一个SnailJob启动事件"); + } +} diff --git a/src/main/java/com/example/po/FailOrderPo.java b/src/main/java/com/example/po/FailOrderPo.java new file mode 100644 index 0000000..d9bbf31 --- /dev/null +++ b/src/main/java/com/example/po/FailOrderPo.java @@ -0,0 +1,41 @@ +package com.example.po; + +import java.time.LocalDateTime; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 处理失败的订单信息表 + */ +@TableName("fail_order") +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class FailOrderPo { + + @TableId(value = "id", type = IdType.AUTO) + private Long id; + // 订单ID + private String orderId; + // 来源ID + private Integer sourceId; + // 场景名称 + private String sceneName; + // 执行器名称 + private String executorName; + // 参数信息 + private String args; + // 创建时间 + private LocalDateTime createDate; + // 更新时间 + private LocalDateTime updateDate; + +} diff --git a/src/main/java/com/example/po/PhoneNumberPo.java b/src/main/java/com/example/po/PhoneNumberPo.java new file mode 100644 index 0000000..5b1b204 --- /dev/null +++ b/src/main/java/com/example/po/PhoneNumberPo.java @@ -0,0 +1,39 @@ +package com.example.po; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +/** + * phone_number + * + * @author JiChenWang + * @since 2024/6/30 11:48 + */ +@TableName("phone_number") +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class PhoneNumberPo { + + /** + * 主键 + */ + private Long id; + + /** + * 手机号 + */ + private String phoneNumber; + + /** + * 创建时间 + */ + private LocalDateTime createTime; + +} diff --git a/src/main/java/com/example/service/LocalRemoteService.java b/src/main/java/com/example/service/LocalRemoteService.java new file mode 100644 index 0000000..ca37c72 --- /dev/null +++ b/src/main/java/com/example/service/LocalRemoteService.java @@ -0,0 +1,22 @@ +package com.example.service; + +/** + * @author: www.byteblogs.com + * @date : 2023-09-06 09:02 + */ +public interface LocalRemoteService { + + void localRemote(); + + String remoteRetryWithLocalRemote(String requestId); + + boolean localRetryWithPropagationRequired(String params); + + boolean localRetryWithPropagationRequiredNew(String params); + + boolean localRetryWithTryCatch1(String params); + + boolean localRetryWithTryCatch2(String params); + + boolean localRetryWithTwoRetryMethod(String params); +} diff --git a/src/main/java/com/example/service/LocalRetryService.java b/src/main/java/com/example/service/LocalRetryService.java new file mode 100644 index 0000000..2e452cd --- /dev/null +++ b/src/main/java/com/example/service/LocalRetryService.java @@ -0,0 +1,38 @@ +package com.example.service; + +import com.aizuda.snailjob.client.core.annotation.Retryable; +import com.aizuda.snailjob.client.core.retryer.RetryType; +import com.example.vo.OrderVo; + +/** + * @author: www.byteblogs.com + * @date : 2023-09-06 09:03 + */ +public interface LocalRetryService { + + void localRetry(String params); + + boolean localRetryWithTwoRetryMethod(String params); + + @Retryable(scene = "localRetryWithAnnoOnInterface", retryStrategy = RetryType.ONLY_LOCAL) + void localRetryWithAnnoOnInterface(String params); + + void localRetryWithBasicParams(String params); + + void localRetryIncludeException(String params); + + void localRetryExcludeException(String type); + + void localRetryIsThrowException(String params); + + boolean localRetryWithRetryMethod(OrderVo orderVo); + + boolean localRetryWithPropagationRequired(String params); + + boolean localRetryWithPropagationRequiredNew(String params); + + boolean localRetryWithTryCatch1(String params); + + boolean localRetryWithTryCatch2(String params); + +} diff --git a/src/main/java/com/example/service/ManualRetryExecutorMethodService.java b/src/main/java/com/example/service/ManualRetryExecutorMethodService.java new file mode 100644 index 0000000..4e859c5 --- /dev/null +++ b/src/main/java/com/example/service/ManualRetryExecutorMethodService.java @@ -0,0 +1,10 @@ +package com.example.service; + +/** + * @author: www.byteblogs.com + * @date : 2023-09-06 09:04 + */ +public interface ManualRetryExecutorMethodService { + + void myExecutorMethod(String params); +} diff --git a/src/main/java/com/example/service/RemoteRetryService.java b/src/main/java/com/example/service/RemoteRetryService.java new file mode 100644 index 0000000..6faf082 --- /dev/null +++ b/src/main/java/com/example/service/RemoteRetryService.java @@ -0,0 +1,37 @@ +package com.example.service; + +import com.example.vo.OrderVo; + +/** + * @author: www.byteblogs.com + * @date : 2023-09-06 09:04 + */ +public interface RemoteRetryService { + + void remoteRetry(String params); + + void remoteSync(String params); + + boolean remoteRetryWithIdempotentId(OrderVo orderVo); + + boolean retryWithSingleParamIdempotentGenerate(String params); + + boolean retryWithMulParamIdempotentGenerate(String uuid, Integer intVal, Double doubleVal, + Character character, OrderVo orderVo); + + boolean remoteRetryWithRetryMethod(OrderVo orderVo); + + boolean remoteRetryWithCompleteCallback(String scene, OrderVo orderVo); + + boolean remoteRetryWithBizNo(OrderVo orderVo); + + boolean localRetryWithPropagationRequired(String params); + + boolean localRetryWithPropagationRequiredNew(String params); + + boolean localRetryWithTryCatch1(String params); + + boolean localRetryWithTryCatch2(String params); + + boolean localRetryWithTwoRetryMethod(String params); +} diff --git a/src/main/java/com/example/service/impl/LocalRemoteServiceImpl.java b/src/main/java/com/example/service/impl/LocalRemoteServiceImpl.java new file mode 100644 index 0000000..fd59c0b --- /dev/null +++ b/src/main/java/com/example/service/impl/LocalRemoteServiceImpl.java @@ -0,0 +1,91 @@ +package com.example.service.impl; + +import com.aizuda.snailjob.client.core.annotation.Retryable; +import com.aizuda.snailjob.client.core.retryer.RetryType; +import com.example.handler.LocalAndRemoteRetryHandler; +import com.example.service.LocalRemoteService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.concurrent.TimeUnit; + +/** + * 模拟先本地再远程重试案例 + * + * @author www.byteblogs.com + * @date 2023-07-18 22:19:30 + * @since 2.1.0 + */ +@Service +@RequiredArgsConstructor +@Slf4j +public class LocalRemoteServiceImpl implements LocalRemoteService { + private final LocalAndRemoteRetryHandler localAndRemoteRetryHandler; + + @Override + @Retryable(scene = "localRemote", retryStrategy = RetryType.LOCAL_REMOTE) + public void localRemote() { + System.out.println("local retry 方法开始执行"); + double i = 1 / 0; + } + + /** + * 使用先本地再远程的策略同步上传重试请求 retryStrategy = LOCAL_REMOTE 代表本地重试3次后再执行远程上报 async = false 代表使用同步上传的方式 timeout = 1 代表超时时间为1 + * unit = MINUTES 代表超时时间的单位是分钟 + */ + @Override + @Retryable(scene = "remoteRetryWithSync", retryStrategy = RetryType.LOCAL_REMOTE, + async = false, timeout = 1, unit = TimeUnit.MINUTES) + public String remoteRetryWithLocalRemote(String requestId) { + double i = 1 / 0; + return requestId; + } + + @Override + @Retryable(scene = "localRetryWithPropagationRequired", retryStrategy = RetryType.LOCAL_REMOTE) + public boolean localRetryWithPropagationRequired(String params) { + localAndRemoteRetryHandler.localRetryWithRequires(params); + return false; + } + + @Override + @Retryable(scene = "localRetryWithPropagationRequiredNew", retryStrategy = RetryType.LOCAL_REMOTE) + public boolean localRetryWithPropagationRequiredNew(String params) { + localAndRemoteRetryHandler.localRetryWithRequiresNew(params); + return false; + } + + @Override + @Retryable(scene = "localRetryWithTryCatch1", retryStrategy = RetryType.LOCAL_REMOTE) + public boolean localRetryWithTryCatch1(String params) { + try { + // 内部方法需要触发重试 + localAndRemoteRetryHandler.localRetryWithRequiresNew(params); + } catch (Exception e) { + log.error("inner method encountered an exception", e); + } + return false; + } + + @Override + @Retryable(scene = "localRetryWithTryCatch2", retryStrategy = RetryType.LOCAL_REMOTE) + public boolean localRetryWithTryCatch2(String params) { + // 由于传播机制为{REQUIRED},异常被捕获,所以内部不会触发重试 + try { + localAndRemoteRetryHandler.localRetryWithRequires(params); + } catch (Exception e) { + log.error("inner method encountered an exception", e); + } + + return false; + } + + @Override + public boolean localRetryWithTwoRetryMethod(String params) { + localAndRemoteRetryHandler.retryMethod1(params); + localAndRemoteRetryHandler.retryMethod2(params); + return false; + } + +} diff --git a/src/main/java/com/example/service/impl/LocalRetryServiceImpl.java b/src/main/java/com/example/service/impl/LocalRetryServiceImpl.java new file mode 100644 index 0000000..d451a58 --- /dev/null +++ b/src/main/java/com/example/service/impl/LocalRetryServiceImpl.java @@ -0,0 +1,159 @@ +package com.example.service.impl; + +import com.aizuda.snailjob.client.core.retryer.RetryType; +import com.example.customized.OrderRetryMethod; +import com.example.handler.OnlyLocalRetryHandler; +import com.example.service.LocalRetryService; +import com.example.vo.OrderVo; +import com.example.exception.ParamException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.aizuda.snailjob.client.core.annotation.Retryable; + +/** + * snail-job中的本地重试demo + */ + +@Component +@Slf4j +public class LocalRetryServiceImpl implements LocalRetryService { + + @Autowired + private OnlyLocalRetryHandler onlyLocalRetryHandler; + + /** + * 入门案例 + * 我们仅仅需要指定场景值scene就可以给方法赋予重试逻辑 + * 其他的参数均可以省略或者使用默认值 + * [参数释义] + * 场景值scene对应我们后台的场景参数,用于后续的分组 + * 在微服务中建议大家直接使用集群的服务名称即可 + */ + @Override + @Retryable(scene = "localRetry", retryStrategy = RetryType.ONLY_LOCAL) + public void localRetry(String params) { + System.out.println("local retry 方法开始执行"); + double i = 1 / 0; + } + + @Override + public boolean localRetryWithTwoRetryMethod(final String params) { + onlyLocalRetryHandler.retryMethod1(params); + onlyLocalRetryHandler.retryMethod2(params); + return true; + } + + @Override + public void localRetryWithAnnoOnInterface(final String params) { + double i = 1 / 0; + } + + /** + * 指定基础参数 + * localTimes 本地重试次数 + * localInterval 本地重试间隔时间(默认单位为秒) + * unit 超时时间单位 + * 以下案例指的是本地重试4次,每次重试之间间隔10s + */ + @Override + @Retryable(scene = "localRetryWithBasicParams", localTimes = 4, localInterval = 10, retryStrategy = RetryType.ONLY_LOCAL) + public void localRetryWithBasicParams(String params) { + System.out.println("local retry with basic params 方法开始执行"); + double i = 1 / 0; + } + + /** + * 指定异常参数 + * include参数指的是当我们遭遇到指定异常时进行重试 + * 在这个案例中我们处理两个场景: + * 抛出指定异常,例如抛出自定义的ParamException异常,观察是否会重试 + * 抛出非指定异常,例如我们在这里产生一个异常,观察是否会重试 + * 注意:如果此时我们在include 中指定参数为BusinessException(ParamException的父类),同样也会进行重试逻辑 + * 更多用法:如果我们需要在include中指定多个参数,可以使用 include = {ParamException.class,NullPointerException.class} + */ + @Override + @Retryable(scene = "localRetryIncludeException", include = ParamException.class, retryStrategy = RetryType.ONLY_LOCAL) + public void localRetryIncludeException(String params) { + System.out.println("local retry include exception 方法开始执行"); + if ("NullPointerException".equals(params)) { + throw new NullPointerException(); + } else { + throw new ParamException("此处发生了指定异常Param Exception"); + } + } + + /** + * 这个参数的作用和include是相反的 + * exclude参数指的是当我们遇到指定异常时则不会进行重试 + * 比如在下述案例中我们指定了遇到ParamException和ArithmeticException后不进行重试 + */ + @Override + @Retryable(scene = "localRetryExcludeException", exclude = {ParamException.class, ArithmeticException.class}, retryStrategy = RetryType.ONLY_LOCAL) + public void localRetryExcludeException(String type) { + System.out.println("local retry exclude exception 方法开始执行"); + + if ("ParamException".equals(type)) { + throw new ParamException("此处发生了指定异常Param Exception"); + } else if ("ArithmeticException".equals(type)) { + throw new ParamException("此处发生了指定异常Arithme Exception"); + } else { + throw new UnsupportedOperationException("未知异常"); + } + } + + @Override + @Retryable(scene = "localRetryIsThrowException", isThrowException = false, retryStrategy = RetryType.ONLY_LOCAL) + public void localRetryIsThrowException(String params) { + System.out.println("local retry is throw exception 方法开始执行"); + throw new ParamException("此处发生了参数异常"); + } + + @Override + @Retryable(scene = "localRetryWithRetryMethod", retryStrategy = RetryType.ONLY_LOCAL, + retryMethod = OrderRetryMethod.class) + public boolean localRetryWithRetryMethod(OrderVo orderVo) { + throw new NullPointerException(); + } + + @Override + @Retryable(scene = "localRetryWithPropagationRequired", retryStrategy = RetryType.ONLY_LOCAL) + public boolean localRetryWithPropagationRequired(String params) { + onlyLocalRetryHandler.localRetry(params); + return false; + } + + @Override + @Retryable(scene = "localRetryWithPropagationRequiredNew", retryStrategy = RetryType.ONLY_LOCAL) + public boolean localRetryWithPropagationRequiredNew(final String params) { + onlyLocalRetryHandler.localRetryWithRequiresNew(params); + return false; + } + + @Override + @Retryable(scene = "localRetryWithTryCatch1", retryStrategy = RetryType.ONLY_LOCAL) + public boolean localRetryWithTryCatch1(String params) { + try { + // 内部方法需要触发重试 + onlyLocalRetryHandler.localRetryWithRequiresNew(params); + } catch (Exception e) { + log.error("inner method encountered an exception", e); + } + return false; + } + + @Override + @Retryable(scene = "localRetryWithTryCatch2", retryStrategy = RetryType.ONLY_LOCAL) + public boolean localRetryWithTryCatch2(String params) { + // 由于传播机制为{REQUIRED},异常被捕获,所以内部不会触发重试 + try { + onlyLocalRetryHandler.localRetry(params); + } catch (Exception e) { + log.error("inner method encountered an exception", e); + } + + return false; + } + +} diff --git a/src/main/java/com/example/service/impl/ManualRetryExecutorMethodServiceImpl.java b/src/main/java/com/example/service/impl/ManualRetryExecutorMethodServiceImpl.java new file mode 100644 index 0000000..f1e7109 --- /dev/null +++ b/src/main/java/com/example/service/impl/ManualRetryExecutorMethodServiceImpl.java @@ -0,0 +1,33 @@ +package com.example.service.impl; + +import com.aizuda.snailjob.client.core.retryer.RetryTaskTemplateBuilder; +import com.aizuda.snailjob.client.core.retryer.SnailJobTemplate; +import com.example.executor.ManualRetryExecutorTask; +import com.example.service.ManualRetryExecutorMethodService; +import com.example.vo.OrderVo; +import org.springframework.stereotype.Component; + +/** + * snail-job中的手动重试 + */ +@Component +public class ManualRetryExecutorMethodServiceImpl implements ManualRetryExecutorMethodService { + + public void myExecutorMethod(String params) { + OrderVo orderVo = OrderVo.builder() + .orderId(params) + .source(1) + .build(); + SnailJobTemplate snailJobTemplate = RetryTaskTemplateBuilder.newBuilder() + // 手动指定场景名称 + .withScene(ManualRetryExecutorTask.SCENE) + // 指定要执行的任务 + .withExecutorMethod(ManualRetryExecutorTask.class) + // 指定参数 + .withParam(orderVo) + .build(); + // 执行模板 + snailJobTemplate.executeRetry(); + } + +} diff --git a/src/main/java/com/example/service/impl/RemoteRetryServiceImpl.java b/src/main/java/com/example/service/impl/RemoteRetryServiceImpl.java new file mode 100644 index 0000000..9da8a9b --- /dev/null +++ b/src/main/java/com/example/service/impl/RemoteRetryServiceImpl.java @@ -0,0 +1,165 @@ +package com.example.service.impl; + +import java.util.concurrent.TimeUnit; + +import cn.hutool.core.lang.Assert; +import com.aizuda.snailjob.client.core.intercepter.RetrySiteSnapshot; +import com.aizuda.snailjob.common.core.enums.RetryStatusEnum; +import com.example.handler.OnlyRemoteRetryHandler; +import com.example.service.RemoteRetryService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import com.aizuda.snailjob.client.core.annotation.Retryable; +import com.aizuda.snailjob.client.core.retryer.RetryType; +import com.example.customized.MultiParamIdempotentGenerate; +import com.example.customized.OrderIdempotentIdGenerate; +import com.example.customized.OrderCompleteCallback; +import com.example.customized.OrderRetryMethod; +import com.example.customized.SingleParamIdempotentGenerate; +import com.example.vo.OrderVo; + +/** + * snail-job中的远程重试 + */ +@Component +@Slf4j +@RequiredArgsConstructor +public class RemoteRetryServiceImpl implements RemoteRetryService { + private final OnlyRemoteRetryHandler onlyRemoteRetryHandler; + + /** + * 定义一个最基本的远程重试方法 + */ + @Override + @Retryable(scene = "remoteRetry", retryStrategy = RetryType.ONLY_REMOTE) + public void remoteRetry(String params) { + System.out.println("远程重试方法启动"); + double i = 1 / 0; + } + + @Override + @Retryable(scene = "remoteRetrySync", retryStrategy = RetryType.ONLY_REMOTE, async = false, timeout = 500, unit = TimeUnit.MILLISECONDS) + public void remoteSync(String params) { + System.out.println("同步远程重试方法启动"); + double i = 1 / 0; + } + + /** + * 使用自定义的幂等Id生成类 OrderIdempotentIdGenerate + */ + @Override + @Retryable(scene = "remoteRetryWithIdempotentId", retryStrategy = RetryType.ONLY_REMOTE, + idempotentId = OrderIdempotentIdGenerate.class) + public boolean remoteRetryWithIdempotentId(OrderVo orderVo) { + double i = 1 / 0; + return true; + } + + /** + * 使用自定义的幂等Id生成类 SingleParamIdempotentGenerate 测试单参数场景下 + */ + @Override + @Retryable(scene = "retryWithSingleParamIdempotentGenerate", retryStrategy = RetryType.ONLY_REMOTE, + idempotentId = SingleParamIdempotentGenerate.class) + public boolean retryWithSingleParamIdempotentGenerate(String params) { + throw new NullPointerException(); + } + + /** + * 使用自定义的幂等Id生成类 MultiParamIdempotentGenerate 测试多个参数场景下的解析 + */ + @Override + @Retryable(scene = "retryWithMulParamIdempotentGenerate", retryStrategy = RetryType.ONLY_REMOTE, + idempotentId = MultiParamIdempotentGenerate.class) + public boolean retryWithMulParamIdempotentGenerate(String uuid, Integer intVal, Double doubleVal, + Character character, OrderVo orderVo) { + throw new NullPointerException(); + } + + /** + * 使用自定义的异常处理类 OrderRetryMethod + */ + @Override + @Retryable(scene = "remoteRetryWithRetryMethod", retryStrategy = RetryType.ONLY_REMOTE, + retryMethod = OrderRetryMethod.class) + public boolean remoteRetryWithRetryMethod(OrderVo orderVo) { + throw new NullPointerException(); + } + + /** + * 自定义BizNo + */ + @Retryable(scene = "remoteRetryWithBizNo", retryStrategy = RetryType.ONLY_REMOTE, bizNo = "#orderVo.orderId") + public boolean remoteRetryWithBizNo(OrderVo orderVo) { + throw new NullPointerException(); + } + + @Override + @Retryable(scene = "localRetryWithPropagationRequired", retryStrategy = RetryType.LOCAL_REMOTE) + public boolean localRetryWithPropagationRequired(String params) { + onlyRemoteRetryHandler.localRetryWithRequires(params); + return false; + } + + @Override + public boolean localRetryWithPropagationRequiredNew(String params) { + onlyRemoteRetryHandler.localRetryWithRequiresNew(params); + return false; + } + + @Override + public boolean localRetryWithTryCatch1(String params) { + try { + // 内部方法需要触发重试 + onlyRemoteRetryHandler.localRetryWithRequiresNew(params); + } catch (Exception e) { + log.error("inner method encountered an exception", e); + } + return false; + } + + @Override + public boolean localRetryWithTryCatch2(String params) { + // 由于传播机制为{REQUIRED},异常被捕获,所以内部不会触发重试 + try { + onlyRemoteRetryHandler.localRetryWithRequires(params); + } catch (Exception e) { + log.error("inner method encountered an exception", e); + } + + return false; + } + + @Override + public boolean localRetryWithTwoRetryMethod(String params) { + onlyRemoteRetryHandler.retryMethod1(params); + onlyRemoteRetryHandler.retryMethod2(params); + return false; + } + + /** + * 使用自定义的retryCompleteCallback类 OrderCompleteCallback + */ + @Retryable(scene = "remoteRetryWithCompleteCallback", retryStrategy = RetryType.ONLY_REMOTE, + retryCompleteCallback = OrderCompleteCallback.class) + public boolean remoteRetryWithCompleteCallback(String scene, OrderVo orderVo) { + + Assert.notNull(RetrySiteSnapshot.getStage(), () -> new IllegalArgumentException("测试回调")); + + // 本地重试阶段,执行失败,远程的执行成功 + if (RetrySiteSnapshot.getStage().equals(RetrySiteSnapshot.EnumStage.LOCAL.getStage())) { + // 生成的结果在50%的概率下执行这里的逻辑 + throw new NullPointerException(); + } + + if (scene.equals(RetryStatusEnum.MAX_COUNT.name())) { + throw new NullPointerException(); + } + + return true; + } + + +} diff --git a/src/main/java/com/example/spi/MySnailJobListener.java b/src/main/java/com/example/spi/MySnailJobListener.java new file mode 100644 index 0000000..bb67603 --- /dev/null +++ b/src/main/java/com/example/spi/MySnailJobListener.java @@ -0,0 +1,30 @@ +package com.example.spi; + +import com.aizuda.snailjob.client.core.event.SnailJobListener; +import com.aizuda.snailjob.common.core.util.JsonUtil; +import lombok.extern.slf4j.Slf4j; + +/** + * @author: www.byteblogs.com + * @date : 2023-08-28 11:20 + */ +@Slf4j +public class MySnailJobListener implements SnailJobListener { + + @Override + public void beforeRetry(final String sceneName, final String executorClassName, final Object[] params) { + log.info("------> beforeRetry sceneName:[{}] executorClassName:[{}] params:[{}]", + sceneName, executorClassName, JsonUtil.toJsonString(params)); + } + + @Override + public void successOnRetry(final Object result, final String sceneName, final String executorClassName) { + log.info("------> successOnRetry sceneName:[{}] executorClassName:[{}] result:[{}]", + sceneName, executorClassName, JsonUtil.toJsonString(result)); + } + + @Override + public void failureOnRetry(final String sceneName, final String executorClassName, final Throwable e) { + log.info("------> failureOnRetry sceneName:[{}] executorClassName:[{}]", sceneName, executorClassName, e); + } +} diff --git a/src/main/java/com/example/spi/TTLRetrySiteSnapshotContext.java b/src/main/java/com/example/spi/TTLRetrySiteSnapshotContext.java new file mode 100644 index 0000000..68866f1 --- /dev/null +++ b/src/main/java/com/example/spi/TTLRetrySiteSnapshotContext.java @@ -0,0 +1,32 @@ +package com.example.spi; + +import com.aizuda.snailjob.client.core.RetrySiteSnapshotContext; +import com.alibaba.ttl.TransmittableThreadLocal; + +/** + * @author: www.byteblogs.com + * @date : 2023-08-28 11:30 + */ +public class TTLRetrySiteSnapshotContext implements RetrySiteSnapshotContext { + + private final ThreadLocal threadLocal; + + public TTLRetrySiteSnapshotContext() { + this.threadLocal = new TransmittableThreadLocal<>(); + } + + @Override + public void set(final T value) { + threadLocal.set(value); + } + + @Override + public void remove() { + threadLocal.remove(); + } + + @Override + public T get() { + return threadLocal.get(); + } +} diff --git a/src/main/java/com/example/util/CodeGen.java b/src/main/java/com/example/util/CodeGen.java new file mode 100644 index 0000000..2e82204 --- /dev/null +++ b/src/main/java/com/example/util/CodeGen.java @@ -0,0 +1,47 @@ +package com.example.util; + +import com.baomidou.mybatisplus.generator.AutoGenerator; +import com.baomidou.mybatisplus.generator.config.DataSourceConfig; +import com.baomidou.mybatisplus.generator.config.GlobalConfig; +import com.baomidou.mybatisplus.generator.config.PackageConfig; +import com.baomidou.mybatisplus.generator.config.StrategyConfig; + +public class CodeGen { + + private static String dataSourceUrl = "jdbc:mysql://localhost:3306/snail_job_260?useSSL=false&characterEncoding=utf8&useUnicode=true"; + + private static String userName = "root"; + + private static String password = "root"; + + public static void main(String[] args) { + DataSourceConfig dataSourceConfig = new DataSourceConfig.Builder(dataSourceUrl, userName, password).build(); + + // 全局配置 + GlobalConfig globalConfig = new GlobalConfig.Builder() + .outputDir("src/main/java") + .author("xiaowoniu") + .build(); + + // 策略配置 + StrategyConfig strategyConfig = new StrategyConfig.Builder() + .addInclude("workflow") // 需要生成的表名 + .build(); + + // 包配置 + PackageConfig packageConfig = new PackageConfig.Builder() + .parent("com.aizuda.snailjob.template.datasource.persistence.po") + .moduleName("snail-job-springboot") + .build(); + + // 代码生成器 + AutoGenerator generator = new AutoGenerator(dataSourceConfig) + .global(globalConfig) + .strategy(strategyConfig) + .packageInfo(packageConfig); + + // 执行生成代码 + generator.execute(); + + } +} diff --git a/src/main/java/com/example/vo/OrderVo.java b/src/main/java/com/example/vo/OrderVo.java new file mode 100644 index 0000000..5f33af9 --- /dev/null +++ b/src/main/java/com/example/vo/OrderVo.java @@ -0,0 +1,23 @@ +package com.example.vo; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +//import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class OrderVo { + @Schema(description = "订单ID,用于唯一标识订单的编号", accessMode = Schema.AccessMode.READ_WRITE) + private String orderId; // 订单ID,用于唯一标识订单的编号 + @Schema(description = "订单来源信息,1-手机端下单 2-PC端下单", accessMode = Schema.AccessMode.READ_WRITE) + private Integer source; // 订单来源信息,1-手机端下单 2-PC端下单 +} diff --git a/src/main/resources/META-INF/services/com.aizuda.snailjob.client.core.RetrySiteSnapshotContext b/src/main/resources/META-INF/services/com.aizuda.snailjob.client.core.RetrySiteSnapshotContext index 5f55ca8..5faefde 100644 --- a/src/main/resources/META-INF/services/com.aizuda.snailjob.client.core.RetrySiteSnapshotContext +++ b/src/main/resources/META-INF/services/com.aizuda.snailjob.client.core.RetrySiteSnapshotContext @@ -1 +1 @@ -#com.example.snailjob.spi.TTLRetrySiteSnapshotContext \ No newline at end of file +#com.example.spi.TTLRetrySiteSnapshotContext \ No newline at end of file diff --git a/src/main/resources/META-INF/services/com.aizuda.snailjob.client.core.event.SnailJobListener b/src/main/resources/META-INF/services/com.aizuda.snailjob.client.core.event.SnailJobListener index b6fc55e..a882c30 100644 --- a/src/main/resources/META-INF/services/com.aizuda.snailjob.client.core.event.SnailJobListener +++ b/src/main/resources/META-INF/services/com.aizuda.snailjob.client.core.event.SnailJobListener @@ -1 +1 @@ -#com.example.snailjob.listener.MySnailJobListener \ No newline at end of file +#com.example.listener.MySnailJobListener \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index b03e06c..d0b2516 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -8,10 +8,10 @@ spring: profiles: active: dev datasource: - name: snail_job - url: jdbc:mysql://localhost:3306/snail_job?useSSL=false&characterEncoding=utf8&useUnicode=true - username: root - password: root + name: demo + url: jdbc:mysql://localhost:3306/demo?useSSL=false&characterEncoding=utf8&useUnicode=true + username: pig + password: 123456 type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.cj.jdbc.Driver hikari: @@ -26,7 +26,7 @@ spring: mybatis-plus: mapper-locations: classpath:/mapper/*.xml - typeAliasesPackage: com.example.snail.job.po + typeAliasesPackage: com.example.po global-config: db-config: field-strategy: NOT_EMPTY @@ -50,7 +50,7 @@ snail-job: # 命名空间 namespace: 764d604ec6fc45f68cd92514c40e9e1a # 接入组名 - group: snail_job_demo_group + group: example_group # 接入组 token token: SJ_Wyz3dmsdbDOkDujOTSSoBjGQP1BMsVnj # 客户端绑定IP,必须服务器可以访问到;默认自动推断,在服务器无法调度客户端时需要手动配置 diff --git a/src/main/resources/logback-boot.xml b/src/main/resources/logback-boot.xml index 5ec0889..7fb3dc4 100644 --- a/src/main/resources/logback-boot.xml +++ b/src/main/resources/logback-boot.xml @@ -1,6 +1,6 @@ - + @@ -60,22 +60,22 @@ - - 100 + + 100 1024 - + - - 100 + + 100 1024 - + - - 100 + + 100 1024 - + @@ -84,10 +84,10 @@ - - - - - + + + + +