springboot集成quartz并实现定时任务管理

依赖:

<quartz.version>2.3.0</quartz.version><dependency><groupId>org.quartz-scheduler</groupId><artifactId>quartz</artifactId><version>${quartz.version}</version><exclusions><exclusion><groupId>com.mchange</groupId><artifactId>c3p0</artifactId></exclusion><exclusion><groupId>com.zaxxer</groupId><artifactId>HikariCP-java6</artifactId></exclusion></exclusions></dependency>
定时任务配置ScheduleConfig
/*** Copyright (c) 2018 人人开源 All rights reserved.** https://www.renren.io** 版权所有,侵权必究!*/package io.global.iot.modules.job.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;import javax.sql.DataSource;
import java.util.Properties;/*** 定时任务配置** @author Mark sunlightcs@gmail.com*/
@Configuration
public class ScheduleConfig {@Beanpublic SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) {SchedulerFactoryBean factory = new SchedulerFactoryBean();factory.setDataSource(dataSource);//quartz参数Properties prop = new Properties();prop.put("org.quartz.scheduler.instanceName", "GlobalScheduler");prop.put("org.quartz.scheduler.instanceId", "AUTO");//线程池配置prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");prop.put("org.quartz.threadPool.threadCount", "20");prop.put("org.quartz.threadPool.threadPriority", "5");prop.put("org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread", "true");//JobStore配置prop.put("org.quartz.jobStore.class", "org.springframework.scheduling.quartz.LocalDataSourceJobStore");//集群配置prop.put("org.quartz.jobStore.isClustered", "false");prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000");prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1");prop.put("org.quartz.jobStore.misfireThreshold", "12000");prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_");//prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?");//PostgreSQL数据库,需要打开此注释//prop.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.PostgreSQLDelegate");factory.setQuartzProperties(prop);factory.setSchedulerName("LeaseScheduler");//延时启动factory.setStartupDelay(30);factory.setApplicationContextSchedulerContextKey("applicationContextKey");//可选,QuartzScheduler 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了//factory.setOverwriteExistingJobs(false);//设置自动启动,默认为truefactory.setAutoStartup(true);factory.setWaitForJobsToCompleteOnShutdown(true);return factory;}
}
定时任务管理相关表结构:
CREATE TABLE `schedule_job` (`id` bigint NOT NULL COMMENT 'id',`bean_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'spring bean名称',`params` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '参数',`cron_expression` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'cron表达式',`status` tinyint unsigned DEFAULT NULL COMMENT '任务状态  0:暂停  1:正常',`remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注',`creator` bigint DEFAULT NULL COMMENT '创建者',`create_date` datetime DEFAULT NULL COMMENT '创建时间',`updater` bigint DEFAULT NULL COMMENT '更新者',`update_date` datetime DEFAULT NULL COMMENT '更新时间',PRIMARY KEY (`id`) USING BTREE,KEY `idx_create_date` (`create_date`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='定时任务';CREATE TABLE `schedule_job_log` (`id` bigint NOT NULL COMMENT 'id',`job_id` bigint NOT NULL COMMENT '任务id',`bean_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'spring bean名称',`params` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '参数',`status` tinyint unsigned NOT NULL COMMENT '任务状态    0:失败    1:成功',`error` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '失败信息',`times` int NOT NULL COMMENT '耗时(单位:毫秒)',`create_date` datetime DEFAULT NULL COMMENT '创建时间',PRIMARY KEY (`id`) USING BTREE,KEY `idx_job_id` (`job_id`) USING BTREE,KEY `idx_create_date` (`create_date`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='定时任务日志';
基础实体类
package io.global.iot.common.entity;import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;import java.io.Serializable;
import java.util.Date;/*** 基础实体类,所有实体都需要继承*/
@Data
public abstract class SysBaseEntity implements Serializable {/*** id*/@TableIdprivate Long id;/*** 创建者*/@TableField(fill = FieldFill.INSERT)private Long  creator;/*** 创建时间*/@TableField(fill = FieldFill.INSERT)private Date createDate;
}
Entity、DTO、Dao、Dao映射文件、Service、Service实现
/*** Copyright (c) 2018 人人开源 All rights reserved.** https://www.renren.io** 版权所有,侵权必究!*/package io.global.iot.modules.job.entity;import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import io.global.iot.common.entity.SysBaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;import java.util.Date;/*** 定时任务** @author Mark sunlightcs@gmail.com*/
@Data
@EqualsAndHashCode(callSuper=false)
@TableName("schedule_job")
public class ScheduleJobEntity extends SysBaseEntity {private static final long serialVersionUID = 1L;/*** spring bean名称*/private String beanName;/*** 参数*/private String params;/*** cron表达式*/private String cronExpression;/*** 任务状态  0:暂停  1:正常*/private Integer status;/*** 备注*/private String remark;/*** 更新者*/@TableField(fill = FieldFill.INSERT_UPDATE)private Long updater;/*** 更新时间*/@TableField(fill = FieldFill.INSERT_UPDATE)private Date updateDate;
}
/*** Copyright (c) 2018 人人开源 All rights reserved.** https://www.renren.io** 版权所有,侵权必究!*/package io.global.iot.modules.job.entity;import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;import java.io.Serializable;
import java.util.Date;/*** 定时任务日志** @author Mark sunlightcs@gmail.com*/
@Data
@TableName("schedule_job_log")
public class ScheduleJobLogEntity implements Serializable {private static final long serialVersionUID = 1L;/*** id*/@TableIdprivate Long id;/*** 任务id*/private Long jobId;/*** spring bean名称*/private String beanName;/*** 参数*/private String params;/*** 任务状态    0:失败    1:成功*/private Integer status;/*** 失败信息*/private String error;/*** 耗时(单位:毫秒)*/private Integer times;/*** 创建时间*/private Date createDate;}
/*** Copyright (c) 2018 人人开源 All rights reserved.** https://www.renren.io** 版权所有,侵权必究!*/package io.global.iot.modules.job.dto;import com.fasterxml.jackson.annotation.JsonProperty;
import io.global.iot.common.validator.group.AddGroup;
import io.global.iot.common.validator.group.DefaultGroup;
import io.global.iot.common.validator.group.UpdateGroup;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Null;
import lombok.Data;
import org.hibernate.validator.constraints.Range;
import java.io.Serializable;
import java.util.Date;/*** 定时任务** @author Mark sunlightcs@gmail.com* @since 1.0.0*/
@Data
@Schema(description = "定时任务")
public class ScheduleJobDTO implements Serializable {private static final long serialVersionUID = 1L;@Schema(description = "id")@Null(message="{id.null}", groups = AddGroup.class)@NotNull(message="{id.require}", groups = UpdateGroup.class)private Long id;@Schema(description = "spring bean名称")@NotBlank(message = "{schedule.bean.require}", groups = DefaultGroup.class)private String beanName;@Schema(description = "参数")private String params;@Schema(description = "cron表达式")@NotBlank(message = "{schedule.cron.require}", groups = DefaultGroup.class)private String cronExpression;@Schema(description = "任务状态  0:暂停  1:正常")@Range(min=0, max=1, message = "{schedule.status.range}", groups = DefaultGroup.class)private Integer status;@Schema(description = "备注")private String remark;@Schema(description = "创建时间")@JsonProperty(access = JsonProperty.Access.READ_ONLY)private Date createDate;}
/*** Copyright (c) 2018 人人开源 All rights reserved.** https://www.renren.io** 版权所有,侵权必究!*/package io.global.iot.modules.job.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;import java.io.Serializable;
import java.util.Date;/*** 定时任务日志** @author Mark sunlightcs@gmail.com* @since 1.0.0*/
@Data
@Schema(description = "定时任务日志")
public class ScheduleJobLogDTO implements Serializable {private static final long serialVersionUID = 1L;@Schema(description = "id")private Long id;@Schema(description = "任务id")private Long jobId;@Schema(description = "spring bean名称")private String beanName;@Schema(description = "参数")private String params;@Schema(description = "任务状态    0:失败    1:成功")private Integer status;@Schema(description = "失败信息")private String error;@Schema(description = "耗时(单位:毫秒)")private Integer times;@Schema(description = "创建时间")private Date createDate;}
/*** Copyright (c) 2018 人人开源 All rights reserved.** https://www.renren.io** 版权所有,侵权必究!*/package io.global.iot.modules.job.dao;import io.global.iot.common.dao.BaseDao;
import io.global.iot.modules.job.entity.ScheduleJobEntity;
import org.apache.ibatis.annotations.Mapper;import java.util.Map;/*** 定时任务** @author Mark sunlightcs@gmail.com*/
@Mapper
public interface ScheduleJobDao extends BaseDao<ScheduleJobEntity> {/*** 批量更新状态*/int updateBatch(Map<String, Object> map);
}
/*** Copyright (c) 2018 人人开源 All rights reserved.** https://www.renren.io** 版权所有,侵权必究!*/package io.global.iot.modules.job.dao;
import io.global.iot.common.dao.BaseDao;
import io.global.iot.modules.job.entity.ScheduleJobLogEntity;
import org.apache.ibatis.annotations.Mapper;/*** 定时任务日志** @author Mark sunlightcs@gmail.com*/
@Mapper
public interface ScheduleJobLogDao extends BaseDao<ScheduleJobLogEntity> {}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="io.global.iot.modules.job.dao.ScheduleJobDao"><!-- 批量更新状态 --><update id="updateBatch">update schedule_job set status = #{status} where id in<foreach item="id" collection="ids"  open="(" separator="," close=")">#{id}</foreach></update></mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="io.global.iot.modules.job.dao.ScheduleJobLogDao"></mapper>
/*** Copyright (c) 2018 人人开源 All rights reserved.** https://www.renren.io** 版权所有,侵权必究!*/package io.global.iot.modules.job.service;import io.global.iot.common.page.PageData;
import io.global.iot.common.service.BaseService;
import io.global.iot.modules.job.dto.ScheduleJobDTO;
import io.global.iot.modules.job.entity.ScheduleJobEntity;import java.util.Map;/*** 定时任务** @author Mark sunlightcs@gmail.com*/
public interface ScheduleJobService extends BaseService<ScheduleJobEntity> {PageData<ScheduleJobDTO> page(Map<String, Object> params);ScheduleJobDTO get(Long id);/*** 保存定时任务*/void save(ScheduleJobDTO dto);/*** 更新定时任务*/void update(ScheduleJobDTO dto);/*** 批量删除定时任务*/void deleteBatch(Long[] ids);/*** 批量更新定时任务状态*/int updateBatch(Long[] ids, int status);/*** 立即执行*/void run(Long[] ids);/*** 暂停运行*/void pause(Long[] ids);/*** 恢复运行*/void resume(Long[] ids);
}
/*** Copyright (c) 2018 人人开源 All rights reserved.** https://www.renren.io** 版权所有,侵权必究!*/package io.global.iot.modules.job.service;import io.global.iot.common.page.PageData;
import io.global.iot.common.service.BaseService;
import io.global.iot.modules.job.dto.ScheduleJobLogDTO;
import io.global.iot.modules.job.entity.ScheduleJobLogEntity;import java.util.Map;/*** 定时任务日志** @author Mark sunlightcs@gmail.com*/
public interface ScheduleJobLogService extends BaseService<ScheduleJobLogEntity> {PageData<ScheduleJobLogDTO> page(Map<String, Object> params);ScheduleJobLogDTO get(Long id);
}
/*** Copyright (c) 2018 人人开源 All rights reserved.** https://www.renren.io** 版权所有,侵权必究!*/package io.global.iot.modules.job.service.impl;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.global.iot.common.constant.Constant;
import io.global.iot.common.page.PageData;
import io.global.iot.common.service.impl.BaseServiceImpl;
import io.global.iot.common.utils.ConvertUtils;
import io.global.iot.modules.job.dao.ScheduleJobDao;
import io.global.iot.modules.job.dto.ScheduleJobDTO;
import io.global.iot.modules.job.entity.ScheduleJobEntity;
import io.global.iot.modules.job.service.ScheduleJobService;
import io.global.iot.modules.job.utils.ScheduleUtils;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.quartz.Scheduler;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;@Service
public class ScheduleJobServiceImpl extends BaseServiceImpl<ScheduleJobDao, ScheduleJobEntity> implements ScheduleJobService {@Resourceprivate Scheduler scheduler;@Overridepublic PageData<ScheduleJobDTO> page(Map<String, Object> params) {IPage<ScheduleJobEntity> page = baseDao.selectPage(getPage(params, Constant.CREATE_DATE, false),getWrapper(params));return getPageData(page, ScheduleJobDTO.class);}@Overridepublic ScheduleJobDTO get(Long id) {ScheduleJobEntity entity = baseDao.selectById(id);return ConvertUtils.sourceToTarget(entity, ScheduleJobDTO.class);}private QueryWrapper<ScheduleJobEntity> getWrapper(Map<String, Object> params){String beanName = (String)params.get("beanName");QueryWrapper<ScheduleJobEntity> wrapper = new QueryWrapper<>();wrapper.like(StringUtils.isNotBlank(beanName), "bean_name", beanName);return wrapper;}@Override@Transactional(rollbackFor = Exception.class)public void save(ScheduleJobDTO dto) {ScheduleJobEntity entity = ConvertUtils.sourceToTarget(dto, ScheduleJobEntity.class);entity.setStatus(Constant.ScheduleStatus.NORMAL.getValue());this.insert(entity);ScheduleUtils.createScheduleJob(scheduler, entity);}@Override@Transactional(rollbackFor = Exception.class)public void update(ScheduleJobDTO dto) {ScheduleJobEntity entity = ConvertUtils.sourceToTarget(dto, ScheduleJobEntity.class);ScheduleUtils.updateScheduleJob(scheduler, entity);this.updateById(entity);}@Override@Transactional(rollbackFor = Exception.class)public void deleteBatch(Long[] ids) {for(Long id : ids){ScheduleUtils.deleteScheduleJob(scheduler, id);}//删除数据this.deleteBatchIds(Arrays.asList(ids));}@Overridepublic int updateBatch(Long[] ids, int status){Map<String, Object> map = new HashMap<>(2);map.put("ids", ids);map.put("status", status);return baseDao.updateBatch(map);}@Override@Transactional(rollbackFor = Exception.class)public void run(Long[] ids) {for(Long id : ids){ScheduleUtils.run(scheduler, this.selectById(id));}}@Override@Transactional(rollbackFor = Exception.class)public void pause(Long[] ids) {for(Long id : ids){ScheduleUtils.pauseJob(scheduler, id);}updateBatch(ids, Constant.ScheduleStatus.PAUSE.getValue());}@Override@Transactional(rollbackFor = Exception.class)public void resume(Long[] ids) {for(Long id : ids){ScheduleUtils.resumeJob(scheduler, id);}updateBatch(ids, Constant.ScheduleStatus.NORMAL.getValue());}}
/*** Copyright (c) 2018 人人开源 All rights reserved.** https://www.renren.io** 版权所有,侵权必究!*/package io.global.iot.modules.job.service.impl;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.global.iot.common.constant.Constant;
import io.global.iot.common.page.PageData;
import io.global.iot.common.service.impl.BaseServiceImpl;
import io.global.iot.common.utils.ConvertUtils;
import io.global.iot.modules.job.dao.ScheduleJobLogDao;
import io.global.iot.modules.job.dto.ScheduleJobLogDTO;
import io.global.iot.modules.job.entity.ScheduleJobLogEntity;
import io.global.iot.modules.job.service.ScheduleJobLogService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;import java.util.Map;@Service
public class ScheduleJobLogServiceImpl extends BaseServiceImpl<ScheduleJobLogDao, ScheduleJobLogEntity> implements ScheduleJobLogService {@Overridepublic PageData<ScheduleJobLogDTO> page(Map<String, Object> params) {IPage<ScheduleJobLogEntity> page = baseDao.selectPage(getPage(params, Constant.CREATE_DATE, false),getWrapper(params));return getPageData(page, ScheduleJobLogDTO.class);}private QueryWrapper<ScheduleJobLogEntity> getWrapper(Map<String, Object> params){String jobId = (String)params.get("jobId");QueryWrapper<ScheduleJobLogEntity> wrapper = new QueryWrapper<>();wrapper.eq(StringUtils.isNotBlank(jobId), "job_id", jobId);return wrapper;}@Overridepublic ScheduleJobLogDTO get(Long id) {ScheduleJobLogEntity entity = baseDao.selectById(id);return ConvertUtils.sourceToTarget(entity, ScheduleJobLogDTO.class);}}

工具类:

/*** Copyright (c) 2018 人人开源 All rights reserved.** https://www.renren.io** 版权所有,侵权必究!*/package io.global.iot.modules.job.utils;import io.global.iot.common.constant.Constant;
import io.global.iot.common.exception.ExceptionUtils;
import io.global.iot.common.utils.SpringContextUtils;
import io.global.iot.modules.job.entity.ScheduleJobEntity;
import io.global.iot.modules.job.entity.ScheduleJobLogEntity;
import io.global.iot.modules.job.service.ScheduleJobLogService;
import org.quartz.JobExecutionContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.quartz.QuartzJobBean;import java.lang.reflect.Method;
import java.util.Date;/*** 定时任务** @author Mark sunlightcs@gmail.com*/
public class ScheduleJob extends QuartzJobBean {private Logger logger = LoggerFactory.getLogger(getClass());@Overrideprotected void executeInternal(JobExecutionContext context) {ScheduleJobEntity scheduleJob = (ScheduleJobEntity) context.getMergedJobDataMap().get(ScheduleUtils.JOB_PARAM_KEY);//数据库保存执行记录ScheduleJobLogEntity log = new ScheduleJobLogEntity();log.setJobId(scheduleJob.getId());log.setBeanName(scheduleJob.getBeanName());log.setParams(scheduleJob.getParams());log.setCreateDate(new Date());//任务开始时间long startTime = System.currentTimeMillis();try {//执行任务logger.info("任务准备执行,任务ID:{}", scheduleJob.getId());Object target = SpringContextUtils.getBean(scheduleJob.getBeanName());Method method = target.getClass().getDeclaredMethod("run", String.class);method.invoke(target, scheduleJob.getParams());//任务执行总时长long times = System.currentTimeMillis() - startTime;log.setTimes((int)times);//任务状态log.setStatus(Constant.SUCCESS);logger.info("任务执行完毕,任务ID:{}  总共耗时:{} 毫秒", scheduleJob.getId(), times);} catch (Exception e) {logger.error("任务执行失败,任务ID:{}", scheduleJob.getId(), e);//任务执行总时长long times = System.currentTimeMillis() - startTime;log.setTimes((int)times);//任务状态log.setStatus(Constant.FAIL);log.setError(ExceptionUtils.getErrorStackTrace(e));}finally {//获取spring beanScheduleJobLogService scheduleJobLogService = SpringContextUtils.getBean(ScheduleJobLogService.class);scheduleJobLogService.insert(log);}}
}
/*** Copyright (c) 2018 人人开源 All rights reserved.** https://www.renren.io** 版权所有,侵权必究!*/package io.global.iot.modules.job.utils;import io.global.iot.common.constant.Constant;
import io.global.iot.common.exception.CommonException;
import io.global.iot.common.exception.ErrorCode;
import io.global.iot.modules.job.entity.ScheduleJobEntity;
import org.quartz.*;/*** 定时任务工具类** @author Mark sunlightcs@gmail.com*/
public class ScheduleUtils {private final static String JOB_NAME = "TASK_";/*** 任务调度参数key*/public static final String JOB_PARAM_KEY = "JOB_PARAM_KEY";/*** 获取触发器key*/public static TriggerKey getTriggerKey(Long jobId) {return TriggerKey.triggerKey(JOB_NAME + jobId);}/*** 获取jobKey*/public static JobKey getJobKey(Long jobId) {return JobKey.jobKey(JOB_NAME + jobId);}/*** 获取表达式触发器*/public static CronTrigger getCronTrigger(Scheduler scheduler, Long jobId) {try {return (CronTrigger) scheduler.getTrigger(getTriggerKey(jobId));} catch (SchedulerException e) {throw new CommonException(ErrorCode.JOB_ERROR, e);}}/*** 创建定时任务*/public static void createScheduleJob(Scheduler scheduler, ScheduleJobEntity scheduleJob) {try {//构建job信息JobDetail jobDetail = JobBuilder.newJob(ScheduleJob.class).withIdentity(getJobKey(scheduleJob.getId())).build();//表达式调度构建器CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression()).withMisfireHandlingInstructionDoNothing();//按新的cronExpression表达式构建一个新的triggerCronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(scheduleJob.getId())).withSchedule(scheduleBuilder).build();//放入参数,运行时的方法可以获取jobDetail.getJobDataMap().put(JOB_PARAM_KEY, scheduleJob);scheduler.scheduleJob(jobDetail, trigger);//暂停任务if(scheduleJob.getStatus() == Constant.ScheduleStatus.PAUSE.getValue()){pauseJob(scheduler, scheduleJob.getId());}} catch (SchedulerException e) {throw new CommonException(ErrorCode.JOB_ERROR, e);}}/*** 更新定时任务*/public static void updateScheduleJob(Scheduler scheduler, ScheduleJobEntity scheduleJob) {try {TriggerKey triggerKey = getTriggerKey(scheduleJob.getId());//表达式调度构建器CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression()).withMisfireHandlingInstructionDoNothing();CronTrigger trigger = getCronTrigger(scheduler, scheduleJob.getId());//按新的cronExpression表达式重新构建triggertrigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();//参数trigger.getJobDataMap().put(JOB_PARAM_KEY, scheduleJob);scheduler.rescheduleJob(triggerKey, trigger);//暂停任务if(scheduleJob.getStatus() == Constant.ScheduleStatus.PAUSE.getValue()){pauseJob(scheduler, scheduleJob.getId());}} catch (SchedulerException e) {throw new CommonException(ErrorCode.JOB_ERROR, e);}}/*** 立即执行任务*/public static void run(Scheduler scheduler, ScheduleJobEntity scheduleJob) {try {//参数JobDataMap dataMap = new JobDataMap();dataMap.put(JOB_PARAM_KEY, scheduleJob);scheduler.triggerJob(getJobKey(scheduleJob.getId()), dataMap);} catch (SchedulerException e) {throw new CommonException(ErrorCode.JOB_ERROR, e);}}/*** 暂停任务*/public static void pauseJob(Scheduler scheduler, Long jobId) {try {scheduler.pauseJob(getJobKey(jobId));} catch (SchedulerException e) {throw new CommonException(ErrorCode.JOB_ERROR, e);}}/*** 恢复任务*/public static void resumeJob(Scheduler scheduler, Long jobId) {try {scheduler.resumeJob(getJobKey(jobId));} catch (SchedulerException e) {throw new CommonException(ErrorCode.JOB_ERROR, e);}}/*** 删除定时任务*/public static void deleteScheduleJob(Scheduler scheduler, Long jobId) {try {scheduler.deleteJob(getJobKey(jobId));} catch (SchedulerException e) {throw new CommonException(ErrorCode.JOB_ERROR, e);}}
}
初始化定时任务数据:
/*** Copyright (c) 2018 人人开源 All rights reserved.** https://www.renren.io** 版权所有,侵权必究!*/package io.global.iot.modules.job.init;import io.global.iot.modules.job.dao.ScheduleJobDao;
import io.global.iot.modules.job.entity.ScheduleJobEntity;
import io.global.iot.modules.job.utils.ScheduleUtils;
import jakarta.annotation.Resource;
import org.quartz.CronTrigger;
import org.quartz.Scheduler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;import java.util.List;/*** 初始化定时任务数据** @author Mark sunlightcs@gmail.com*/
@Component
public class JobCommandLineRunner implements CommandLineRunner {@Autowiredprivate Scheduler scheduler;@Resourceprivate ScheduleJobDao scheduleJobDao;@Overridepublic void run(String... args) {List<ScheduleJobEntity> scheduleJobList = scheduleJobDao.selectList(null);for(ScheduleJobEntity scheduleJob : scheduleJobList){CronTrigger cronTrigger = ScheduleUtils.getCronTrigger(scheduler, scheduleJob.getId());//如果不存在,则创建if(cronTrigger == null) {ScheduleUtils.createScheduleJob(scheduler, scheduleJob);}else {ScheduleUtils.updateScheduleJob(scheduler, scheduleJob);}}}
}

测试定时任务:

package io.global.iot.modules.job.task;import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;/*** @author wanglu* @date 2021/10/26 10:14*/
@Slf4j
@Component("cardStatusChangeTask")
public class CardStatusChangeTask implements ITask {@Overridepublic void run(String params) {log.info("satTask params: {}", params);long startTime = System.currentTimeMillis();log.info("satTask定时任务正在执行 speedTime: {}", System.currentTimeMillis() - startTime);}
}
INSERT INTO `global_iot`.`schedule_job`(`id`, `bean_name`, `params`, `cron_expression`, `status`, `remark`, `creator`, `create_date`, `updater`, `update_date`) VALUES (1481537149966696450, 'cardStatusChangeTask', 'cardStatusChange', '*/10 * * * * ?', 1, '卡片状态变更', 1067246875800000001, '2023-09-25 14:28:54', 1067246875800000001, '2023-09-25 14:28:54');

quartz自带表结构

--  quartz自带表结构
CREATE TABLE QRTZ_JOB_DETAILS(SCHED_NAME VARCHAR(120) NOT NULL,JOB_NAME VARCHAR(200) NOT NULL,JOB_GROUP VARCHAR(200) NOT NULL,DESCRIPTION VARCHAR(250) NULL,JOB_CLASS_NAME VARCHAR(250) NOT NULL,IS_DURABLE VARCHAR(1) NOT NULL,IS_NONCONCURRENT VARCHAR(1) NOT NULL,IS_UPDATE_DATA VARCHAR(1) NOT NULL,REQUESTS_RECOVERY VARCHAR(1) NOT NULL,JOB_DATA BLOB NULL,PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP))ENGINE=InnoDB DEFAULT CHARSET=utf8;CREATE TABLE QRTZ_TRIGGERS (SCHED_NAME VARCHAR(120) NOT NULL,TRIGGER_NAME VARCHAR(200) NOT NULL,TRIGGER_GROUP VARCHAR(200) NOT NULL,JOB_NAME VARCHAR(200) NOT NULL,JOB_GROUP VARCHAR(200) NOT NULL,DESCRIPTION VARCHAR(250) NULL,NEXT_FIRE_TIME BIGINT(13) NULL,PREV_FIRE_TIME BIGINT(13) NULL,PRIORITY INTEGER NULL,TRIGGER_STATE VARCHAR(16) NOT NULL,TRIGGER_TYPE VARCHAR(8) NOT NULL,START_TIME BIGINT(13) NOT NULL,END_TIME BIGINT(13) NULL,CALENDAR_NAME VARCHAR(200) NULL,MISFIRE_INSTR SMALLINT(2) NULL,JOB_DATA BLOB NULL,PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP))ENGINE=InnoDB DEFAULT CHARSET=utf8;CREATE TABLE QRTZ_SIMPLE_TRIGGERS (SCHED_NAME VARCHAR(120) NOT NULL,TRIGGER_NAME VARCHAR(200) NOT NULL,TRIGGER_GROUP VARCHAR(200) NOT NULL,REPEAT_COUNT BIGINT(7) NOT NULL,REPEAT_INTERVAL BIGINT(12) NOT NULL,TIMES_TRIGGERED BIGINT(10) NOT NULL,PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))ENGINE=InnoDB DEFAULT CHARSET=utf8;CREATE TABLE QRTZ_CRON_TRIGGERS (SCHED_NAME VARCHAR(120) NOT NULL,TRIGGER_NAME VARCHAR(200) NOT NULL,TRIGGER_GROUP VARCHAR(200) NOT NULL,CRON_EXPRESSION VARCHAR(120) NOT NULL,TIME_ZONE_ID VARCHAR(80),PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))ENGINE=InnoDB DEFAULT CHARSET=utf8;CREATE TABLE QRTZ_SIMPROP_TRIGGERS
(SCHED_NAME VARCHAR(120) NOT NULL,TRIGGER_NAME VARCHAR(200) NOT NULL,TRIGGER_GROUP VARCHAR(200) NOT NULL,STR_PROP_1 VARCHAR(512) NULL,STR_PROP_2 VARCHAR(512) NULL,STR_PROP_3 VARCHAR(512) NULL,INT_PROP_1 INT NULL,INT_PROP_2 INT NULL,LONG_PROP_1 BIGINT NULL,LONG_PROP_2 BIGINT NULL,DEC_PROP_1 NUMERIC(13,4) NULL,DEC_PROP_2 NUMERIC(13,4) NULL,BOOL_PROP_1 VARCHAR(1) NULL,BOOL_PROP_2 VARCHAR(1) NULL,PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))ENGINE=InnoDB DEFAULT CHARSET=utf8;CREATE TABLE QRTZ_BLOB_TRIGGERS (SCHED_NAME VARCHAR(120) NOT NULL,TRIGGER_NAME VARCHAR(200) NOT NULL,TRIGGER_GROUP VARCHAR(200) NOT NULL,BLOB_DATA BLOB NULL,PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),INDEX (SCHED_NAME,TRIGGER_NAME, TRIGGER_GROUP),FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))ENGINE=InnoDB DEFAULT CHARSET=utf8;CREATE TABLE QRTZ_CALENDARS (SCHED_NAME VARCHAR(120) NOT NULL,CALENDAR_NAME VARCHAR(200) NOT NULL,CALENDAR BLOB NOT NULL,PRIMARY KEY (SCHED_NAME,CALENDAR_NAME))ENGINE=InnoDB DEFAULT CHARSET=utf8;CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS (SCHED_NAME VARCHAR(120) NOT NULL,TRIGGER_GROUP VARCHAR(200) NOT NULL,PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP))ENGINE=InnoDB DEFAULT CHARSET=utf8;CREATE TABLE QRTZ_FIRED_TRIGGERS (SCHED_NAME VARCHAR(120) NOT NULL,ENTRY_ID VARCHAR(95) NOT NULL,TRIGGER_NAME VARCHAR(200) NOT NULL,TRIGGER_GROUP VARCHAR(200) NOT NULL,INSTANCE_NAME VARCHAR(200) NOT NULL,FIRED_TIME BIGINT(13) NOT NULL,SCHED_TIME BIGINT(13) NOT NULL,PRIORITY INTEGER NOT NULL,STATE VARCHAR(16) NOT NULL,JOB_NAME VARCHAR(200) NULL,JOB_GROUP VARCHAR(200) NULL,IS_NONCONCURRENT VARCHAR(1) NULL,REQUESTS_RECOVERY VARCHAR(1) NULL,PRIMARY KEY (SCHED_NAME,ENTRY_ID))ENGINE=InnoDB DEFAULT CHARSET=utf8;CREATE TABLE QRTZ_SCHEDULER_STATE (SCHED_NAME VARCHAR(120) NOT NULL,INSTANCE_NAME VARCHAR(200) NOT NULL,LAST_CHECKIN_TIME BIGINT(13) NOT NULL,CHECKIN_INTERVAL BIGINT(13) NOT NULL,PRIMARY KEY (SCHED_NAME,INSTANCE_NAME))ENGINE=InnoDB DEFAULT CHARSET=utf8;CREATE TABLE QRTZ_LOCKS (SCHED_NAME VARCHAR(120) NOT NULL,LOCK_NAME VARCHAR(40) NOT NULL,PRIMARY KEY (SCHED_NAME,LOCK_NAME))ENGINE=InnoDB DEFAULT CHARSET=utf8;CREATE INDEX IDX_QRTZ_J_REQ_RECOVERY ON QRTZ_JOB_DETAILS(SCHED_NAME,REQUESTS_RECOVERY);
CREATE INDEX IDX_QRTZ_J_GRP ON QRTZ_JOB_DETAILS(SCHED_NAME,JOB_GROUP);CREATE INDEX IDX_QRTZ_T_J ON QRTZ_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_JG ON QRTZ_TRIGGERS(SCHED_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_C ON QRTZ_TRIGGERS(SCHED_NAME,CALENDAR_NAME);
CREATE INDEX IDX_QRTZ_T_G ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);
CREATE INDEX IDX_QRTZ_T_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_N_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_N_G_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_NEXT_FIRE_TIME ON QRTZ_TRIGGERS(SCHED_NAME,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_ST ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE);CREATE INDEX IDX_QRTZ_FT_TRIG_INST_NAME ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME);
CREATE INDEX IDX_QRTZ_FT_INST_JOB_REQ_RCVRY ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY);
CREATE INDEX IDX_QRTZ_FT_J_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_FT_JG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_FT_T_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP);
CREATE INDEX IDX_QRTZ_FT_TG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);

