malei05 hai 3 semanas
achega
e366cafc34
Modificáronse 45 ficheiros con 3573 adicións e 0 borrados
  1. 76 0
      .gitignore
  2. 257 0
      pom.xml
  3. 23 0
      src/main/java/net/yyc/quartz/QuartzApplication.java
  4. 50 0
      src/main/java/net/yyc/quartz/config/AutowireCapableBeanJobFactory.java
  5. 135 0
      src/main/java/net/yyc/quartz/config/QuartzConfig.java
  6. 35 0
      src/main/java/net/yyc/quartz/config/QuartzCustomizerConfig.java
  7. 52 0
      src/main/java/net/yyc/quartz/config/QuartzFactory.java
  8. 63 0
      src/main/java/net/yyc/quartz/config/QuartzInitJob.java
  9. 44 0
      src/main/java/net/yyc/quartz/config/QuartzInvokeFactory.java
  10. 63 0
      src/main/java/net/yyc/quartz/constants/JobTypeQuartzEnum.java
  11. 94 0
      src/main/java/net/yyc/quartz/constants/QuartzEnum.java
  12. 266 0
      src/main/java/net/yyc/quartz/controller/SysJobController.java
  13. 82 0
      src/main/java/net/yyc/quartz/controller/SysJobLogController.java
  14. 151 0
      src/main/java/net/yyc/quartz/entity/SysJob.java
  15. 140 0
      src/main/java/net/yyc/quartz/entity/SysJobLog.java
  16. 67 0
      src/main/java/net/yyc/quartz/event/EventAutoConfiguration.java
  17. 36 0
      src/main/java/net/yyc/quartz/event/SysJobEvent.java
  18. 47 0
      src/main/java/net/yyc/quartz/event/SysJobListener.java
  19. 33 0
      src/main/java/net/yyc/quartz/event/SysJobLogEvent.java
  20. 46 0
      src/main/java/net/yyc/quartz/event/SysJobLogListener.java
  21. 35 0
      src/main/java/net/yyc/quartz/exception/TaskException.java
  22. 33 0
      src/main/java/net/yyc/quartz/mapper/SysJobLogMapper.java
  23. 33 0
      src/main/java/net/yyc/quartz/mapper/SysJobMapper.java
  24. 31 0
      src/main/java/net/yyc/quartz/service/SysJobLogService.java
  25. 31 0
      src/main/java/net/yyc/quartz/service/SysJobService.java
  26. 39 0
      src/main/java/net/yyc/quartz/service/impl/SysJobLogServiceImpl.java
  27. 39 0
      src/main/java/net/yyc/quartz/service/impl/SysJobServiceImpl.java
  28. 249 0
      src/main/java/net/yyc/quartz/task/SpringBeanTaskDemo.java
  29. 99 0
      src/main/java/net/yyc/quartz/util/HttpUtil.java
  30. 37 0
      src/main/java/net/yyc/quartz/util/ITaskInvok.java
  31. 62 0
      src/main/java/net/yyc/quartz/util/JarTaskInvok.java
  32. 87 0
      src/main/java/net/yyc/quartz/util/JavaClassTaskInvok.java
  33. 47 0
      src/main/java/net/yyc/quartz/util/R.java
  34. 122 0
      src/main/java/net/yyc/quartz/util/RedisLock.java
  35. 134 0
      src/main/java/net/yyc/quartz/util/RestTaskInvok.java
  36. 29 0
      src/main/java/net/yyc/quartz/util/SecurityUtils.java
  37. 85 0
      src/main/java/net/yyc/quartz/util/SpringBeanTaskInvok.java
  38. 49 0
      src/main/java/net/yyc/quartz/util/SpringContextHolder.java
  39. 50 0
      src/main/java/net/yyc/quartz/util/TaskInvokFactory.java
  40. 125 0
      src/main/java/net/yyc/quartz/util/TaskInvokeUtil.java
  41. 241 0
      src/main/java/net/yyc/quartz/util/TaskUtil.java
  42. 28 0
      src/main/resources/bootstrap.yml
  43. 74 0
      src/main/resources/logback-spring.xml
  44. 24 0
      src/main/resources/mapper/SysJobLogMapper.xml
  45. 30 0
      src/main/resources/mapper/SysJobMapper.xml

+ 76 - 0
.gitignore

@@ -0,0 +1,76 @@
+# 忽略匹配下列规则的Git 提交 V2.1.0
+### gradle ###
+.gradle
+/build/
+!gradle/wrapper/gradle-wrapper.jar
+
+### STS ###
+.settings/
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+bin/
+
+### IntelliJ IDEA ###
+.idea
+.qoder
+*.iws
+*.iml
+*.ipr
+*.lock
+rebel.xml
+
+### NetBeans ###
+nbproject/private/
+build/
+nbbuild/
+nbdist/
+.nb-gradle/
+
+### maven ###
+target/
+*.war
+*.ear
+*.zip
+*.tar
+*.tar.gz
+
+### logs ####
+/logs/
+*.log
+
+### temp ignore ###
+*.cache
+*.diff
+*.patch
+*.tmp
+*.pdf
+*.java~
+*.properties~
+*.xml~
+
+### system ignore ###
+.DS_Store
+Thumbs.db
+Servers
+.metadata
+upload
+gen_code
+
+### node ###
+node_modules
+
+### oneself ###
+.idea/
+.vscode/
+.claude/
+hnqz-upms/hnqz-upms-biz/logs/
+CLAUDE.md
+
+### Protobuf ###
+**/src/main/**/com/qunzhixinxi/hnqz/admin/message/
+
+CLAUDE.md

+ 257 - 0
pom.xml

@@ -0,0 +1,257 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>2.3.12.RELEASE</version>
+        <relativePath/>
+    </parent>
+
+    <groupId>net.yyc.quartz</groupId>
+    <artifactId>yyc-quartz-service</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+    <packaging>jar</packaging>
+
+    <name>yyc-quartz-service</name>
+    <description>基于Quartz的分布式定时任务服务</description>
+
+    <properties>
+        <java.version>1.8</java.version>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <spring-cloud.version>Hoxton.SR8</spring-cloud.version>
+        <spring-cloud-alibaba.version>2.2.1.RELEASE</spring-cloud-alibaba.version>
+        <mybatis-plus.version>3.3.2</mybatis-plus.version>
+        <druid.version>1.1.22</druid.version>
+    </properties>
+
+    <dependencies>
+        <!-- Spring Boot 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>
+
+        <!-- Spring Boot Actuator -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-actuator</artifactId>
+        </dependency>
+
+        <!-- Nacos Discovery -->
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
+        </dependency>
+
+        <!-- Nacos Config -->
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
+        </dependency>
+
+        <!-- MyBatis Plus -->
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-boot-starter</artifactId>
+            <version>${mybatis-plus.version}</version>
+        </dependency>
+
+        <!-- MySQL -->
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+
+        <!-- Druid 连接池 -->
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>druid-spring-boot-starter</artifactId>
+            <version>${druid.version}</version>
+        </dependency>
+
+        <!-- Spring Security OAuth2 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-security</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-oauth2</artifactId>
+        </dependency>
+
+        <!-- Sentinel -->
+        <dependency>
+            <groupId>com.alibaba.csp</groupId>
+            <artifactId>sentinel-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
+        </dependency>
+
+        <!-- Redis -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+        </dependency>
+
+        <!-- hnqz 依赖 -->
+        <dependency>
+            <groupId>com.qunzhixinxi</groupId>
+            <artifactId>hnqz-upms-api</artifactId>
+            <version>3.9.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.qunzhixinxi</groupId>
+            <artifactId>hnqz-common-log</artifactId>
+            <version>3.9.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.qunzhixinxi</groupId>
+            <artifactId>hnqz-common-data</artifactId>
+            <version>3.9.0</version>
+        </dependency>
+        <dependency>
+            <groupId>net.yyc.common</groupId>
+            <artifactId>yyc-common-security</artifactId>
+            <version>1.0.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>net.yyc.common</groupId>
+            <artifactId>yyc-common-sentinel</artifactId>
+            <version>1.0.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>net.yyc.common</groupId>
+            <artifactId>yyc-common-gray</artifactId>
+            <version>1.0.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>net.yyc.common</groupId>
+            <artifactId>yyc-common-datasource</artifactId>
+            <version>1.0.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>net.yyc.common</groupId>
+            <artifactId>yyc-common-feign</artifactId>
+            <version>1.0.0-SNAPSHOT</version>
+        </dependency>
+
+        <!-- Hutool 工具库 -->
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>5.7.7</version>
+        </dependency>
+
+        <!-- Apache HttpClient -->
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+            <version>4.5.13</version>
+        </dependency>
+
+        <!-- Lombok -->
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <optional>true</optional>
+        </dependency>
+
+        <!-- Spring Boot Test -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <dependencyManagement>
+        <dependencies>
+            <!-- Spring Cloud -->
+            <dependency>
+                <groupId>org.springframework.cloud</groupId>
+                <artifactId>spring-cloud-dependencies</artifactId>
+                <version>${spring-cloud.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <!-- Spring Cloud Alibaba -->
+            <dependency>
+                <groupId>com.alibaba.cloud</groupId>
+                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
+                <version>${spring-cloud-alibaba.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+        <finalName>${project.artifactId}</finalName>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>pre</id>
+            <properties>
+                <profiles.active>pre</profiles.active>
+                <nacos.username>nacos</nacos.username>
+                <nacos.password>n1a2c3o4s5</nacos.password>
+                <nacos.server-addr>nacos.yaoyi.internal:57260</nacos.server-addr>
+                <profiles.group>cso_hnqz</profiles.group>
+                <profiles.namespace>1eb949ef-72f7-48f0-a5b4-65d4595ac444</profiles.namespace>
+                <profiles.daemon.quartz.port>9003</profiles.daemon.quartz.port>
+            </properties>
+        </profile>
+
+        <profile>
+            <id>prod</id>
+            <properties>
+                <profiles.active>prod</profiles.active>
+                <nacos.username>nacos</nacos.username>
+                <nacos.password>n1a2c3o4s5</nacos.password>
+                <nacos.server-addr>nacos.yaoyi.internal:57260</nacos.server-addr>
+                <profiles.group>cso_hnqz</profiles.group>
+                <profiles.namespace>5bea430d-da82-4205-bfdb-0d08997734b1</profiles.namespace>
+                <profiles.daemon.quartz.port>5007</profiles.daemon.quartz.port>
+            </properties>
+        </profile>
+
+        <profile>
+            <id>luoxin</id>
+            <properties>
+                <profiles.active>luoxin</profiles.active>
+                <nacos.username>nacos</nacos.username>
+                <nacos.password>n1a2c3o4s5</nacos.password>
+                <nacos.server-addr>nacos.yaoyi.internal:57260</nacos.server-addr>
+                <profiles.group>luoxin_hnqz</profiles.group>
+                <profiles.namespace>5bea430d-da82-4205-bfdb-0d08997734b1</profiles.namespace>
+                <profiles.daemon.quartz.port>5007</profiles.daemon.quartz.port>
+            </properties>
+        </profile>
+    </profiles>
+</project>

+ 23 - 0
src/main/java/net/yyc/quartz/QuartzApplication.java

@@ -0,0 +1,23 @@
+package net.yyc.quartz;
+
+import net.yyc.common.datasource.annotation.EnableDynamicDataSource;
+import net.yyc.common.feign.annotation.EnableQuartzFeignClients;
+import net.yyc.common.security.annotation.EnableQuartzResourceServer;
+import org.springframework.boot.SpringApplication;
+import org.springframework.cloud.client.SpringCloudApplication;
+
+/**
+ * @author frwcloud
+ * @date 2019/01/23 定时任务模块
+ */
+@EnableQuartzFeignClients
+@SpringCloudApplication
+@EnableQuartzResourceServer
+@EnableDynamicDataSource
+public class QuartzApplication {
+
+	public static void main(String[] args) {
+		SpringApplication.run(QuartzApplication.class, args);
+	}
+
+}

+ 50 - 0
src/main/java/net/yyc/quartz/config/AutowireCapableBeanJobFactory.java

@@ -0,0 +1,50 @@
+/*
+ *    Copyright (c) 2018-2025, hnqz 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: hnqz
+ */
+
+package net.yyc.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;
+	}
+
+}

