This commit is contained in:
yuhaiming
2026-09-16 10:11:41 +08:00
commit 926426fe4f
901 changed files with 103701 additions and 0 deletions

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-modules</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ruoyi-job</artifactId>
<description>
任务调度
</description>
<dependencies>
<!-- 通用工具-->
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-json</artifactId>
</dependency>
<!-- 调度模块 -->
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-job</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,19 @@
package org.dromara.job.entity;
import java.math.BigDecimal;
/**
* 账单数据传输对象。
*
* @param billId 账单 ID
* @param billChannel 账单渠道
* @param billDate 账单日期
* @param billAmount 账单金额
*/
public record BillDTO(
Long billId,
String billChannel,
String billDate,
BigDecimal billAmount
) {
}

View File

@@ -0,0 +1 @@
package org.dromara.job;

View File

@@ -0,0 +1,45 @@
package org.dromara.job.snailjob;
import com.aizuda.snailjob.client.job.core.annotation.JobExecutor;
import com.aizuda.snailjob.client.job.core.dto.JobArgs;
import com.aizuda.snailjob.common.log.SnailJobLog;
import com.aizuda.snailjob.model.dto.ExecuteResult;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.job.entity.BillDTO;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
/**
* DAG工作流任务-模拟支付宝账单任务
* <a href="https://juejin.cn/post/7487860254114644019"></a>
*
* @author 老马
*/
@Component
@JobExecutor(name = "alipayBillTask")
public class AlipayBillTask {
/**
* 执行支付宝账单模拟任务,并将结果写入工作流上下文。
*
* @param jobArgs 任务执行参数
* @return 执行结果
* @throws InterruptedException 任务被中断时抛出
*/
public ExecuteResult jobExecute(JobArgs jobArgs) throws InterruptedException {
// 设置清算日期
String settlementDate = (String) jobArgs.getWfContext().get("settlementDate");
if (StringUtils.equals(settlementDate, "sysdate")) {
settlementDate = DateUtils.now();
}
BillDTO billDTO = new BillDTO(23456789L, "alipay", settlementDate, new BigDecimal("2345.67"));
// 把billDTO对象放入上下文进行传递
jobArgs.appendContext("alipay", JsonUtils.toJsonString(billDTO));
SnailJobLog.REMOTE.info("上下文: {}", jobArgs.getWfContext());
return ExecuteResult.success(billDTO);
}
}

View File

@@ -0,0 +1,52 @@
package org.dromara.job.snailjob;
import com.aizuda.snailjob.client.job.core.annotation.JobExecutor;
import com.aizuda.snailjob.client.job.core.dto.JobArgs;
import com.aizuda.snailjob.common.log.SnailJobLog;
import com.aizuda.snailjob.model.dto.ExecuteResult;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.job.entity.BillDTO;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
/**
* DAG工作流任务-模拟汇总账单任务
* <a href="https://juejin.cn/post/7487860254114644019"></a>
*
* @author 老马
*/
@Component
@JobExecutor(name = "summaryBillTask")
public class SummaryBillTask {
/**
* 汇总工作流上下文中的微信与支付宝账单金额。
*
* @param jobArgs 任务执行参数
* @return 汇总执行结果
* @throws InterruptedException 任务被中断时抛出
*/
public ExecuteResult jobExecute(JobArgs jobArgs) throws InterruptedException {
// 获得微信账单
BigDecimal wechatAmount = BigDecimal.valueOf(0);
String wechat = (String) jobArgs.getWfContext("wechat");
if (StringUtils.isNotBlank(wechat)) {
BillDTO wechatBillDTO = JsonUtils.parseObject(wechat, BillDTO.class);
wechatAmount = wechatBillDTO.billAmount();
}
// 获得支付宝账单
BigDecimal alipayAmount = BigDecimal.valueOf(0);
String alipay = (String) jobArgs.getWfContext("alipay");
if (StringUtils.isNotBlank(alipay)) {
BillDTO alipayBillDTO = JsonUtils.parseObject(alipay, BillDTO.class);
alipayAmount = alipayBillDTO.billAmount();
}
// 汇总账单
BigDecimal totalAmount = wechatAmount.add(alipayAmount);
SnailJobLog.REMOTE.info("总金额: {}", totalAmount);
return ExecuteResult.success(totalAmount);
}
}