项目结构:

测试结果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/88399.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

“的修“报修工单管理系统有哪些功能和作用?

“的修"报修工单管理系统是基于微信平台的&#xff0c;无需下载和安装&#xff0c;只需扫码即可使用。它是一个维保过程管控的理想解决方案&#xff0c;适用于企业、学校、医院、物业等单位需要设备维保的场景。“的修"报修小程序具有独立部署和可控的数据安全性&…

手把手教你实现:将后端SpringBoot项目部署到华为云服务器上

前言 前提&#xff1a;有一个后端项目&#xff0c;项目能够运行在本地&#xff0c;可以通过本地访问&#xff08;localhost&#xff09; 如果没有可以看这篇&#xff1a;一个基于SpringBoot的后端项目 注册华为云账号 华为云官网 购买云服务器 产品 -> 华为云耀云服务器…

【数据结构-树】哈夫曼树

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kuan 的首页,持续学…

图片编辑小程序源码/拼图小程序源码

图片编辑小程序源码&#xff0c;拼图小程序源码。全能、便捷的图片编辑工具。实现了图片裁剪、添加文字、涂鸦、拼长图、拼相框等图片编辑功能&#xff0c;另外还有一个简易的表情包制作功能。 主要有以下几个功能&#xff1a;图片裁剪、添加文字、涂鸦功能、拼长图、拼相框、表…