+ 135 - 0
src/main/java/net/yyc/quartz/config/QuartzConfig.java

@@ -0,0 +1,135 @@
+/*
+ *    Copyright (c) 2018-2025, hnqz 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: hnqz
+ */
+
+package net.yyc.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.quartz.SchedulerFactoryBean;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * @author 郑健楠
+ */
+@Configuration
+@ConditionalOnClass({ Scheduler.class, SchedulerFactoryBean.class })
+@EnableConfigurationProperties({ QuartzProperties.class })
+public class QuartzConfig {
+
+	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 QuartzConfig(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();
+	}
+
+}

+ 35 - 0
src/main/java/net/yyc/quartz/config/QuartzCustomizerConfig.java

@@ -0,0 +1,35 @@
+/*
+ *    Copyright (c) 2018-2025, hnqz 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: hnqz
+ */
+
+package net.yyc.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 QuartzCustomizerConfig implements SchedulerFactoryBeanCustomizer {
+
+	@Override
+	public void customize(SchedulerFactoryBean schedulerFactoryBean) {
+		schedulerFactoryBean.setWaitForJobsToCompleteOnShutdown(true);
+	}
+
+}

+ 52 - 0
src/main/java/net/yyc/quartz/config/QuartzFactory.java

@@ -0,0 +1,52 @@
+/*
+ *    Copyright (c) 2018-2025, hnqz 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: hnqz
+ */
+
+package net.yyc.quartz.config;
+
+import net.yyc.quartz.constants.QuartzEnum;
+import net.yyc.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;
+
+import javax.annotation.Resource;
+
+/**
+ * @author 郑健楠
+ *
+ * <p>
+ * 动态任务工厂
+ */
+@Slf4j
+@DisallowConcurrentExecution
+public class QuartzFactory implements Job {
+
+	@Resource
+	private QuartzInvokeFactory hnqzQuartzInvokeFactory;
+
+	@Override
+	@SneakyThrows
+	public void execute(JobExecutionContext jobExecutionContext) {
+		SysJob sysJob = (SysJob) jobExecutionContext.getMergedJobDataMap()
+				.get(QuartzEnum.SCHEDULE_JOB_KEY.getType());
+		hnqzQuartzInvokeFactory.init(sysJob, jobExecutionContext.getTrigger());
+	}
+
+}

+ 63 - 0
src/main/java/net/yyc/quartz/config/QuartzInitJob.java

@@ -0,0 +1,63 @@
+/*
+ *    Copyright (c) 2018-2025, hnqz 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: hnqz
+ */
+
+package net.yyc.quartz.config;
+
+import net.yyc.quartz.constants.QuartzEnum;
+import net.yyc.quartz.service.SysJobService;
+import net.yyc.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 QuartzInitJob {
+
+	private final SysJobService sysJobService;
+
+	private final TaskUtil taskUtil;
+
+	private final Scheduler scheduler;
+
+	@Bean
+	public void customize() {
+		sysJobService.list().forEach(sysjob -> {
+			if (QuartzEnum.JOB_STATUS_RELEASE.getType().equals(sysjob.getJobStatus())) {
+				taskUtil.removeJob(sysjob, scheduler);
+			}
+			else if (QuartzEnum.JOB_STATUS_RUNNING.getType().equals(sysjob.getJobStatus())) {
+				taskUtil.resumeJob(sysjob, scheduler);
+			}
+			else if (QuartzEnum.JOB_STATUS_NOT_RUNNING.getType().equals(sysjob.getJobStatus())) {
+				taskUtil.pauseJob(sysjob, scheduler);
+			}
+			else {
+				taskUtil.removeJob(sysjob, scheduler);
+			}
+		});
+	}
+
+}

+ 44 - 0
src/main/java/net/yyc/quartz/config/QuartzInvokeFactory.java

@@ -0,0 +1,44 @@
+/*
+ *    Copyright (c) 2018-2025, hnqz 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: hnqz
+ */
+
+package net.yyc.quartz.config;
+
+import net.yyc.quartz.entity.SysJob;
+import net.yyc.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;
+
+/**
+ * @author 郑健楠
+ */
+@Aspect
+@Slf4j
+@AllArgsConstructor
+public class QuartzInvokeFactory {
+
+	private final ApplicationEventPublisher publisher;
+
+	@SneakyThrows
+	void init(SysJob sysJob, Trigger trigger) {
+		publisher.publishEvent(new SysJobEvent(sysJob, trigger));
+	}
+
+}

+ 63 - 0
src/main/java/net/yyc/quartz/constants/JobTypeQuartzEnum.java

@@ -0,0 +1,63 @@
+/*
+ *    Copyright (c) 2018-2025, hnqz 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: hnqz
+ */
+
+package net.yyc.quartz.constants;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @author hnqz
+ * @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;
+
+}

+ 94 - 0
src/main/java/net/yyc/quartz/constants/QuartzEnum.java

@@ -0,0 +1,94 @@
+/*
+ *    Copyright (c) 2018-2025, hnqz 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: hnqz
+ */
+
+package net.yyc.quartz.constants;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @author 郑健楠
+ *
+ * <p>
+ * 定时任务枚举
+ */
+@Getter
+@AllArgsConstructor
+public enum QuartzEnum {
+
+	/**
+	 * 错失执行策略默认
+	 */
+	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;
+
+}

+ 266 - 0
src/main/java/net/yyc/quartz/controller/SysJobController.java

