1.初始化版本,2.引入thirdpart模块,引入canteen模块,初始化菜品分类,spu

This commit is contained in:
zjf committed 2024-12-06 16:54:30 +08:00
commit aff77150d6
1595 files changed
+168833

No files matched your search

@@ -0,0 +1,25 @@
package com.bsd.pjcloud.daemon.quartz;
import com.bsd.pjcloud.common.feign.annotation.EnablePjcloudFeignClients;
import com.bsd.pjcloud.common.security.annotation.EnablePjcloudResourceServer;
import com.bsd.pjcloud.common.swagger.annotation.EnableOpenApi;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* @author frwcloud
* @date 2019/01/23 定时任务模块
*/
@EnableOpenApi("job")
@EnablePjcloudFeignClients
@EnablePjcloudResourceServer
@EnableDiscoveryClient
@SpringBootApplication
public class PjcloudDaemonQuartzApplication {
public static void main(String[] args) {
SpringApplication.run(PjcloudDaemonQuartzApplication.class, args);
}
}
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2018-2025, pjcloud 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: pjcloud
*/
package com.bsd.pjcloud.daemon.quartz.config;
import org.quartz.JobKey;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;
import org.springframework.util.Assert;
/**
* @author 郑健楠
*/
class AutowireCapableBeanJobFactory extends SpringBeanJobFactory {
private final AutowireCapableBeanFactory beanFactory;
AutowireCapableBeanJobFactory(AutowireCapableBeanFactory beanFactory) {
Assert.notNull(beanFactory, "Bean factory must not be null");
this.beanFactory = beanFactory;
}
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
Object jobInstance = super.createJobInstance(bundle);
this.beanFactory.autowireBean(jobInstance);
// 此处必须注入 beanName 不然sentinel 报错
JobKey jobKey = bundle.getTrigger().getJobKey();
String beanName = jobKey + jobKey.getName();
this.beanFactory.initializeBean(jobInstance, beanName);
return jobInstance;
}
}
@@ -0,0 +1,63 @@
/*
* Copyright (c) 2018-2025, pjcloud 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: pjcloud
*/
package com.bsd.pjcloud.daemon.quartz.config;
import com.bsd.pjcloud.daemon.quartz.constants.PjcloudQuartzEnum;
import com.bsd.pjcloud.daemon.quartz.service.SysJobService;
import com.bsd.pjcloud.daemon.quartz.util.TaskUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Scheduler;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.annotation.Configuration;
/**
* @author 郑健楠
* <p>
* 初始化加载定时任务
*/
@Slf4j
@Configuration
@RequiredArgsConstructor
public class PjcloudInitQuartzJob implements InitializingBean {
private final SysJobService sysJobService;
private final TaskUtil taskUtil;
private final Scheduler scheduler;
@Override
public void afterPropertiesSet() throws Exception {
sysJobService.list().forEach(sysjob -> {
if (PjcloudQuartzEnum.JOB_STATUS_RELEASE.getType().equals(sysjob.getJobStatus())) {
taskUtil.removeJob(sysjob, scheduler);
}
else if (PjcloudQuartzEnum.JOB_STATUS_RUNNING.getType().equals(sysjob.getJobStatus())) {
taskUtil.resumeJob(sysjob, scheduler);
}
else if (PjcloudQuartzEnum.JOB_STATUS_NOT_RUNNING.getType().equals(sysjob.getJobStatus())) {
taskUtil.pauseJob(sysjob, scheduler);
}
else {
taskUtil.removeJob(sysjob, scheduler);
}
});
}
}
@@ -0,0 +1,126 @@
/*
* Copyright (c) 2018-2025, pjcloud 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: pjcloud
*/
package com.bsd.pjcloud.daemon.quartz.config;
import com.bsd.pjcloud.common.core.factory.YamlPropertySourceFactory;
import org.quartz.Calendar;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.Trigger;
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.context.annotation.PropertySource;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import java.util.List;
import java.util.Map;
import java.util.Properties;
/**
* @author 郑健楠
*/
@EnableAsync
@Configuration
@PropertySource(value = "classpath:quartz-config.yml", factory = YamlPropertySourceFactory.class)
@ConditionalOnClass({ Scheduler.class, SchedulerFactoryBean.class })
@EnableConfigurationProperties({ QuartzProperties.class })
public class PjcloudQuartzConfig {
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 PjcloudQuartzConfig(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) {
for (SchedulerFactoryBeanCustomizer customizer : this.customizers) {
customizer.customize(schedulerFactoryBean);
}
}
}
/**
* 通过SchedulerFactoryBean获取Scheduler的实例
* @return
*/
@Bean
public Scheduler scheduler() {
return quartzScheduler().getScheduler();
}
}
@@ -0,0 +1,35 @@
/*
* Copyright (c) 2018-2025, pjcloud 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: pjcloud
*/
package com.bsd.pjcloud.daemon.quartz.config;
import org.springframework.boot.autoconfigure.quartz.SchedulerFactoryBeanCustomizer;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
/**
* @author 郑健楠
*/
@Configuration
public class PjcloudQuartzCustomizerConfig implements SchedulerFactoryBeanCustomizer {
@Override
public void customize(SchedulerFactoryBean schedulerFactoryBean) {
schedulerFactoryBean.setWaitForJobsToCompleteOnShutdown(true);
}
}
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2018-2025, pjcloud 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: pjcloud
*/
package com.bsd.pjcloud.daemon.quartz.config;
import com.bsd.pjcloud.daemon.quartz.constants.PjcloudQuartzEnum;
import com.bsd.pjcloud.daemon.quartz.entity.SysJob;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.springframework.beans.factory.annotation.Autowired;
/**
* @author 郑健楠
*
* <p>
* 动态任务工厂
*/
@Slf4j
@DisallowConcurrentExecution
public class PjcloudQuartzFactory implements Job {
@Autowired
private PjcloudQuartzInvokeFactory pjcloudQuartzInvokeFactory;
@Override
@SneakyThrows
public void execute(JobExecutionContext jobExecutionContext) {
SysJob sysJob = (SysJob) jobExecutionContext.getMergedJobDataMap()
.get(PjcloudQuartzEnum.SCHEDULE_JOB_KEY.getType());
pjcloudQuartzInvokeFactory.init(sysJob, jobExecutionContext.getTrigger());
}
}
@@ -0,0 +1,46 @@
/*
* Copyright (c) 2018-2025, pjcloud 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: pjcloud
*/
package com.bsd.pjcloud.daemon.quartz.config;
import com.bsd.pjcloud.daemon.quartz.entity.SysJob;
import com.bsd.pjcloud.daemon.quartz.event.SysJobEvent;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.Aspect;
import org.quartz.Trigger;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
/**
* @author 郑健楠
*/
@Slf4j
@Aspect
@Service
@AllArgsConstructor
public class PjcloudQuartzInvokeFactory {
private final ApplicationEventPublisher publisher;
@SneakyThrows
void init(SysJob sysJob, Trigger trigger) {
publisher.publishEvent(new SysJobEvent(sysJob, trigger));
}
}
@@ -0,0 +1,63 @@
/*
* Copyright (c) 2018-2025, pjcloud 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: pjcloud
*/
package com.bsd.pjcloud.daemon.quartz.constants;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @author pjcloud
* @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;
}
@@ -0,0 +1,94 @@
/*
* Copyright (c) 2018-2025, pjcloud 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: pjcloud
*/
package com.bsd.pjcloud.daemon.quartz.constants;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @author 郑健楠
*
* <p>
* 定时任务枚举
*/
@Getter
@AllArgsConstructor
public enum PjcloudQuartzEnum {
/**
* 错失执行策略默认
*/
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;
}
@@ -0,0 +1,313 @@
/*
* Copyright (c) 2018-2025, pjcloud 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: pjcloud
*/
package com.bsd.pjcloud.daemon.quartz.controller;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bsd.pjcloud.common.core.util.R;
import com.bsd.pjcloud.common.excel.annotation.ResponseExcel;
import com.bsd.pjcloud.common.log.annotation.SysLog;
import com.bsd.pjcloud.common.security.annotation.HasPermission;
import com.bsd.pjcloud.common.security.util.SecurityUtils;
import com.bsd.pjcloud.daemon.quartz.constants.PjcloudQuartzEnum;
import com.bsd.pjcloud.daemon.quartz.entity.SysJob;
import com.bsd.pjcloud.daemon.quartz.entity.SysJobLog;
import com.bsd.pjcloud.daemon.quartz.service.SysJobLogService;
import com.bsd.pjcloud.daemon.quartz.service.SysJobService;
import com.bsd.pjcloud.daemon.quartz.util.TaskUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import org.quartz.Scheduler;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* @author frwcloud
* @date 2019-01-27 10:04:42
* <p>
* 定时任务管理
*/
@RestController
@AllArgsConstructor
@RequestMapping("/sys-job")
@Tag(description = "sys-job", name = "定时任务")
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
public class SysJobController {
private final SysJobService sysJobService;
private final SysJobLogService sysJobLogService;
private final TaskUtil taskUtil;
private final Scheduler scheduler;
/**
* 定时任务分页查询
* @param page 分页对象
* @param sysJob 定时任务调度表
* @return
*/
@GetMapping("/page")
@Operation(description = "分页定时业务查询")
public R getSysJobPage(Page page, SysJob sysJob) {
LambdaQueryWrapper<SysJob> wrapper = Wrappers.<SysJob>lambdaQuery()
.like(StrUtil.isNotBlank(sysJob.getJobName()), SysJob::getJobName, sysJob.getJobName())
.like(StrUtil.isNotBlank(sysJob.getJobGroup()), SysJob::getJobGroup, sysJob.getJobGroup())
.eq(StrUtil.isNotBlank(sysJob.getJobStatus()), SysJob::getJobStatus, sysJob.getJobGroup())
.eq(StrUtil.isNotBlank(sysJob.getJobExecuteStatus()), SysJob::getJobExecuteStatus,
sysJob.getJobExecuteStatus());
return R.ok(sysJobService.page(page, wrapper));
}
/**
* 通过id查询定时任务
* @param id id
* @return R
*/
@GetMapping("/{id}")
@Operation(description = "唯一标识查询定时任务")
public R getById(@PathVariable("id") Long id) {
return R.ok(sysJobService.getById(id));
}
/**
* 新增定时任务
* @param sysJob 定时任务调度表
* @return R
*/
@SysLog("新增定时任务")
@PostMapping
@HasPermission("job_sys_job_add")
@Operation(description = "新增定时任务")
public R save(@RequestBody SysJob sysJob) {
// 初始化任务
taskUtil.addOrUpateJob(sysJob, scheduler);
sysJob.setJobStatus(PjcloudQuartzEnum.JOB_STATUS_RELEASE.getType());
sysJob.setCreateBy(SecurityUtils.getUser().getUsername());
return R.ok(sysJobService.save(sysJob));
}
/**
* 修改定时任务
* @param sysJob 定时任务调度表
* @return R
*/
@SysLog("修改定时任务")
@PutMapping
@HasPermission("job_sys_job_edit")
@Operation(description = "修改定时任务")
public R updateById(@RequestBody SysJob sysJob) {
sysJob.setUpdateBy(SecurityUtils.getUser().getUsername());
SysJob querySysJob = this.sysJobService.getById(sysJob.getJobId());
if (PjcloudQuartzEnum.JOB_STATUS_NOT_RUNNING.getType().equals(querySysJob.getJobStatus())) {
this.taskUtil.addOrUpateJob(sysJob, scheduler);
sysJobService.updateById(sysJob);
}
else if (PjcloudQuartzEnum.JOB_STATUS_RELEASE.getType().equals(querySysJob.getJobStatus())) {
sysJobService.updateById(sysJob);
}
return R.ok();
}
/**
* 通过id删除定时任务
* @param id id
* @return R
*/
@SysLog("删除定时任务")
@DeleteMapping("/{id}")
@HasPermission("job_sys_job_del")
@Operation(description = "唯一标识查询定时任务,暂停任务才能删除")
public R removeById(@PathVariable Long id) {
SysJob querySysJob = this.sysJobService.getById(id);
if (PjcloudQuartzEnum.JOB_STATUS_NOT_RUNNING.getType().equals(querySysJob.getJobStatus())) {
this.taskUtil.removeJob(querySysJob, scheduler);
this.sysJobService.removeById(id);
}
else if (PjcloudQuartzEnum.JOB_STATUS_RELEASE.getType().equals(querySysJob.getJobStatus())) {
this.sysJobService.removeById(id);
}
return R.ok();
}
/**
* 暂停全部定时任务
* @return
*/
@SysLog("暂停全部定时任务")
@PostMapping("/shutdown-jobs")
@HasPermission("job_sys_job_shutdown_job")
@Operation(description = "暂停全部定时任务")
public R shutdownJobs() {
taskUtil.pauseJobs(scheduler);
long count = this.sysJobService.count(
new LambdaQueryWrapper<SysJob>().eq(SysJob::getJobStatus, PjcloudQuartzEnum.JOB_STATUS_RUNNING.getType()));
if (count <= 0) {
return R.ok("无正在运行定时任务");
}
else {
// 更新定时任务状态条件,运行状态2更新为暂停状态2
this.sysJobService.update(
SysJob.builder().jobStatus(PjcloudQuartzEnum.JOB_STATUS_NOT_RUNNING.getType()).build(),
new UpdateWrapper<SysJob>().lambda()
.eq(SysJob::getJobStatus, PjcloudQuartzEnum.JOB_STATUS_RUNNING.getType()));
return R.ok("暂停成功");
}
}
/**
* 启动全部定时任务
* @return
*/
@SysLog("启动全部定时任务")
@PostMapping("/start-jobs")
@HasPermission("job_sys_job_start_job")
@Operation(description = "启动全部定时任务")
public R startJobs() {
// 更新定时任务状态条件,暂停状态3更新为运行状态2
this.sysJobService.update(SysJob.builder().jobStatus(PjcloudQuartzEnum.JOB_STATUS_RUNNING.getType()).build(),
new UpdateWrapper<SysJob>().lambda()
.eq(SysJob::getJobStatus, PjcloudQuartzEnum.JOB_STATUS_NOT_RUNNING.getType()));
taskUtil.startJobs(scheduler);
return R.ok();
}
/**
* 刷新全部定时任务
* @return
*/
@SysLog("刷新全部定时任务")
@PostMapping("/refresh-jobs")
@HasPermission("job_sys_job_refresh_job")
@Operation(description = "刷新全部定时任务")
public R refreshJobs() {
sysJobService.list().forEach((sysjob) -> {
if (PjcloudQuartzEnum.JOB_STATUS_RELEASE.getType().equals(sysjob.getJobStatus())
|| PjcloudQuartzEnum.JOB_STATUS_DEL.getType().equals(sysjob.getJobStatus())) {
taskUtil.removeJob(sysjob, scheduler);
}
else if (PjcloudQuartzEnum.JOB_STATUS_RUNNING.getType().equals(sysjob.getJobStatus())
|| PjcloudQuartzEnum.JOB_STATUS_NOT_RUNNING.getType().equals(sysjob.getJobStatus())) {
taskUtil.addOrUpateJob(sysjob, scheduler);
}
else {
taskUtil.removeJob(sysjob, scheduler);
}
});
return R.ok();
}
/**
* 启动定时任务
* @param jobId
* @return
*/
@SysLog("启动定时任务")
@PostMapping("/start-job/{id}")
@HasPermission("job_sys_job_start_job")
@Operation(description = "启动定时任务")
public R startJob(@PathVariable("id") Long jobId) {
SysJob querySysJob = this.sysJobService.getById(jobId);
if (querySysJob != null && PjcloudQuartzEnum.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(PjcloudQuartzEnum.JOB_STATUS_RUNNING.getType()).build());
return R.ok();
}
/**
* 启动定时任务
* @param jobId
* @return
*/
@SysLog("立刻执行定时任务")
@PostMapping("/run-job/{id}")
@HasPermission("job_sys_job_run_job")
@Operation(description = "立刻执行定时任务")
public R runJob(@PathVariable("id") Long jobId) {
SysJob querySysJob = this.sysJobService.getById(jobId);
return TaskUtil.runOnce(scheduler, querySysJob) ? R.ok() : R.failed();
}
/**
* 暂停定时任务
* @return
*/
@SysLog("暂停定时任务")
@PostMapping("/shutdown-job/{id}")
@HasPermission("job_sys_job_shutdown_job")
@Operation(description = "暂停定时任务")
public R shutdownJob(@PathVariable("id") Long id) {
SysJob querySysJob = this.sysJobService.getById(id);
// 更新定时任务状态条件,运行状态2更新为暂停状态3
this.sysJobService.updateById(SysJob.builder()
.jobId(querySysJob.getJobId())
.jobStatus(PjcloudQuartzEnum.JOB_STATUS_NOT_RUNNING.getType())
.build());
taskUtil.pauseJob(querySysJob, scheduler);
return R.ok();
}
/**
* 唯一标识查询定时执行日志
* @return
*/
@GetMapping("/job-log")
@Operation(description = "唯一标识查询定时执行日志")
public R getJobLog(Page page, SysJobLog sysJobLog) {
return R.ok(sysJobLogService.page(page, Wrappers.query(sysJobLog)));
}
/**
* 检验任务名称和任务组联合是否唯一
* @return
*/
@GetMapping("/is-valid-task-name")
@Operation(description = "检验任务名称和任务组联合是否唯一")
public R isValidTaskName(@RequestParam String jobName, @RequestParam String jobGroup) {
return this.sysJobService
.count(Wrappers.query(SysJob.builder().jobName(jobName).jobGroup(jobGroup).build())) > 0
? R.failed("任务重复,请检查此组内是否已包含同名任务") : R.ok();
}
/**
* 导出任务
* @param sysJob
* @return
*/
@ResponseExcel
@GetMapping("/export")
@Operation(description = "导出任务")
public List<SysJob> export(SysJob sysJob) {
return sysJobService.list(Wrappers.query(sysJob));
}
}
@@ -0,0 +1,65 @@
/*
* Copyright (c) 2018-2025, pjcloud 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: pjcloud
*/
package com.bsd.pjcloud.daemon.quartz.controller;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bsd.pjcloud.common.core.util.R;
import com.bsd.pjcloud.daemon.quartz.entity.SysJobLog;
import com.bsd.pjcloud.daemon.quartz.service.SysJobLogService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.*;
/**
* @author frwcloud
* <p>
* 定时任务执行日志表
*/
@RestController
@AllArgsConstructor
@RequestMapping("/sys-job-log")
@Tag(description = "sys-job-log", name = "定时任务日志")
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
public class SysJobLogController {
private final SysJobLogService sysJobLogService;
/**
* 分页查询
* @param page 分页对象
* @param sysJobLog 定时任务执行日志表
* @return
*/
@GetMapping("/page")
@Operation(description = "分页定时任务日志查询")
public R getSysJobLogPage(Page page, SysJobLog sysJobLog) {
return R.ok(sysJobLogService.page(page, Wrappers.query(sysJobLog)));
}
@DeleteMapping
@Operation(description = "批量删除日志")
public R deleteLogs(@RequestBody Long[] ids) {
return R.ok(sysJobLogService.removeBatchByIds(CollUtil.toList(ids)));
}
}
@@ -0,0 +1,167 @@
/*
* Copyright (c) 2018-2025, pjcloud 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: pjcloud
*/
package com.bsd.pjcloud.daemon.quartz.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.bsd.pjcloud.common.core.util.TenantTable;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.time.LocalDateTime;
/**
* 定时任务调度表
*
* @author frwcloud
*/
@Data
@Builder
@TenantTable
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "定时任务")
@EqualsAndHashCode(callSuper = true)
public class SysJob extends Model<SysJob> {
private static final long serialVersionUID = 1L;
/**
* 任务id
*/
@TableId(value = "job_id", type = IdType.ASSIGN_ID)
private Long jobId;
/**
* 任务名称
*/
private String jobName;
/**
* 任务组名
*/
private String jobGroup;
/**
* 组内执行顺利,值越大执行优先级越高,最大值9,最小值1
*/
private String jobOrder;
/**
* 1、java类;2、spring bean名称;3、rest调用;4、jar调用;9其他
*/
private String jobType;
/**
* job_type=3时,rest调用地址,仅支持rest get协议,需要增加String返回值,0成功,1失败;job_type=4时,jar路径;其它值为空
*/
private String executePath;
/**
* job_type=1时,类完整路径;job_type=2时,spring bean名称;其它值为空
*/
private String className;
/**
* 任务方法
*/
private String methodName;
/**
* 参数值
*/
private String methodParamsValue;
/**
* cron执行表达式
*/
private String cronExpression;
/**
* 错失执行策略(1错失周期立即执行 2错失周期执行一次 3下周期执行)
*/
private String misfirePolicy;
/**
* 1、多租户任务;2、非多租户任务
*/
private String jobTenantType;
/**
* 状态(0、未发布;1、已发布;2、运行中;3、暂停;4、删除;)
*/
private String jobStatus;
/**
* 状态(0正常 1异常)
*/
private String jobExecuteStatus;
/**
* 创建者
*/
@TableField(fill = FieldFill.INSERT)
private String createBy;
/**
* 更新者
*/
@TableField(fill = FieldFill.UPDATE)
private String updateBy;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
/**
* 修改时间
*/
@TableField(fill = FieldFill.UPDATE)
private LocalDateTime updateTime;
/**
* 首次执行时间
*/
private LocalDateTime startTime;
/**
* 上次执行时间
*/
private LocalDateTime previousTime;
/**
* 下次执行时间
*/
private LocalDateTime nextTime;
/**
* 租户id
*/
private Long tenantId;
/**
* 备注信息
*/
private String remark;
}
@@ -0,0 +1,135 @@
/*
* Copyright (c) 2018-2025, pjcloud 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: pjcloud
*/
package com.bsd.pjcloud.daemon.quartz.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.bsd.pjcloud.common.core.util.TenantTable;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.time.LocalDateTime;
/**
* 定时任务执行日志表
*
* @author frwcloud
* @date 2019-01-27 13:40:20
*/
@Data
@Builder
@TenantTable
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "定时任务日志")
@EqualsAndHashCode(callSuper = true)
public class SysJobLog extends Model<SysJobLog> {
private static final long serialVersionUID = 1L;
/**
* 任务日志ID
*/
@TableId(value = "job_log_id", type = IdType.ASSIGN_ID)
private Long jobLogId;
/**
* 任务id
*/
private Long jobId;
/**
* 任务名称
*/
private String jobName;
/**
* 任务组名
*/
private String jobGroup;
/**
* 组内执行顺利,值越大执行优先级越高,最大值9,最小值1
*/
private String jobOrder;
/**
* 1、java类;2、spring bean名称;3、rest调用;4、jar调用;9其他
*/
private String jobType;
/**
* job_type=3时,rest调用地址,仅支持post协议;job_type=4时,jar路径;其它值为空
*/
private String executePath;
/**
* job_type=1时,类完整路径;job_type=2时,spring bean名称;其它值为空
*/
private String className;
/**
* 任务方法
*/
private String methodName;
/**
* 参数值
*/
private String methodParamsValue;
/**
* cron执行表达式
*/
private String cronExpression;
/**
* 日志信息
*/
private String jobMessage;
/**
* 执行状态(0正常 1失败)
*/
private String jobLogStatus;
/**
* 执行时间
*/
private String executeTime;
/**
* 异常信息
*/
private String exceptionInfo;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
/**
* 租户id
*/
private Long tenantId;
}
@@ -0,0 +1,36 @@
/*
* Copyright (c) 2018-2025, pjcloud 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: pjcloud
*/
package com.bsd.pjcloud.daemon.quartz.event;
import com.bsd.pjcloud.daemon.quartz.entity.SysJob;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.quartz.Trigger;
/**
* @author frwcloud 定时任务多线程事件
*/
@Getter
@AllArgsConstructor
public class SysJobEvent {
private final SysJob sysJob;
private final Trigger trigger;
}
@@ -0,0 +1,49 @@
/*
* Copyright (c) 2018-2025, pjcloud 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: pjcloud
*/
package com.bsd.pjcloud.daemon.quartz.event;
import com.bsd.pjcloud.daemon.quartz.entity.SysJob;
import com.bsd.pjcloud.daemon.quartz.util.TaskInvokUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Trigger;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
/**
* @author frwcloud 异步监听定时任务事件
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class SysJobListener {
private final TaskInvokUtil taskInvokUtil;
@Async
@Order
@EventListener(SysJobEvent.class)
public void comSysJob(SysJobEvent event) {
SysJob sysJob = event.getSysJob();
Trigger trigger = event.getTrigger();
taskInvokUtil.invokMethod(sysJob, trigger);
}
}
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2018-2025, pjcloud 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: pjcloud
*/
package com.bsd.pjcloud.daemon.quartz.event;
import com.bsd.pjcloud.daemon.quartz.entity.SysJobLog;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @author frwcloud 定时任务日志多线程事件
*/
@Getter
@AllArgsConstructor
public class SysJobLogEvent {
private final SysJobLog sysJobLog;
}
@@ -0,0 +1,53 @@
/*
* Copyright (c) 2018-2025, pjcloud 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: pjcloud
*/
package com.bsd.pjcloud.daemon.quartz.event;
import com.bsd.pjcloud.daemon.quartz.entity.SysJobLog;
import com.bsd.pjcloud.daemon.quartz.service.SysJobLogService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.Objects;
/**
* @author frwcloud 异步监听定时任务日志事件
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class SysJobLogListener {
private final SysJobLogService sysJobLogService;
@Async
@Order
@EventListener(SysJobLogEvent.class)
public void saveSysJobLog(SysJobLogEvent event) {
SysJobLog sysJobLog = event.getSysJobLog();
// 只保存发布状态的任务日志
if (Objects.nonNull(sysJobLog.getJobId())){
sysJobLogService.save(sysJobLog);
}
log.info("执行定时任务日志");
}
}
@@ -0,0 +1,35 @@
/*
* Copyright (c) 2018-2025, pjcloud 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: pjcloud
*/
package com.bsd.pjcloud.daemon.quartz.exception;
/**
* 定时任务异常
*
* @author 郑健楠
*/
public class TaskException extends Exception {
public TaskException() {
super();
}
public TaskException(String msg) {
super(msg);
}
}
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2018-2025, pjcloud 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: pjcloud
*/
package com.bsd.pjcloud.daemon.quartz.mapper;
import com.bsd.pjcloud.common.data.datascope.PjcloudBaseMapper;
import com.bsd.pjcloud.daemon.quartz.entity.SysJobLog;
import org.apache.ibatis.annotations.Mapper;
/**
* 定时任务执行日志表
*
* @author frwcloud
* @date 2019-01-27 13:40:20
*/
@Mapper
public interface SysJobLogMapper extends PjcloudBaseMapper<SysJobLog> {
}
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2018-2025, pjcloud 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: pjcloud
*/
package com.bsd.pjcloud.daemon.quartz.mapper;
import com.bsd.pjcloud.common.data.datascope.PjcloudBaseMapper;
import com.bsd.pjcloud.daemon.quartz.entity.SysJob;
import org.apache.ibatis.annotations.Mapper;
/**
* 定时任务调度表
*
* @author frwcloud
* @date 2019-01-27 10:04:42
*/
@Mapper
public interface SysJobMapper extends PjcloudBaseMapper<SysJob> {
}
@@ -0,0 +1,31 @@
/*
* Copyright (c) 2018-2025, pjcloud 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: pjcloud
*/
package com.bsd.pjcloud.daemon.quartz.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.bsd.pjcloud.daemon.quartz.entity.SysJobLog;
/**
* 定时任务执行日志表
*
* @author frwcloud
* @date 2019-01-27 13:40:20
*/
public interface SysJobLogService extends IService<SysJobLog> {
}
@@ -0,0 +1,31 @@
/*
* Copyright (c) 2018-2025, pjcloud 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: pjcloud
*/
package com.bsd.pjcloud.daemon.quartz.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.bsd.pjcloud.daemon.quartz.entity.SysJob;
/**
* 定时任务调度表
*
* @author frwcloud
* @date 2019-01-27 10:04:42
*/
public interface SysJobService extends IService<SysJob> {
}
@@ -0,0 +1,39 @@
/*
* Copyright (c) 2018-2025, pjcloud 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: pjcloud
*/
package com.bsd.pjcloud.daemon.quartz.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bsd.pjcloud.daemon.quartz.entity.SysJobLog;
import com.bsd.pjcloud.daemon.quartz.mapper.SysJobLogMapper;
import com.bsd.pjcloud.daemon.quartz.service.SysJobLogService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* 定时任务执行日志表
*
* @author frwcloud
* @date 2019-01-27 13:40:20
*/
@Slf4j
@Service
@AllArgsConstructor
public class SysJobLogServiceImpl extends ServiceImpl<SysJobLogMapper, SysJobLog> implements SysJobLogService {
}
@@ -0,0 +1,39 @@
/*
* Copyright (c) 2018-2025, pjcloud 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: pjcloud
*/
package com.bsd.pjcloud.daemon.quartz.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bsd.pjcloud.daemon.quartz.entity.SysJob;
import com.bsd.pjcloud.daemon.quartz.mapper.SysJobMapper;
import com.bsd.pjcloud.daemon.quartz.service.SysJobService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* 定时任务调度表
*
* @author frwcloud
* @date 2019-01-27 10:04:42
*/
@Slf4j
@Service
@AllArgsConstructor
public class SysJobServiceImpl extends ServiceImpl<SysJobMapper, SysJob> implements SysJobService {
}
@@ -0,0 +1,34 @@
package com.bsd.pjcloud.daemon.quartz.task;
import com.bsd.pjcloud.common.core.util.R;
import com.bsd.pjcloud.common.security.annotation.Inner;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDateTime;
/**
* 用于测试REST风格调用的demo
*
* @author lishangbu
* @date 2019/3/25
*/
@Slf4j
@RestController
@RequestMapping("/inner-job")
public class RestTaskDemo {
/**
* 测试REST风格调用定时任务的演示方法
*/
@Inner(value = false)
@GetMapping("/{param}")
public R demoMethod(@PathVariable("param") String param) {
log.info("测试于:{},传入参数{}", LocalDateTime.now(), param);
return R.ok();
}
}
@@ -0,0 +1,43 @@
/*
* Copyright (c) 2018-2025, pjcloud 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: pjcloud
*/
package com.bsd.pjcloud.daemon.quartz.task;
import com.bsd.pjcloud.daemon.quartz.constants.PjcloudQuartzEnum;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
/**
* @author 郑健楠
*/
@Slf4j
@Component("demo")
public class SpringBeanTaskDemo {
/**
* 测试Spring Bean的演示方法
*/
@SneakyThrows
public String demoMethod(String para) {
log.info("测试于:{},输入参数{}", LocalDateTime.now(), para);
return PjcloudQuartzEnum.JOB_LOG_STATUS_SUCCESS.getType();
}
}
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2018-2025, pjcloud 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: pjcloud
*/
package com.bsd.pjcloud.daemon.quartz.util;
import com.bsd.pjcloud.daemon.quartz.entity.SysJob;
import com.bsd.pjcloud.daemon.quartz.exception.TaskException;
/**
* 定时任务反射实现接口类
*
* @author 郑健楠
*/
public interface ITaskInvok {
/**
* 执行反射方法
* @param sysJob 配置类
* @throws TaskException
*/
void invokMethod(SysJob sysJob) throws TaskException;
}
@@ -0,0 +1,62 @@
/*
* Copyright (c) 2018-2025, pjcloud 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: pjcloud
*/
package com.bsd.pjcloud.daemon.quartz.util;
import cn.hutool.core.util.StrUtil;
import com.bsd.pjcloud.daemon.quartz.entity.SysJob;
import com.bsd.pjcloud.daemon.quartz.exception.TaskException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* 定时任务可执行jar反射实现
*
* @author 郑健楠
*/
@Slf4j
@Component("jarTaskInvok")
public class JarTaskInvok implements ITaskInvok {
@Override
public void invokMethod(SysJob sysJob) throws TaskException {
ProcessBuilder processBuilder = new ProcessBuilder();
File jar = new File(sysJob.getExecutePath());
processBuilder.directory(jar.getParentFile());
List<String> commands = new ArrayList<>();
commands.add("java");
commands.add("-jar");
commands.add(sysJob.getExecutePath());
if (StrUtil.isNotEmpty(sysJob.getMethodParamsValue())) {
commands.add(sysJob.getMethodParamsValue());
}
processBuilder.command(commands);
try {
processBuilder.start();
}
catch (IOException e) {
log.error("定时任务jar反射执行异常,执行任务:{}", sysJob.getExecutePath());
throw new TaskException("定时任务jar反射执行异常,执行任务:" + sysJob.getExecutePath());
}
}
}
@@ -0,0 +1,87 @@
/*
* Copyright (c) 2018-2025, pjcloud 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: pjcloud
*/
package com.bsd.pjcloud.daemon.quartz.util;
import cn.hutool.core.util.StrUtil;
import com.bsd.pjcloud.daemon.quartz.constants.PjcloudQuartzEnum;
import com.bsd.pjcloud.daemon.quartz.entity.SysJob;
import com.bsd.pjcloud.daemon.quartz.exception.TaskException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* 定时任务java class反射实现
*
* @author 郑健楠
*/
@Component("javaClassTaskInvok")
@Slf4j
public class JavaClassTaskInvok implements ITaskInvok {
@Override
public void invokMethod(SysJob sysJob) throws TaskException {
Object obj;
Class clazz;
Method method;
Object returnValue;
try {
if (StrUtil.isNotEmpty(sysJob.getMethodParamsValue())) {
clazz = Class.forName(sysJob.getClassName());
obj = clazz.newInstance();
method = clazz.getDeclaredMethod(sysJob.getMethodName(), String.class);
returnValue = method.invoke(obj, sysJob.getMethodParamsValue());
}
else {
clazz = Class.forName(sysJob.getClassName());
obj = clazz.newInstance();
method = clazz.getDeclaredMethod(sysJob.getMethodName());
returnValue = method.invoke(obj);
}
if (StrUtil.isEmpty(returnValue.toString())
|| PjcloudQuartzEnum.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());
}
}
}
@@ -0,0 +1,54 @@
/*
* Copyright (c) 2018-2025, pjcloud 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: pjcloud
*/
package com.bsd.pjcloud.daemon.quartz.util;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import com.bsd.pjcloud.common.core.constant.CommonConstants;
import com.bsd.pjcloud.daemon.quartz.entity.SysJob;
import com.bsd.pjcloud.daemon.quartz.exception.TaskException;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* 定时任务rest反射实现
*
* @author 郑健楠
*/
@Slf4j
@AllArgsConstructor
@Component("restTaskInvok")
public class RestTaskInvok implements ITaskInvok {
@Override
public void invokMethod(SysJob sysJob) throws TaskException {
try {
HttpRequest request = HttpUtil.createGet(sysJob.getExecutePath());
if (sysJob.getTenantId() != null) {
request.header(CommonConstants.TENANT_ID, sysJob.getTenantId().toString());
}
request.execute();
}
catch (Exception e) {
log.error("定时任务restTaskInvok异常,执行任务:{}", sysJob.getExecutePath());
throw new TaskException("定时任务restTaskInvok业务执行失败,任务:" + sysJob.getExecutePath());
}
}
}
@@ -0,0 +1,79 @@
/*
* Copyright (c) 2018-2025, pjcloud 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: pjcloud
*/
package com.bsd.pjcloud.daemon.quartz.util;
import cn.hutool.core.util.StrUtil;
import com.bsd.pjcloud.common.core.util.SpringContextHolder;
import com.bsd.pjcloud.daemon.quartz.constants.PjcloudQuartzEnum;
import com.bsd.pjcloud.daemon.quartz.entity.SysJob;
import com.bsd.pjcloud.daemon.quartz.exception.TaskException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* 定时任务spring bean反射实现
*
* @author 郑健楠
*/
@Component("springBeanTaskInvok")
@Slf4j
public class SpringBeanTaskInvok implements ITaskInvok {
@Override
public void invokMethod(SysJob sysJob) throws TaskException {
Object target;
Method method;
Object returnValue;
// 通过Spring上下文去找 也有可能找不到
target = SpringContextHolder.getBean(sysJob.getClassName());
try {
if (StrUtil.isNotEmpty(sysJob.getMethodParamsValue())) {
method = target.getClass().getDeclaredMethod(sysJob.getMethodName(), String.class);
ReflectionUtils.makeAccessible(method);
returnValue = method.invoke(target, sysJob.getMethodParamsValue());
}
else {
method = target.getClass().getDeclaredMethod(sysJob.getMethodName());
ReflectionUtils.makeAccessible(method);
returnValue = method.invoke(target);
}
if (StrUtil.isEmpty(returnValue.toString())
|| PjcloudQuartzEnum.JOB_LOG_STATUS_FAIL.getType().equals(returnValue.toString())) {
log.error("定时任务springBeanTaskInvok异常,执行任务:{}", sysJob.getClassName());
throw new TaskException("定时任务springBeanTaskInvok业务执行失败,任务:" + sysJob.getClassName());
}
}
catch (NoSuchMethodException e) {
log.error("定时任务spring bean反射异常方法未找到,执行任务:{}", sysJob.getClassName());
throw new TaskException("定时任务spring bean反射异常方法未找到,执行任务:" + sysJob.getClassName());
}
catch (IllegalAccessException e) {
log.error("定时任务spring bean反射异常,执行任务:{}", sysJob.getClassName());
throw new TaskException("定时任务spring bean反射异常,执行任务:" + sysJob.getClassName());
}
catch (InvocationTargetException e) {
log.error("定时任务spring bean反射执行异常,执行任务:{}", sysJob.getClassName());
throw new TaskException("定时任务spring bean反射执行异常,执行任务:" + sysJob.getClassName());
}
}
}
@@ -0,0 +1,50 @@
package com.bsd.pjcloud.daemon.quartz.util;
import cn.hutool.core.util.StrUtil;
import com.bsd.pjcloud.common.core.util.SpringContextHolder;
import com.bsd.pjcloud.daemon.quartz.constants.JobTypeQuartzEnum;
import com.bsd.pjcloud.daemon.quartz.exception.TaskException;
import lombok.extern.slf4j.Slf4j;
/**
* @author Hccake
* @version 1.0
* @date 2019/8/8 15:40 TaskInvok工厂类
*/
@Slf4j
public class TaskInvokFactory {
/**
* 根据对应jobType获取对应 invoker
* @param jobType
* @return
* @throws TaskException
*/
public static ITaskInvok getInvoker(String jobType) throws TaskException {
if (StrUtil.isBlank(jobType)) {
log.info("获取TaskInvok传递参数有误,jobType:{}", jobType);
throw new TaskException("");
}
ITaskInvok iTaskInvok = null;
if (JobTypeQuartzEnum.JAVA.getType().equals(jobType)) {
iTaskInvok = SpringContextHolder.getBean("javaClassTaskInvok");
}
else if (JobTypeQuartzEnum.SPRING_BEAN.getType().equals(jobType)) {
iTaskInvok = SpringContextHolder.getBean("springBeanTaskInvok");
}
else if (JobTypeQuartzEnum.REST.getType().equals(jobType)) {
iTaskInvok = SpringContextHolder.getBean("restTaskInvok");
}
else if (JobTypeQuartzEnum.JAR.getType().equals(jobType)) {
iTaskInvok = SpringContextHolder.getBean("jarTaskInvok");
}
else if (StrUtil.isBlank(jobType)) {
log.info("定时任务类型无对应反射方式,反射类型:{}", jobType);
throw new TaskException("");
}
return iTaskInvok;
}
}
@@ -0,0 +1,118 @@
/*
* Copyright (c) 2018-2025, pjcloud 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: pjcloud
*/
package com.bsd.pjcloud.daemon.quartz.util;
import cn.hutool.core.util.StrUtil;
import com.bsd.pjcloud.common.data.tenant.TenantBroker;
import com.bsd.pjcloud.daemon.quartz.constants.PjcloudQuartzEnum;
import com.bsd.pjcloud.daemon.quartz.entity.SysJob;
import com.bsd.pjcloud.daemon.quartz.entity.SysJobLog;
import com.bsd.pjcloud.daemon.quartz.event.SysJobLogEvent;
import com.bsd.pjcloud.daemon.quartz.service.SysJobService;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.quartz.CronTrigger;
import org.quartz.Trigger;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
import java.time.ZoneId;
import java.util.Date;
/**
* 定时任务反射工具类
*
* @author 郑健楠
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class TaskInvokUtil {
private final ApplicationEventPublisher publisher;
private final SysJobService sysJobService;
@SneakyThrows
public void invokMethod(SysJob sysJob, Trigger trigger) {
// 执行开始时间
long startTime;
// 执行结束时间
long endTime;
// 获取执行开始时间
startTime = System.currentTimeMillis();
// 更新定时任务表内的状态、执行时间、上次执行时间、下次执行时间等信息
SysJob updateSysjob = new SysJob();
updateSysjob.setJobId(sysJob.getJobId());
// 日志
SysJobLog sysJobLog = new SysJobLog();
sysJobLog.setJobId(sysJob.getJobId());
sysJobLog.setJobName(sysJob.getJobName());
sysJobLog.setJobGroup(sysJob.getJobGroup());
sysJobLog.setJobOrder(sysJob.getJobOrder());
sysJobLog.setJobType(sysJob.getJobType());
sysJobLog.setExecutePath(sysJob.getExecutePath());
sysJobLog.setClassName(sysJob.getClassName());
sysJobLog.setMethodName(sysJob.getMethodName());
sysJobLog.setMethodParamsValue(sysJob.getMethodParamsValue());
sysJobLog.setCronExpression(sysJob.getCronExpression());
sysJobLog.setTenantId(sysJob.getTenantId());
try {
// 执行任务
ITaskInvok iTaskInvok = TaskInvokFactory.getInvoker(sysJob.getJobType());
// 确保租户上下文有值,使得当前线程中的多租户特性生效。
TenantBroker.runAs(sysJob.getTenantId(), tenantId -> iTaskInvok.invokMethod(sysJob));
// 记录成功状态
sysJobLog.setJobMessage(PjcloudQuartzEnum.JOB_LOG_STATUS_SUCCESS.getDescription());
sysJobLog.setJobLogStatus(PjcloudQuartzEnum.JOB_LOG_STATUS_SUCCESS.getType());
// 任务表信息更新
updateSysjob.setJobExecuteStatus(PjcloudQuartzEnum.JOB_LOG_STATUS_SUCCESS.getType());
}
catch (Throwable e) {
log.error("定时任务执行失败,任务名称:{};任务组名:{},cron执行表达式:{},执行时间:{}", sysJob.getJobName(), sysJob.getJobGroup(),
sysJob.getCronExpression(), new Date());
// 记录失败状态
sysJobLog.setJobMessage(PjcloudQuartzEnum.JOB_LOG_STATUS_FAIL.getDescription());
sysJobLog.setJobLogStatus(PjcloudQuartzEnum.JOB_LOG_STATUS_FAIL.getType());
sysJobLog.setExceptionInfo(StrUtil.sub(e.getMessage(), 0, 2000));
// 任务表信息更新
updateSysjob.setJobExecuteStatus(PjcloudQuartzEnum.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);
}
}
}
@@ -0,0 +1,244 @@
/*
* Copyright (c) 2018-2025, pjcloud 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: pjcloud
*/
package com.bsd.pjcloud.daemon.quartz.util;
import com.bsd.pjcloud.daemon.quartz.config.PjcloudQuartzFactory;
import com.bsd.pjcloud.daemon.quartz.constants.PjcloudQuartzEnum;
import com.bsd.pjcloud.daemon.quartz.entity.SysJob;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.springframework.stereotype.Component;
/**
* 定时任务的工具类
*
* @author 郑健楠
*/
@Slf4j
@Component
public class TaskUtil {
/**
* 获取定时任务的唯一key
* @param sysjob
* @return
*/
public static JobKey getJobKey(SysJob sysjob) {
return JobKey.jobKey(sysjob.getJobName(), sysjob.getJobGroup());
}
/**
* 获取定时任务触发器cron的唯一key
* @param sysjob
* @return
*/
public static TriggerKey getTriggerKey(SysJob sysjob) {
return TriggerKey.triggerKey(sysjob.getJobName(), sysjob.getJobGroup());
}
/**
* 添加或更新定时任务
* @param sysjob
* @param scheduler
*/
public void addOrUpateJob(SysJob sysjob, Scheduler scheduler) {
CronTrigger trigger = null;
try {
JobKey jobKey = getJobKey(sysjob);
// 获得触发器
TriggerKey triggerKey = getTriggerKey(sysjob);
trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
// 判断触发器是否存在(如果存在说明之前运行过但是在当前被禁用了,如果不存在说明一次都没运行过)
if (trigger == null) {
// 新建一个工作任务 指定任务类型为串接进行的
JobDetail jobDetail = JobBuilder.newJob(PjcloudQuartzFactory.class).withIdentity(jobKey).build();
// 将任务信息添加到任务信息中
jobDetail.getJobDataMap().put(PjcloudQuartzEnum.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(PjcloudQuartzEnum.SCHEDULE_JOB_KEY.getType(), sysjob);
// 重启
scheduler.rescheduleJob(triggerKey, trigger);
}
// 如任务状态为暂停
if (sysjob.getJobStatus().equals(PjcloudQuartzEnum.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(PjcloudQuartzEnum.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 (PjcloudQuartzEnum.MISFIRE_DEFAULT.getType().equals(sysJob.getMisfirePolicy())) {
return cronScheduleBuilder;
}
else if (PjcloudQuartzEnum.MISFIRE_IGNORE_MISFIRES.getType().equals(sysJob.getMisfirePolicy())) {
return cronScheduleBuilder.withMisfireHandlingInstructionIgnoreMisfires();
}
else if (PjcloudQuartzEnum.MISFIRE_FIRE_AND_PROCEED.getType().equals(sysJob.getMisfirePolicy())) {
return cronScheduleBuilder.withMisfireHandlingInstructionFireAndProceed();
}
else if (PjcloudQuartzEnum.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);
}
}
@@ -0,0 +1,21 @@
server:
port: 5007
quartz:
enabled: true
spring:
application:
name: @artifactId@
cloud:
nacos:
username: @nacos.username@
password: @nacos.password@
discovery:
server-addr: ${NACOS_HOST:pjcloud-register}:${NACOS_PORT:8848}
config:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
config:
import:
- optional:nacos:application-@profiles.active@.yml
- optional:nacos:${spring.application.name}-@profiles.active@.yml
@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
小技巧: 在根pom里面设置统一存放路径,统一管理方便维护
<properties>
<log-path>/Users/pjcloud</log-path>
</properties>
1. 其他模块加日志输出,直接copy本文件放在resources 目录即可
2. 注意修改 <property name="${log-path}/log.path" value=""/> 的value模块
-->
<configuration debug="false" scan="false">
<property name="log.path" value="logs/${project.artifactId}"/>
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN"
value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<!-- 彩色日志依赖的渲染类 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
<conversionRule conversionWord="wex"
converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
<conversionRule conversionWord="wEx"
converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
<!-- Console log output -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<!-- Log file debug output -->
<appender name="debug" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/debug.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.path}/%d{yyyy-MM, aux}/debug.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>50MB</maxFileSize>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%date [%thread] %-5level [%logger{50}] %file:%line - %msg%n</pattern>
</encoder>
</appender>
<!-- Log file error output -->
<appender name="error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/error.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.path}/%d{yyyy-MM}/error.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>50MB</maxFileSize>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%date [%thread] %-5level [%logger{50}] %file:%line - %msg%n</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
</appender>
<logger name="org.activiti.engine.impl.db" level="DEBUG">
<appender-ref ref="debug"/>
</logger>
<!--nacos 心跳 INFO 屏蔽-->
<logger name="com.alibaba.nacos" level="OFF">
<appender-ref ref="error"/>
</logger>
<!-- Level: FATAL 0 ERROR 3 WARN 4 INFO 6 DEBUG 7 -->
<root level="INFO">
<appender-ref ref="console"/>
<appender-ref ref="debug"/>
<appender-ref ref="error"/>
</root>
</configuration>
@@ -0,0 +1,24 @@
# 如下配置为使用数据库存储定时任务,属性不经常修改,所以不放在 application.yml 中统一管理
spring:
quartz:
properties:
org:
quartz:
scheduler:
instanceName: clusteredScheduler
instanceId: AUTO
jobStore:
class: org.springframework.scheduling.quartz.LocalDataSourceJobStore # 数据库存储
driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate # 数据库代理
tablePrefix: QRTZ_ # 表前缀
isClustered: false # 集群
clusterCheckinInterval: 30000 # 集群检查间隔
useProperties: false
threadPool:
class: org.quartz.simpl.SimpleThreadPool # 线程池
threadCount: 3 # 线程数量
threadPriority: 5 # 线程优先级
threadsInheritContextClassLoaderOfInitializingThread: true # 是否继承类加载器
job-store-type: jdbc # 持久化到数据库
jdbc:
initialize-schema: never # 不初始化表结构