mybati缓存了解

title: “mybati缓存了解” createTime: 2021-12-08T12:19:5708:00 updateTime: 2021-12-08T12:19:5708:00 draft: false author: “ggball” tags: [“mybatis”] categories: [“java”] description: “mybati缓存了解” mybatis的缓存 首先来看下mybatis对缓存的规范&…

Ingress Controller

什么是 Ingress Controller &#xff1f; 在云原生生态中&#xff0c;通常来讲&#xff0c;入口控制器( Ingress Controller )是 Kubernetes 中的一个关键组件&#xff0c;用于管理入口资源对象。 Ingress 资源对象用于定义来自外网的 HTTP 和 HTTPS 规则&#xff0c;以控制进…

el-image 和 el-table冲突层级冲突问题

其中原理&#xff0c;很多博客已经所过了&#xff0c;table组件中使用图片&#xff0c;会出现层级过低问题&#xff0c; 网上大部分解决方式是 使用穿透 // 单元格样式 ::v-deep(.el-table__cell) {position: static !important; }我在此不推荐这种解决方式&#xff0c;原因&a…

Leetcode228. 汇总区间

力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 给定一个 无重复元素 的 有序 整数数组 nums 。 返回 恰好覆盖数组中所有数字 的 最小有序 区间范围列表 。也就是说&#xff0c;nums 的每个元素都恰好被某个区间范围所覆盖&#xff0c;并且不存在属…