@@ -0,0 +1,266 @@
+/*
+ *    Copyright (c) 2018-2025, hnqz 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: hnqz
+ */
+
+package net.yyc.quartz.controller;
+
+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 net.yyc.common.core.util.R;
+import net.yyc.common.security.util.SecurityUtils;
+import net.yyc.quartz.entity.SysJob;
+import net.yyc.quartz.entity.SysJobLog;
+import net.yyc.quartz.service.SysJobLogService;
+import net.yyc.quartz.service.SysJobService;
+import net.yyc.quartz.util.TaskUtil;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.quartz.Scheduler;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import static net.yyc.quartz.constants.QuartzEnum.*;
+
+/**
+ * @author frwcloud
+ * @date 2019-01-27 10:04:42
+ * <p>
+ * 定时任务管理
+ */
+@Slf4j
+@RestController
+@AllArgsConstructor
+@RequestMapping("/sys-job")
+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")
+	public R getSysJobPage(Page page, SysJob sysJob) {
+		return R.ok(sysJobService.page(page, Wrappers.query(sysJob)));
+	}
+
+	/**
+	 * 通过id查询定时任务
+	 * @param id id
+	 * @return R
+	 */
+	@GetMapping("/{id}")
+	public R getById(@PathVariable("id") Integer id) {
+		return R.ok(sysJobService.getById(id));
+	}
+
+	/**
+	 * 新增定时任务
+	 * @param sysJob 定时任务调度表
+	 * @return R
+	 */
+	@PostMapping
+	@PreAuthorize("@pms.hasPermission('job_sys_job_add')")
+	public R save(@RequestBody SysJob sysJob) {
+		log.info("新增定时任务");
+		sysJob.setJobStatus(JOB_STATUS_RELEASE.getType());
+		sysJob.setCreateBy(SecurityUtils.getUser().getUsername());
+		return R.ok(sysJobService.save(sysJob));
+	}
+
+	/**
+	 * 修改定时任务
+	 * @param sysJob 定时任务调度表
+	 * @return R
+	 */
+	@PutMapping
+	@PreAuthorize("@pms.hasPermission('job_sys_job_edit')")
+	public R updateById(@RequestBody SysJob sysJob) {
+		log.info("修改定时任务");
+		sysJob.setUpdateBy(SecurityUtils.getUser().getUsername());
+		SysJob querySysJob = this.sysJobService.getById(sysJob.getJobId());
+		if (JOB_STATUS_NOT_RUNNING.getType().equals(querySysJob.getJobStatus())) {
+			this.taskUtil.addOrUpateJob(sysJob, scheduler);
+			sysJobService.updateById(sysJob);
+		}
+		else if (JOB_STATUS_RELEASE.getType().equals(querySysJob.getJobStatus())) {
+			sysJobService.updateById(sysJob);
+		}
+		return R.ok();
+	}
+
+	/**
+	 * 通过id删除定时任务
+	 * @param id id
+	 * @return R
+	 */
+	@DeleteMapping("/{id}")
+	@PreAuthorize("@pms.hasPermission('job_sys_job_del')")
+	public R removeById(@PathVariable Integer id) {
+		log.info("删除定时任务: {}", id);
+		SysJob querySysJob = this.sysJobService.getById(id);
+		if (JOB_STATUS_NOT_RUNNING.getType().equals(querySysJob.getJobStatus())) {
+			this.taskUtil.removeJob(querySysJob, scheduler);
+			this.sysJobService.removeById(id);
+		}
+		else if (JOB_STATUS_RELEASE.getType().equals(querySysJob.getJobStatus())) {
+			this.sysJobService.removeById(id);
+		}
+		return R.ok();
+	}
+
+	/**
+	 * 暂停全部定时任务
+	 * @return
+	 */
+	@PostMapping("/shutdown-jobs")
+	@PreAuthorize("@pms.hasPermission('job_sys_job_shutdown_job')")
+	public R shutdownJobs() {
+		log.info("暂停全部定时任务");
+		taskUtil.pauseJobs(scheduler);
+		int count = this.sysJobService
+				.count(new LambdaQueryWrapper<SysJob>().eq(SysJob::getJobStatus, JOB_STATUS_RUNNING.getType()));
+		if (count <= 0) {
+			return R.ok("无正在运行定时任务");
+		}
+		else {
+			// 更新定时任务状态条件,运行状态2更新为暂停状态2
+			this.sysJobService.update(SysJob.builder().jobStatus(JOB_STATUS_NOT_RUNNING.getType()).build(),
+					new UpdateWrapper<SysJob>().lambda().eq(SysJob::getJobStatus, JOB_STATUS_RUNNING.getType()));
+			return R.ok("暂停成功");
+		}
+	}
+
+	/**
+	 * 启动全部定时任务
+	 * @return
+	 */
+	@PostMapping("/start-jobs")
+	@PreAuthorize("@pms.hasPermission('job_sys_job_start_job')")
+	public R startJobs() {
+		log.info("启动全部定时任务");
+		// 更新定时任务状态条件,暂停状态3更新为运行状态2
+		this.sysJobService.update(SysJob.builder().jobStatus(JOB_STATUS_RUNNING.getType()).build(),
+				new UpdateWrapper<SysJob>().lambda().eq(SysJob::getJobStatus, JOB_STATUS_NOT_RUNNING.getType()));
+		taskUtil.startJobs(scheduler);
+		return R.ok();
+	}
+
+	/**
+	 * 刷新全部定时任务
+	 * @return
+	 */
+	@PostMapping("/refresh-jobs")
+	@PreAuthorize("@pms.hasPermission('job_sys_job_refresh_job')")
+	public R refreshJobs() {
+		log.info("刷新全部定时任务");
+		sysJobService.list().forEach((sysjob) -> {
+			if (JOB_STATUS_RELEASE.getType().equals(sysjob.getJobStatus())
+					|| JOB_STATUS_DEL.getType().equals(sysjob.getJobStatus())) {
+				taskUtil.removeJob(sysjob, scheduler);
+			}
+			else if (JOB_STATUS_RUNNING.getType().equals(sysjob.getJobStatus())
+					|| JOB_STATUS_NOT_RUNNING.getType().equals(sysjob.getJobStatus())) {
+				taskUtil.addOrUpateJob(sysjob, scheduler);
+			}
+			else {
+				taskUtil.removeJob(sysjob, scheduler);
+			}
+		});
+		return R.ok();
+	}
+
+	/**
+	 * 启动定时任务
+	 * @param jobId
+	 * @return
+	 */
+	@PostMapping("/start-job/{id}")
+	@PreAuthorize("@pms.hasPermission('job_sys_job_start_job')")
+	public R startJob(@PathVariable("id") Integer jobId) {
+		log.info("启动定时任务: {}", jobId);
+		SysJob querySysJob = this.sysJobService.getById(jobId);
+		if (querySysJob != null && 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(JOB_STATUS_RUNNING.getType()).build());
+		return R.ok();
+	}
+
+	/**
+	 * 启动定时任务
+	 * @param jobId
+	 * @return
+	 */
+	@PostMapping("/run-job/{id}")
+	@PreAuthorize("@pms.hasPermission('job_sys_job_run_job')")
+	public R runJob(@PathVariable("id") Integer jobId) {
+		log.info("立刻执行定时任务: {}", jobId);
+		SysJob querySysJob = this.sysJobService.getById(jobId);
+		return TaskUtil.runOnce(scheduler, querySysJob) ? R.ok() : R.failed();
+	}
+
+	/**
+	 * 暂停定时任务
+	 * @return
+	 */
+	@PostMapping("/shutdown-job/{id}")
+	@PreAuthorize("@pms.hasPermission('job_sys_job_shutdown_job')")
+	public R shutdownJob(@PathVariable("id") Integer id) {
+		log.info("暂停定时任务: {}", id);
+		SysJob querySysJob = this.sysJobService.getById(id);
+		// 更新定时任务状态条件,运行状态2更新为暂停状态3
+		this.sysJobService.updateById(
+				SysJob.builder().jobId(querySysJob.getJobId()).jobStatus(JOB_STATUS_NOT_RUNNING.getType()).build());
+		taskUtil.pauseJob(querySysJob, scheduler);
+		return R.ok();
+	}
+
+	/**
+	 * 唯一标识查询定时执行日志
+	 * @return
+	 */
+	@GetMapping("/job-log")
+	public R getJobLog(Page page, SysJobLog sysJobLog) {
+		return R.ok(sysJobLogService.page(page, Wrappers.query(sysJobLog)));
+	}
+
+	/**
+	 * 检验任务名称和任务组联合是否唯一
+	 * @return
+	 */
+	@GetMapping("/is-valid-task-name")
+	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();
+	}
+
+}

+ 82 - 0
src/main/java/net/yyc/quartz/controller/SysJobLogController.java

@@ -0,0 +1,82 @@
+package net.yyc.quartz.controller;
+
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import net.yyc.quartz.entity.SysJobLog;
+import net.yyc.quartz.service.SysJobLogService;
+import lombok.AllArgsConstructor;
+import net.yyc.common.core.util.R;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
+
+/**
+ * @author frwcloud
+ * <p>
+ * 定时任务执行日志表
+ */
+@RestController
+@AllArgsConstructor
+@RequestMapping("/sys-job-log")
+public class SysJobLogController {
+
+    private final SysJobLogService sysJobLogService;
+
+    private final String j27 = "同一用户,在同一企业,连续<font color='red'>%s</font>月结算金额相同";
+    private final String j28 = "同一企业,同一月份,给<font color='red'>%s</font>个及以上用户支付相同金额";
+    private final String j29 = "同一用户,连续<font color='red'>%s</font>月没有定位打卡";
+    private final String j30 = "同一用户,连续<font color='red'>%s</font>月结算金额超过<font color='red'>%s</font>金额";
+
+    /**
+     * 分页查询
+     *
+     * @param page      分页对象
+     * @param sysJobLog 定时任务执行日志表
+     * @return
+     */
+    @GetMapping("/page")
+    public R getSysJobLogPage(Page<SysJobLog> page, SysJobLog sysJobLog) {
+
+        sysJobLogService.page(page, Wrappers.<SysJobLog>lambdaQuery()
+                .in(SysJobLog::getJobId, 27, 28, 29, 30)
+                .like(StrUtil.isNotBlank(sysJobLog.getJobName()), SysJobLog::getJobName, sysJobLog.getJobName())
+                .eq(StrUtil.isNotBlank(sysJobLog.getJobLogStatus()), SysJobLog::getJobLogStatus, sysJobLog.getJobLogStatus())
+                .between(sysJobLog.getStartTime() != null && sysJobLog.getEndTime() != null, SysJobLog::getCreateTime, sysJobLog.getStartTime(), sysJobLog.getEndTime())
+                .orderByDesc(SysJobLog::getCreateTime)
+
+        );
+
+        page.getRecords().forEach(r -> {
+
+            // 匹配
+            String jobName;
+            if (r.getJobId() == 27) {
+                jobName = String.format(j27, r.getMethodParamsValue());
+            } else if (r.getJobId() == 28) {
+                jobName = String.format(j28, r.getMethodParamsValue());
+            } else if (r.getJobId() == 29) {
+                String[] split = r.getMethodParamsValue().split(StrUtil.COMMA);
+                jobName = String.format(j29, split[0]);
+            } else if (r.getJobId() == 30) {
+                String[] split = r.getMethodParamsValue().split(StrUtil.COMMA);
+                jobName = String.format(j30, split[0], split[1]);
+            } else {
+                jobName = r.getJobName();
+            }
+            r.setJobName(jobName);
+
+            LocalDateTime createTime = r.getCreateTime();
+            String executeTime = r.getExecuteTime();
+            int execTime = StrUtil.isBlank(executeTime) ? 0 : Integer.parseInt(executeTime);
+            LocalDateTime plus = createTime.plus(execTime, ChronoUnit.MILLIS);
+            r.setEndTime(plus);
+        });
+
+        return R.ok(page);
+    }
+
+}