View File

@@ -0,0 +1,31 @@
package org.dromara.job.snailjob;
import com.aizuda.snailjob.client.job.core.annotation.JobExecutor;
import com.aizuda.snailjob.client.job.core.dto.JobArgs;
import com.aizuda.snailjob.common.core.util.JsonUtil;
import com.aizuda.snailjob.common.log.SnailJobLog;
import com.aizuda.snailjob.model.dto.ExecuteResult;
import org.springframework.stereotype.Component;
/**
* 正常任务
* <a href="https://juejin.cn/post/7418074037392293914"></a>
*
* @author 老马
*/
@Component
@JobExecutor(name = "testJobExecutor")
public class TestAnnoJobExecutor {
/**
* 执行示例注解任务。
*
* @param jobArgs 任务参数
* @return 执行结果
*/
public ExecuteResult jobExecute(JobArgs jobArgs) {
SnailJobLog.LOCAL.info("testJobExecutor. jobArgs:{}", JsonUtil.toJsonString(jobArgs));
SnailJobLog.REMOTE.info("testJobExecutor. jobArgs:{}", JsonUtil.toJsonString(jobArgs));
return ExecuteResult.success("测试成功");
}
}

View File

@@ -0,0 +1,46 @@
package org.dromara.job.snailjob;
import cn.hutool.core.util.RandomUtil;
import com.aizuda.snailjob.client.job.core.annotation.JobExecutor;
import com.aizuda.snailjob.client.job.core.dto.JobArgs;
import com.aizuda.snailjob.common.log.SnailJobLog;
import com.aizuda.snailjob.model.dto.ExecuteResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* 广播任务
* <a href="https://juejin.cn/post/7422948006150438950"></a>
*
* @author 老马
*/
@Slf4j
@Component
@JobExecutor(name = "testBroadcastJob")
public class TestBroadcastJob {
/**
* 客户端端口。
*/
@Value("${snail-job.port}")
private int clientPort;
/**
* 执行广播任务示例,随机模拟成功或失败。
*
* @param jobArgs 任务执行参数
* @return 执行结果
*/
public ExecuteResult jobExecute(JobArgs jobArgs) {
int randomInt = RandomUtil.randomInt(100);
log.info("随机数: {}", randomInt);
SnailJobLog.REMOTE.info("随机数: {},客户端端口:{}", randomInt, clientPort);
if (randomInt < 50) {
throw new RuntimeException("随机数小于50,收集日志任务执行失败");
}
// 获得jobArgs 中传入的相加的两个数
return ExecuteResult.success("随机数大于50,收集日志任务执行成功");
}
}

View File

@@ -0,0 +1,25 @@
package org.dromara.job.snailjob;
import com.aizuda.snailjob.client.job.core.dto.JobArgs;
import com.aizuda.snailjob.client.job.core.executor.AbstractJobExecutor;
import com.aizuda.snailjob.model.dto.ExecuteResult;
import org.springframework.stereotype.Component;
/**
* @author opensnail
* @date 2024-05-17
*/
@Component
public class TestClassJobExecutor extends AbstractJobExecutor {
/**
* 执行测试类任务。
*
* @param jobArgs 任务参数
* @return 任务执行结果
*/
@Override
protected ExecuteResult doJobExecute(JobArgs jobArgs) {
return ExecuteResult.success("TestJobExecutor测试成功");
}
}

View File