二、VXLAN BGP EVPN基本原理

VXLAN BGP EVPN基本原理 1、BGP EVPN2、BGP EVPN路由2.1、Type2路由——MAC/IP路由2.2、Type3路由——Inclusive Multicast路由2.3、Type5路由——Inclusive Multicast路由 ————————————————————————————————————————————————…

聚焦云原生安全|如何为5G边缘云和工业互联网应用筑牢安全防线

9月22日&#xff0c;2023年中国信息通信业发展高层论坛5G工业互联网分论坛在北京顺利举办。 作为国内云原生安全领导厂商&#xff0c;安全狗受邀出席此次活动。 据悉&#xff0c;中国信息通信业发展高层论坛是致力于研究信息通信业发展新问题、新趋势&#xff0c;推动信息通信…

uniapp项目实践总结(二十三)网页和小程序应用打包教程

导语&#xff1a;当你的应用程序开发完成后&#xff0c;在发布到互联网之前&#xff0c;需要进行打包操作&#xff0c;包括网页端、小程序端的打包。 目录 准备工作网页打包小程序打包 准备工作 在打包之前&#xff0c;请保证你的 uniapp 应用程序编译到网页、小程序是可以正…

标准化、逻辑回归、随机梯度参数估计

机器学习入门 数据预处理&#xff1a; 将&#xff1f;替换为缺失值 data data.replace(to_replace"?",valuenp.nan)丢掉缺失值 data.dropna(how"any) #howall删除全是缺失值的行和列 #haowany删除有缺失值的行和列将数据集划分成测试集和训练集 data[colu…