+ 151 - 0
src/main/java/net/yyc/quartz/entity/SysJob.java

@@ -0,0 +1,151 @@
+package net.yyc.quartz.entity;
+
+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 lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+import java.time.LocalDateTime;
+
+/**
+ * 定时任务调度表
+ *
+ * @author frwcloud
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@EqualsAndHashCode(callSuper = true)
+public class SysJob extends Model<SysJob> {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 任务id
+	 */
+	@TableId(value = "job_id", type = IdType.AUTO)
+	private Integer 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;
+
+	/**
+	 * 创建者
+	 */
+	private String createBy;
+
+	/**
+	 * 创建时间
+	 */
+	private LocalDateTime createTime;
+
+	/**
+	 * 更新者
+	 */
+	private String updateBy;
+
+	/**
+	 * 更新时间
+	 */
+	private LocalDateTime updateTime;
+
+	/**
+	 * 首次执行时间
+	 */
+	private LocalDateTime startTime;
+
+	/**
+	 * 上次执行时间
+	 */
+	private LocalDateTime previousTime;
+
+	/**
+	 * 下次执行时间
+	 */
+	private LocalDateTime nextTime;
+
+	/**
+	 * 租户id
+	 */
+	private Integer tenantId;
+
+	/**
+	 * 备注信息
+	 */
+	private String remark;
+
+	/**
+	 * 日志标记
+	 */
+	@TableField(exist = false)
+	private String uuid;
+
+}

+ 140 - 0
src/main/java/net/yyc/quartz/entity/SysJobLog.java

@@ -0,0 +1,140 @@
+
+
+package net.yyc.quartz.entity;
+
+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 lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+import java.time.LocalDateTime;
+
+/**
+ * 定时任务执行日志表
+ *
+ * @author frwcloud
+ * @date 2019-01-27 13:40:20
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class SysJobLog extends Model<SysJobLog> {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 任务日志ID
+	 */
+	@TableId(value = "job_log_id", type = IdType.AUTO)
+	private Integer jobLogId;
+
+	/**
+	 * 任务id
+	 */
+	private Integer 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;
+
+	/**
+	 * 创建时间
+	 */
+	private LocalDateTime createTime;
+
+	/**
+	 * 租户id
+	 */
+	private Integer tenantId;
+
+	/**
+	 * 查询时间
+	 */
+	@TableField(exist = false)
+	private LocalDateTime startTime;
+
+	/**
+	 * 查询时间
+	 */
+	@TableField(exist = false)
+	private LocalDateTime endTime;
+
+	/**
+	 * uuid
+	 */
+	private String uuid;
+
+	/**
+	 * 是否有统计内容
+	 */
+	@TableField(exist = false)
+	private Boolean empty = true;
+}

+ 67 - 0
src/main/java/net/yyc/quartz/event/EventAutoConfiguration.java

@@ -0,0 +1,67 @@
+/*
+ *    Copyright (c) 2018-2025, hnqz 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: hnqz
+ */
+
+package net.yyc.quartz.event;
+
+import net.yyc.quartz.config.QuartzInvokeFactory;
+import net.yyc.quartz.service.SysJobLogService;
+import net.yyc.quartz.util.TaskInvokeUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.EnableAsync;
+
+/**
+ * @author frwcloud
+ * @date 2018/6/28
+ * <p>
+ * 多线程自动配置
+ */
+@EnableAsync
+@Configuration
+@ConditionalOnWebApplication
+public class EventAutoConfiguration {
+
+	@Autowired
+	private TaskInvokeUtil taskInvokeUtil;
+
+	@Autowired
+	private SysJobLogService sysJobLogService;
+
+	@Bean
+	public SysJobListener sysJobListener() {
+		return new SysJobListener(taskInvokeUtil);
+	}
+
+	@Bean
+	public QuartzInvokeFactory hnqzQuartzInvokeFactory(ApplicationEventPublisher publisher) {
+		return new QuartzInvokeFactory(publisher);
+	}
+
+	@Bean
+	public SysJobLogListener sysJobLogListener() {
+		return new SysJobLogListener(sysJobLogService);
+	}
+
+	@Bean
+	public TaskInvokeUtil taskInvokUtil(ApplicationEventPublisher publisher) {
+		return new TaskInvokeUtil(publisher);
+	}
+
+}

+ 36 - 0
src/main/java/net/yyc/quartz/event/SysJobEvent.java

@@ -0,0 +1,36 @@
+/*
+ *    Copyright (c) 2018-2025, hnqz 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: hnqz
+ */
+
+package net.yyc.quartz.event;
+
+import net.yyc.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;
+
+}

+ 47 - 0
src/main/java/net/yyc/quartz/event/SysJobListener.java

@@ -0,0 +1,47 @@
+/*
+ *    Copyright (c) 2018-2025, hnqz 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: hnqz
+ */
+
+package net.yyc.quartz.event;
+
+import net.yyc.quartz.entity.SysJob;
+import net.yyc.quartz.util.TaskInvokeUtil;
+import lombok.AllArgsConstructor;
+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;
+
+/**
+ * @author frwcloud 异步监听定时任务事件
+ */
+@Slf4j
+@AllArgsConstructor
+public class SysJobListener {
+
+	private TaskInvokeUtil taskInvokeUtil;
+
+	@Async
+	@Order
+	@EventListener(SysJobEvent.class)
+	public void comSysJob(SysJobEvent event) {
+		SysJob sysJob = event.getSysJob();
+		Trigger trigger = event.getTrigger();
+		taskInvokeUtil.invokeMethod(sysJob, trigger);
+	}
+
+}

+ 33 - 0
src/main/java/net/yyc/quartz/event/SysJobLogEvent.java

@@ -0,0 +1,33 @@
+/*
+ *    Copyright (c) 2018-2025, hnqz 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: hnqz
+ */
+
+package net.yyc.quartz.event;
+
+import net.yyc.quartz.entity.SysJobLog;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @author frwcloud 定时任务日志多线程事件
+ */
+@Getter
+@AllArgsConstructor
+public class SysJobLogEvent {
+
+	private final SysJobLog sysJobLog;
+
+}

+ 46 - 0
src/main/java/net/yyc/quartz/event/SysJobLogListener.java

@@ -0,0 +1,46 @@
+/*
+ *    Copyright (c) 2018-2025, hnqz 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: hnqz
+ */
+
+package net.yyc.quartz.event;
+
+import net.yyc.quartz.entity.SysJobLog;
+import net.yyc.quartz.service.SysJobLogService;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.event.EventListener;
+import org.springframework.core.annotation.Order;
+import org.springframework.scheduling.annotation.Async;
+
+/**
+ * @author frwcloud 异步监听定时任务日志事件
+ */
+@Slf4j
+@AllArgsConstructor
+public class SysJobLogListener {
+
+	private SysJobLogService sysJobLogService;
+
+	@Async
+	@Order
+	@EventListener(SysJobLogEvent.class)
+	public void saveSysJobLog(SysJobLogEvent event) {
+		SysJobLog sysJobLog = event.getSysJobLog();
+		sysJobLogService.save(sysJobLog);
+		log.info("执行定时任务日志");
+	}
+
+}

+ 35 - 0
src/main/java/net/yyc/quartz/exception/TaskException.java

@@ -0,0 +1,35 @@
+/*
+ *    Copyright (c) 2018-2025, hnqz 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: hnqz
+ */
+
+package net.yyc.quartz.exception;
+
+/**
+ * 定时任务异常
+ *
+ * @author 郑健楠
+ */
+public class TaskException extends Exception {
+
+	public TaskException() {
+		super();
+	}
+
+	public TaskException(String msg) {
+		super(msg);
+	}
+
+}

+ 33 - 0
src/main/java/net/yyc/quartz/mapper/SysJobLogMapper.java

@@ -0,0 +1,33 @@
+/*
+ *    Copyright (c) 2018-2025, hnqz 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: hnqz
+ */
+
+package net.yyc.quartz.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import net.yyc.quartz.entity.SysJobLog;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 定时任务执行日志表
+ *
+ * @author frwcloud
+ * @date 2019-01-27 13:40:20
+ */
+@Mapper
+public interface SysJobLogMapper extends BaseMapper<SysJobLog> {
+
+}

+ 33 - 0
src/main/java/net/yyc/quartz/mapper/SysJobMapper.java

@@ -0,0 +1,33 @@
+/*
+ *    Copyright (c) 2018-2025, hnqz 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: hnqz
+ */
+
+package net.yyc.quartz.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import net.yyc.quartz.entity.SysJob;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 定时任务调度表
+ *
+ * @author frwcloud
+ * @date 2019-01-27 10:04:42
+ */
+@Mapper
+public interface SysJobMapper extends BaseMapper<SysJob> {
+
+}

+ 31 - 0
src/main/java/net/yyc/quartz/service/SysJobLogService.java

