当前位置: 首页 > news >正文

Quartz框架实现根据设置的cron表达式进行定时任务执行

一、总体架构与技术选型

  • 任务调度框架:Quartz 2.3.x(企业级 Java 定时任务框架)
  • 容器与依赖注入:Spring(提供 Bean 管理与反射调用支持)
  • 业务域模型:Workflow(工作流)、SysJob(调度任务)、WorkflowTaskPlan(工作任务实例)
  • 关键模块职责
  • 前端:传递 Cron 表达式及工作流配置(不在当前仓库,后端侧接收)
  • 后端控制层:保存/更新工作流(不在本次引用代码中)
  • 工作流服务:在“已发布 + 有 Cron”的前提下,为工作流创建 Quartz 任务
  • Quartz 工具:构建 JobDetail/CronTrigger,注册到 Scheduler
  • 运行时执行:解析 invokeTarget,反射调用 Spring Bean 方法,执行业务
  • 二、端到端执行链路(从前端 Cron 到任务执行)

  • 前端在工作流表单中配置 Cron 表达式,提交保存
  • 后端保存 Workflow 后,调用 WorkflowScheduleService.createWorkflowScheduleJob(workflow)
  • 后端构建 SysJob,设置:
  • jobId=workflow.id
  • jobGroup="WORKFLOW"
  • invokeTarget="workflowScheduleJob.executeWorkflow(123L)"(关键)
  • cronExpression=workflow.cronExpression
  • 其他调度策略
  • 通过 ScheduleUtils.createScheduleJob 注册 Quartz 任务
  • Quartz 到点触发:
  • 解析 invokeTarget → 定位 Spring Bean 与方法
  • 反射调用 WorkflowScheduleJob.executeWorkflow(Long workflowId)
  • 业务执行:读取工作流配置,创建实际的 WorkflowTaskPlan 任务

