diff --git a/austin-cron/pom.xml b/austin-cron/pom.xml new file mode 100644 index 0000000..a45cc07 --- /dev/null +++ b/austin-cron/pom.xml @@ -0,0 +1,30 @@ + + + + austin + com.java3y.austin + 0.0.1-SNAPSHOT + + 4.0.0 + + austin-cron + + 8 + 8 + + + + + com.java3y.austin + austin-support + 0.0.1-SNAPSHOT + + + com.xuxueli + xxl-job-core + + + + \ No newline at end of file diff --git a/austin-cron/src/main/java/com/java3y/austin/config/XxlJobConfig.java b/austin-cron/src/main/java/com/java3y/austin/config/XxlJobConfig.java new file mode 100644 index 0000000..df1aa11 --- /dev/null +++ b/austin-cron/src/main/java/com/java3y/austin/config/XxlJobConfig.java @@ -0,0 +1,43 @@ +package com.java3y.austin.config; + +import com.xxl.job.core.executor.impl.XxlJobSpringExecutor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Slf4j +@Configuration +public class XxlJobConfig { + + @Value("${xxl.job.admin.addresses}") + private String adminAddresses; + @Value("${xxl.job.executor.appname}") + private String appName; + @Value("${xxl.job.executor.ip}") + private String ip; + @Value("${xxl.job.executor.port}") + private int port; + @Value("${xxl.job.accessToken}") + private String accessToken; + @Value("${xxl.job.executor.logpath}") + private String logPath; + @Value("${xxl.job.executor.logretentiondays}") + private int logRetentionDays; + + @Bean + public XxlJobSpringExecutor xxlJobExecutor() { + // 创建 XxlJobSpringExecutor 执行器 + XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor(); + xxlJobSpringExecutor.setAdminAddresses(adminAddresses); + xxlJobSpringExecutor.setAppname(appName); + xxlJobSpringExecutor.setIp(ip); + xxlJobSpringExecutor.setPort(port); + xxlJobSpringExecutor.setAccessToken(accessToken); + xxlJobSpringExecutor.setLogPath(logPath); + xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays); + // 返回 + return xxlJobSpringExecutor; + } + +} diff --git a/austin-cron/src/main/java/com/java3y/austin/constants/XxlJobConstant.java b/austin-cron/src/main/java/com/java3y/austin/constants/XxlJobConstant.java new file mode 100644 index 0000000..8c98cfc --- /dev/null +++ b/austin-cron/src/main/java/com/java3y/austin/constants/XxlJobConstant.java @@ -0,0 +1,36 @@ +package com.java3y.austin.constants; + + +/** + * xxl-job常量信息 + * + * @author 3y + */ +public class XxlJobConstant { + + /** + * 接口路径 + */ + public static final String LOGIN_URL = "/xxl-job-admin/login"; + public static final String INSERT_URL = "/jobinfo/add"; + public static final String UPDATE_URL = "/jobinfo/update"; + public static final String DELETE_URL = "/jobinfo/remove"; + public static final String RUN_URL = "/jobinfo/start"; + public static final String STOP_URL = "/jobinfo/stop"; + + /** + * 执行器名称 + */ + public static final String HANDLER_NAME = "austinJobHandler"; + + /** + * 超时时间 + */ + public static final Integer TIME_OUT = 120; + + /** + * 失败重试次数 + */ + public static final Integer RETRY_COUNT = 2; + +} diff --git a/austin-cron/src/main/java/com/java3y/austin/entity/XxlJobInfo.java b/austin-cron/src/main/java/com/java3y/austin/entity/XxlJobInfo.java new file mode 100644 index 0000000..95e1ef9 --- /dev/null +++ b/austin-cron/src/main/java/com/java3y/austin/entity/XxlJobInfo.java @@ -0,0 +1,57 @@ +package com.java3y.austin.entity; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; + + +/** + * xxl 任务信息 配置 + */ +@Data +@Accessors(chain = true) +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class XxlJobInfo implements Serializable { + + private Integer id; // 主键ID + + private int jobGroup; // 执行器主键ID + private String jobDesc; + + private Date addTime; + private Date updateTime; + + private String author; // 负责人 + private String alarmEmail; // 报警邮件 + + private String scheduleType; // 调度类型 + private String scheduleConf; // 调度配置,值含义取决于调度类型 + + private String misfireStrategy; // 调度过期策略 + + private String executorRouteStrategy; // 执行器路由策略 + private String executorHandler; // 执行器,任务Handler名称 + private String executorParam; // 执行器,任务参数 + private String executorBlockStrategy; // 阻塞处理策略 + private int executorTimeout; // 任务执行超时时间,单位秒 + private int executorFailRetryCount; // 失败重试次数 + + private String glueType; // GLUE类型 #com.xxl.job.core.glue.GlueTypeEnum + private String glueSource; // GLUE源代码 + private String glueRemark; // GLUE备注 + private Date glueUpdatetime; // GLUE更新时间 + + private String childJobId; // 子任务ID,多个逗号分隔 + + private int triggerStatus; // 调度状态:0-停止,1-运行 + private long triggerLastTime; // 上次调度时间 + private long triggerNextTime; // 下次调度时间 + +} diff --git a/austin-cron/src/main/java/com/java3y/austin/enums/ExecutorBlockStrategyEnum.java b/austin-cron/src/main/java/com/java3y/austin/enums/ExecutorBlockStrategyEnum.java new file mode 100644 index 0000000..fe4dea5 --- /dev/null +++ b/austin-cron/src/main/java/com/java3y/austin/enums/ExecutorBlockStrategyEnum.java @@ -0,0 +1,26 @@ +package com.java3y.austin.enums; + +/** + * 执行阻塞队列 + * @author 3y + */ +public enum ExecutorBlockStrategyEnum { + /** + * 单机串行 + */ + SERIAL_EXECUTION, + + /** + * 丢弃后续调度 + */ + DISCARD_LATER, + + /** + * 覆盖之前调度 + */ + COVER_EARLY; + + ExecutorBlockStrategyEnum() { + + } +} diff --git a/austin-cron/src/main/java/com/java3y/austin/enums/ExecutorRouteStrategyEnum.java b/austin-cron/src/main/java/com/java3y/austin/enums/ExecutorRouteStrategyEnum.java new file mode 100644 index 0000000..d1e702b --- /dev/null +++ b/austin-cron/src/main/java/com/java3y/austin/enums/ExecutorRouteStrategyEnum.java @@ -0,0 +1,23 @@ +package com.java3y.austin.enums; + + +/** + * 路由策略 + * @author 3y + */ +public enum ExecutorRouteStrategyEnum { + + FIRST, + LAST, + ROUND, + RANDOM, + CONSISTENT_HASH, + LEAST_FREQUENTLY_USED, + LEAST_RECENTLY_USED, + FAILOVER, + BUSYOVER, + SHARDING_BROADCAST; + + ExecutorRouteStrategyEnum() { + } +} diff --git a/austin-cron/src/main/java/com/java3y/austin/enums/GlueTypeEnum.java b/austin-cron/src/main/java/com/java3y/austin/enums/GlueTypeEnum.java new file mode 100644 index 0000000..14fcdca --- /dev/null +++ b/austin-cron/src/main/java/com/java3y/austin/enums/GlueTypeEnum.java @@ -0,0 +1,19 @@ +package com.java3y.austin.enums; + +/** + * GlueTyp 类型(默认BEAN) + * @author 3y + */ +public enum GlueTypeEnum { + + BEAN, + GLUE_GROOVY, + GLUE_SHELL, + GLUE_PYTHON, + GLUE_PHP, + GLUE_NODEJS, + GLUE_POWERSHELL; + + GlueTypeEnum() { + } +} diff --git a/austin-cron/src/main/java/com/java3y/austin/enums/MisfireStrategyEnum.java b/austin-cron/src/main/java/com/java3y/austin/enums/MisfireStrategyEnum.java new file mode 100644 index 0000000..b5fa6ae --- /dev/null +++ b/austin-cron/src/main/java/com/java3y/austin/enums/MisfireStrategyEnum.java @@ -0,0 +1,21 @@ +package com.java3y.austin.enums; + +/** + * 调度过期策略 + * @author 3y + */ +public enum MisfireStrategyEnum { + + /** + * do nothing + */ + DO_NOTHING, + + /** + * fire once now + */ + FIRE_ONCE_NOW; + + MisfireStrategyEnum() { + } +} diff --git a/austin-cron/src/main/java/com/java3y/austin/enums/ScheduleTypeEnum.java b/austin-cron/src/main/java/com/java3y/austin/enums/ScheduleTypeEnum.java new file mode 100644 index 0000000..7fe2e03 --- /dev/null +++ b/austin-cron/src/main/java/com/java3y/austin/enums/ScheduleTypeEnum.java @@ -0,0 +1,23 @@ +package com.java3y.austin.enums; + +/** + * 调度类型 + * @author 3y + */ +public enum ScheduleTypeEnum { + + NONE, + /** + * schedule by cron + */ + CRON, + + /** + * schedule by fixed rate (in seconds) + */ + FIX_RATE; + + ScheduleTypeEnum() { + } + +} diff --git a/austin-cron/src/main/java/com/java3y/austin/handler/CronTaskHandler.java b/austin-cron/src/main/java/com/java3y/austin/handler/CronTaskHandler.java new file mode 100644 index 0000000..ff537a5 --- /dev/null +++ b/austin-cron/src/main/java/com/java3y/austin/handler/CronTaskHandler.java @@ -0,0 +1,19 @@ +package com.java3y.austin.handler; + +import com.xxl.job.core.handler.annotation.XxlJob; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +@Service +@Slf4j +public class CronTaskHandler { + + /** + * 简单任务 + */ + @XxlJob("austinJobHandler") + public void execute() { + log.info("XXL-JOB, Hello World."); + } + +} diff --git a/austin-cron/src/main/java/com/java3y/austin/service/CronTaskService.java b/austin-cron/src/main/java/com/java3y/austin/service/CronTaskService.java new file mode 100644 index 0000000..01ccfc6 --- /dev/null +++ b/austin-cron/src/main/java/com/java3y/austin/service/CronTaskService.java @@ -0,0 +1,42 @@ +package com.java3y.austin.service; + +import com.java3y.austin.entity.XxlJobInfo; +import com.java3y.austin.vo.BasicResultVO; + +/** + * 定时任务服务 + */ +public interface CronTaskService { + + + /** + * 新增/修改 定时任务 + * + * @return 新增时返回任务Id,修改时无返回 + */ + BasicResultVO saveCronTask(XxlJobInfo xxlJobInfo); + + /** + * 删除定时任务 + * + * @param taskId + */ + BasicResultVO deleteCronTask(Integer taskId); + + /** + * 启动定时任务 + * + * @param taskId + */ + BasicResultVO startCronTask(Integer taskId); + + + /** + * 暂停定时任务 + * + * @param taskId + */ + BasicResultVO stopCronTask(Integer taskId); + + +} diff --git a/austin-cron/src/main/java/com/java3y/austin/service/impl/CronTaskServiceImpl.java b/austin-cron/src/main/java/com/java3y/austin/service/impl/CronTaskServiceImpl.java new file mode 100644 index 0000000..c1fd646 --- /dev/null +++ b/austin-cron/src/main/java/com/java3y/austin/service/impl/CronTaskServiceImpl.java @@ -0,0 +1,139 @@ +package com.java3y.austin.service.impl; + +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import com.alibaba.fastjson.JSON; +import com.java3y.austin.constants.XxlJobConstant; +import com.java3y.austin.entity.XxlJobInfo; +import com.java3y.austin.enums.RespStatusEnum; +import com.java3y.austin.service.CronTaskService; +import com.java3y.austin.vo.BasicResultVO; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.net.HttpCookie; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author 3y + */ +@Slf4j +@Service +public class CronTaskServiceImpl implements CronTaskService { + + @Value("${xxl.job.admin.username}") + private String xxlUserName; + + @Value("${xxl.job.admin.password}") + private String xxlPassword; + + @Value("${xxl.job.admin.addresses}") + private String xxlAddresses; + + + @Override + public BasicResultVO saveCronTask(XxlJobInfo xxlJobInfo) { + Map params = JSON.parseObject(JSON.toJSONString(xxlJobInfo), Map.class); + + String path; + if (xxlJobInfo.getId() != null) { + path = xxlAddresses + XxlJobConstant.INSERT_URL; + } else { + path = xxlAddresses + XxlJobConstant.UPDATE_URL; + } + //XxlJobInfo.builder().jobGroup(1).jobDesc() +// Map paramMap = new HashMap<>(); +// paramMap.put("jobGroup", 1); +// paramMap.put("jobDesc", "这是测试任务"); +// paramMap.put("executorRouteStrategy", "FIRST"); +// paramMap.put("cronGen_display", "* * * * * ? *"); +// paramMap.put("scheduleConf", "* * * * * ? *"); +// paramMap.put("year", "2"); +// paramMap.put("misfireStrategy", "DO_NOTHING"); +// paramMap.put("glueType", "BEAN"); +// paramMap.put("schedule_conf_CRON", "* * * * * ? *"); +// paramMap.put("executorHandler", "messageJob"); // 此处hander需提前在项目中定义 +// paramMap.put("executorBlockStrategy", "SERIAL_EXECUTION"); +// paramMap.put("executorTimeout", 0); +// paramMap.put("executorFailRetryCount", 1); +// paramMap.put("author", "admin"); +// paramMap.put("glueRemark", "GLUE代码初始化"); +// paramMap.put("triggerStatus", 1); +// paramMap.put("scheduleType", "CRON"); + + HttpResponse response = HttpRequest.post(path).form(params).cookie(getCookie()).execute(); + if (!response.isOk()) { + log.error("TaskService#saveTask fail:{}", JSON.toJSONString(response.body())); + return BasicResultVO.fail(RespStatusEnum.SERVICE_ERROR, JSON.toJSONString(response.body())); + } + return BasicResultVO.success(JSON.toJSONString(response.body())); + } + + @Override + public BasicResultVO deleteCronTask(Integer taskId) { + HashMap params = new HashMap<>(); + params.put("id", taskId); + String path = xxlAddresses + XxlJobConstant.DELETE_URL; + HttpResponse response = HttpRequest.post(path).form(params).cookie(getCookie()).execute(); + if (!response.isOk()) { + log.error("TaskService#deleteCronTask fail:{}", JSON.toJSONString(response.body())); + return BasicResultVO.fail(RespStatusEnum.SERVICE_ERROR, JSON.toJSONString(response.body())); + } + return BasicResultVO.success(); + } + + @Override + public BasicResultVO startCronTask(Integer taskId) { + HashMap params = new HashMap<>(); + params.put("id", taskId); + String path = xxlAddresses + XxlJobConstant.RUN_URL; + HttpResponse response = HttpRequest.post(path).form(params).cookie(getCookie()).execute(); + if (!response.isOk()) { + log.error("TaskService#startCronTask fail:{}", JSON.toJSONString(response.body())); + return BasicResultVO.fail(RespStatusEnum.SERVICE_ERROR, JSON.toJSONString(response.body())); + } + return BasicResultVO.success(); + } + + @Override + public BasicResultVO stopCronTask(Integer taskId) { + HashMap params = new HashMap<>(); + params.put("id", taskId); + + String path = xxlAddresses + XxlJobConstant.STOP_URL; + HttpResponse response = HttpRequest.post(path).form(params).cookie(getCookie()).execute(); + if (!response.isOk()) { + log.error("TaskService#stopCronTask fail:{}", JSON.parseObject(response.body())); + return BasicResultVO.fail(RespStatusEnum.SERVICE_ERROR, JSON.toJSONString(response.body())); + } + return BasicResultVO.success(); + } + + /** + * 获取xxl cookie + * + * @return String + */ + private String getCookie() { + String path = xxlAddresses + XxlJobConstant.LOGIN_URL; + Map hashMap = new HashMap<>(); + hashMap.put("userName", xxlUserName); + hashMap.put("password", xxlPassword); + log.info("TaskService#getCookie params:{}", hashMap); + + HttpResponse response = HttpRequest.post(path).form(hashMap).execute(); + if (response.isOk()) { + List cookies = response.getCookies(); + StringBuilder sb = new StringBuilder(); + for (HttpCookie cookie : cookies) { + sb.append(cookie.toString()); + } + return sb.toString(); + } + log.error("TaskService#getCookie fail:{}", JSON.parseObject(response.body())); + return null; + } +} diff --git a/austin-cron/src/main/java/com/java3y/austin/utils/XxlJobUtils.java b/austin-cron/src/main/java/com/java3y/austin/utils/XxlJobUtils.java new file mode 100644 index 0000000..7fe937b --- /dev/null +++ b/austin-cron/src/main/java/com/java3y/austin/utils/XxlJobUtils.java @@ -0,0 +1,58 @@ +package com.java3y.austin.utils; + +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSON; +import com.java3y.austin.constant.AustinConstant; +import com.java3y.austin.constants.XxlJobConstant; +import com.java3y.austin.domain.MessageTemplate; +import com.java3y.austin.entity.XxlJobInfo; +import com.java3y.austin.enums.*; + +/** + * xxlJob工具类 + * + * @author 3y + */ +public class XxlJobUtils { + + /** + * 构建xxlJobInfo信息 + * + * @param messageTemplate + * @param triggerStatus 是否启动定时任务 + * @return + */ + public static XxlJobInfo buildXxlJobInfo(MessageTemplate messageTemplate) { + + // 判断是否为cron表达式 + String scheduleConf = StrUtil.EMPTY; + String scheduleType = ScheduleTypeEnum.NONE.name(); + if (!messageTemplate.getExpectPushTime().equals(String.valueOf(AustinConstant.FALSE))) { + scheduleType = ScheduleTypeEnum.CRON.name(); + scheduleConf = messageTemplate.getExpectPushTime(); + } + + XxlJobInfo xxlJobInfo = XxlJobInfo.builder().jobGroup(1).jobDesc(messageTemplate.getName()) + .author(messageTemplate.getCreator()) + .scheduleConf(scheduleConf) + .scheduleType(scheduleType) + .misfireStrategy(MisfireStrategyEnum.DO_NOTHING.name()) + .executorBlockStrategy(ExecutorRouteStrategyEnum.CONSISTENT_HASH.name()) + .executorHandler(XxlJobConstant.HANDLER_NAME) + .executorParam(JSON.toJSONString(messageTemplate)) + .executorBlockStrategy(ExecutorBlockStrategyEnum.SERIAL_EXECUTION.name()) + .executorTimeout(XxlJobConstant.TIME_OUT) + .executorFailRetryCount(XxlJobConstant.RETRY_COUNT) + .glueType(GlueTypeEnum.BEAN.name()) + .triggerStatus(AustinConstant.FALSE) + .glueRemark(StrUtil.EMPTY) + .glueSource(StrUtil.EMPTY) + .alarmEmail(StrUtil.EMPTY) + .childJobId(StrUtil.EMPTY).build(); + + if (messageTemplate.getCronTaskId() != null) { + xxlJobInfo.setId(messageTemplate.getCronTaskId()); + } + return xxlJobInfo; + } +} diff --git a/austin-support/src/main/java/com/java3y/austin/domain/MessageTemplate.java b/austin-support/src/main/java/com/java3y/austin/domain/MessageTemplate.java index beabaf2..cae21f6 100644 --- a/austin-support/src/main/java/com/java3y/austin/domain/MessageTemplate.java +++ b/austin-support/src/main/java/com/java3y/austin/domain/MessageTemplate.java @@ -50,6 +50,12 @@ public class MessageTemplate implements Serializable { */ private Integer msgStatus; + /** + * 定时任务Id(由xxl-job返回) + */ + private Integer cronTaskId; + + /** * 发送的Id类型 */ diff --git a/austin-web/pom.xml b/austin-web/pom.xml index f1af1f5..44715d5 100644 --- a/austin-web/pom.xml +++ b/austin-web/pom.xml @@ -31,6 +31,11 @@ austin-service-api-impl 0.0.1-SNAPSHOT + + com.java3y.austin + austin-cron + 0.0.1-SNAPSHOT + diff --git a/austin-web/src/main/java/com/java3y/austin/controller/MessageTemplateController.java b/austin-web/src/main/java/com/java3y/austin/controller/MessageTemplateController.java index 9a64b60..68a1d5c 100644 --- a/austin-web/src/main/java/com/java3y/austin/controller/MessageTemplateController.java +++ b/austin-web/src/main/java/com/java3y/austin/controller/MessageTemplateController.java @@ -48,7 +48,7 @@ public class MessageTemplateController { * 如果Id不存在,则保存 */ @PostMapping("/save") - @ApiOperation("/插入数据") + @ApiOperation("/保存数据") public BasicResultVO saveOrUpdate(@RequestBody MessageTemplate messageTemplate) { MessageTemplate info = messageTemplateService.saveOrUpdate(messageTemplate); @@ -123,4 +123,25 @@ public class MessageTemplateController { return BasicResultVO.success(response); } + + /** + * 启动模板的定时任务 + */ + @PostMapping("start/{id}") + @ApiOperation("/启动模板的定时任务") + public BasicResultVO start(@RequestBody @PathVariable("id") Long id) { + messageTemplateService.startCronTask(id); + + return BasicResultVO.success(); + } + /** + * 启动模板的定时任务 + */ + @PostMapping("stop/{id}") + @ApiOperation("/暂停模板的定时任务") + public BasicResultVO stop(@RequestBody @PathVariable("id") Long id) { + messageTemplateService.stopCronTask(id); + + return BasicResultVO.success(); + } } diff --git a/austin-web/src/main/java/com/java3y/austin/controller/XxlJobController.java b/austin-web/src/main/java/com/java3y/austin/controller/XxlJobController.java new file mode 100644 index 0000000..97200d7 --- /dev/null +++ b/austin-web/src/main/java/com/java3y/austin/controller/XxlJobController.java @@ -0,0 +1,24 @@ +package com.java3y.austin.controller; + + +import com.java3y.austin.service.CronTaskService; +import io.swagger.annotations.Api; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Api(tags = {"定时任务接口"}) +@RestController +public class XxlJobController { + + @Autowired + private CronTaskService cronTaskService; + + + @RequestMapping("/xxl/add/task") + public Integer addTask() { + + // return taskService.saveTask(); + return null; + } +} diff --git a/austin-web/src/main/java/com/java3y/austin/service/MessageTemplateService.java b/austin-web/src/main/java/com/java3y/austin/service/MessageTemplateService.java index 1b30588..63e883c 100644 --- a/austin-web/src/main/java/com/java3y/austin/service/MessageTemplateService.java +++ b/austin-web/src/main/java/com/java3y/austin/service/MessageTemplateService.java @@ -1,6 +1,7 @@ package com.java3y.austin.service; import com.java3y.austin.domain.MessageTemplate; +import com.java3y.austin.vo.BasicResultVO; import com.java3y.austin.vo.MessageTemplateParam; import java.util.List; @@ -60,4 +61,15 @@ public interface MessageTemplateService { * @param id */ void copy(Long id); + + /** + * 启动模板的定时任务 + */ + BasicResultVO startCronTask(Long id); + + /** + * 暂停模板的定时任务 + */ + BasicResultVO stopCronTask(Long id); + } diff --git a/austin-web/src/main/java/com/java3y/austin/service/impl/MessageTemplateServiceImpl.java b/austin-web/src/main/java/com/java3y/austin/service/impl/MessageTemplateServiceImpl.java index 58d3cfa..a929db8 100644 --- a/austin-web/src/main/java/com/java3y/austin/service/impl/MessageTemplateServiceImpl.java +++ b/austin-web/src/main/java/com/java3y/austin/service/impl/MessageTemplateServiceImpl.java @@ -6,9 +6,14 @@ import cn.hutool.core.util.StrUtil; import com.java3y.austin.constant.AustinConstant; import com.java3y.austin.dao.MessageTemplateDao; import com.java3y.austin.domain.MessageTemplate; +import com.java3y.austin.entity.XxlJobInfo; import com.java3y.austin.enums.AuditStatus; import com.java3y.austin.enums.MessageStatus; +import com.java3y.austin.enums.TemplateType; +import com.java3y.austin.service.CronTaskService; import com.java3y.austin.service.MessageTemplateService; +import com.java3y.austin.utils.XxlJobUtils; +import com.java3y.austin.vo.BasicResultVO; import com.java3y.austin.vo.MessageTemplateParam; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.PageRequest; @@ -24,9 +29,13 @@ import java.util.List; */ @Service public class MessageTemplateServiceImpl implements MessageTemplateService { + + @Autowired private MessageTemplateDao messageTemplateDao; + @Autowired + private CronTaskService cronTaskService; @Override public List queryList(MessageTemplateParam param) { @@ -43,10 +52,15 @@ public class MessageTemplateServiceImpl implements MessageTemplateService { public MessageTemplate saveOrUpdate(MessageTemplate messageTemplate) { if (messageTemplate.getId() == null) { initStatus(messageTemplate); + } else { + resetStatus(messageTemplate); } + + messageTemplate.setUpdated(Math.toIntExact(DateUtil.currentSeconds())); return messageTemplateDao.save(messageTemplate); } + @Override public void deleteByIds(List ids) { Iterable messageTemplates = messageTemplateDao.findAllById(ids); @@ -62,9 +76,36 @@ public class MessageTemplateServiceImpl implements MessageTemplateService { @Override public void copy(Long id) { MessageTemplate messageTemplate = messageTemplateDao.findById(id).get(); - MessageTemplate clone = ObjectUtil.clone(messageTemplate); - clone.setId(null); + MessageTemplate clone = ObjectUtil.clone(messageTemplate).setId(null); + messageTemplateDao.save(clone); + } + + @Override + public BasicResultVO startCronTask(Long id) { + // 1.修改模板状态 + MessageTemplate messageTemplate = messageTemplateDao.findById(id).get(); + + // 2.动态创建定时任务并启动 + XxlJobInfo xxlJobInfo = XxlJobUtils.buildXxlJobInfo(messageTemplate); + + BasicResultVO basicResultVO = cronTaskService.saveCronTask(xxlJobInfo); + // basicResultVO.getData() + //cronTaskService.startCronTask() + + MessageTemplate clone = ObjectUtil.clone(messageTemplate).setMsgStatus(MessageStatus.RUN.getCode()).setUpdated(Math.toIntExact(DateUtil.currentSeconds())); + messageTemplateDao.save(clone); + return BasicResultVO.success(); + } + + @Override + public BasicResultVO stopCronTask(Long id) { + // 1.修改模板状态 + MessageTemplate messageTemplate = messageTemplateDao.findById(id).get(); + MessageTemplate clone = ObjectUtil.clone(messageTemplate).setMsgStatus(MessageStatus.STOP.getCode()).setUpdated(Math.toIntExact(DateUtil.currentSeconds())); messageTemplateDao.save(clone); + + // 2.暂停定时任务 + return cronTaskService.stopCronTask(clone.getCronTaskId()); } @@ -79,8 +120,26 @@ public class MessageTemplateServiceImpl implements MessageTemplateService { .setMsgStatus(MessageStatus.INIT.getCode()).setAuditStatus(AuditStatus.WAIT_AUDIT.getCode()) .setCreator("Java3y").setUpdator("Java3y").setTeam("公众号Java3y").setAuditor("3y") .setDeduplicationTime(AustinConstant.FALSE).setIsNightShield(AustinConstant.FALSE) - .setCreated(Math.toIntExact(DateUtil.currentSeconds())).setUpdated(Math.toIntExact(DateUtil.currentSeconds())) + .setCreated(Math.toIntExact(DateUtil.currentSeconds())) .setIsDeleted(AustinConstant.FALSE); + + } + + /** + * 1. 重置模板的状态 + * 2. 修改定时任务信息 + * + * @param messageTemplate + */ + private void resetStatus(MessageTemplate messageTemplate) { + messageTemplate.setUpdator(messageTemplate.getUpdator()) + .setMsgStatus(MessageStatus.INIT.getCode()).setAuditStatus(AuditStatus.WAIT_AUDIT.getCode()); + + if (messageTemplate.getCronTaskId() != null && TemplateType.CLOCKING.getCode().equals(messageTemplate.getTemplateType())) { + XxlJobInfo xxlJobInfo = XxlJobUtils.buildXxlJobInfo(messageTemplate); + cronTaskService.saveCronTask(xxlJobInfo); + cronTaskService.stopCronTask(messageTemplate.getCronTaskId()); + } } diff --git a/austin-web/src/main/java/com/java3y/austin/vo/TaskParam.java b/austin-web/src/main/java/com/java3y/austin/vo/TaskParam.java new file mode 100644 index 0000000..88e0ff6 --- /dev/null +++ b/austin-web/src/main/java/com/java3y/austin/vo/TaskParam.java @@ -0,0 +1,42 @@ +package com.java3y.austin.vo; + + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Map; + +/** + * xxlJob任务的参数 + * @author 3y + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class TaskParam { + + /** + * 模板Id + */ + private String messageTemplateId; + + /** + * cron表达式 + */ + private String cron; + + /** + * 创建者 + */ + private String creator; + + + /** + * 额外参数信息 + */ + private Map extra; + +} diff --git a/austin-web/src/main/resources/application.yml b/austin-web/src/main/resources/application.yml index ce4b6ed..68f78fe 100644 --- a/austin-web/src/main/resources/application.yml +++ b/austin-web/src/main/resources/application.yml @@ -68,4 +68,19 @@ apollo: bootstrap: enabled: true namespaces: boss.austin + +# xxl job TODO +xxl: + job: + admin: + addresses: # 调度中心部署跟地址 [选填]:如调度中心集群部署存在多个地址则用逗号分隔。执行器将会使用该地址进行"执行器心跳注册"和"任务结果回调";为空则关闭自动注册; + username: # 后台用户名 + password: # 后台密码 + executor: + appname: austin # 执行器 AppName [选填]:执行器心跳注册分组依据;为空则关闭自动注册 + ip: # 执行器IP [选填]:默认为空表示自动获取IP,多网卡时可手动设置指定IP,该IP不会绑定Host仅作为通讯实用;地址信息用于 "执行器注册" 和 "调度中心请求并触发任务"; + port: 6666 # ### 执行器端口号 [选填]:小于等于0则自动获取;默认端口为9999,单机部署多个执行器时,注意要配置不同执行器端口; + logpath: # 执行器运行日志文件存储磁盘路径 [选填] :需要对该路径拥有读写权限;为空则使用默认路径; + logretentiondays: 30 # 执行器日志文件保存天数 [选填] : 过期日志自动清理, 限制值大于等于3时生效; 否则, 如-1, 关闭自动清理功能; + accessToken: # 执行器通讯TOKEN [选填]:非空时启用; # tomcat / HikariPool(数据库连接池 配置) TODO \ No newline at end of file diff --git a/pom.xml b/pom.xml index 83a80b2..7fed6ac 100644 --- a/pom.xml +++ b/pom.xml @@ -11,6 +11,7 @@ austin-common austin-support austin-handler + austin-cron @@ -37,7 +38,6 @@ mysql mysql-connector-java 5.1.35 - @@ -111,6 +111,13 @@ 3.0.0 + + + com.xuxueli + xxl-job-core + 2.3.0 + + diff --git a/sql/austin.sql b/sql/austin.sql index f3c729e..d44fc91 100644 --- a/sql/austin.sql +++ b/sql/austin.sql @@ -1,31 +1,33 @@ -create database austin; +create +database austin; CREATE TABLE `message_template` ( - `id` bigint(20) NOT NULL AUTO_INCREMENT, + `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '标题', - `audit_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '当前消息审核状态: 10.待审核 20.审核成功 30.被拒绝', + `audit_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '当前消息审核状态: 10.待审核 20.审核成功 30.被拒绝', `flow_id` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '工单ID', - `msg_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '当前消息状态:10.新建 20.停用 30.启用 40.等待发送 50.发送中 60.发送成功 70.发送失败', - `id_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '消息的发送ID类型:10. userId 20.did 30.手机号 40.openId 50.email', - `send_channel` tinyint(4) NOT NULL DEFAULT '0' COMMENT '消息发送渠道:10.IM 20.Push 30.短信 40.Email 50.公众号 60.小程序', - `template_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '10.运营类 20.技术类接口调用', - `msg_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '10.通知类消息 20.营销类消息 30.验证码类消息', + `msg_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '当前消息状态:10.新建 20.停用 30.启用 40.等待发送 50.发送中 60.发送成功 70.发送失败', + `cron_task_id` int(11) COMMENT '定时任务Id (xxl-job-admin返回)', + `id_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '消息的发送ID类型:10. userId 20.did 30.手机号 40.openId 50.email', + `send_channel` tinyint(4) NOT NULL DEFAULT '0' COMMENT '消息发送渠道:10.IM 20.Push 30.短信 40.Email 50.公众号 60.小程序', + `template_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '10.运营类 20.技术类接口调用', + `msg_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '10.通知类消息 20.营销类消息 30.验证码类消息', `expect_push_time` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '期望发送时间:立即发送.10 定时任务以及周期任务.cron表达式', `msg_content` varchar(600) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '消息内容 占位符用{$var}表示', - `send_account` tinyint(4) NOT NULL DEFAULT '0' COMMENT '发送账号 一个渠道下可存在多个账号', + `send_account` tinyint(4) NOT NULL DEFAULT '0' COMMENT '发送账号 一个渠道下可存在多个账号', `creator` varchar(45) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '创建者', `updator` varchar(45) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '更新者', `auditor` varchar(45) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '审核人', `team` varchar(45) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '业务方团队', `proposer` varchar(45) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '业务方', - `is_deleted` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否删除:0.不删除 1.删除', - `created` int(11) NOT NULL DEFAULT '0' COMMENT '创建时间', - `updated` int(11) NOT NULL DEFAULT '0' COMMENT '更新时间', - `deduplication_time` tinyint(4) NOT NULL DEFAULT '0' COMMENT '去重时间 单位小时', - `is_night_shield` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否夜间屏蔽:0.夜间不屏蔽 1.夜间屏蔽', + `is_deleted` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否删除:0.不删除 1.删除', + `created` int(11) NOT NULL DEFAULT '0' COMMENT '创建时间', + `updated` int(11) NOT NULL DEFAULT '0' COMMENT '更新时间', + `deduplication_time` tinyint(4) NOT NULL DEFAULT '0' COMMENT '去重时间 单位小时', + `is_night_shield` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否夜间屏蔽:0.夜间不屏蔽 1.夜间屏蔽', PRIMARY KEY (`id`), - KEY `idx_channel` (`send_channel`) + KEY `idx_channel` (`send_channel`) ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8mb4 @@ -35,21 +37,21 @@ CREATE TABLE `message_template` CREATE TABLE `sms_record` ( - `id` bigint(20) NOT NULL AUTO_INCREMENT, - `message_template_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '消息模板ID', - `phone` bigint(20) NOT NULL DEFAULT '0' COMMENT '手机号', - `supplier_id` tinyint(4) NOT NULL DEFAULT '0' COMMENT '发送短信渠道商的ID', + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `message_template_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '消息模板ID', + `phone` bigint(20) NOT NULL DEFAULT '0' COMMENT '手机号', + `supplier_id` tinyint(4) NOT NULL DEFAULT '0' COMMENT '发送短信渠道商的ID', `supplier_name` varchar(40) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '发送短信渠道商的名称', `msg_content` varchar(600) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '短信发送的内容', `series_id` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '下发批次的ID', - `charging_num` tinyint(4) NOT NULL DEFAULT '0' COMMENT '计费条数', + `charging_num` tinyint(4) NOT NULL DEFAULT '0' COMMENT '计费条数', `report_content` varchar(50) NOT NULL DEFAULT '' COMMENT '回执内容', - `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '短信状态: 10.发送 20.成功 30.失败', - `send_date` int(11) NOT NULL DEFAULT '0' COMMENT '发送日期:20211112', - `created` int(11) NOT NULL DEFAULT '0' COMMENT '创建时间', - `updated` int(11) NOT NULL DEFAULT '0' COMMENT '更新时间', + `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '短信状态: 10.发送 20.成功 30.失败', + `send_date` int(11) NOT NULL DEFAULT '0' COMMENT '发送日期:20211112', + `created` int(11) NOT NULL DEFAULT '0' COMMENT '创建时间', + `updated` int(11) NOT NULL DEFAULT '0' COMMENT '更新时间', PRIMARY KEY (`id`), - KEY `idx_send_date` (`send_date`) + KEY `idx_send_date` (`send_date`) ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8mb4 @@ -60,7 +62,16 @@ CREATE TABLE `sms_record` INSERT INTO austin.message_template (id, name, audit_status, flow_id, msg_status, id_type, send_channel, template_type, msg_type, expect_push_time, msg_content, send_account, creator, updator, auditor, team, proposer, is_deleted, created, updated, deduplication_time, is_night_shield) VALUES (1, 'test短信', 10, 'yyyy', 10, 30, 30, 10, 10, '0', '{"content":"{$contentValue}"}', 10, 'yyyyc', 'yyyyu', 'yyyyyyz', 'yyyt', 'yyyy22', 0, 1636978066, 1636978066, 1, 0); /*测试短信+url链接追踪*/ -INSERT INTO austin.message_template (id, name, audit_status, flow_id, msg_status, id_type, send_channel, template_type, msg_type, expect_push_time, msg_content, send_account, creator, updator, auditor, team, proposer, is_deleted, created, updated, deduplication_time, is_night_shield) VALUES (2, 'test短信', 10, 'yyyy', 10, 30, 30, 10, 20, '0', '{"content":"{$contentValue}","url":"https://gitee.com/zhongfucheng/austin"}', 10, 'yyyyc', 'yyyyu', 'yyyyyyz', 'yyyt', 'yyyy22', 0, 1637411536, 1637411536, 1, 0); +INSERT INTO austin.message_template (id, name, audit_status, flow_id, msg_status, id_type, send_channel, template_type, + msg_type, expect_push_time, msg_content, send_account, creator, updator, auditor, + team, proposer, is_deleted, created, updated, deduplication_time, is_night_shield) +VALUES (2, 'test短信', 10, 'yyyy', 10, 30, 30, 10, 20, '0', + '{"content":"{$contentValue}","url":"https://gitee.com/zhongfucheng/austin"}', 10, 'yyyyc', 'yyyyu', 'yyyyyyz', + 'yyyt', 'yyyy22', 0, 1637411536, 1637411536, 1, 0); /*测试邮件发送*/ -INSERT INTO austin.message_template (id, name, audit_status, flow_id, msg_status, id_type, send_channel, template_type, msg_type, expect_push_time, msg_content, send_account, creator, updator, auditor, team, proposer, is_deleted, created, updated, deduplication_time, is_night_shield) VALUES (3, 'test邮件', 10, 'yyyy', 10, 50, 40, 20, 10, '0', '{"content":"{$contentValue}","title":"{$title}"}', 10, 'yyyyc', 'yyyyu', 'yyyyyyz', 'yyyt', 'yyyy22', 0, 1641546914, 1641546914, 1, 0); \ No newline at end of file +INSERT INTO austin.message_template (id, name, audit_status, flow_id, msg_status, id_type, send_channel, template_type, + msg_type, expect_push_time, msg_content, send_account, creator, updator, auditor, + team, proposer, is_deleted, created, updated, deduplication_time, is_night_shield) +VALUES (3, 'test邮件', 10, 'yyyy', 10, 50, 40, 20, 10, '0', '{"content":"{$contentValue}","title":"{$title}"}', 10, + 'yyyyc', 'yyyyu', 'yyyyyyz', 'yyyt', 'yyyy22', 0, 1641546914, 1641546914, 1, 0); \ No newline at end of file