@@ -0,0 +1,31 @@
+/*
+ *    Copyright (c) 2018-2025, hnqz 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: hnqz
+ */
+
+package net.yyc.quartz.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import net.yyc.quartz.entity.SysJobLog;
+
+/**
+ * 定时任务执行日志表
+ *
+ * @author frwcloud
+ * @date 2019-01-27 13:40:20
+ */
+public interface SysJobLogService extends IService<SysJobLog> {
+
+}

+ 31 - 0
src/main/java/net/yyc/quartz/service/SysJobService.java

@@ -0,0 +1,31 @@
+/*
+ *    Copyright (c) 2018-2025, hnqz 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: hnqz
+ */
+
+package net.yyc.quartz.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import net.yyc.quartz.entity.SysJob;
+
+/**
+ * 定时任务调度表
+ *
+ * @author frwcloud
+ * @date 2019-01-27 10:04:42
+ */
+public interface SysJobService extends IService<SysJob> {
+
+}

+ 39 - 0
src/main/java/net/yyc/quartz/service/impl/SysJobLogServiceImpl.java

@@ -0,0 +1,39 @@
+/*
+ *    Copyright (c) 2018-2025, hnqz 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: hnqz
+ */
+
+package net.yyc.quartz.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import net.yyc.quartz.entity.SysJobLog;
+import net.yyc.quartz.mapper.SysJobLogMapper;
+import net.yyc.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 {
+
+}

+ 39 - 0
src/main/java/net/yyc/quartz/service/impl/SysJobServiceImpl.java

@@ -0,0 +1,39 @@
+/*
+ *    Copyright (c) 2018-2025, hnqz 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: hnqz
+ */
+
+package net.yyc.quartz.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import net.yyc.quartz.entity.SysJob;
+import net.yyc.quartz.mapper.SysJobMapper;
+import net.yyc.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 {
+
+}

+ 249 - 0
src/main/java/net/yyc/quartz/task/SpringBeanTaskDemo.java

@@ -0,0 +1,249 @@
+/*
+ *    Copyright (c) 2018-2025, hnqz 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: hnqz
+ */
+
+package net.yyc.quartz.task;
+
+
+import com.qunzhixinxi.hnqz.admin.api.feign.RemoteInnerSignApiService;
+import com.qunzhixinxi.hnqz.admin.api.feign.RemoteScheduleTaskService;
+import net.yyc.quartz.constants.QuartzEnum;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import net.yyc.common.core.constant.SecurityConstants;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.time.LocalDateTime;
+
+/**
+ * @author 郑健楠
+ */
+@Slf4j
+@Component("demo")
+public class SpringBeanTaskDemo {
+    @Resource
+    private RemoteScheduleTaskService remoteScheduleTaskService;
+    @Resource
+    private RemoteInnerSignApiService remoteInnerSignApiService;
+
+    @Deprecated
+    @SneakyThrows
+    public String changePackageStatusTask(String para) {
+        log.info("测试于:{},输入参数{}", LocalDateTime.now(), para);
+        return QuartzEnum.JOB_LOG_STATUS_SUCCESS.getType();
+    }
+
+    @Deprecated
+    @SneakyThrows
+    public String getArtileTask(String para) {
+        log.info("废弃方法:{},输入参数{}", LocalDateTime.now(), para);
+        return QuartzEnum.JOB_LOG_STATUS_SUCCESS.getType();
+    }
+
+    @Deprecated
+    @SneakyThrows
+    public String changeTaskRule(String para) {
+        log.info("废弃方法:{},输入参数{}", LocalDateTime.now(), para);
+        return QuartzEnum.JOB_LOG_STATUS_SUCCESS.getType();
+    }
+
+    @Deprecated
+    @SneakyThrows
+    public String buildTaskImg(String para) {
+        log.info("废弃方法:{},输入参数{}", LocalDateTime.now(), para);
+        return QuartzEnum.JOB_LOG_STATUS_SUCCESS.getType();
+    }
+
+    /**
+     * 每日GMVs统计
+     *
+     * @param para
+     * @return
+     */
+    @SneakyThrows
+    public String dailyGMVsStatistic(String para) {
+        log.info("dailyGMVsStatistic开始于:{},输入参数{}", LocalDateTime.now(), para);
+        remoteScheduleTaskService.dailyGMVsStatistic(para, SecurityConstants.FROM_IN);
+        return QuartzEnum.JOB_LOG_STATUS_SUCCESS.getType();
+    }
+
+    @SneakyThrows
+    public String setHotArtileTask(String para) {
+        log.info("setHotArtileTask开始于:{},输入参数{}", LocalDateTime.now(), para);
+        remoteScheduleTaskService.setHotArticle(para, SecurityConstants.FROM_IN);
+        return QuartzEnum.JOB_LOG_STATUS_SUCCESS.getType();
+    }
+
+    /**
+     * 同一用户,连续多月结算金额相同
+     *
+     * @param para 参数(月数)
+     * @return 运行结果
+     */
+    @SneakyThrows
+    public String singleMemberSettleSameAmountInSeveralMonths(String para) {
+        log.info("singleMemberSettleSameAmountInSeveralMonths开始于:{},输入参数{}", LocalDateTime.now(), para);
+        remoteScheduleTaskService.singleMemberSettleSameAmount(para, SecurityConstants.FROM_IN);
+        return QuartzEnum.JOB_LOG_STATUS_SUCCESS.getType();
+    }
+
+    /**
+     * 同一用户 同一税地,连续多月结算金额超过限额
+     *
+     * @param para 参数(月数和限额)
+     * @return 运行结果
+     */
+    @SneakyThrows
+    public String singleMemberSettleAmountOverrunInSeveralMonths(String para) {
+        log.info("singleMemberSettleAmountOverrunInSeveralMonths开始于:{},输入参数{}", LocalDateTime.now(), para);
+        remoteScheduleTaskService.singleMemberSettleAmountOverrun(para, SecurityConstants.FROM_IN);
+        return QuartzEnum.JOB_LOG_STATUS_SUCCESS.getType();
+    }
+
+    /**
+     * 同一企业,当前月给多个用户支付相同金额
+     *
+     * @param para 参数(统计人数)
+     * @return 运行结果
+     */
+    @SneakyThrows
+    public String singleEnterpriseSettleSameAmountForSeveralMembersInCurrentMonth(String para) {
+        log.info("singleEnterpriseSettleSameAmountForSeveralMembersInCurrentMonth开始于:{},输入参数{}", LocalDateTime.now(), para);
+        remoteScheduleTaskService.singleEnterpriseSettleSameAmount(para, SecurityConstants.FROM_IN);
+        return QuartzEnum.JOB_LOG_STATUS_SUCCESS.getType();
+    }
+
+    /**
+     * 同一用户,连续多月没有指定任务类型
+     *
+     * @param para 参数(周期和任务类型)
+     * @return 运行结果
+     */
+    @SneakyThrows
+    public String singleMemberWithoutSuchTaskTypesInSeveralMonths(String para) {
+        log.info("singleMemberWithoutSuchTaskTypesInSeveralMonths开始于:{},输入参数{}", LocalDateTime.now(), para);
+        remoteScheduleTaskService.singleMemberWithoutTaskTypes(para, SecurityConstants.FROM_IN);
+        return QuartzEnum.JOB_LOG_STATUS_SUCCESS.getType();
+    }
+
+    @Deprecated
+    @SneakyThrows
+    public String pushUserCertFailure(String para) {
+        log.info("废弃方法:{},输入参数{}", LocalDateTime.now(), para);
+        return QuartzEnum.JOB_LOG_STATUS_SUCCESS.getType();
+    }
+
+    /**
+     * 每天发送当日结算数据给企业指定邮箱
+     *
+     * @param para 参数(格式为"2024-02-23,1611890565;1611890564")
+     * @return {@link String} 结果
+     */
+    @SneakyThrows
+    public String sendCurrentDaySettleDataToDeptDaily(String para) {
+        log.info("sendCurrentDaySettleDataToDeptDaily开始于:{},输入参数{}", LocalDateTime.now(), para);
+        remoteScheduleTaskService.sendCurrentDaySettleDataToDept(para, SecurityConstants.FROM_IN);
+        return QuartzEnum.JOB_LOG_STATUS_SUCCESS.getType();
+    }
+
+    @Deprecated
+    @SneakyThrows
+    public String batchUpdateCertStatus(String params) {
+        log.info("废弃方法:{},输入参数{}", LocalDateTime.now(), params);
+        return QuartzEnum.JOB_LOG_STATUS_SUCCESS.getType();
+    }
+
+    /**
+     * 锋信易申请每日发布
+     *
+     * @param para
+     * @return
+     */
+    @SneakyThrows
+    public String sendFxyApply(String para) {
+        log.info("sendFxyApply开始于:{},输入参数{}", LocalDateTime.now(), para);
+        remoteScheduleTaskService.sendFxyApply(para, SecurityConstants.FROM_IN);
+        return QuartzEnum.JOB_LOG_STATUS_SUCCESS.getType();
+    }
+
+    /**
+     * 锋信易弹窗点击记录每日发布
+     *
+     * @param para
+     * @return
+     */
+    @SneakyThrows
+    public String sendPromotionRecord(String para) {
+        log.info("sendPromotionRecord开始于:{},输入参数{}", LocalDateTime.now(), para);
+        remoteScheduleTaskService.sendPromotionRecord(para, SecurityConstants.FROM_IN);
+        return QuartzEnum.JOB_LOG_STATUS_SUCCESS.getType();
+    }
+
+    @SneakyThrows
+    public String batchUpdatePackageStatus(String params) {
+        log.info("batchUpdatePackageStatus开始于:{},输入参数{}", LocalDateTime.now(), params);
+        remoteScheduleTaskService.batchUpdatePackageStatus(params, SecurityConstants.FROM_IN);
+        return QuartzEnum.JOB_LOG_STATUS_SUCCESS.getType();
+    }
+
+    @Deprecated
+    @SneakyThrows
+    public String bosskgyjbSignResultConfirm(String para) {
+        log.info("废弃方法:{},输入参数{}", LocalDateTime.now(), para);
+        return QuartzEnum.JOB_LOG_STATUS_SUCCESS.getType();
+    }
+
+    @Deprecated
+    @SneakyThrows
+    public String heChuangLxlwSignResultConfirm(String para) {
+        log.info("废弃方法:{},输入参数{}", LocalDateTime.now(), para);
+        return QuartzEnum.JOB_LOG_STATUS_SUCCESS.getType();
+    }
+
+    @Deprecated
+    @SneakyThrows
+    public String heChuangLxlwSettleResultConfirm(String para) {
+        log.info("废弃方法:{},输入参数{}", LocalDateTime.now(), para);
+        return QuartzEnum.JOB_LOG_STATUS_SUCCESS.getType();
+    }
+
+    @Deprecated
+    @SneakyThrows
+    public String chaEPulxlwSettleResultConfirm(String para) {
+        log.info("废弃方法:{},输入参数{}", LocalDateTime.now(), para);
+        return QuartzEnum.JOB_LOG_STATUS_SUCCESS.getType();
+    }
+
+    /**
+     * 刷新已结算积分包的任务终审积分
+     */
+    @SneakyThrows
+    public String refreshFinalReviewScoreForSettlement(String para) {
+        log.info("refreshFinalReviewScoreForSettlement开始于:{},输入参数{}", LocalDateTime.now(), para);
+        remoteScheduleTaskService.refreshFinalReviewScoreForSettlement(para, SecurityConstants.FROM_IN);
+        return QuartzEnum.JOB_LOG_STATUS_SUCCESS.getType();
+    }
+
+    @SneakyThrows
+    public String asignPullFacePhoto(String para) {
+        log.info("开始执行[爱签人脸截图拉取]: {}", para);
+        String ret = remoteInnerSignApiService.asignPullFacePhotoJob(para, SecurityConstants.FROM_IN);
+        log.info("已触发[爱签人脸截图拉取]: {}", ret);
+        return QuartzEnum.JOB_LOG_STATUS_SUCCESS.getType();
+    }
+
+}