@@ -0,0 +1,66 @@
package org.dromara.job.snailjob;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.extra.spring.SpringUtil;
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.common.log.SnailJobLog;
import com.aizuda.snailjob.model.dto.ExecuteResult;
import org.dromara.common.core.utils.StreamUtils;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
/**
* Map任务 动态分配 只分片不关注结果
* <a href="https://juejin.cn/post/7446362500478894106"></a>
*
* @author 老马
*/
@SuppressWarnings({"unchecked", "rawtypes"})
@Component
@JobExecutor(name = "testMapJobAnnotation")
public class TestMapJobAnnotation {
/**
* 根 Map 任务,将示例数据拆分为多个分片任务。
*
* @param mapArgs Map 任务参数
* @param mapHandler 分片调度处理器
* @return 分片调度结果
*/
@MapExecutor
public ExecuteResult doJobMapExecute(MapArgs mapArgs, MapHandler mapHandler) {
// 生成1~200数值并分片
int partitionSize = 50;
List<Integer> sourceList = IntStream.rangeClosed(1, 200).boxed().toList();
List<List<Integer>> partition = StreamUtils.groupByKey(sourceList, i -> (i - 1) / partitionSize)
.values()
.stream()
.toList();
SnailJobLog.REMOTE.info("端口:{}完成分配任务", SpringUtil.getProperty("server.port"));
return mapHandler.doMap(partition, "doCalc");
}
/**
* 执行单个分片的累加计算。
*
* @param mapArgs 分片任务参数
* @return 分片计算结果
*/
@MapExecutor(taskName = "doCalc")
public ExecuteResult doCalc(MapArgs mapArgs) {
List<Integer> sourceList = (List<Integer>) mapArgs.getMapResult();
// 遍历sourceList的每一个元素,计算出一个累加值partitionTotal
int partitionTotal = sourceList.stream().mapToInt(i -> i).sum();
// 打印日志到服务器
ThreadUtil.sleep(3, TimeUnit.SECONDS);
SnailJobLog.REMOTE.info("端口:{},partitionTotal:{}", SpringUtil.getProperty("server.port"), partitionTotal);
return ExecuteResult.success(partitionTotal);
}
}

View File

@@ -0,0 +1,79 @@
package org.dromara.job.snailjob;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.extra.spring.SpringUtil;
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.ReduceExecutor;
import com.aizuda.snailjob.client.job.core.dto.MapArgs;
import com.aizuda.snailjob.client.job.core.dto.ReduceArgs;
import com.aizuda.snailjob.common.log.SnailJobLog;
import com.aizuda.snailjob.model.dto.ExecuteResult;
import org.dromara.common.core.utils.StreamUtils;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
/**
* MapReduce任务 动态分配 分片后合并结果
* <a href="https://juejin.cn/post/7448551286506913802"></a>
*
* @author 老马
*/
@SuppressWarnings({"unchecked", "rawtypes"})
@Component
@JobExecutor(name = "testMapReduceAnnotation1")
public class TestMapReduceAnnotation1 {
/**
* 根 Map 任务,将示例数据拆分为多个分片任务。
*
* @param mapArgs Map 任务参数
* @param mapHandler 分片调度处理器
* @return 分片调度结果
*/
@MapExecutor
public ExecuteResult rootMapExecute(MapArgs mapArgs, MapHandler mapHandler) {
int partitionSize = 50;
List<Integer> sourceList = IntStream.rangeClosed(1, 200).boxed().toList();
List<List<Integer>> partition = StreamUtils.groupByKey(sourceList, i -> (i - 1) / partitionSize)
.values()
.stream()
.toList();
SnailJobLog.REMOTE.info("端口:{}完成分配任务", SpringUtil.getProperty("server.port"));
return mapHandler.doMap(partition, "doCalc");
}
/**
* 执行单个分片的累加计算。
*
* @param mapArgs 分片任务参数
* @return 分片计算结果
*/
@MapExecutor(taskName = "doCalc")
public ExecuteResult doCalc(MapArgs mapArgs) {
List<Integer> sourceList = (List<Integer>) mapArgs.getMapResult();
// 遍历sourceList的每一个元素,计算出一个累加值partitionTotal
int partitionTotal = sourceList.stream().mapToInt(i -> i).sum();
// 打印日志到服务器
ThreadUtil.sleep(3, TimeUnit.SECONDS);
SnailJobLog.REMOTE.info("端口:{},partitionTotal:{}", SpringUtil.getProperty("server.port"), partitionTotal);
return ExecuteResult.success(partitionTotal);
}
/**
* 汇总所有 Map 分片的计算结果。
*
* @param reduceArgs Reduce 任务参数
* @return 汇总结果
*/
@ReduceExecutor
public ExecuteResult reduceExecute(ReduceArgs reduceArgs) {
int reduceTotal = reduceArgs.getMapResult().stream().mapToInt(i -> Integer.parseInt((String) i)).sum();
SnailJobLog.REMOTE.info("端口:{},reduceTotal:{}", SpringUtil.getProperty("server.port"), reduceTotal);
return ExecuteResult.success(reduceTotal);
}
}