tensor数学运算

运算函数加add减sub乘mul除div矩阵相乘matmul次方pow平方根及其倒数sqrt 和 rsqrt向下/向上取整floor / ceil分离出整数/小数trunc / frac近似解四舍五入round裁剪clamp 1、矩阵元素的加减乘除 注意是矩阵间对应位置元素进行加减乘除 add 和 a torch.rand(3,4) b torch.…

ceph分布式存储部署

一、概述 是一个统一的分布式存储系统&#xff0c;设计初衷是提供较好的性能、可靠性和可扩展性。 特点 1、统一存储 虽然 ceph 底层是一个分布式文件系统&#xff0c;但由于在上层开发了支持对象和块的接口。所以在开源存储软件中&#xff0c;能够一统江湖。至于能不能千秋万…

面试打底稿④ 专业技能的第四部分

简历原文 抽查部分 了解Python的使用&#xff08;第一篇关于Python升级版本bug解决的文章斩获6W阅读&#xff09;&#xff0c;用python实现了几篇图像信息隐藏领 域论文的复现&#xff08;博客中有提及&#xff09;&#xff1b; 了解Django基本框架&#xff0c;写过Django框架的…

【深度学习实验】卷积神经网络(二):自定义简单的二维卷积神经网络

目录 一、实验介绍 二、实验环境 1. 配置虚拟环境 2. 库版本介绍 三、实验内容 0. 导入必要的工具包 1. 二维互相关运算&#xff08;corr2d&#xff09; 2. 二维卷积层类&#xff08;Conv2D&#xff09; a. __init__&#xff08;初始化&#xff09; b. forward(前向传…