+ 99 - 0
src/main/java/net/yyc/quartz/util/HttpUtil.java

@@ -0,0 +1,99 @@
+package net.yyc.quartz.util;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
+
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+
+public class HttpUtil {
+	private static CloseableHttpClient httpClient;
+
+	static {
+		PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
+		cm.setMaxTotal(100);
+		cm.setDefaultMaxPerRoute(20);
+		cm.setDefaultMaxPerRoute(50);
+		httpClient = HttpClients.custom().setConnectionManager(cm).build();
+	}
+
+	public static String get(String url) {
+		CloseableHttpResponse response = null;
+		BufferedReader in = null;
+		String result = "";
+		try {
+			HttpGet httpGet = new HttpGet(url);
+			RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(30000).setConnectionRequestTimeout(30000).setSocketTimeout(30000).build();
+			httpGet.setConfig(requestConfig);
+			httpGet.setConfig(requestConfig);
+			httpGet.addHeader("Content-type", "application/json; charset=utf-8");
+			httpGet.setHeader("Accept", "application/json");
+			response = httpClient.execute(httpGet);
+			in = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
+			StringBuffer sb = new StringBuffer("");
+			String line = "";
+			String NL = System.getProperty("line.separator");
+			while ((line = in.readLine()) != null) {
+				sb.append(line + NL);
+			}
+			in.close();
+			result = sb.toString();
+		} catch (IOException e) {
+			e.printStackTrace();
+		} finally {
+			try {
+				if (null != response) {
+					response.close();
+				}
+			} catch (IOException e) {
+				e.printStackTrace();
+			}
+		}
+		return result;
+	}
+
+	public static String post(String url, String jsonString) {
+		CloseableHttpResponse response = null;
+		BufferedReader in = null;
+		String result = "";
+		try {
+			HttpPost httpPost = new HttpPost(url);
+			RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(30000).setConnectionRequestTimeout(30000).setSocketTimeout(30000).build();
+			httpPost.setConfig(requestConfig);
+			httpPost.setConfig(requestConfig);
+			httpPost.addHeader("Content-type", "application/json; charset=utf-8");
+			httpPost.setHeader("Accept", "application/json");
+			httpPost.setEntity(new StringEntity(jsonString, Charset.forName("UTF-8")));
+			response = httpClient.execute(httpPost);
+			in = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
+			StringBuffer sb = new StringBuffer("");
+			String line = "";
+			String NL = System.getProperty("line.separator");
+			while ((line = in.readLine()) != null) {
+				sb.append(line + NL);
+			}
+			in.close();
+			result = sb.toString();
+		} catch (IOException e) {
+			e.printStackTrace();
+		} finally {
+			try {
+				if (null != response) {
+					response.close();
+				}
+			} catch (IOException e) {
+				e.printStackTrace();
+			}
+		}
+		return result;
+	}
+
+}

+ 37 - 0
src/main/java/net/yyc/quartz/util/ITaskInvok.java

@@ -0,0 +1,37 @@
+/*
+ *    Copyright (c) 2018-2025, hnqz 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: hnqz
+ */
+
+package net.yyc.quartz.util;
+
+import net.yyc.quartz.entity.SysJob;
+import net.yyc.quartz.exception.TaskException;
+
+/**
+ * 定时任务反射实现接口类
+ *
+ * @author 郑健楠
+ */
+public interface ITaskInvok {
+
+	/**
+	 * 执行反射方法
+	 * @param sysJob 配置类
+	 * @throws TaskException
+	 */
+	void invokMethod(SysJob sysJob) throws TaskException;
+
+}

+ 62 - 0
src/main/java/net/yyc/quartz/util/JarTaskInvok.java

@@ -0,0 +1,62 @@
+/*
+ *    Copyright (c) 2018-2025, hnqz 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: hnqz
+ */
+
+package net.yyc.quartz.util;
+
+import cn.hutool.core.util.StrUtil;
+import net.yyc.quartz.entity.SysJob;
+import net.yyc.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());
+		}
+	}
+
+}

+ 87 - 0
src/main/java/net/yyc/quartz/util/JavaClassTaskInvok.java

@@ -0,0 +1,87 @@
+/*
+ *    Copyright (c) 2018-2025, hnqz 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: hnqz
+ */
+
+package net.yyc.quartz.util;
+
+import cn.hutool.core.util.StrUtil;
+import net.yyc.quartz.constants.QuartzEnum;
+import net.yyc.quartz.entity.SysJob;
+import net.yyc.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())
+					|| QuartzEnum.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());
+		}
+
+	}
+
+}

+ 47 - 0
src/main/java/net/yyc/quartz/util/R.java

@@ -0,0 +1,47 @@
+package net.yyc.quartz.util;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 通用响应结果
+ */
+@Data
+public class R<T> implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private int code;
+    private String msg;
+    private T data;
+
+    public R() {
+    }
+
+    public R(int code, String msg) {
+        this.code = code;
+        this.msg = msg;
+    }
+
+    public R(int code, String msg, T data) {
+        this.code = code;
+        this.msg = msg;
+        this.data = data;
+    }
+
+    public static <T> R<T> ok() {
+        return new R<>(0, "操作成功");
+    }
+
+    public static <T> R<T> ok(T data) {
+        return new R<>(0, "操作成功", data);
+    }
+
+    public static <T> R<T> failed() {
+        return new R<>(1, "操作失败");
+    }
+
+    public static <T> R<T> failed(String msg) {
+        return new R<>(1, msg);
+    }
+}

+ 122 - 0
src/main/java/net/yyc/quartz/util/RedisLock.java