View File

@@ -0,0 +1,43 @@
package org.dromara.job.snailjob;
import cn.hutool.core.convert.Convert;
import com.aizuda.snailjob.client.job.core.annotation.JobExecutor;
import com.aizuda.snailjob.client.job.core.dto.JobArgs;
import com.aizuda.snailjob.common.log.SnailJobLog;
import com.aizuda.snailjob.model.dto.ExecuteResult;
import org.springframework.stereotype.Component;
/**
* 静态分片 根据服务端任务参数分片
* <a href="https://juejin.cn/post/7426232375703896101"></a>
*
* @author 老马
*/
@Component
@JobExecutor(name = "testStaticShardingJob")
public class TestStaticShardingJob {
/**
* 执行静态分片任务示例。
*
* @param jobArgs 任务执行参数
* @return 执行结果
*/
public ExecuteResult jobExecute(JobArgs jobArgs) {
String jobParams = Convert.toStr(jobArgs.getJobParams());
SnailJobLog.LOCAL.info("开始执行分片任务,参数:{}", jobParams);
// 获得jobArgs 中传入的开始id和结束id
String[] split = jobParams.split(",");
Long fromId = Long.parseLong(split[0]);
Long toId = Long.parseLong(split[1]);
// 模拟数据库操作,对范围id,进行加密处理
try {
SnailJobLog.REMOTE.info("开始对id范围:{}进行加密处理", fromId + "-" + toId);
Thread.sleep(3000);
SnailJobLog.REMOTE.info("对id范围:{}进行加密处理完成", fromId + "-" + toId);
} catch (InterruptedException e) {
return ExecuteResult.failure("任务执行失败");
}
return ExecuteResult.success("执行分片任务完成");
}
}

View File

@@ -0,0 +1,46 @@
package org.dromara.job.snailjob;
import com.aizuda.snailjob.client.job.core.annotation.JobExecutor;
import com.aizuda.snailjob.client.job.core.dto.JobArgs;
import com.aizuda.snailjob.common.log.SnailJobLog;
import com.aizuda.snailjob.model.dto.ExecuteResult;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.job.entity.BillDTO;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
/**
* DAG工作流任务-模拟微信账单任务
* <a href="https://juejin.cn/post/7487860254114644019"></a>
*
* @author 老马
*/
@Component
@JobExecutor(name = "wechatBillTask")
public class WechatBillTask {
/**
* 执行微信账单模拟任务,并将结果写入工作流上下文。
*
* @param jobArgs 任务执行参数
* @return 执行结果
* @throws InterruptedException 任务被中断时抛出
*/
public ExecuteResult jobExecute(JobArgs jobArgs) throws InterruptedException {
// 从上下文中获得清算日期并设置,如果上下文中清算日期
// 是sysdate设置为当前日期;否则取管理页面设置的值
String settlementDate = (String) jobArgs.getWfContext().get("settlementDate");
if (StringUtils.equals(settlementDate, "sysdate")) {
settlementDate = DateUtils.now();
}
BillDTO billDTO = new BillDTO(123456789L, "wechat", settlementDate, new BigDecimal("1234.56"));
// 把billDTO对象放入上下文进行传递
jobArgs.appendContext("wechat", JsonUtils.toJsonString(billDTO));
SnailJobLog.REMOTE.info("上下文: {}", jobArgs.getWfContext());
return ExecuteResult.success(billDTO);
}
}