三、关键代码与职责说明(带参数含义)

  • 创建/更新/删除工作流调度任务:WorkflowScheduleService
  •     private SysJob buildSysJob(Workflow workflow) {SysJob job = new SysJob();// 使用工作流ID作为任务IDjob.setJobId(workflow.getId());// 设置任务组名job.setJobGroup(WORKFLOW_JOB_GROUP);// 设置任务名称job.setJobName("工作流任务-" + workflow.getName());// 设置调用目标字符串:格式为 "beanName.methodName(参数)"// workflowScheduleJob 是 Spring Bean 名称// executeWorkflow 是要调用的方法// 参数需要加 L 后缀表示 Long 类型job.setInvokeTarget("workflowScheduleJob.executeWorkflow(" + workflow.getId() + "L)");// 设置cron表达式job.setCronExpression(workflow.getCronExpression());// 设置任务状态:根据工作流状态设置if ("pubed".equals(workflow.getStatus())) {job.setStatus(ScheduleConstants.Status.NORMAL.getValue());} else {job.setStatus(ScheduleConstants.Status.PAUSE.getValue());}// 设置并发执行:0=允许 1=禁止job.setConcurrent("1");// 设置错误策略:1=立即执行 2=执行一次 3=放弃执行job.setMisfirePolicy(ScheduleConstants.MISFIRE_DEFAULT);return job;}

  • 核心参数说明
  • jobId: 使用 workflow.id 作为唯一键
  • jobGroup: "WORKFLOW",用于隔离任务命名空间
  • jobName: 便于日志/排障的可读名称
  • invokeTarget: 反射调用入口,必须为 “bean.method(args)”,参数类型需与方法签名匹配(如 Long → 123L)
  • cronExpression: Quartz Cron 表达式(6 位或 7 位均可,项目内做了兼容处理)
  • status: 任务是否启动(已发布 PUBED 则启动)
  • concurrent: “1=禁止并发执行”,“0=允许并发执行”(项目中用字符串约定)
  • misfirePolicy: 错过触发时的补偿策略(详见后文)
  • Quartz 注册:ScheduleUtils.createScheduleJob
  •     public static void createScheduleJob(Scheduler scheduler, SysJob job) throws SchedulerException, TaskException{Class<? extends Job> jobClass = getQuartzJobClass(job);// 构建job信息Long jobId = job.getJobId();String jobGroup = job.getJobGroup();JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(getJobKey(jobId, jobGroup)).build();// 处理Cron表达式,解决day-of-week和day-of-month冲突问题String cronExpression = job.getCronExpression();if (cronExpression != null) {cronExpression = cronExpression.trim();String[] parts = cronExpression.split("\\s+");// 如果是6位表达式且第3位和第5位都是*,则替换其中一个为?if (parts.length == 6 && "*".equals(parts[2]) && "*".equals(parts[4])) {// 将day-of-week替换为?,保持每天执行的语义cronExpression = parts[0] + " " + parts[1] + " " + parts[2] + " " + parts[3] + " " + parts[4] + " ?";}}// 表达式调度构建器CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);cronScheduleBuilder = handleCronScheduleMisfirePolicy(job, cronScheduleBuilder);// 按新的cronExpression表达式构建一个新的triggerCronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(jobId, jobGroup)).withSchedule(cronScheduleBuilder).build();// 放入参数,运行时的方法可以获取jobDetail.getJobDataMap().put(ScheduleConstants.TASK_PROPERTIES, job);// 判断是否存在if (scheduler.checkExists(getJobKey(jobId, jobGroup))){// 防止创建时存在数据问题 先移除,然后在执行创建操作scheduler.deleteJob(getJobKey(jobId, jobGroup));}// 判断任务是否过期if (StringUtils.isNotNull(CronUtils.getNextExecution(job.getCronExpression()))){// 执行调度任务scheduler.scheduleJob(jobDetail, trigger);}// 暂停任务if (job.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue())){scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup));}}
    关键点
    兼容 6 段 Cron 表达式:在“日”和“周”都为 * 时,自动将“周”置为 ?,避免冲突
    Misfire 策略通过 handleCronScheduleMisfirePolicy 统一装配
    同名任务防重复:若已存在则先删除再创建
    过期判断:若未来无下一次触发时间则不注册
    任务状态为暂停时立即 pause
    反射调用器:JobInvokeUtil(解析 invokeTarget → 定位 Bean.Method(args))
        public static void invokeMethod(SysJob sysJob) throws Exception{String invokeTarget = sysJob.getInvokeTarget();String beanName = getBeanName(invokeTarget);String methodName = getMethodName(invokeTarget);List<Object[]> methodParams = getMethodParams(invokeTarget);if (!isValidClassName(beanName)){Object bean = SpringUtils.getBean(beanName);invokeMethod(bean, methodName, methodParams);}else{Object bean = Class.forName(beanName).getDeclaredConstructor().newInstance();invokeMethod(bean, methodName, methodParams);}}
        public static String getBeanName(String invokeTarget){String beanName = StringUtils.substringBefore(invokeTarget, "(");return StringUtils.substringBeforeLast(beanName, ".");}public static String getMethodName(String invokeTarget){String methodName = StringUtils.substringBefore(invokeTarget, "(");return StringUtils.substringAfterLast(methodName, ".");}
        public static List<Object[]> getMethodParams(String invokeTarget){String methodStr = StringUtils.substringBetween(invokeTarget, "(", ")");if (StringUtils.isEmpty(methodStr)){return null;}String[] methodParams = methodStr.split(",(?=([^\"']*[\"'][^\"']*[\"'])*[^\"']*$)");List<Object[]> classs = new LinkedList<>();for (int i = 0; i < methodParams.length; i++){String str = StringUtils.trimToEmpty(methodParams[i]);// String字符串类型,以'或"开头if (StringUtils.startsWithAny(str, "'", "\"")){classs.add(new Object[] { StringUtils.substring(str, 1, str.length() - 1), String.class });}// boolean布尔类型,等于true或者falseelse if ("true".equalsIgnoreCase(str) || "false".equalsIgnoreCase(str)){classs.add(new Object[] { Boolean.valueOf(str), Boolean.class });}// long长整形,以L结尾else if (StringUtils.endsWith(str, "L")){classs.add(new Object[] { Long.valueOf(StringUtils.substring(str, 0, str.length() - 1)), Long.class });}// double浮点类型,以D结尾else if (StringUtils.endsWith(str, "D")){classs.add(new Object[] { Double.valueOf(StringUtils.substring(str, 0, str.length() - 1)), Double.class });}// 其他类型归类为整形else{classs.add(new Object[] { Integer.valueOf(str), Integer.class });}}return classs;}

  • 参数规则
  • String: 'abc' 或 "abc"
  • Long: 123L
  • Double: 3.14D
  • Boolean: true/false
  • 其他缺省为 Integer
  • 实际执行业务:WorkflowScheduleJob.executeWorkflow
  • @Component("workflowScheduleJob")
    public class WorkflowScheduleJob extends AbstractQuartzJob {private static final Logger log = LoggerFactory.getLogger(WorkflowScheduleJob.class);@Autowiredprivate WorkflowTaskPlanServiceImpl workflowTaskPlanService;@Autowiredprivate WorkflowServiceImpl workflowService;/*** 执行工作流定时任务(供Quartz通过反射调用)* @param workflowId 工作流ID*/public void executeWorkflow(Long workflowId) {if (workflowId == null) {log.error("工作流定时任务执行失败:工作流ID为空");return;}log.info("开始执行工作流定时任务,工作流ID:{}", workflowId);try {// 查询工作流信息Workflow workflow = workflowService.selectWorkflowById(workflowId);if (workflow == null) {log.error("工作流定时任务执行失败:工作流不存在,工作流ID:{}", workflowId);return;}// 创建任务计划WorkflowTaskPlan taskPlan = new WorkflowTaskPlan();taskPlan.setWorkflowId(workflowId);taskPlan.setExecUserId(workflow.getExecUserId());taskPlan.setCreateTime(new Date());taskPlan.setStatus("undo");taskPlan.setSiteIds(workflow.getSiteIds());taskPlan.setDirNodes(workflow.getNodes());// 根据工作流类型选择插入方法if ("worker".equals(workflow.getType()) || "train".equals(workflow.getType())) {// 指导作业或培训任务workflowTaskPlanService.insertGuidePlan(taskPlan);} else {// 普通巡检任务workflowTaskPlanService.insertWorkflowTaskPlan(taskPlan);}log.info("工作流定时任务执行成功,工作流ID:{}", workflowId);} catch (Exception e) {log.error("工作流定时任务执行异常,工作流ID:{}", workflowId, e);throw new RuntimeException(e);}}

  • 关键点
  • Bean 名称为 workflowScheduleJob(显式命名,便于 invokeTarget 绑定)
  • 提供公共方法 executeWorkflow(Long) 作为反射入口
  • 根据 workflow.type 分支执行业务(培训/指导 vs 普通巡检)
  • 以 Workflow 的配置来构造 WorkflowTaskPlan 任务实例(站点/节点/执行人等)

四、原理与关键设计决策

  • 为什么使用 invokeTarget 反射?
  • Quartz 的通用执行器通过字符串路由到任何 Spring Bean 的公开方法,避免为每种任务单独写一个 Job 类,强扩展、低耦合
  • 错误案例:workflowId(123) 会被当作 Bean 名称 workflowId,Spring 找不到该 Bean → 报错
  • 正确案例:workflowScheduleJob.executeWorkflow(123L) → Bean + 方法 + 参数类型匹配
  • Cron 表达式的兼容与修正
  • 常见 6 段表达式(秒 分 时 日 月 周),Quartz 要求“日”和“周”至少一个用 ?
  • 代码中自动将冲突情况下的“周”改为 ?,避免用户输入不规范导致任务不注册
  • 并发控制
  • job.setConcurrent("1") 表示“禁止并发执行”,即上一次未完成时,下一次触发不并发进入
  • ScheduleUtils.getQuartzJobClass 据此选择 QuartzJobExecution 或 QuartzDisallowConcurrentExecution
  • Misfire(错过触发)策略
  • MISFIRE_DEFAULT(项目中默认):通常对应 Fire-And-Proceed 或框架默认策略
  • 可切到 Ignore/DoNothing 等,取决于业务是否需要补偿执行
  • 任务幂等性
  • 如有可能重复触发或失败重试,建议在 executeWorkflow 内加入幂等防重(例如当天/当时段已生成计划则跳过)

五、从前端到执行的“数据与参数”清单

  • 前端提交:
  • workflow.id: Long
  • workflow.name: String
  • workflow.cronExpression: String(如 0 0 9 * * ?)
  • workflow.status: pubed 表示已发布
  • workflow.type: worker/train/normal...
  • 其他业务字段:execUserId/siteIds/nodes/firstExecTime/pushCycle/pushUnit...
  • 后端创建 Quartz 任务使用的参数映射:
  • jobId ← workflow.id
  • jobGroup ← "WORKFLOW"
  • jobName ← "工作流任务-" + workflow.name
  • invokeTarget ← "workflowScheduleJob.executeWorkflow(" + workflow.id + "L)"
  • cronExpression ← workflow.cronExpression
  • status ← workflow.status(PUBED=启动,其他=暂停)
  • concurrent ← "1"(项目约定禁止并发)
  • misfirePolicy ← DEFAULT

六、常见问题与排查

  • 报错:NoSuchBeanDefinitionException: No bean named 'workflowId' available
  • 说明:invokeTarget 错配为 workflowId(123),应改为 "workflowScheduleJob.executeWorkflow(123L)"
  • Cron 不触发
  • 是否 6 段/7 段表达式有效?是否“日/周”冲突?项目内有自动修正,但依然建议前端正确校验
  • status 是否为暂停?是否 nextFireTime 为空?
  • 任务是否重复、被覆盖或未注册(日志查看)
  • 方法签名不匹配
  • 参数 Long 必须 L 后缀,否则按 Integer 解析导致 NoSuchMethodException

七、如何扩展“任务A做A事、任务B做B事”

  • 保持 invokeTarget 统一为 workflowScheduleJob.executeWorkflow(workflowId)
  • 在 executeWorkflow 内根据 Workflow.type / Workflow.category / Workflow.config 等执行不同策略
  • 如需复杂分发,可以引入策略模式:
  • Map<String, WorkflowExecutor> typeToExecutor,按 workflow.getType() 路由

八、验证与观测建议

  • 创建/更新工作流后,观察后台日志中“任务创建成功”与“Quartz 注册成功”的日志
  • 使用 Scheduler 查询 Trigger 的 nextFireTime 来验证未来触发点
  • 运行时打印 invokeTarget/bean/method 解析结果用于排障
  • 增加审计日志:任务生成的 WorkflowTaskPlan 记录关键字段

九、结语

这套设计用 Quartz 实现“工作流级别”的定时触发,通过 invokeTarget 将调度与业务解耦,配合 Workflow 的配置驱动,实现“同框架、异业务”的统一调度。关键在于:

  • invokeTarget 规范
  • Cron 合法性与冲突修正
  • 并发与 Misfire 策略
  • 执行入口的稳健性与幂等

所有代码

   /*** 新增工作流** @param workflow 工作流* @return 结果*/@Override@Transactionalpublic AjaxResult insertWorkflow(Workflow workflow) {//检查工作流名称是否重复String name = workflow.getName();List<Workflow> res = workflowMapper.selectWorkflowByName(name, null);if (res.size() > 0) {return AjaxResult.error("工作流名称重复,请更换工作流名称。");}workflow.setCreateTime(DateUtils.getNowDate());// 获取登录用户所属公司idLong deptId = SecurityUtils.getLoginUser().getDeptId();workflow.setDeptId(deptId);workflowMapper.insertWorkflow(workflow);Long workflowId = workflow.getId();//保存线条List<WorkflowLinks> links = workflow.getLinks();for (WorkflowLinks link : links) {link.setWorkflowId(workflowId);workflowLinksMapper.insertWorkflowLinks(link);}//保存节点List<WorkflowNodes> nodes = workflow.getNodes();for (WorkflowNodes node : nodes) {node.setWorkflowId(workflowId);workflowNodesMapper.insertWorkflowNodes(node);}// 保存站点关联关系List<Long> sites = workflow.getSiteIds();if (sites != null && !sites.isEmpty()) {for (Long site : sites) {WorkflowSiteRel workflowSiteRel = new WorkflowSiteRel();workflowSiteRel.setSiteId(site);workflowSiteRel.setWorkflowId(workflowId);workflowSiteRelMapper.insertWorkflowSiteRel(workflowSiteRel);}}// 如果配置了cron表达式,创建定时任务workflowScheduleService.createWorkflowScheduleJob(workflow);return AjaxResult.success(workflowId);}
package com.lt.project.workflow.service;import com.lt.common.constant.ScheduleConstants;
import com.lt.common.exception.job.TaskException;
import com.lt.common.utils.StringUtils;
import com.lt.common.utils.job.ScheduleUtils;
import com.lt.project.monitor.domain.SysJob;
import com.lt.project.workflow.domain.Workflow;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;/*** 工作流定时任务管理服务* 负责创建、更新、删除工作流的Quartz定时任务* * @author system*/
@Service
public class WorkflowScheduleService {private static final Logger log = LoggerFactory.getLogger(WorkflowScheduleService.class);@Autowiredprivate Scheduler scheduler;/*** 工作流任务组名*/private static final String WORKFLOW_JOB_GROUP = "WORKFLOW";/*** 创建工作流定时任务* * @param workflow 工作流对象*/public void createWorkflowScheduleJob(Workflow workflow) {// 检查是否配置了cron表达式if (StringUtils.isEmpty(workflow.getCronExpression())) {log.info("工作流ID:{} 未配置cron表达式,跳过创建定时任务", workflow.getId());return;}// 检查工作流状态,只有已发布的工作流才创建定时任务if (!"pubed".equals(workflow.getStatus())) {log.info("工作流ID:{} 状态不是已发布,跳过创建定时任务", workflow.getId());return;}try {// 构建SysJob对象SysJob job = buildSysJob(workflow);// 创建定时任务ScheduleUtils.createScheduleJob(scheduler, job);log.info("工作流定时任务创建成功,工作流ID:{},cron表达式:{}", workflow.getId(), workflow.getCronExpression());} catch (SchedulerException e) {log.error("创建工作流定时任务失败,工作流ID:{}", workflow.getId(), e);} catch (TaskException e) {log.error("创建工作流定时任务失败,工作流ID:{}", workflow.getId(), e);}}/*** 更新工作流定时任务* * @param workflow 工作流对象*/public void updateWorkflowScheduleJob(Workflow workflow) {// 先删除旧的定时任务deleteWorkflowScheduleJob(workflow.getId());// 再创建新的定时任务createWorkflowScheduleJob(workflow);}/*** 删除工作流定时任务* * @param workflowId 工作流ID*/public void deleteWorkflowScheduleJob(Long workflowId) {try {scheduler.deleteJob(ScheduleUtils.getJobKey(workflowId, WORKFLOW_JOB_GROUP));log.info("工作流定时任务删除成功,工作流ID:{}", workflowId);} catch (SchedulerException e) {log.error("删除工作流定时任务失败,工作流ID:{}", workflowId, e);}}/*** 暂停工作流定时任务* * @param workflowId 工作流ID*/public void pauseWorkflowScheduleJob(Long workflowId) {try {scheduler.pauseJob(ScheduleUtils.getJobKey(workflowId, WORKFLOW_JOB_GROUP));log.info("工作流定时任务暂停成功,工作流ID:{}", workflowId);} catch (SchedulerException e) {log.error("暂停工作流定时任务失败,工作流ID:{}", workflowId, e);}}/*** 恢复工作流定时任务* * @param workflowId 工作流ID*/public void resumeWorkflowScheduleJob(Long workflowId) {try {scheduler.resumeJob(ScheduleUtils.getJobKey(workflowId, WORKFLOW_JOB_GROUP));log.info("工作流定时任务恢复成功,工作流ID:{}", workflowId);} catch (SchedulerException e) {log.error("恢复工作流定时任务失败,工作流ID:{}", workflowId, e);}}/*** 构建SysJob对象* * @param workflow 工作流对象* @return SysJob对象*/private SysJob buildSysJob(Workflow workflow) {SysJob job = new SysJob();// 使用工作流ID作为任务IDjob.setJobId(workflow.getId());// 设置任务组名job.setJobGroup(WORKFLOW_JOB_GROUP);// 设置任务名称job.setJobName("工作流任务-" + workflow.getName());// 设置调用目标字符串:格式为 "beanName.methodName(参数)"// workflowScheduleJob 是 Spring Bean 名称// executeWorkflow 是要调用的方法// 参数需要加 L 后缀表示 Long 类型job.setInvokeTarget("workflowScheduleJob.executeWorkflow(" + workflow.getId() + "L)");// 设置cron表达式job.setCronExpression(workflow.getCronExpression());// 设置任务状态:根据工作流状态设置if ("pubed".equals(workflow.getStatus())) {job.setStatus(ScheduleConstants.Status.NORMAL.getValue());} else {job.setStatus(ScheduleConstants.Status.PAUSE.getValue());}// 设置并发执行:0=允许 1=禁止job.setConcurrent("1");// 设置错误策略:1=立即执行 2=执行一次 3=放弃执行job.setMisfirePolicy(ScheduleConstants.MISFIRE_DEFAULT);return job;}
}
package com.lt.common.utils.job;import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import com.lt.common.constant.Constants;
import com.lt.common.constant.ScheduleConstants;
import com.lt.common.exception.job.TaskException;
import com.lt.common.exception.job.TaskException.Code;
import com.lt.common.utils.StringUtils;
import com.lt.common.utils.spring.SpringUtils;
import com.lt.project.monitor.domain.SysJob;/*** 定时任务工具类* * @author ruoyi**/
public class ScheduleUtils
{/*** 得到quartz任务类** @param sysJob 执行计划* @return 具体执行任务类*/private static Class<? extends Job> getQuartzJobClass(SysJob sysJob){boolean isConcurrent = "0".equals(sysJob.getConcurrent());return isConcurrent ? QuartzJobExecution.class : QuartzDisallowConcurrentExecution.class;}/*** 构建任务触发对象*/public static TriggerKey getTriggerKey(Long jobId, String jobGroup){return TriggerKey.triggerKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup);}/*** 构建任务键对象*/public static JobKey getJobKey(Long jobId, String jobGroup){return JobKey.jobKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup);}/*** 创建定时任务*/public static void createScheduleJob(Scheduler scheduler, SysJob job) throws SchedulerException, TaskException{Class<? extends Job> jobClass = getQuartzJobClass(job);// 构建job信息Long jobId = job.getJobId();String jobGroup = job.getJobGroup();JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(getJobKey(jobId, jobGroup)).build();// 处理Cron表达式,解决day-of-week和day-of-month冲突问题String cronExpression = job.getCronExpression();if (cronExpression != null) {cronExpression = cronExpression.trim();String[] parts = cronExpression.split("\\s+");// 如果是6位表达式且第3位和第5位都是*,则替换其中一个为?if (parts.length == 6 && "*".equals(parts[2]) && "*".equals(parts[4])) {// 将day-of-week替换为?,保持每天执行的语义cronExpression = parts[0] + " " + parts[1] + " " + parts[2] + " " + parts[3] + " " + parts[4] + " ?";}}// 表达式调度构建器CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);cronScheduleBuilder = handleCronScheduleMisfirePolicy(job, cronScheduleBuilder);// 按新的cronExpression表达式构建一个新的triggerCronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(jobId, jobGroup)).withSchedule(cronScheduleBuilder).build();// 放入参数,运行时的方法可以获取jobDetail.getJobDataMap().put(ScheduleConstants.TASK_PROPERTIES, job);// 判断是否存在if (scheduler.checkExists(getJobKey(jobId, jobGroup))){// 防止创建时存在数据问题 先移除,然后在执行创建操作scheduler.deleteJob(getJobKey(jobId, jobGroup));}// 判断任务是否过期if (StringUtils.isNotNull(CronUtils.getNextExecution(job.getCronExpression()))){// 执行调度任务scheduler.scheduleJob(jobDetail, trigger);}// 暂停任务if (job.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue())){scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup));}}/*** 设置定时任务策略*/public static CronScheduleBuilder handleCronScheduleMisfirePolicy(SysJob job, CronScheduleBuilder cb)throws TaskException{switch (job.getMisfirePolicy()){case ScheduleConstants.MISFIRE_DEFAULT:return cb;case ScheduleConstants.MISFIRE_IGNORE_MISFIRES:return cb.withMisfireHandlingInstructionIgnoreMisfires();case ScheduleConstants.MISFIRE_FIRE_AND_PROCEED:return cb.withMisfireHandlingInstructionFireAndProceed();case ScheduleConstants.MISFIRE_DO_NOTHING:return cb.withMisfireHandlingInstructionDoNothing();default:throw new TaskException("The task misfire policy '" + job.getMisfirePolicy()+ "' cannot be used in cron schedule tasks", Code.CONFIG_ERROR);}}/*** 检查包名是否为白名单配置* * @param invokeTarget 目标字符串* @return 结果*/public static boolean whiteList(String invokeTarget){String packageName = StringUtils.substringBefore(invokeTarget, "(");int count = StringUtils.countMatches(packageName, ".");if (count > 1){return StringUtils.containsAnyIgnoreCase(invokeTarget, Constants.JOB_WHITELIST_STR);}Object obj = SpringUtils.getBean(StringUtils.split(invokeTarget, ".")[0]);String beanPackageName = obj.getClass().getPackage().getName();return StringUtils.containsAnyIgnoreCase(beanPackageName, Constants.JOB_WHITELIST_STR)&& !StringUtils.containsAnyIgnoreCase(beanPackageName, Constants.JOB_ERROR_STR);}
}
package com.lt.common.utils.job;import java.text.ParseException;
import java.util.Date;
import org.quartz.CronExpression;/*** cron表达式工具类* * @author ruoyi**/
public class CronUtils
{/*** 返回一个布尔值代表一个给定的Cron表达式的有效性** @param cronExpression Cron表达式* @return boolean 表达式是否有效*/public static boolean isValid(String cronExpression){return CronExpression.isValidExpression(cronExpression);}/*** 返回一个字符串值,表示该消息无效Cron表达式给出有效性** @param cronExpression Cron表达式* @return String 无效时返回表达式错误描述,如果有效返回null*/public static String getInvalidMessage(String cronExpression){try{new CronExpression(cronExpression);return null;}catch (ParseException pe){return pe.getMessage();}}/*** 返回下一个执行时间根据给定的Cron表达式** @param cronExpression Cron表达式* @return Date 下次Cron表达式执行时间*/public static Date getNextExecution(String cronExpression){try{// 处理Cron表达式,解决day-of-week和day-of-month冲突问题String processedCronExpression = cronExpression;if (processedCronExpression != null) {processedCronExpression = processedCronExpression.trim();String[] parts = processedCronExpression.split("\\s+");// 如果是6位表达式且第3位和第5位都是*,则替换其中一个为?if (parts.length == 6 && "*".equals(parts[2]) && "*".equals(parts[4])) {// 将day-of-week替换为?,保持每天执行的语义processedCronExpression = parts[0] + " " + parts[1] + " " + parts[2] + " " + parts[3] + " " + parts[4] + " ?";}}CronExpression cron = new CronExpression(processedCronExpression);return cron.getNextValidTimeAfter(new Date(System.currentTimeMillis()));}catch (ParseException e){throw new IllegalArgumentException(e.getMessage());}}
}

http://www.dtcms.com/a/540708.html

相关文章:

  • linux20 线程同步--信号量
  • 内核的文件预取逻辑及blockdev的相关配置
  • [特殊字符] Web 字体裁剪优化实践:把 42MB 字体包瘦到 1.6MB
  • 平滑过渡,破解多库并存:浙人医基于金仓KFS的医疗信创实战解析
  • 做经营性的网站需要注册什么条件网站构思
  • Answer企业社区实战:零成本搭建技术问答平台,远程协作效率提升300%!
  • “听书”比“看书”更省力?
  • 大连 手机网站案例网站定位方案
  • window安装MYSQL5.5出错:a windows service with the name MYSQL alreadyexists....
  • 珠海做网站报价影响网站排名的因素
  • 6.1.2.2 大数据方法论与实践指南-离线任务SQL 任务开发规范
  • Java 大视界 -- Java 大数据在智能交通高速公路收费系统优化与通行效率提升实战(429)
  • 网站可以做怀孕单吗平面设计图数字标识
  • 图神经网络入门:手写一个 VanillaGNN-从邻接矩阵理解图神经网络的消息传递
  • 网站模版带后台酒类招商网站大全
  • 营销型网站创建网页制作三剑客通常指
  • 【笔试真题】- 电信-2025.10.11
  • 云渲染与传统渲染:核心差异与适用场景分析
  • 什么是流程监控?如何构建跨系统BPM的实时监控体系?
  • 直通滤波....
  • eclipse做网站代码惠州市
  • 零基础新手小白快速了解掌握服务集群与自动化运维(十五)Redis模块-Redis主从复制
  • 视频网站自己怎么做的正规的大宗商品交易平台
  • vue3 实现贪吃蛇手机版01
  • 胶州网站建设dch100室内装修设计师工资一般多少钱
  • 计算机视觉、医学图像处理、深度学习、多模态融合方向分析
  • 小白入门:基于k8s搭建训练集群,实战CIFAR-10图像分类
  • 关系型数据库大王Mysql——DML语句操作示例
  • VNC安装
  • 网站建设论文 php苏州关键词排名提升