@@ -0,0 +1,122 @@
+package net.yyc.quartz.util;
+
+import com.google.common.collect.Lists;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.script.DefaultRedisScript;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+
+import java.time.Duration;
+import java.util.Objects;
+import java.util.Random;
+import java.util.UUID;
+
+/**
+ * Redis 分布式锁(与 upms-biz {@code com.qunzhixinxi.hnqz.admin.util.RedisLock} 行为一致,供 quartz 使用同一 Redis 互斥)
+ */
+@Slf4j
+public class RedisLock {
+
+	private final RedisTemplate redisTemplate;
+
+	private static final DefaultRedisScript<Long> UNLOCK_LUA;
+
+	static {
+		StringBuilder script = new StringBuilder();
+		script.append("if redis.call(\"get\",KEYS[1]) == ARGV[1] ");
+		script.append("then ");
+		script.append("    return redis.call(\"del\",KEYS[1]) ");
+		script.append("else ");
+		script.append("    return 0 ");
+		script.append("end ");
+		UNLOCK_LUA = new DefaultRedisScript<>();
+		UNLOCK_LUA.setResultType(Long.class);
+		UNLOCK_LUA.setScriptText(script.toString());
+	}
+
+	private String lockKey;
+	private String lockValue;
+	private int expireTime = 60;
+	private long timeOut = 100;
+	private volatile boolean locked = false;
+	private final Random random = new Random();
+
+	public RedisLock(RedisTemplate redisTemplate, String lockKey) {
+		this.redisTemplate = redisTemplate;
+		this.lockKey = lockKey + "_lock";
+	}
+
+	public RedisLock(RedisTemplate redisTemplate, String lockKey, int expireTime) {
+		this(redisTemplate, lockKey);
+		this.expireTime = expireTime;
+	}
+
+	public RedisLock(RedisTemplate redisTemplate, String lockKey, long timeOut) {
+		this(redisTemplate, lockKey);
+		this.timeOut = timeOut;
+	}
+
+	public RedisLock(RedisTemplate<? extends Object, ? extends Object> redisTemplate, String lockKey, int expireTime, long timeOut) {
+		this(redisTemplate, lockKey, expireTime);
+		this.timeOut = timeOut;
+	}
+
+	public boolean tryLock() {
+		lockValue = UUID.randomUUID().toString();
+		long timeout = timeOut * 1000000;
+		long nowTime = System.nanoTime();
+		while ((System.nanoTime() - nowTime) < timeout) {
+			if (this.set(lockKey, lockValue, expireTime)) {
+				locked = true;
+				return locked;
+			}
+			this.sleep(10, 50000);
+		}
+		return locked;
+	}
+
+	public boolean lock() {
+		lockValue = UUID.randomUUID().toString();
+		locked = this.set(lockKey, lockValue, expireTime);
+		return locked;
+	}
+
+	public boolean lockBlock() {
+		lockValue = UUID.randomUUID().toString();
+		while (true) {
+			if (this.set(lockKey, lockValue, expireTime)) {
+				locked = true;
+				return locked;
+			}
+			this.sleep(10, 50000);
+		}
+	}
+
+	public boolean unlock() {
+		if (locked) {
+			Long ret = (Long) redisTemplate.execute(UNLOCK_LUA, Lists.newArrayList(lockKey), lockValue);
+			boolean success = Objects.equals(1L, ret);
+			locked = !success;
+			return success;
+		}
+		return true;
+	}
+
+	private boolean set(final String key, final String value, final long seconds) {
+		Assert.isTrue(!StringUtils.isEmpty(key), "key不能为空");
+		boolean ret = Objects.equals(true, redisTemplate.opsForValue().setIfAbsent(key, value, Duration.ofSeconds(seconds)));
+		if (ret) {
+			log.info("获取锁{}的时间:{}", key, System.currentTimeMillis());
+		}
+		return ret;
+	}
+
+	private void sleep(long millis, int nanos) {
+		try {
+			Thread.sleep(millis, random.nextInt(nanos));
+		} catch (InterruptedException e) {
+			log.info("获取分布式锁休眠被中断:", e);
+		}
+	}
+}

+ 134 - 0
src/main/java/net/yyc/quartz/util/RestTaskInvok.java

@@ -0,0 +1,134 @@
+/*
+ *    Copyright (c) 2018-2025, hnqz 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: hnqz
+ */
+
+package net.yyc.quartz.util;
+
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.http.HttpRequest;
+import cn.hutool.http.HttpUtil;
+import lombok.extern.slf4j.Slf4j;
+import net.yyc.common.core.constant.CommonConstants;
+import net.yyc.common.core.constant.SecurityConstants;
+import net.yyc.quartz.entity.SysJob;
+import net.yyc.quartz.exception.TaskException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cloud.client.ServiceInstance;
+import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
+import org.springframework.stereotype.Component;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 定时任务 REST 反射实现
+ * 支持 service-name 模式(http://service-name/path)和 host:port 模式(http://host:port/path)
+ *
+ * @author 郑健楠
+ */
+@Slf4j
+@Component
+public class RestTaskInvok implements ITaskInvok {
+
+	@Autowired
+	private LoadBalancerClient loadBalancer;
+
+	@Override
+	public void invokMethod(SysJob sysJob) throws TaskException {
+		try {
+			String executePath = sysJob.getExecutePath();
+			String resolvedUrl = resolveUrl(executePath);
+
+			HttpRequest request = HttpUtil.createPost(resolvedUrl);
+
+			// 租户 header
+			if (sysJob.getTenantId() != null) {
+				request.header(CommonConstants.TENANT_ID, sysJob.getTenantId().toString());
+			}
+
+			// 内部调用 header(绕过安全拦截器)
+			request.header(SecurityConstants.FROM, SecurityConstants.FROM_IN);
+
+			// 预留扩展 header(可由子类或配置覆盖)
+			addExtraHeaders(request, sysJob);
+
+			// 表单参数(params 对应 Controller @RequestParam)
+			String methodParams = sysJob.getMethodParamsValue();
+			if (StrUtil.isNotBlank(methodParams)) {
+				Map<String, Object> formData = new HashMap<>();
+				formData.put("params", methodParams);
+				request.form(formData);
+			}
+
+			request.execute();
+			log.debug("REST任务调用完成, url={}, params={}", resolvedUrl, methodParams);
+		}
+		catch (Exception e) {
+			log.error("定时任务restTaskInvok异常,执行任务:{}", sysJob.getExecutePath());
+			throw new TaskException("定时任务restTaskInvok业务执行失败,任务:" + sysJob.getExecutePath());
+		}
+	}
+
+	/**
+	 * 解析 URL,支持两种模式:
+	 * 1. service-name 模式:http://service-name/path → 通过 LoadBalancer 解析为实际地址
+	 * 2. host:port 模式:http://host:port/path → 直接使用
+	 *
+	 * @param executePath 原始执行路径
+	 * @return 解析后的实际 URL
+	 */
+	private String resolveUrl(String executePath) {
+		if (executePath == null || !executePath.startsWith("http://")) {
+			return executePath;
+		}
+
+		try {
+			// 提取服务名和路径,例如: http://hnqz-upms-biz/admin/schedule-task/xxx
+			String path = executePath.substring(7);
+			int slashIndex = path.indexOf('/');
+
+			if (slashIndex == -1) {
+				return executePath;
+			}
+
+			String serviceName = path.substring(0, slashIndex);
+			String servicePath = path.substring(slashIndex);
+
+			// 尝试通过 LoadBalancer 解析服务名
+			ServiceInstance instance = loadBalancer.choose(serviceName);
+			if (instance != null) {
+				return instance.getUri().toString() + servicePath;
+			}
+		} catch (Exception e) {
+			log.warn("服务名解析失败,使用原路径: {}", executePath, e);
+		}
+
+		// host:port 模式直接返回原路径
+		return executePath;
+	}
+
+	/**
+	 * 扩展点:子类可覆盖此方法添加额外的 header
+	 * 目前预留,未来可通过 SysJob 新增字段(如 invokeHeaders)实现动态配置
+	 *
+	 * @param request HTTP 请求
+	 * @param sysJob  任务实体
+	 */
+	protected void addExtraHeaders(HttpRequest request, SysJob sysJob) {
+		// 预留扩展点,可通过 sysJob.invokeHeaders 或配置文件动态添加 header
+	}
+
+}

+ 29 - 0
src/main/java/net/yyc/quartz/util/SecurityUtils.java

@@ -0,0 +1,29 @@
+package net.yyc.quartz.util;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 安全工具类(简化版)
+ */
+public class SecurityUtils {
+
+    /**
+     * 获取当前登录用户信息
+     */
+    public static UserPrincipal getUser() {
+        // 简化实现,实际项目中可对接真实认证系统
+        UserPrincipal user = new UserPrincipal();
+        user.setUsername("system");
+        return user;
+    }
+
+    @Data
+    public static class UserPrincipal implements Serializable {
+        private static final long serialVersionUID = 1L;
+        private Long userId;
+        private String username;
+        private String roles;
+    }
+}

+ 85 - 0
src/main/java/net/yyc/quartz/util/SpringBeanTaskInvok.java

@@ -0,0 +1,85 @@
+/*
+ *    Copyright (c) 2018-2025, hnqz 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: hnqz
+ */
+
+package net.yyc.quartz.util;
+
+import cn.hutool.core.util.StrUtil;
+import net.yyc.common.core.util.SpringContextHolder;
+import net.yyc.quartz.constants.QuartzEnum;
+import net.yyc.quartz.entity.SysJob;
+import net.yyc.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);
+				int jobId = sysJob.getJobId();
+				String params = sysJob.getMethodParamsValue();
+				if (jobId ==  27 || jobId == 28 || jobId == 29 || jobId == 30){
+					params = params+ "," +sysJob.getUuid();
+				}
+				returnValue = method.invoke(target, params);
+			}
+			else {
+				method = target.getClass().getDeclaredMethod(sysJob.getMethodName());
+				ReflectionUtils.makeAccessible(method);
+				returnValue = method.invoke(target);
+			}
+			if (StrUtil.isEmpty(returnValue.toString())
+					|| QuartzEnum.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());
+			e.printStackTrace();
+			throw new TaskException("定时任务spring bean反射执行异常,执行任务:" + sysJob.getClassName());
+		}
+	}
+
+}

+ 49 - 0
src/main/java/net/yyc/quartz/util/SpringContextHolder.java

@@ -0,0 +1,49 @@
+package net.yyc.quartz.util;
+
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Component;
+
+/**
+ * Spring 上下文持有者
+ */
+@Component
+public class SpringContextHolder implements ApplicationContextAware {
+
+    private static ApplicationContext applicationContext;
+
+    @Override
+    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+        SpringContextHolder.applicationContext = applicationContext;
+    }
+
+    /**
+     * 获取 Spring 应用上下文
+     */
+    public static ApplicationContext getApplicationContext() {
+        return applicationContext;
+    }
+
+    /**
+     * 获取指定类型的 Bean
+     *
+     * @param name Bean 名称
+     * @return Bean 实例
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T getBean(String name) {
+        return (T) applicationContext.getBean(name);
+    }
+
+    /**
+     * 获取指定类型的 Bean
+     *
+     * @param clazz Bean 类型
+     * @return Bean 实例
+     */
+    public static <T> T getBean(Class<T> clazz) {
+        return applicationContext.getBean(clazz);
+    }
+
+}

