feat: initial iShare project code

This commit is contained in:
purovps
2026-02-16 23:20:59 +08:00
parent 8c83a6fd46
commit 6f270a972e
1910 changed files with 218015 additions and 0 deletions

View File

@@ -0,0 +1,18 @@
FROM pig4cloud/java:8-jre
MAINTAINER wangiegie@gmail.com
ENV TZ=Asia/Shanghai
ENV JAVA_OPTS="-Xms128m -Xmx256m -Djava.security.egd=file:/dev/./urandom"
RUN ln -sf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
RUN mkdir -p /pigx-daemon-quartz
WORKDIR /pigx-daemon-quartz
EXPOSE 5007
ADD ./target/pigx-daemon-quartz.jar ./
CMD sleep 120;java $JAVA_OPTS -jar pigx-daemon-quartz.jar

View File

@@ -0,0 +1,126 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-visual</artifactId>
<version>5.2.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>pigx-daemon-quartz</artifactId>
<packaging>jar</packaging>
<description>基于quartz后台跑批定时任务模块</description>
<dependencies>
<!--注册中心客户端-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--配置中心客户端-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--日志处理-->
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-common-log</artifactId>
</dependency>
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-common-data</artifactId>
</dependency>
<!--mybatis-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!--数据库-->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<!--mssql-->
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
</dependency>
<!-- ojdbc8 -->
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
</dependency>
<!--PG-->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<!-- druid 连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
</dependency>
<!--swagger-->
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-common-swagger</artifactId>
</dependency>
<!--spring security 、oauth、jwt依赖-->
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-common-security</artifactId>
</dependency>
<!-- sentinel-->
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-common-sentinel</artifactId>
</dependency>
<!--灰度支持-->
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-common-gray</artifactId>
</dependency>
<!--web 模块-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--undertow容器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<!-- quartz 模块 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<configuration>
<skip>false</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,25 @@
package com.pig4cloud.pigx.daemon.quartz;
import com.pig4cloud.pigx.common.feign.annotation.EnablePigxFeignClients;
import com.pig4cloud.pigx.common.security.annotation.EnablePigxResourceServer;
import com.pig4cloud.pigx.common.swagger.annotation.EnableOpenApi;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* @author frwcloud
* @date 2019/01/23 定时任务模块
*/
@EnableOpenApi("job")
@EnablePigxFeignClients
@EnablePigxResourceServer
@EnableDiscoveryClient
@SpringBootApplication
public class PigxDaemonQuartzApplication {
public static void main(String[] args) {
SpringApplication.run(PigxDaemonQuartzApplication.class, args);
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.daemon.quartz.config;
import org.quartz.JobKey;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;
import org.springframework.util.Assert;
/**
* @author 郑健楠
*/
class AutowireCapableBeanJobFactory extends SpringBeanJobFactory {
private final AutowireCapableBeanFactory beanFactory;
AutowireCapableBeanJobFactory(AutowireCapableBeanFactory beanFactory) {
Assert.notNull(beanFactory, "Bean factory must not be null");
this.beanFactory = beanFactory;
}
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
Object jobInstance = super.createJobInstance(bundle);
this.beanFactory.autowireBean(jobInstance);
// 此处必须注入 beanName 不然sentinel 报错
JobKey jobKey = bundle.getTrigger().getJobKey();
String beanName = jobKey + jobKey.getName();
this.beanFactory.initializeBean(jobInstance, beanName);
return jobInstance;
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.daemon.quartz.config;
import com.pig4cloud.pigx.daemon.quartz.constants.PigxQuartzEnum;
import com.pig4cloud.pigx.daemon.quartz.service.SysJobService;
import com.pig4cloud.pigx.daemon.quartz.util.TaskUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Scheduler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author 郑健楠
* <p>
* 初始化加载定时任务
*/
@Slf4j
@Configuration
@AllArgsConstructor
public class PigxInitQuartzJob {
private final SysJobService sysJobService;
private final TaskUtil taskUtil;
private final Scheduler scheduler;
@Bean
public void customize() {
sysJobService.list().forEach(sysjob -> {
if (PigxQuartzEnum.JOB_STATUS_RELEASE.getType().equals(sysjob.getJobStatus())) {
taskUtil.removeJob(sysjob, scheduler);
}
else if (PigxQuartzEnum.JOB_STATUS_RUNNING.getType().equals(sysjob.getJobStatus())) {
taskUtil.resumeJob(sysjob, scheduler);
}
else if (PigxQuartzEnum.JOB_STATUS_NOT_RUNNING.getType().equals(sysjob.getJobStatus())) {
taskUtil.pauseJob(sysjob, scheduler);
}
else {
taskUtil.removeJob(sysjob, scheduler);
}
});
}
}

View File

@@ -0,0 +1,137 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.daemon.quartz.config;
import org.quartz.Calendar;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.Trigger;
import org.quartz.ee.servlet.QuartzInitializerListener;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.quartz.QuartzProperties;
import org.springframework.boot.autoconfigure.quartz.SchedulerFactoryBeanCustomizer;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
/**
* @author 郑健楠
*/
@EnableAsync
@Configuration
@ConditionalOnClass({ Scheduler.class, SchedulerFactoryBean.class })
@EnableConfigurationProperties({ QuartzProperties.class })
public class PigxQuartzConfig {
private final QuartzProperties properties;
private final List<SchedulerFactoryBeanCustomizer> customizers;
private final JobDetail[] jobDetails;
private final Map<String, Calendar> calendars;
private final Trigger[] triggers;
private final ApplicationContext applicationContext;
public PigxQuartzConfig(QuartzProperties properties,
ObjectProvider<List<SchedulerFactoryBeanCustomizer>> customizers, ObjectProvider<JobDetail[]> jobDetails,
ObjectProvider<Map<String, Calendar>> calendars, ObjectProvider<Trigger[]> triggers,
ApplicationContext applicationContext) {
this.properties = properties;
this.customizers = customizers.getIfAvailable();
this.jobDetails = jobDetails.getIfAvailable();
this.calendars = calendars.getIfAvailable();
this.triggers = triggers.getIfAvailable();
this.applicationContext = applicationContext;
}
@Bean
@ConditionalOnMissingBean
public SchedulerFactoryBean quartzScheduler() {
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
schedulerFactoryBean.setJobFactory(
new AutowireCapableBeanJobFactory(this.applicationContext.getAutowireCapableBeanFactory()));
if (!this.properties.getProperties().isEmpty()) {
schedulerFactoryBean.setQuartzProperties(this.asProperties(this.properties.getProperties()));
}
if (this.jobDetails != null && this.jobDetails.length > 0) {
schedulerFactoryBean.setJobDetails(this.jobDetails);
}
if (this.calendars != null && !this.calendars.isEmpty()) {
schedulerFactoryBean.setCalendars(this.calendars);
}
if (this.triggers != null && this.triggers.length > 0) {
schedulerFactoryBean.setTriggers(this.triggers);
}
this.customize(schedulerFactoryBean);
return schedulerFactoryBean;
}
private Properties asProperties(Map<String, String> source) {
Properties properties = new Properties();
properties.putAll(source);
return properties;
}
private void customize(SchedulerFactoryBean schedulerFactoryBean) {
if (this.customizers != null) {
Iterator var2 = this.customizers.iterator();
while (var2.hasNext()) {
SchedulerFactoryBeanCustomizer customizer = (SchedulerFactoryBeanCustomizer) var2.next();
customizer.customize(schedulerFactoryBean);
}
}
}
/**
* 初始化监听器
* @return
*/
@Bean
public QuartzInitializerListener executorListener() {
return new QuartzInitializerListener();
}
/**
* 通过SchedulerFactoryBean获取Scheduler的实例
* @return
*/
@Bean
public Scheduler scheduler() {
return quartzScheduler().getScheduler();
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.daemon.quartz.config;
import org.springframework.boot.autoconfigure.quartz.SchedulerFactoryBeanCustomizer;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
/**
* @author 郑健楠
*/
@Configuration
public class PigxQuartzCustomizerConfig implements SchedulerFactoryBeanCustomizer {
@Override
public void customize(SchedulerFactoryBean schedulerFactoryBean) {
schedulerFactoryBean.setWaitForJobsToCompleteOnShutdown(true);
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.daemon.quartz.config;
import com.pig4cloud.pigx.daemon.quartz.constants.PigxQuartzEnum;
import com.pig4cloud.pigx.daemon.quartz.entity.SysJob;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.springframework.beans.factory.annotation.Autowired;
/**
* @author 郑健楠
*
* <p>
* 动态任务工厂
*/
@Slf4j
@DisallowConcurrentExecution
public class PigxQuartzFactory implements Job {
@Autowired
private PigxQuartzInvokeFactory pigxQuartzInvokeFactory;
@Override
@SneakyThrows
public void execute(JobExecutionContext jobExecutionContext) {
SysJob sysJob = (SysJob) jobExecutionContext.getMergedJobDataMap()
.get(PigxQuartzEnum.SCHEDULE_JOB_KEY.getType());
pigxQuartzInvokeFactory.init(sysJob, jobExecutionContext.getTrigger());
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.daemon.quartz.config;
import com.pig4cloud.pigx.daemon.quartz.entity.SysJob;
import com.pig4cloud.pigx.daemon.quartz.event.SysJobEvent;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.Aspect;
import org.quartz.Trigger;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
/**
* @author 郑健楠
*/
@Slf4j
@Aspect
@Service
@AllArgsConstructor
public class PigxQuartzInvokeFactory {
private final ApplicationEventPublisher publisher;
@SneakyThrows
void init(SysJob sysJob, Trigger trigger) {
publisher.publishEvent(new SysJobEvent(sysJob, trigger));
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.daemon.quartz.constants;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @author lengleng
* @date 2019-03-14
* <p>
* 任务类型枚举
*/
@Getter
@AllArgsConstructor
public enum JobTypeQuartzEnum {
/**
* 反射java类
*/
JAVA("1", "反射java类"),
/**
* spring bean 的方式
*/
SPRING_BEAN("2", "spring bean容器实例"),
/**
* rest 调用
*/
REST("3", "rest调用"),
/**
* jar
*/
JAR("4", "jar调用");
/**
* 类型
*/
private final String type;
/**
* 描述
*/
private final String description;
}

View File

@@ -0,0 +1,94 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.daemon.quartz.constants;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @author 郑健楠
*
* <p>
* 定时任务枚举
*/
@Getter
@AllArgsConstructor
public enum PigxQuartzEnum {
/**
* 错失执行策略默认
*/
MISFIRE_DEFAULT("0", "默认"),
/**
* 错失执行策略-立即执行错失任务
*/
MISFIRE_IGNORE_MISFIRES("1", "立即执行错失任务"),
/**
* 错失执行策略-触发一次执行周期执行
*/
MISFIRE_FIRE_AND_PROCEED("2", "触发一次执行周期执行"),
/**
* 错失执行策略-不触发执行周期执行
*/
MISFIRE_DO_NOTHING("3", "不触发周期执行"),
/**
* 任务详细信息的key
*/
SCHEDULE_JOB_KEY("scheduleJob", "获取任务详细信息的key"),
/**
* JOB执行状态0执行成功
*/
JOB_LOG_STATUS_SUCCESS("0", "执行成功"),
/**
* JOB执行状态1执行失败
*/
JOB_LOG_STATUS_FAIL("1", "执行失败"),
/**
* JOB状态1已发布
*/
JOB_STATUS_RELEASE("1", "已发布"),
/**
* JOB状态2运行中
*/
JOB_STATUS_RUNNING("2", "运行中"),
/**
* JOB状态3暂停
*/
JOB_STATUS_NOT_RUNNING("3", "暂停"),
/**
* JOB状态4删除
*/
JOB_STATUS_DEL("4", "删除");
/**
* 类型
*/
private final String type;
/**
* 描述
*/
private final String description;
}

View File

@@ -0,0 +1,310 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.daemon.quartz.controller;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.excel.annotation.ResponseExcel;
import com.pig4cloud.pigx.common.log.annotation.SysLog;
import com.pig4cloud.pigx.common.security.util.SecurityUtils;
import com.pig4cloud.pigx.daemon.quartz.constants.PigxQuartzEnum;
import com.pig4cloud.pigx.daemon.quartz.entity.SysJob;
import com.pig4cloud.pigx.daemon.quartz.entity.SysJobLog;
import com.pig4cloud.pigx.daemon.quartz.service.SysJobLogService;
import com.pig4cloud.pigx.daemon.quartz.service.SysJobService;
import com.pig4cloud.pigx.daemon.quartz.util.TaskUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import org.quartz.Scheduler;
import org.springframework.http.HttpHeaders;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* @author frwcloud
* @date 2019-01-27 10:04:42
* <p>
* 定时任务管理
*/
@RestController
@AllArgsConstructor
@RequestMapping("/sys-job")
@Tag(description = "sys-job", name = "定时任务")
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
public class SysJobController {
private final SysJobService sysJobService;
private final SysJobLogService sysJobLogService;
private final TaskUtil taskUtil;
private final Scheduler scheduler;
/**
* 定时任务分页查询
* @param page 分页对象
* @param sysJob 定时任务调度表
* @return
*/
@GetMapping("/page")
@Operation(description = "分页定时业务查询")
public R getSysJobPage(Page page, SysJob sysJob) {
LambdaQueryWrapper<SysJob> wrapper = Wrappers.<SysJob>lambdaQuery()
.like(StrUtil.isNotBlank(sysJob.getJobName()), SysJob::getJobName, sysJob.getJobName())
.like(StrUtil.isNotBlank(sysJob.getJobGroup()), SysJob::getJobGroup, sysJob.getJobGroup())
.eq(StrUtil.isNotBlank(sysJob.getJobStatus()), SysJob::getJobStatus, sysJob.getJobGroup())
.eq(StrUtil.isNotBlank(sysJob.getJobExecuteStatus()), SysJob::getJobExecuteStatus,
sysJob.getJobExecuteStatus());
return R.ok(sysJobService.page(page, wrapper));
}
/**
* 通过id查询定时任务
* @param id id
* @return R
*/
@GetMapping("/{id}")
@Operation(description = "唯一标识查询定时任务")
public R getById(@PathVariable("id") Long id) {
return R.ok(sysJobService.getById(id));
}
/**
* 新增定时任务
* @param sysJob 定时任务调度表
* @return R
*/
@SysLog("新增定时任务")
@PostMapping
@PreAuthorize("@pms.hasPermission('job_sys_job_add')")
@Operation(description = "新增定时任务")
public R save(@RequestBody SysJob sysJob) {
sysJob.setJobStatus(PigxQuartzEnum.JOB_STATUS_RELEASE.getType());
sysJob.setCreateBy(SecurityUtils.getUser().getUsername());
this.taskUtil.removeJob(sysJob, scheduler);
return R.ok();
}
/**
* 修改定时任务
* @param sysJob 定时任务调度表
* @return R
*/
@SysLog("修改定时任务")
@PutMapping
@PreAuthorize("@pms.hasPermission('job_sys_job_edit')")
@Operation(description = "修改定时任务")
public R updateById(@RequestBody SysJob sysJob) {
sysJob.setUpdateBy(SecurityUtils.getUser().getUsername());
SysJob querySysJob = this.sysJobService.getById(sysJob.getJobId());
if (PigxQuartzEnum.JOB_STATUS_NOT_RUNNING.getType().equals(querySysJob.getJobStatus())) {
this.taskUtil.addOrUpateJob(sysJob, scheduler);
sysJobService.updateById(sysJob);
}
else if (PigxQuartzEnum.JOB_STATUS_RELEASE.getType().equals(querySysJob.getJobStatus())) {
sysJobService.updateById(sysJob);
}
return R.ok();
}
/**
* 通过id删除定时任务
* @param id id
* @return R
*/
@SysLog("删除定时任务")
@DeleteMapping("/{id}")
@PreAuthorize("@pms.hasPermission('job_sys_job_del')")
@Operation(description = "唯一标识查询定时任务,暂停任务才能删除")
public R removeById(@PathVariable Long id) {
SysJob querySysJob = this.sysJobService.getById(id);
if (PigxQuartzEnum.JOB_STATUS_NOT_RUNNING.getType().equals(querySysJob.getJobStatus())) {
this.taskUtil.removeJob(querySysJob, scheduler);
this.sysJobService.removeById(id);
}
else if (PigxQuartzEnum.JOB_STATUS_RELEASE.getType().equals(querySysJob.getJobStatus())) {
this.sysJobService.removeById(id);
}
return R.ok();
}
/**
* 暂停全部定时任务
* @return
*/
@SysLog("暂停全部定时任务")
@PostMapping("/shutdown-jobs")
@PreAuthorize("@pms.hasPermission('job_sys_job_shutdown_job')")
@Operation(description = "暂停全部定时任务")
public R shutdownJobs() {
taskUtil.pauseJobs(scheduler);
long count = this.sysJobService.count(
new LambdaQueryWrapper<SysJob>().eq(SysJob::getJobStatus, PigxQuartzEnum.JOB_STATUS_RUNNING.getType()));
if (count <= 0) {
return R.ok("无正在运行定时任务");
}
else {
// 更新定时任务状态条件运行状态2更新为暂停状态2
this.sysJobService.update(
SysJob.builder().jobStatus(PigxQuartzEnum.JOB_STATUS_NOT_RUNNING.getType()).build(),
new UpdateWrapper<SysJob>().lambda().eq(SysJob::getJobStatus,
PigxQuartzEnum.JOB_STATUS_RUNNING.getType()));
return R.ok("暂停成功");
}
}
/**
* 启动全部定时任务
* @return
*/
@SysLog("启动全部定时任务")
@PostMapping("/start-jobs")
@PreAuthorize("@pms.hasPermission('job_sys_job_start_job')")
@Operation(description = "启动全部定时任务")
public R startJobs() {
// 更新定时任务状态条件暂停状态3更新为运行状态2
this.sysJobService.update(SysJob.builder().jobStatus(PigxQuartzEnum.JOB_STATUS_RUNNING.getType()).build(),
new UpdateWrapper<SysJob>().lambda().eq(SysJob::getJobStatus,
PigxQuartzEnum.JOB_STATUS_NOT_RUNNING.getType()));
taskUtil.startJobs(scheduler);
return R.ok();
}
/**
* 刷新全部定时任务
* @return
*/
@SysLog("刷新全部定时任务")
@PostMapping("/refresh-jobs")
@PreAuthorize("@pms.hasPermission('job_sys_job_refresh_job')")
@Operation(description = "刷新全部定时任务")
public R refreshJobs() {
sysJobService.list().forEach((sysjob) -> {
if (PigxQuartzEnum.JOB_STATUS_RELEASE.getType().equals(sysjob.getJobStatus())
|| PigxQuartzEnum.JOB_STATUS_DEL.getType().equals(sysjob.getJobStatus())) {
taskUtil.removeJob(sysjob, scheduler);
}
else if (PigxQuartzEnum.JOB_STATUS_RUNNING.getType().equals(sysjob.getJobStatus())
|| PigxQuartzEnum.JOB_STATUS_NOT_RUNNING.getType().equals(sysjob.getJobStatus())) {
taskUtil.addOrUpateJob(sysjob, scheduler);
}
else {
taskUtil.removeJob(sysjob, scheduler);
}
});
return R.ok();
}
/**
* 启动定时任务
* @param jobId
* @return
*/
@SysLog("启动定时任务")
@PostMapping("/start-job/{id}")
@PreAuthorize("@pms.hasPermission('job_sys_job_start_job')")
@Operation(description = "启动定时任务")
public R startJob(@PathVariable("id") Long jobId) {
SysJob querySysJob = this.sysJobService.getById(jobId);
if (querySysJob != null && PigxQuartzEnum.JOB_LOG_STATUS_FAIL.getType().equals(querySysJob.getJobStatus())) {
taskUtil.addOrUpateJob(querySysJob, scheduler);
}
else {
taskUtil.resumeJob(querySysJob, scheduler);
}
// 更新定时任务状态条件暂停状态3更新为运行状态2
this.sysJobService.updateById(
SysJob.builder().jobId(jobId).jobStatus(PigxQuartzEnum.JOB_STATUS_RUNNING.getType()).build());
return R.ok();
}
/**
* 启动定时任务
* @param jobId
* @return
*/
@SysLog("立刻执行定时任务")
@PostMapping("/run-job/{id}")
@PreAuthorize("@pms.hasPermission('job_sys_job_run_job')")
@Operation(description = "立刻执行定时任务")
public R runJob(@PathVariable("id") Long jobId) {
SysJob querySysJob = this.sysJobService.getById(jobId);
return TaskUtil.runOnce(scheduler, querySysJob) ? R.ok() : R.failed();
}
/**
* 暂停定时任务
* @return
*/
@SysLog("暂停定时任务")
@PostMapping("/shutdown-job/{id}")
@PreAuthorize("@pms.hasPermission('job_sys_job_shutdown_job')")
@Operation(description = "暂停定时任务")
public R shutdownJob(@PathVariable("id") Long id) {
SysJob querySysJob = this.sysJobService.getById(id);
// 更新定时任务状态条件运行状态2更新为暂停状态3
this.sysJobService.updateById(SysJob.builder().jobId(querySysJob.getJobId())
.jobStatus(PigxQuartzEnum.JOB_STATUS_NOT_RUNNING.getType()).build());
taskUtil.pauseJob(querySysJob, scheduler);
return R.ok();
}
/**
* 唯一标识查询定时执行日志
* @return
*/
@GetMapping("/job-log")
@Operation(description = "唯一标识查询定时执行日志")
public R getJobLog(Page page, SysJobLog sysJobLog) {
return R.ok(sysJobLogService.page(page, Wrappers.query(sysJobLog)));
}
/**
* 检验任务名称和任务组联合是否唯一
* @return
*/
@GetMapping("/is-valid-task-name")
@Operation(description = "检验任务名称和任务组联合是否唯一")
public R isValidTaskName(@RequestParam String jobName, @RequestParam String jobGroup) {
return this.sysJobService
.count(Wrappers.query(SysJob.builder().jobName(jobName).jobGroup(jobGroup).build())) > 0
? R.failed("任务重复,请检查此组内是否已包含同名任务") : R.ok();
}
/**
* 导出任务
* @param sysJob
* @return
*/
@ResponseExcel
@GetMapping("/export")
@Operation(description = "导出任务")
public List<SysJob> export(SysJob sysJob) {
return sysJobService.list(Wrappers.query(sysJob));
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.daemon.quartz.controller;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.daemon.quartz.entity.SysJobLog;
import com.pig4cloud.pigx.daemon.quartz.service.SysJobLogService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.*;
/**
* @author frwcloud
* <p>
* 定时任务执行日志表
*/
@RestController
@AllArgsConstructor
@RequestMapping("/sys-job-log")
@Tag(description = "sys-job-log", name = "定时任务日志")
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
public class SysJobLogController {
private final SysJobLogService sysJobLogService;
/**
* 分页查询
* @param page 分页对象
* @param sysJobLog 定时任务执行日志表
* @return
*/
@GetMapping("/page")
@Operation(description = "分页定时任务日志查询")
public R getSysJobLogPage(Page page, SysJobLog sysJobLog) {
return R.ok(sysJobLogService.page(page, Wrappers.query(sysJobLog)));
}
@DeleteMapping
@Operation(description = "批量删除日志")
public R deleteLogs(@RequestBody Long[] ids) {
return R.ok(sysJobLogService.removeBatchByIds(CollUtil.toList(ids)));
}
}

View File

@@ -0,0 +1,167 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.daemon.quartz.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
/**
* 定时任务调度表
*
* @author frwcloud
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "定时任务")
public class SysJob extends Model<SysJob> {
private static final long serialVersionUID = 1L;
/**
* 任务id
*/
@TableId(value = "job_id", type = IdType.ASSIGN_ID)
private Long jobId;
/**
* 任务名称
*/
private String jobName;
/**
* 任务组名
*/
private String jobGroup;
/**
* 组内执行顺利值越大执行优先级越高最大值9最小值1
*/
private String jobOrder;
/**
* 1、java类;2、spring bean名称;3、rest调用;4、jar调用;9其他
*/
private String jobType;
/**
* job_type=3时rest调用地址仅支持rest get协议,需要增加String返回值0成功1失败;job_type=4时jar路径;其它值为空
*/
private String executePath;
/**
* job_type=1时类完整路径;job_type=2时spring bean名称;其它值为空
*/
private String className;
/**
* 任务方法
*/
private String methodName;
/**
* 参数值
*/
private String methodParamsValue;
/**
* cron执行表达式
*/
private String cronExpression;
/**
* 错失执行策略1错失周期立即执行 2错失周期执行一次 3下周期执行
*/
private String misfirePolicy;
/**
* 1、多租户任务;2、非多租户任务
*/
private String jobTenantType;
/**
* 状态0、未发布;1、已发布;2、运行中;3、暂停;4、删除;
*/
private String jobStatus;
/**
* 状态0正常 1异常
*/
private String jobExecuteStatus;
/**
* 创建者
*/
@TableField(fill = FieldFill.INSERT)
private String createBy;
/**
* 更新者
*/
@TableField(fill = FieldFill.UPDATE)
private String updateBy;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
/**
* 修改时间
*/
@TableField(fill = FieldFill.UPDATE)
private LocalDateTime updateTime;
/**
* 首次执行时间
*/
private LocalDateTime startTime;
/**
* 上次执行时间
*/
private LocalDateTime previousTime;
/**
* 下次执行时间
*/
private LocalDateTime nextTime;
/**
* 租户id
*/
private Long tenantId;
/**
* 备注信息
*/
private String remark;
}

View File

@@ -0,0 +1,135 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.daemon.quartz.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
/**
* 定时任务执行日志表
*
* @author frwcloud
* @date 2019-01-27 13:40:20
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "定时任务日志")
public class SysJobLog extends Model<SysJobLog> {
private static final long serialVersionUID = 1L;
/**
* 任务日志ID
*/
@TableId(value = "job_log_id", type = IdType.ASSIGN_ID)
private Long jobLogId;
/**
* 任务id
*/
private Long jobId;
/**
* 任务名称
*/
private String jobName;
/**
* 任务组名
*/
private String jobGroup;
/**
* 组内执行顺利值越大执行优先级越高最大值9最小值1
*/
private String jobOrder;
/**
* 1、java类;2、spring bean名称;3、rest调用;4、jar调用;9其他
*/
private String jobType;
/**
* job_type=3时rest调用地址仅支持post协议;job_type=4时jar路径;其它值为空
*/
private String executePath;
/**
* job_type=1时类完整路径;job_type=2时spring bean名称;其它值为空
*/
private String className;
/**
* 任务方法
*/
private String methodName;
/**
* 参数值
*/
private String methodParamsValue;
/**
* cron执行表达式
*/
private String cronExpression;
/**
* 日志信息
*/
private String jobMessage;
/**
* 执行状态0正常 1失败
*/
private String jobLogStatus;
/**
* 执行时间
*/
private String executeTime;
/**
* 异常信息
*/
private String exceptionInfo;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
/**
* 租户id
*/
private Long tenantId;
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.daemon.quartz.event;
import com.pig4cloud.pigx.daemon.quartz.entity.SysJob;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.quartz.Trigger;
/**
* @author frwcloud 定时任务多线程事件
*/
@Getter
@AllArgsConstructor
public class SysJobEvent {
private final SysJob sysJob;
private final Trigger trigger;
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.daemon.quartz.event;
import com.pig4cloud.pigx.daemon.quartz.entity.SysJob;
import com.pig4cloud.pigx.daemon.quartz.util.TaskInvokUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Trigger;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
/**
* @author frwcloud 异步监听定时任务事件
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class SysJobListener {
private final TaskInvokUtil taskInvokUtil;
@Async
@Order
@EventListener(SysJobEvent.class)
public void comSysJob(SysJobEvent event) {
SysJob sysJob = event.getSysJob();
Trigger trigger = event.getTrigger();
taskInvokUtil.invokMethod(sysJob, trigger);
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.daemon.quartz.event;
import com.pig4cloud.pigx.daemon.quartz.entity.SysJobLog;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @author frwcloud 定时任务日志多线程事件
*/
@Getter
@AllArgsConstructor
public class SysJobLogEvent {
private final SysJobLog sysJobLog;
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.daemon.quartz.event;
import com.pig4cloud.pigx.daemon.quartz.entity.SysJobLog;
import com.pig4cloud.pigx.daemon.quartz.service.SysJobLogService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
/**
* @author frwcloud 异步监听定时任务日志事件
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class SysJobLogListener {
private final SysJobLogService sysJobLogService;
@Async
@Order
@EventListener(SysJobLogEvent.class)
public void saveSysJobLog(SysJobLogEvent event) {
SysJobLog sysJobLog = event.getSysJobLog();
sysJobLogService.save(sysJobLog);
log.info("执行定时任务日志");
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.daemon.quartz.exception;
/**
* 定时任务异常
*
* @author 郑健楠
*/
public class TaskException extends Exception {
public TaskException() {
super();
}
public TaskException(String msg) {
super(msg);
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.daemon.quartz.mapper;
import com.pig4cloud.pigx.common.data.datascope.PigxBaseMapper;
import com.pig4cloud.pigx.daemon.quartz.entity.SysJobLog;
import org.apache.ibatis.annotations.Mapper;
/**
* 定时任务执行日志表
*
* @author frwcloud
* @date 2019-01-27 13:40:20
*/
@Mapper
public interface SysJobLogMapper extends PigxBaseMapper<SysJobLog> {
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.daemon.quartz.mapper;
import com.pig4cloud.pigx.common.data.datascope.PigxBaseMapper;
import com.pig4cloud.pigx.daemon.quartz.entity.SysJob;
import org.apache.ibatis.annotations.Mapper;
/**
* 定时任务调度表
*
* @author frwcloud
* @date 2019-01-27 10:04:42
*/
@Mapper
public interface SysJobMapper extends PigxBaseMapper<SysJob> {
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.daemon.quartz.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.pig4cloud.pigx.daemon.quartz.entity.SysJobLog;
/**
* 定时任务执行日志表
*
* @author frwcloud
* @date 2019-01-27 13:40:20
*/
public interface SysJobLogService extends IService<SysJobLog> {
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.daemon.quartz.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.pig4cloud.pigx.daemon.quartz.entity.SysJob;
/**
* 定时任务调度表
*
* @author frwcloud
* @date 2019-01-27 10:04:42
*/
public interface SysJobService extends IService<SysJob> {
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.daemon.quartz.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pig4cloud.pigx.daemon.quartz.entity.SysJobLog;
import com.pig4cloud.pigx.daemon.quartz.mapper.SysJobLogMapper;
import com.pig4cloud.pigx.daemon.quartz.service.SysJobLogService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* 定时任务执行日志表
*
* @author frwcloud
* @date 2019-01-27 13:40:20
*/
@Slf4j
@Service
@AllArgsConstructor
public class SysJobLogServiceImpl extends ServiceImpl<SysJobLogMapper, SysJobLog> implements SysJobLogService {
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.daemon.quartz.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pig4cloud.pigx.daemon.quartz.entity.SysJob;
import com.pig4cloud.pigx.daemon.quartz.mapper.SysJobMapper;
import com.pig4cloud.pigx.daemon.quartz.service.SysJobService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* 定时任务调度表
*
* @author frwcloud
* @date 2019-01-27 10:04:42
*/
@Slf4j
@Service
@AllArgsConstructor
public class SysJobServiceImpl extends ServiceImpl<SysJobMapper, SysJob> implements SysJobService {
}

View File

@@ -0,0 +1,34 @@
package com.pig4cloud.pigx.daemon.quartz.task;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.security.annotation.Inner;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDateTime;
/**
* 用于测试REST风格调用的demo
*
* @author lishangbu
* @date 2019/3/25
*/
@Slf4j
@RestController
@RequestMapping("/inner-job")
public class RestTaskDemo {
/**
* 测试REST风格调用定时任务的演示方法
*/
@Inner(value = false)
@GetMapping("/{param}")
public R demoMethod(@PathVariable("param") String param) {
log.info("测试于:{},传入参数{}", LocalDateTime.now(), param);
return R.ok();
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.daemon.quartz.task;
import com.pig4cloud.pigx.daemon.quartz.constants.PigxQuartzEnum;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
/**
* @author 郑健楠
*/
@Slf4j
@Component("demo")
public class SpringBeanTaskDemo {
/**
* 测试Spring Bean的演示方法
*/
@SneakyThrows
public String demoMethod(String para) {
log.info("测试于:{},输入参数{}", LocalDateTime.now(), para);
return PigxQuartzEnum.JOB_LOG_STATUS_SUCCESS.getType();
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.daemon.quartz.util;
import com.pig4cloud.pigx.daemon.quartz.entity.SysJob;
import com.pig4cloud.pigx.daemon.quartz.exception.TaskException;
/**
* 定时任务反射实现接口类
*
* @author 郑健楠
*/
public interface ITaskInvok {
/**
* 执行反射方法
* @param sysJob 配置类
* @throws TaskException
*/
void invokMethod(SysJob sysJob) throws TaskException;
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.daemon.quartz.util;
import cn.hutool.core.util.StrUtil;
import com.pig4cloud.pigx.daemon.quartz.entity.SysJob;
import com.pig4cloud.pigx.daemon.quartz.exception.TaskException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* 定时任务可执行jar反射实现
*
* @author 郑健楠
*/
@Slf4j
@Component("jarTaskInvok")
public class JarTaskInvok implements ITaskInvok {
@Override
public void invokMethod(SysJob sysJob) throws TaskException {
ProcessBuilder processBuilder = new ProcessBuilder();
File jar = new File(sysJob.getExecutePath());
processBuilder.directory(jar.getParentFile());
List<String> commands = new ArrayList<>();
commands.add("java");
commands.add("-jar");
commands.add(sysJob.getExecutePath());
if (StrUtil.isNotEmpty(sysJob.getMethodParamsValue())) {
commands.add(sysJob.getMethodParamsValue());
}
processBuilder.command(commands);
try {
processBuilder.start();
}
catch (IOException e) {
log.error("定时任务jar反射执行异常,执行任务:{}", sysJob.getExecutePath());
throw new TaskException("定时任务jar反射执行异常,执行任务:" + sysJob.getExecutePath());
}
}
}

View File

@@ -0,0 +1,87 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.daemon.quartz.util;
import cn.hutool.core.util.StrUtil;
import com.pig4cloud.pigx.daemon.quartz.constants.PigxQuartzEnum;
import com.pig4cloud.pigx.daemon.quartz.entity.SysJob;
import com.pig4cloud.pigx.daemon.quartz.exception.TaskException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* 定时任务java class反射实现
*
* @author 郑健楠
*/
@Component("javaClassTaskInvok")
@Slf4j
public class JavaClassTaskInvok implements ITaskInvok {
@Override
public void invokMethod(SysJob sysJob) throws TaskException {
Object obj;
Class clazz;
Method method;
Object returnValue;
try {
if (StrUtil.isNotEmpty(sysJob.getMethodParamsValue())) {
clazz = Class.forName(sysJob.getClassName());
obj = clazz.newInstance();
method = clazz.getDeclaredMethod(sysJob.getMethodName(), String.class);
returnValue = method.invoke(obj, sysJob.getMethodParamsValue());
}
else {
clazz = Class.forName(sysJob.getClassName());
obj = clazz.newInstance();
method = clazz.getDeclaredMethod(sysJob.getMethodName());
returnValue = method.invoke(obj);
}
if (StrUtil.isEmpty(returnValue.toString())
|| PigxQuartzEnum.JOB_LOG_STATUS_FAIL.getType().equals(returnValue.toString())) {
log.error("定时任务javaClassTaskInvok异常,执行任务:{}", sysJob.getClassName());
throw new TaskException("定时任务javaClassTaskInvok业务执行失败,任务:" + sysJob.getClassName());
}
}
catch (ClassNotFoundException e) {
log.error("定时任务java反射类没有找到,执行任务:{}", sysJob.getClassName());
throw new TaskException("定时任务java反射类没有找到,执行任务:" + sysJob.getClassName());
}
catch (IllegalAccessException e) {
log.error("定时任务java反射类异常,执行任务:{}", sysJob.getClassName());
throw new TaskException("定时任务java反射类异常,执行任务:" + sysJob.getClassName());
}
catch (InstantiationException e) {
log.error("定时任务java反射类异常,执行任务:{}", sysJob.getClassName());
throw new TaskException("定时任务java反射类异常,执行任务:" + sysJob.getClassName());
}
catch (NoSuchMethodException e) {
log.error("定时任务java反射执行方法名异常,执行任务:{}", sysJob.getClassName());
throw new TaskException("定时任务java反射执行方法名异常,执行任务:" + sysJob.getClassName());
}
catch (InvocationTargetException e) {
log.error("定时任务java反射执行异常,执行任务:{}", sysJob.getClassName());
throw new TaskException("定时任务java反射执行异常,执行任务:" + sysJob.getClassName());
}
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.daemon.quartz.util;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import com.pig4cloud.pigx.common.core.constant.CommonConstants;
import com.pig4cloud.pigx.daemon.quartz.entity.SysJob;
import com.pig4cloud.pigx.daemon.quartz.exception.TaskException;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* 定时任务rest反射实现
*
* @author 郑健楠
*/
@Slf4j
@AllArgsConstructor
@Component("restTaskInvok")
public class RestTaskInvok implements ITaskInvok {
@Override
public void invokMethod(SysJob sysJob) throws TaskException {
try {
HttpRequest request = HttpUtil.createGet(sysJob.getExecutePath());
if (sysJob.getTenantId() != null) {
request.header(CommonConstants.TENANT_ID, sysJob.getTenantId().toString());
}
request.execute();
}
catch (Exception e) {
log.error("定时任务restTaskInvok异常,执行任务:{}", sysJob.getExecutePath());
throw new TaskException("定时任务restTaskInvok业务执行失败,任务:" + sysJob.getExecutePath());
}
}
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.daemon.quartz.util;
import cn.hutool.core.util.StrUtil;
import com.pig4cloud.pigx.common.core.util.SpringContextHolder;
import com.pig4cloud.pigx.daemon.quartz.constants.PigxQuartzEnum;
import com.pig4cloud.pigx.daemon.quartz.entity.SysJob;
import com.pig4cloud.pigx.daemon.quartz.exception.TaskException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* 定时任务spring bean反射实现
*
* @author 郑健楠
*/
@Component("springBeanTaskInvok")
@Slf4j
public class SpringBeanTaskInvok implements ITaskInvok {
@Override
public void invokMethod(SysJob sysJob) throws TaskException {
Object target;
Method method;
Object returnValue;
// 通过Spring上下文去找 也有可能找不到
target = SpringContextHolder.getBean(sysJob.getClassName());
try {
if (StrUtil.isNotEmpty(sysJob.getMethodParamsValue())) {
method = target.getClass().getDeclaredMethod(sysJob.getMethodName(), String.class);
ReflectionUtils.makeAccessible(method);
returnValue = method.invoke(target, sysJob.getMethodParamsValue());
}
else {
method = target.getClass().getDeclaredMethod(sysJob.getMethodName());
ReflectionUtils.makeAccessible(method);
returnValue = method.invoke(target);
}
if (StrUtil.isEmpty(returnValue.toString())
|| PigxQuartzEnum.JOB_LOG_STATUS_FAIL.getType().equals(returnValue.toString())) {
log.error("定时任务springBeanTaskInvok异常,执行任务:{}", sysJob.getClassName());
throw new TaskException("定时任务springBeanTaskInvok业务执行失败,任务:" + sysJob.getClassName());
}
}
catch (NoSuchMethodException e) {
log.error("定时任务spring bean反射异常方法未找到,执行任务:{}", sysJob.getClassName());
throw new TaskException("定时任务spring bean反射异常方法未找到,执行任务:" + sysJob.getClassName());
}
catch (IllegalAccessException e) {
log.error("定时任务spring bean反射异常,执行任务:{}", sysJob.getClassName());
throw new TaskException("定时任务spring bean反射异常,执行任务:" + sysJob.getClassName());
}
catch (InvocationTargetException e) {
log.error("定时任务spring bean反射执行异常,执行任务:{}", sysJob.getClassName());
throw new TaskException("定时任务spring bean反射执行异常,执行任务:" + sysJob.getClassName());
}
}
}

View File

@@ -0,0 +1,50 @@
package com.pig4cloud.pigx.daemon.quartz.util;
import cn.hutool.core.util.StrUtil;
import com.pig4cloud.pigx.common.core.util.SpringContextHolder;
import com.pig4cloud.pigx.daemon.quartz.constants.JobTypeQuartzEnum;
import com.pig4cloud.pigx.daemon.quartz.exception.TaskException;
import lombok.extern.slf4j.Slf4j;
/**
* @author Hccake
* @version 1.0
* @date 2019/8/8 15:40 TaskInvok工厂类
*/
@Slf4j
public class TaskInvokFactory {
/**
* 根据对应jobType获取对应 invoker
* @param jobType
* @return
* @throws TaskException
*/
public static ITaskInvok getInvoker(String jobType) throws TaskException {
if (StrUtil.isBlank(jobType)) {
log.info("获取TaskInvok传递参数有误jobType:{}", jobType);
throw new TaskException("");
}
ITaskInvok iTaskInvok = null;
if (JobTypeQuartzEnum.JAVA.getType().equals(jobType)) {
iTaskInvok = SpringContextHolder.getBean("javaClassTaskInvok");
}
else if (JobTypeQuartzEnum.SPRING_BEAN.getType().equals(jobType)) {
iTaskInvok = SpringContextHolder.getBean("springBeanTaskInvok");
}
else if (JobTypeQuartzEnum.REST.getType().equals(jobType)) {
iTaskInvok = SpringContextHolder.getBean("restTaskInvok");
}
else if (JobTypeQuartzEnum.JAR.getType().equals(jobType)) {
iTaskInvok = SpringContextHolder.getBean("jarTaskInvok");
}
else if (StrUtil.isBlank(jobType)) {
log.info("定时任务类型无对应反射方式,反射类型:{}", jobType);
throw new TaskException("");
}
return iTaskInvok;
}
}

View File

@@ -0,0 +1,118 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.daemon.quartz.util;
import cn.hutool.core.util.StrUtil;
import com.pig4cloud.pigx.common.data.tenant.TenantBroker;
import com.pig4cloud.pigx.daemon.quartz.constants.PigxQuartzEnum;
import com.pig4cloud.pigx.daemon.quartz.entity.SysJob;
import com.pig4cloud.pigx.daemon.quartz.entity.SysJobLog;
import com.pig4cloud.pigx.daemon.quartz.event.SysJobLogEvent;
import com.pig4cloud.pigx.daemon.quartz.service.SysJobService;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.quartz.CronTrigger;
import org.quartz.Trigger;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
import java.time.ZoneId;
import java.util.Date;
/**
* 定时任务反射工具类
*
* @author 郑健楠
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class TaskInvokUtil {
private final ApplicationEventPublisher publisher;
private final SysJobService sysJobService;
@SneakyThrows
public void invokMethod(SysJob sysJob, Trigger trigger) {
// 执行开始时间
long startTime;
// 执行结束时间
long endTime;
// 获取执行开始时间
startTime = System.currentTimeMillis();
// 更新定时任务表内的状态、执行时间、上次执行时间、下次执行时间等信息
SysJob updateSysjob = new SysJob();
updateSysjob.setJobId(sysJob.getJobId());
// 日志
SysJobLog sysJobLog = new SysJobLog();
sysJobLog.setJobId(sysJob.getJobId());
sysJobLog.setJobName(sysJob.getJobName());
sysJobLog.setJobGroup(sysJob.getJobGroup());
sysJobLog.setJobOrder(sysJob.getJobOrder());
sysJobLog.setJobType(sysJob.getJobType());
sysJobLog.setExecutePath(sysJob.getExecutePath());
sysJobLog.setClassName(sysJob.getClassName());
sysJobLog.setMethodName(sysJob.getMethodName());
sysJobLog.setMethodParamsValue(sysJob.getMethodParamsValue());
sysJobLog.setCronExpression(sysJob.getCronExpression());
sysJobLog.setTenantId(sysJob.getTenantId());
try {
// 执行任务
ITaskInvok iTaskInvok = TaskInvokFactory.getInvoker(sysJob.getJobType());
// 确保租户上下文有值,使得当前线程中的多租户特性生效。
TenantBroker.runAs(sysJob.getTenantId(), tenantId -> iTaskInvok.invokMethod(sysJob));
// 记录成功状态
sysJobLog.setJobMessage(PigxQuartzEnum.JOB_LOG_STATUS_SUCCESS.getDescription());
sysJobLog.setJobLogStatus(PigxQuartzEnum.JOB_LOG_STATUS_SUCCESS.getType());
// 任务表信息更新
updateSysjob.setJobExecuteStatus(PigxQuartzEnum.JOB_LOG_STATUS_SUCCESS.getType());
}
catch (Throwable e) {
log.error("定时任务执行失败,任务名称:{};任务组名:{}cron执行表达式{},执行时间:{}", sysJob.getJobName(), sysJob.getJobGroup(),
sysJob.getCronExpression(), new Date());
// 记录失败状态
sysJobLog.setJobMessage(PigxQuartzEnum.JOB_LOG_STATUS_FAIL.getDescription());
sysJobLog.setJobLogStatus(PigxQuartzEnum.JOB_LOG_STATUS_FAIL.getType());
sysJobLog.setExceptionInfo(StrUtil.sub(e.getMessage(), 0, 2000));
// 任务表信息更新
updateSysjob.setJobExecuteStatus(PigxQuartzEnum.JOB_LOG_STATUS_FAIL.getType());
}
finally {
// 记录执行时间 立刻执行使用的是simpleTeigger
if (trigger instanceof CronTrigger) {
updateSysjob.setStartTime(
trigger.getStartTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime());
updateSysjob.setPreviousTime(
trigger.getPreviousFireTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime());
updateSysjob.setNextTime(
trigger.getNextFireTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime());
}
// 记录执行时长
endTime = System.currentTimeMillis();
sysJobLog.setExecuteTime(String.valueOf(endTime - startTime));
publisher.publishEvent(new SysJobLogEvent(sysJobLog));
sysJobService.updateById(updateSysjob);
}
}
}

View File

@@ -0,0 +1,240 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.daemon.quartz.util;
import com.pig4cloud.pigx.daemon.quartz.config.PigxQuartzFactory;
import com.pig4cloud.pigx.daemon.quartz.constants.PigxQuartzEnum;
import com.pig4cloud.pigx.daemon.quartz.entity.SysJob;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.springframework.stereotype.Component;
/**
* 定时任务的工具类
*
* @author 郑健楠
*/
@Slf4j
@Component
public class TaskUtil {
/**
* 获取定时任务的唯一key
* @param sysjob
* @return
*/
public static JobKey getJobKey(SysJob sysjob) {
return JobKey.jobKey(sysjob.getJobName(), sysjob.getJobGroup());
}
/**
* 获取定时任务触发器cron的唯一key
* @param sysjob
* @return
*/
public static TriggerKey getTriggerKey(SysJob sysjob) {
return TriggerKey.triggerKey(sysjob.getJobName(), sysjob.getJobGroup());
}
/**
* 添加或更新定时任务
* @param sysjob
* @param scheduler
*/
public void addOrUpateJob(SysJob sysjob, Scheduler scheduler) {
CronTrigger trigger = null;
try {
JobKey jobKey = getJobKey(sysjob);
// 获得触发器
TriggerKey triggerKey = getTriggerKey(sysjob);
trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
// 判断触发器是否存在(如果存在说明之前运行过但是在当前被禁用了,如果不存在说明一次都没运行过)
if (trigger == null) {
// 新建一个工作任务 指定任务类型为串接进行的
JobDetail jobDetail = JobBuilder.newJob(PigxQuartzFactory.class).withIdentity(jobKey).build();
// 将任务信息添加到任务信息中
jobDetail.getJobDataMap().put(PigxQuartzEnum.SCHEDULE_JOB_KEY.getType(), sysjob);
// 将cron表达式进行转换
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(sysjob.getCronExpression());
cronScheduleBuilder = this.handleCronScheduleMisfirePolicy(sysjob, cronScheduleBuilder);
// 创建触发器并将cron表达式对象给塞入
trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(cronScheduleBuilder)
.build();
// 在调度器中将触发器和任务进行组合
scheduler.scheduleJob(jobDetail, trigger);
}
else {
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(sysjob.getCronExpression());
cronScheduleBuilder = this.handleCronScheduleMisfirePolicy(sysjob, cronScheduleBuilder);
// 按照新的规则进行
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(cronScheduleBuilder)
.build();
// 将任务信息更新到任务信息中
trigger.getJobDataMap().put(PigxQuartzEnum.SCHEDULE_JOB_KEY.getType(), sysjob);
// 重启
scheduler.rescheduleJob(triggerKey, trigger);
}
// 如任务状态为暂停
if (sysjob.getJobStatus().equals(PigxQuartzEnum.JOB_STATUS_NOT_RUNNING.getType())) {
this.pauseJob(sysjob, scheduler);
}
}
catch (SchedulerException e) {
log.error("添加或更新定时任务,失败信息:{}", e.getMessage());
}
}
/**
* 立即执行一次任务
*/
public static boolean runOnce(Scheduler scheduler, SysJob sysJob) {
try {
// 参数
JobDataMap dataMap = new JobDataMap();
dataMap.put(PigxQuartzEnum.SCHEDULE_JOB_KEY.getType(), sysJob);
scheduler.triggerJob(getJobKey(sysJob), dataMap);
}
catch (SchedulerException e) {
log.error("立刻执行定时任务,失败信息:{}", e.getMessage());
return false;
}
return true;
}
/**
* 暂停定时任务
* @param sysjob
* @param scheduler
*/
public void pauseJob(SysJob sysjob, Scheduler scheduler) {
try {
if (scheduler != null) {
scheduler.pauseJob(getJobKey(sysjob));
}
}
catch (SchedulerException e) {
log.error("暂停任务失败,失败信息:{}", e.getMessage());
}
}
/**
* 恢复定时任务
* @param sysjob
* @param scheduler
*/
public void resumeJob(SysJob sysjob, Scheduler scheduler) {
try {
if (scheduler != null) {
scheduler.resumeJob(getJobKey(sysjob));
}
}
catch (SchedulerException e) {
log.error("恢复任务失败,失败信息:{}", e.getMessage());
}
}
/**
* 移除定时任务
* @param sysjob
* @param scheduler
*/
public void removeJob(SysJob sysjob, Scheduler scheduler) {
try {
if (scheduler != null) {
// 停止触发器
scheduler.pauseTrigger(getTriggerKey(sysjob));
// 移除触发器
scheduler.unscheduleJob(getTriggerKey(sysjob));
// 删除任务
scheduler.deleteJob(getJobKey(sysjob));
}
}
catch (Exception e) {
log.error("移除定时任务失败,失败信息:{}", e.getMessage());
}
}
/**
* 启动所有运行定时任务
* @param scheduler
*/
public void startJobs(Scheduler scheduler) {
try {
if (scheduler != null) {
scheduler.resumeAll();
}
}
catch (SchedulerException e) {
log.error("启动所有运行定时任务失败,失败信息:{}", e.getMessage());
}
}
/**
* 停止所有运行定时任务
* @param scheduler
*/
public void pauseJobs(Scheduler scheduler) {
try {
if (scheduler != null) {
scheduler.pauseAll();
}
}
catch (Exception e) {
log.error("暂停所有运行定时任务失败,失败信息:{}", e.getMessage());
}
}
/**
* 获取错失执行策略方法
* @param sysJob
* @param cronScheduleBuilder
* @return
*/
private CronScheduleBuilder handleCronScheduleMisfirePolicy(SysJob sysJob,
CronScheduleBuilder cronScheduleBuilder) {
if (PigxQuartzEnum.MISFIRE_DEFAULT.getType().equals(sysJob.getMisfirePolicy())) {
return cronScheduleBuilder;
}
else if (PigxQuartzEnum.MISFIRE_IGNORE_MISFIRES.getType().equals(sysJob.getMisfirePolicy())) {
return cronScheduleBuilder.withMisfireHandlingInstructionIgnoreMisfires();
}
else if (PigxQuartzEnum.MISFIRE_FIRE_AND_PROCEED.getType().equals(sysJob.getMisfirePolicy())) {
return cronScheduleBuilder.withMisfireHandlingInstructionFireAndProceed();
}
else if (PigxQuartzEnum.MISFIRE_DO_NOTHING.getType().equals(sysJob.getMisfirePolicy())) {
return cronScheduleBuilder.withMisfireHandlingInstructionDoNothing();
}
else {
return cronScheduleBuilder;
}
}
/**
* 判断cron表达式是否正确
* @param cronExpression
* @return
*/
public boolean isValidCron(String cronExpression) {
return CronExpression.isValidExpression(cronExpression);
}
}

View File

@@ -0,0 +1,21 @@
server:
port: 5007
quartz:
enabled: true
spring:
application:
name: @artifactId@
cloud:
nacos:
username: @nacos.username@
password: @nacos.password@
discovery:
server-addr: ${NACOS_HOST:pigx-register}:${NACOS_PORT:8848}
config:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
config:
import:
- optional:nacos:application-@profiles.active@.yml
- optional:nacos:${spring.application.name}-@profiles.active@.yml

View File

@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
小技巧: 在根pom里面设置统一存放路径统一管理方便维护
<properties>
<log-path>/Users/lengleng</log-path>
</properties>
1. 其他模块加日志输出直接copy本文件放在resources 目录即可
2. 注意修改 <property name="${log-path}/log.path" value=""/> 的value模块
-->
<configuration debug="false" scan="false">
<property name="log.path" value="logs/${project.artifactId}"/>
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN"
value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<!-- 彩色日志依赖的渲染类 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
<conversionRule conversionWord="wex"
converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
<conversionRule conversionWord="wEx"
converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
<!-- Console log output -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<!-- Log file debug output -->
<appender name="debug" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/debug.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.path}/%d{yyyy-MM, aux}/debug.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>50MB</maxFileSize>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%date [%thread] %-5level [%logger{50}] %file:%line - %msg%n</pattern>
</encoder>
</appender>
<!-- Log file error output -->
<appender name="error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/error.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.path}/%d{yyyy-MM}/error.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>50MB</maxFileSize>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%date [%thread] %-5level [%logger{50}] %file:%line - %msg%n</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
</appender>
<logger name="org.activiti.engine.impl.db" level="DEBUG">
<appender-ref ref="debug"/>
</logger>
<!--nacos 心跳 INFO 屏蔽-->
<logger name="com.alibaba.nacos" level="OFF">
<appender-ref ref="error"/>
</logger>
<!-- Level: FATAL 0 ERROR 3 WARN 4 INFO 6 DEBUG 7 -->
<root level="INFO">
<appender-ref ref="console"/>
<appender-ref ref="debug"/>
<appender-ref ref="error"/>
</root>
</configuration>