第十四届蓝桥杯大赛软件赛决赛 C/C++ 大学 B 组 试题 B: 双子数

[蓝桥杯 2023 国 B] 双子数 试题 B: 双子数 【问题描述】 若一个正整数 x x x 可以被表示为 p 2 q 2 p^2 \times q^2 p2q2&#xff0c;其中 p p p、 q q q 为质数且 p ≠ q p \neq q pq&#xff0c;则 x x x 是 一个 “双子数”。请计算区间 [ 2333 , 233333333333…

千兆光模块和万兆光模块的差别是什么?

千兆光模块和万兆光模块是目前使用最广泛的光模块之一&#xff0c;它们之间有什么差别呢&#xff1f;下面从传输速率、光纤类型、距离等多个方面详细分析千兆光模块和万兆光模块的差别。 一、传输速率 千兆光模块的传输速率为1.25Gbps&#xff0c;而万兆光模块的传输速率为10…

记录一下 malloc 是如何分配内存的

系统深入学习笔记-malloc 以 32 位系统为例&#xff0c;&#xff0c;通过这张图你可以看到&#xff0c;用户空间内存从低到高分别是 6 种不同的内存段&#xff1a; 代码段&#xff0c;包括二进制可执行代码&#xff1b;数据段&#xff0c;包括已初始化的静态常量和全局变量B…

燃气安全如何保障?万宾燃气管网监测系统时刻感知管网运行态势

近年来随着我国城镇化建设的加快&#xff0c;燃气已经成为每个家庭的必需品。然而&#xff0c;每年夏季频繁发生的燃气爆炸事故&#xff0c;已经严重危害人民生命财产安全危害社会公共安全和公共利益。为了保障燃气安全运行&#xff0c;近日&#xff0c;许多城市都在大力推进燃…