+ 50 - 0
src/main/java/net/yyc/quartz/util/TaskInvokFactory.java

@@ -0,0 +1,50 @@
+package net.yyc.quartz.util;
+
+import cn.hutool.core.util.StrUtil;
+import net.yyc.common.core.util.SpringContextHolder;
+import net.yyc.quartz.constants.JobTypeQuartzEnum;
+import net.yyc.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;
+	}
+
+}

+ 125 - 0
src/main/java/net/yyc/quartz/util/TaskInvokeUtil.java

@@ -0,0 +1,125 @@
+/*
+ *    Copyright (c) 2018-2025, hnqz 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: hnqz
+ */
+
+package net.yyc.quartz.util;
+
+import cn.hutool.core.util.IdUtil;
+import cn.hutool.core.util.StrUtil;
+import net.yyc.quartz.constants.QuartzEnum;
+import net.yyc.quartz.entity.SysJob;
+import net.yyc.quartz.entity.SysJobLog;
+import net.yyc.quartz.event.SysJobLogEvent;
+import net.yyc.quartz.service.SysJobService;
+import lombok.RequiredArgsConstructor;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.annotation.Aspect;
+import org.quartz.CronTrigger;
+import org.quartz.Trigger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationEventPublisher;
+
+import java.time.ZoneId;
+import java.util.Date;
+
+/**
+ * 定时任务反射工具类
+ *
+ * @author 郑健楠
+ */
+@Slf4j
+@Aspect
+@RequiredArgsConstructor
+public class TaskInvokeUtil {
+
+	private final ApplicationEventPublisher publisher;
+
+	@Autowired
+	private SysJobService sysJobService;
+
+	@SneakyThrows
+	public void invokeMethod(SysJob sysJob, Trigger trigger) {
+
+		// 执行开始时间
+		long startTime;
+		// 执行结束时间
+		long endTime;
+		// 获取执行开始时间
+		startTime = System.currentTimeMillis();
+		// 日志uuid
+		String uuid = IdUtil.fastSimpleUUID();
+		// 更新定时任务表内的状态、执行时间、上次执行时间、下次执行时间等信息
+		SysJob updateSysjob = new SysJob();
+		updateSysjob.setJobId(sysJob.getJobId());
+		// 日志
+		SysJobLog sysJobLog = new SysJobLog();
+		sysJobLog.setUuid(uuid);
+		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());
+			// 设置uuid
+			sysJob.setUuid(uuid);
+			// 直接执行任务
+			iTaskInvok.invokMethod(sysJob);
+
+			// 记录成功状态
+			sysJobLog.setJobMessage(QuartzEnum.JOB_LOG_STATUS_SUCCESS.getDescription());
+			sysJobLog.setJobLogStatus(QuartzEnum.JOB_LOG_STATUS_SUCCESS.getType());
+			// 任务表信息更新
+			updateSysjob.setJobExecuteStatus(QuartzEnum.JOB_LOG_STATUS_SUCCESS.getType());
+		}
+		catch (Throwable e) {
+			log.error("定时任务执行失败,任务名称:{};任务组名:{},cron执行表达式:{},执行时间:{}", sysJob.getJobName(), sysJob.getJobGroup(),
+					sysJob.getCronExpression(), new Date());
+			// 记录失败状态
+			sysJobLog.setJobMessage(QuartzEnum.JOB_LOG_STATUS_FAIL.getDescription());
+			sysJobLog.setJobLogStatus(QuartzEnum.JOB_LOG_STATUS_FAIL.getType());
+			sysJobLog.setExceptionInfo(StrUtil.sub(e.getMessage(), 0, 2000));
+			// 任务表信息更新
+			updateSysjob.setJobExecuteStatus(QuartzEnum.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);
+		}
+	}
+
+}

+ 241 - 0
src/main/java/net/yyc/quartz/util/TaskUtil.java

@@ -0,0 +1,241 @@
+/*
+ *    Copyright (c) 2018-2025, hnqz 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: hnqz
+ */
+
+package net.yyc.quartz.util;
+
+import net.yyc.quartz.config.QuartzFactory;
+import net.yyc.quartz.entity.SysJob;
+import lombok.extern.slf4j.Slf4j;
+import org.quartz.*;
+import org.springframework.stereotype.Component;
+
+import static net.yyc.quartz.constants.QuartzEnum.*;
+
+/**
+ * 定时任务的工具类
+ *
+ * @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(QuartzFactory.class).withIdentity(jobKey).build();
+				// 将任务信息添加到任务信息中
+				jobDetail.getJobDataMap().put(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(SCHEDULE_JOB_KEY.getType(), sysjob);
+				// 重启
+				scheduler.rescheduleJob(triggerKey, trigger);
+			}
+			// 如任务状态为暂停
+			if (sysjob.getJobStatus().equals(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(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 (MISFIRE_DEFAULT.getType().equals(sysJob.getMisfirePolicy())) {
+			return cronScheduleBuilder;
+		}
+		else if (MISFIRE_IGNORE_MISFIRES.getType().equals(sysJob.getMisfirePolicy())) {
+			return cronScheduleBuilder.withMisfireHandlingInstructionIgnoreMisfires();
+		}
+		else if (MISFIRE_FIRE_AND_PROCEED.getType().equals(sysJob.getMisfirePolicy())) {
+			return cronScheduleBuilder.withMisfireHandlingInstructionFireAndProceed();
+		}
+		else if (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);
+	}
+
+}

+ 28 - 0
src/main/resources/bootstrap.yml

@@ -0,0 +1,28 @@
+server:
+  port: @profiles.daemon.quartz.port@
+
+quartz:
+  enabled: true
+
+spring:
+  profiles:
+    active: @profiles.active@
+  application:
+    name: @artifactId@
+  cloud:
+    nacos:
+      username: @nacos.username@
+      password: @nacos.password@
+      discovery:
+        server-addr: @nacos.server-addr@
+        namespace: @profiles.namespace@
+        group: @profiles.group@
+      config:
+        server-addr: @nacos.server-addr@
+        namespace: @profiles.namespace@
+        group: @profiles.group@
+        file-extension: yml
+        shared-configs:
+          - data-id: application.yml
+            group: @profiles.group@
+            refresh: true

+ 74 - 0
src/main/resources/logback-spring.xml

@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    小技巧: 在根pom里面设置统一存放路径,统一管理方便维护
+    <properties>
+        <log-path>/Users/hnqz</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 [%X{traceId},%X{spanId}] [%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 [%X{traceId},%X{spanId}] [%logger{50}] %file:%line - %msg%n</pattern>
+		</encoder>
+		<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+			<level>ERROR</level>
+		</filter>
+	</appender>
+
+	<!-- 请求响应日志 (已移除外部依赖)
+	<include resource="com/qunzhixinxi/hnqz/common/log/request-logging-file-appender.xml"/>
+	-->
+
+	<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"/>
+	</root>
+</configuration>

+ 24 - 0
src/main/resources/mapper/SysJobLogMapper.xml

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="net.yyc.quartz.mapper.SysJobLogMapper">
+	<resultMap id="sysJobLogMap" type="net.yyc.quartz.entity.SysJobLog">
+		<id property="jobLogId" column="job_log_id"/>
+		<result property="jobId" column="job_id"/>
+		<result property="jobName" column="job_name"/>
+		<result property="jobGroup" column="job_group"/>
+		<result property="jobOrder" column="job_order"/>
+		<result property="jobType" column="job_type"/>
+		<result property="executePath" column="execute_path"/>
+		<result property="className" column="class_name"/>
+		<result property="methodName" column="method_name"/>
+		<result property="methodParamsValue" column="method_params_value"/>
+		<result property="cronExpression" column="cron_expression"/>
+		<result property="jobMessage" column="job_message"/>
+		<result property="jobLogStatus" column="job_log_status"/>
+		<result property="executeTime" column="execute_time"/>
+		<result property="exceptionInfo" column="exception_info"/>
+		<result property="createTime" column="create_time"/>
+		<result property="tenantId" column="tenant_id"/>
+		<result property="uuid" column="uuid"/>
+	</resultMap>
+</mapper>

+ 30 - 0
src/main/resources/mapper/SysJobMapper.xml

@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="net.yyc.quartz.mapper.SysJobMapper">
+	<resultMap id="sysJobMap" type="net.yyc.quartz.entity.SysJob">
+		<id property="jobId" column="job_id"/>
+		<result property="jobName" column="job_name"/>
+		<result property="jobGroup" column="job_group"/>
+		<result property="jobOrder" column="job_order"/>
+		<result property="jobType" column="job_type"/>
+		<result property="executePath" column="execute_path"/>
+		<result property="className" column="class_name"/>
+		<result property="methodName" column="method_name"/>
+		<result property="methodParamsValue" column="method_params_value"/>
+		<result property="cronExpression" column="cron_expression"/>
+		<result property="misfirePolicy" column="misfire_policy"/>
+		<result property="jobTenantType" column="job_tenant_type"/>
+		<result property="jobStatus" column="job_status"/>
+		<result property="jobExecuteStatus" column="job_execute_status"/>
+		<result property="createBy" column="create_by"/>
+		<result property="createTime" column="create_time"/>
+		<result property="updateBy" column="update_by"/>
+		<result property="updateTime" column="update_time"/>
+		<result property="startTime" column="start_time"/>
+		<result property="previousTime" column="previous_time"/>
+		<result property="nextTime" column="next_time"/>
+		<result property="tenantId" column="tenant_id"/>
+		<result property="remark" column="remark"/>
+		<result property="uuid" column="uuid"/>
+	</resultMap>
+</mapper>