diff --git a/README.md b/README.md
index 7bfd3ab..9610eb9 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,6 @@
## 项目说明
- FastBoot是采用SpringBoot、SpringSecurity、Mybatis-Plus等框架,开发的一套SpringBoot快速开发系统,使用门槛极低,且采用MIT开源协议,完全免费开源,可免费用于**商业项目**等场景。
+- 采用组件模式,扩展不同的业务功能,可以很方便的实现各种业务需求,且不会导致系统臃肿,若想使用某个组件,按需引入即可,反之亦然。
- 开发文档:[https://maku.net/docs/fast-boot](https://maku.net/docs/fast-boot)
- 演示环境:https://demo.maku.net/fast-boot
diff --git a/db/mysql.sql b/db/mysql/fast-boot.sql
similarity index 100%
rename from db/mysql.sql
rename to db/mysql/fast-boot.sql
diff --git a/db/mysql/module/fast-boot-quartz.sql b/db/mysql/module/fast-boot-quartz.sql
new file mode 100644
index 0000000..8d56961
--- /dev/null
+++ b/db/mysql/module/fast-boot-quartz.sql
@@ -0,0 +1,216 @@
+INSERT INTO sys_menu (pid, name, url, authority, type, open_style, icon, sort, version, deleted, creator, create_time, updater, update_time) VALUES (1, '定时任务', 'quartz/schedule/index', NULL, 0, 0, 'icon-reloadtime', 0, 0, 0, 10000, now(), 10000, now());
+
+SET @menuId = @@identity;
+INSERT INTO sys_menu (pid, name, url, authority, type, open_style, icon, sort, version, deleted, creator, create_time, updater, update_time) VALUES ((SELECT @menuId), '查看', '', 'schedule:page', 1, 0, '', 0, 0, 0, 10000, now(), 10000, now());
+INSERT INTO sys_menu (pid, name, url, authority, type, open_style, icon, sort, version, deleted, creator, create_time, updater, update_time) VALUES ((SELECT @menuId), '新增', '', 'schedule:save', 1, 0, '', 1, 0, 0, 10000, now(), 10000, now());
+INSERT INTO sys_menu (pid, name, url, authority, type, open_style, icon, sort, version, deleted, creator, create_time, updater, update_time) VALUES ((SELECT @menuId), '修改', '', 'schedule:update,schedule:info', 1, 0, '', 2, 0, 0, 10000, now(), 10000, now());
+INSERT INTO sys_menu (pid, name, url, authority, type, open_style, icon, sort, version, deleted, creator, create_time, updater, update_time) VALUES ((SELECT @menuId), '删除', '', 'schedule:delete', 1, 0, '', 3, 0, 0, 10000, now(), 10000, now());
+INSERT INTO sys_menu (pid, name, url, authority, type, open_style, icon, sort, version, deleted, creator, create_time, updater, update_time) VALUES ((SELECT @menuId), '立即运行', '', 'schedule:run', 1, 0, '', 2, 0, 0, 10000, now(), 10000, now());
+INSERT INTO sys_menu (pid, name, url, authority, type, open_style, icon, sort, version, deleted, creator, create_time, updater, update_time) VALUES ((SELECT @menuId), '日志', '', 'schedule:log', 1, 0, '', 4, 0, 0, 10000, now(), 10000, now());
+
+INSERT INTO sys_dict_type (dict_type, dict_name, remark, sort, version, deleted, creator, create_time, updater, update_time) VALUES ('schedule_group', '定时任务', '任务组名', 0, 0, 0, 10000, now(), 10000, now());
+
+SET @typeId = @@identity;
+INSERT INTO sys_dict_data (dict_type_id, dict_label, dict_value, remark, sort, version, deleted, creator, create_time, updater, update_time) VALUES ((SELECT @typeId), '默认', 'default', '', 0, 0, 0, 10000, now(), 10000, now());
+INSERT INTO sys_dict_data (dict_type_id, dict_label, dict_value, remark, sort, version, deleted, creator, create_time, updater, update_time) VALUES ((SELECT @typeId), '系统', 'system', '', 1, 0, 0, 10000, now(), 10000, now());
+
+INSERT INTO sys_dict_type (dict_type, dict_name, remark, sort, version, deleted, creator, create_time, updater, update_time) VALUES ('schedule_status', '定时任务', '状态', 0, 0, 0, 10000, now(), 10000, now());
+
+SET @typeId = @@identity;
+INSERT INTO sys_dict_data (dict_type_id, dict_label, dict_value, remark, sort, version, deleted, creator, create_time, updater, update_time) VALUES ((SELECT @typeId), '暂停', '0', '', 0, 0, 0, 10000, now(), 10000, now());
+INSERT INTO sys_dict_data (dict_type_id, dict_label, dict_value, remark, sort, version, deleted, creator, create_time, updater, update_time) VALUES ((SELECT @typeId), '正常', '1', '', 1, 0, 0, 10000, now(), 10000, now());
+
+
+DROP TABLE IF EXISTS schedule_job;
+DROP TABLE IF EXISTS schedule_job_log;
+
+CREATE TABLE schedule_job (
+ id bigint NOT NULL AUTO_INCREMENT COMMENT 'id',
+ job_name varchar(200) COMMENT '名称',
+ job_group varchar(100) COMMENT '分组',
+ bean_name varchar(200) COMMENT 'spring bean名称',
+ method varchar(100) COMMENT '执行方法',
+ params varchar(2000) COMMENT '参数',
+ cron_expression varchar(100) COMMENT 'cron表达式',
+ status tinyint unsigned COMMENT '状态 0:暂停 1:正常',
+ concurrent tinyint unsigned COMMENT '是否并发 0:禁止 1:允许',
+ remark varchar(255) COMMENT '备注',
+ version int COMMENT '版本号',
+ deleted tinyint COMMENT '删除标识 0:正常 1:已删除',
+ creator bigint COMMENT '创建者',
+ create_time datetime COMMENT '创建时间',
+ updater bigint COMMENT '更新者',
+ update_time datetime COMMENT '更新时间',
+ PRIMARY KEY (id)
+) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COMMENT='定时任务';
+
+CREATE TABLE schedule_job_log (
+ id bigint NOT NULL AUTO_INCREMENT COMMENT 'id',
+ job_id bigint NOT NULL COMMENT '任务id',
+ job_name varchar(200) COMMENT '任务名称',
+ job_group varchar(100) COMMENT '任务组名',
+ bean_name varchar(200) COMMENT 'spring bean名称',
+ method varchar(100) COMMENT '执行方法',
+ params varchar(2000) COMMENT '参数',
+ status tinyint unsigned NOT NULL COMMENT '任务状态 0:失败 1:成功',
+ error varchar(2000) COMMENT '异常信息',
+ times bigint NOT NULL COMMENT '耗时(单位:毫秒)',
+ create_time datetime COMMENT '创建时间',
+ PRIMARY KEY (id),
+ key idx_job_id (job_id)
+) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COMMENT='定时任务日志';
+
+
+INSERT INTO schedule_job (id, job_name, job_group, bean_name, method, params, cron_expression, status, concurrent, remark, version, deleted, creator, create_time, updater, update_time) VALUES (1, '测试任务', 'system', 'testTask', 'run', '123', '0 * * * * ? *', 0, 0, '', 14, 0, 10000, now(), 10000, now());
+
+
+
+
+-- ----------------------------------------------------------
+-- 以下为Quartz框架,自带的表结构
+-- ----------------------------------------------------------
+
+DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
+DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
+DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
+DROP TABLE IF EXISTS QRTZ_LOCKS;
+DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
+DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
+DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
+DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
+DROP TABLE IF EXISTS QRTZ_TRIGGERS;
+DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
+DROP TABLE IF EXISTS QRTZ_CALENDARS;
+
+create table QRTZ_JOB_DETAILS (
+ sched_name varchar(120) not null comment '调度名称',
+ job_name varchar(200) not null comment '任务名称',
+ job_group varchar(200) not null comment '任务组名',
+ description varchar(250) null comment '相关介绍',
+ job_class_name varchar(250) not null comment '执行任务类名称',
+ is_durable varchar(1) not null comment '是否持久化',
+ is_nonconcurrent varchar(1) not null comment '是否并发',
+ is_update_data varchar(1) not null comment '是否更新数据',
+ requests_recovery varchar(1) not null comment '是否接受恢复执行',
+ job_data blob null comment '存放持久化job对象',
+ primary key (sched_name, job_name, job_group)
+) ENGINE=InnoDB COMMENT = '任务详细信息表';
+
+create table QRTZ_TRIGGERS (
+ sched_name varchar(120) not null comment '调度名称',
+ trigger_name varchar(200) not null comment '触发器的名字',
+ trigger_group varchar(200) not null comment '触发器所属组的名字',
+ job_name varchar(200) not null comment 'qrtz_job_details表job_name的外键',
+ job_group varchar(200) not null comment 'qrtz_job_details表job_group的外键',
+ description varchar(250) null comment '相关介绍',
+ next_fire_time bigint(13) null comment '上一次触发时间(毫秒)',
+ prev_fire_time bigint(13) null comment '下一次触发时间(默认为-1表示不触发)',
+ priority integer null comment '优先级',
+ trigger_state varchar(16) not null comment '触发器状态',
+ trigger_type varchar(8) not null comment '触发器的类型',
+ start_time bigint(13) not null comment '开始时间',
+ end_time bigint(13) null comment '结束时间',
+ calendar_name varchar(200) null comment '日程表名称',
+ misfire_instr smallint(2) null comment '补偿执行的策略',
+ job_data blob null comment '存放持久化job对象',
+ 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 COMMENT = '触发器详细信息表';
+
+create table QRTZ_SIMPLE_TRIGGERS (
+ sched_name varchar(120) not null comment '调度名称',
+ trigger_name varchar(200) not null comment 'qrtz_triggers表trigger_name的外键',
+ trigger_group varchar(200) not null comment 'qrtz_triggers表trigger_group的外键',
+ repeat_count bigint(7) not null comment '重复的次数统计',
+ repeat_interval bigint(12) not null comment '重复的间隔时间',
+ times_triggered bigint(10) not null comment '已经触发的次数',
+ 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 COMMENT = '简单触发器的信息表';
+
+create table QRTZ_CRON_TRIGGERS (
+ sched_name varchar(120) not null comment '调度名称',
+ trigger_name varchar(200) not null comment 'qrtz_triggers表trigger_name的外键',
+ trigger_group varchar(200) not null comment 'qrtz_triggers表trigger_group的外键',
+ cron_expression varchar(200) not null comment 'cron表达式',
+ time_zone_id varchar(80) comment '时区',
+ 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 COMMENT = 'Cron类型的触发器表';
+
+create table QRTZ_BLOB_TRIGGERS (
+ sched_name varchar(120) not null comment '调度名称',
+ trigger_name varchar(200) not null comment 'qrtz_triggers表trigger_name的外键',
+ trigger_group varchar(200) not null comment 'qrtz_triggers表trigger_group的外键',
+ blob_data blob null comment '存放持久化Trigger对象',
+ 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 COMMENT = 'Blob类型的触发器表';
+
+create table QRTZ_CALENDARS (
+ sched_name varchar(120) not null comment '调度名称',
+ calendar_name varchar(200) not null comment '日历名称',
+ calendar blob not null comment '存放持久化calendar对象',
+ primary key (sched_name, calendar_name)
+) ENGINE=InnoDB COMMENT = '日历信息表';
+
+create table QRTZ_PAUSED_TRIGGER_GRPS (
+ sched_name varchar(120) not null comment '调度名称',
+ trigger_group varchar(200) not null comment 'qrtz_triggers表trigger_group的外键',
+ primary key (sched_name, trigger_group)
+) ENGINE=InnoDB COMMENT = '暂停的触发器表';
+
+create table QRTZ_FIRED_TRIGGERS (
+ sched_name varchar(120) not null comment '调度名称',
+ entry_id varchar(95) not null comment '调度器实例id',
+ trigger_name varchar(200) not null comment 'qrtz_triggers表trigger_name的外键',
+ trigger_group varchar(200) not null comment 'qrtz_triggers表trigger_group的外键',
+ instance_name varchar(200) not null comment '调度器实例名',
+ fired_time bigint(13) not null comment '触发的时间',
+ sched_time bigint(13) not null comment '定时器制定的时间',
+ priority integer not null comment '优先级',
+ state varchar(16) not null comment '状态',
+ job_name varchar(200) null comment '任务名称',
+ job_group varchar(200) null comment '任务组名',
+ is_nonconcurrent varchar(1) null comment '是否并发',
+ requests_recovery varchar(1) null comment '是否接受恢复执行',
+ primary key (sched_name, entry_id)
+) ENGINE=InnoDB COMMENT = '已触发的触发器表';
+
+create table QRTZ_SCHEDULER_STATE (
+ sched_name varchar(120) not null comment '调度名称',
+ instance_name varchar(200) not null comment '实例名称',
+ last_checkin_time bigint(13) not null comment '上次检查时间',
+ checkin_interval bigint(13) not null comment '检查间隔时间',
+ primary key (sched_name, instance_name)
+) ENGINE=InnoDB COMMENT = '调度器状态表';
+
+create table QRTZ_LOCKS (
+ sched_name varchar(120) not null comment '调度名称',
+ lock_name varchar(40) not null comment '悲观锁名称',
+ primary key (sched_name, lock_name)
+) ENGINE=InnoDB COMMENT = '存储的悲观锁信息表';
+
+create table QRTZ_SIMPROP_TRIGGERS (
+ sched_name varchar(120) not null comment '调度名称',
+ trigger_name varchar(200) not null comment 'qrtz_triggers表trigger_name的外键',
+ trigger_group varchar(200) not null comment 'qrtz_triggers表trigger_group的外键',
+ str_prop_1 varchar(512) null comment 'String类型的trigger的第一个参数',
+ str_prop_2 varchar(512) null comment 'String类型的trigger的第二个参数',
+ str_prop_3 varchar(512) null comment 'String类型的trigger的第三个参数',
+ int_prop_1 int null comment 'int类型的trigger的第一个参数',
+ int_prop_2 int null comment 'int类型的trigger的第二个参数',
+ long_prop_1 bigint null comment 'long类型的trigger的第一个参数',
+ long_prop_2 bigint null comment 'long类型的trigger的第二个参数',
+ dec_prop_1 numeric(13,4) null comment 'decimal类型的trigger的第一个参数',
+ dec_prop_2 numeric(13,4) null comment 'decimal类型的trigger的第二个参数',
+ bool_prop_1 varchar(1) null comment 'Boolean类型的trigger的第一个参数',
+ bool_prop_2 varchar(1) null comment 'Boolean类型的trigger的第二个参数',
+ 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 COMMENT = '同步机制的行锁表';
\ No newline at end of file
diff --git a/fast-boot-module/README.md b/fast-boot-module/README.md
new file mode 100644
index 0000000..3d18fb1
--- /dev/null
+++ b/fast-boot-module/README.md
@@ -0,0 +1,21 @@
+## 说明
+fast-boot 是采用组件模式,扩展不同的业务功能,可以很方便的实现各种业务需求,且不会导致系统臃肿,若想使用某个组件,按需引入即可,反之亦然。
+
+## 引入
+如果需要使用对应的组件,如:`fast-boot-quartz`,则需要在`fast-boot/fast-server/pom.xml`里面引入,如下所示:
+
+```xml
+
+ net.maku
+ fast-boot-quartz
+ ${revision}
+
+```
+
+## SQL语句
+引入组件时,还需要执行对应的SQL文件,初始化表结构和菜单等。
+如果使用的是MySQL数据库,则需要执行以下SQL文件:
+
+```
+fast-boot/db/mysql/module/fast-boot-quartz.sql
+```
\ No newline at end of file
diff --git a/fast-boot-module/fast-boot-quartz/pom.xml b/fast-boot-module/fast-boot-quartz/pom.xml
new file mode 100644
index 0000000..eb51d90
--- /dev/null
+++ b/fast-boot-module/fast-boot-quartz/pom.xml
@@ -0,0 +1,23 @@
+
+
+ net.maku
+ fast-boot-module
+ ${revision}
+
+ 4.0.0
+ fast-boot-quartz
+ jar
+
+
+
+ net.maku
+ fast-framework
+ ${revision}
+
+
+ org.quartz-scheduler
+ quartz
+
+
+
+
\ No newline at end of file
diff --git a/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/controller/ScheduleJobController.java b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/controller/ScheduleJobController.java
new file mode 100644
index 0000000..48e2455
--- /dev/null
+++ b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/controller/ScheduleJobController.java
@@ -0,0 +1,102 @@
+package net.maku.quartz.controller;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.AllArgsConstructor;
+import net.maku.framework.common.page.PageResult;
+import net.maku.framework.common.utils.Result;
+import net.maku.quartz.convert.ScheduleJobConvert;
+import net.maku.quartz.entity.ScheduleJobEntity;
+import net.maku.quartz.query.ScheduleJobQuery;
+import net.maku.quartz.service.ScheduleJobService;
+import net.maku.quartz.utils.CronUtils;
+import net.maku.quartz.vo.ScheduleJobVO;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+import java.util.List;
+
+/**
+* 定时任务
+*
+* @author 阿沐 babamu@126.com
+*/
+@RestController
+@RequestMapping("schedule")
+@Tag(name="定时任务")
+@AllArgsConstructor
+public class ScheduleJobController {
+ private final ScheduleJobService scheduleJobService;
+
+ @GetMapping("page")
+ @Operation(summary = "分页")
+ @PreAuthorize("hasAuthority('schedule:page')")
+ public Result> page(@Valid ScheduleJobQuery query){
+ PageResult page = scheduleJobService.page(query);
+
+ return Result.ok(page);
+ }
+
+ @GetMapping("{id}")
+ @Operation(summary = "信息")
+ @PreAuthorize("hasAuthority('schedule:info')")
+ public Result get(@PathVariable("id") Long id){
+ ScheduleJobEntity entity = scheduleJobService.getById(id);
+
+ return Result.ok(ScheduleJobConvert.INSTANCE.convert(entity));
+ }
+
+ @PostMapping
+ @Operation(summary = "保存")
+ @PreAuthorize("hasAuthority('schedule:save')")
+ public Result save(@RequestBody ScheduleJobVO vo){
+ if (!CronUtils.isValid(vo.getCronExpression())) {
+ return Result.error("操作失败,Cron表达式不正确");
+ }
+
+ scheduleJobService.save(vo);
+
+ return Result.ok();
+ }
+
+ @PutMapping
+ @Operation(summary = "修改")
+ @PreAuthorize("hasAuthority('schedule:update')")
+ public Result update(@RequestBody @Valid ScheduleJobVO vo) {
+ if (!CronUtils.isValid(vo.getCronExpression())) {
+ return Result.error("操作失败,Cron表达式不正确");
+ }
+
+ scheduleJobService.update(vo);
+
+ return Result.ok();
+ }
+
+ @DeleteMapping
+ @Operation(summary = "删除")
+ @PreAuthorize("hasAuthority('schedule:delete')")
+ public Result delete(@RequestBody List idList){
+ scheduleJobService.delete(idList);
+
+ return Result.ok();
+ }
+
+ @PutMapping("run")
+ @Operation(summary = "立即执行")
+ @PreAuthorize("hasAuthority('schedule:run')")
+ public Result run(@RequestBody ScheduleJobVO vo){
+ scheduleJobService.run(vo);
+
+ return Result.ok();
+ }
+
+ @PutMapping("change-status")
+ @Operation(summary = "修改状态")
+ @PreAuthorize("hasAuthority('schedule:update')")
+ public Result changeStatus(@RequestBody ScheduleJobVO vo){
+ scheduleJobService.changeStatus(vo);
+
+ return Result.ok();
+ }
+}
\ No newline at end of file
diff --git a/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/controller/ScheduleJobLogController.java b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/controller/ScheduleJobLogController.java
new file mode 100644
index 0000000..09ee49f
--- /dev/null
+++ b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/controller/ScheduleJobLogController.java
@@ -0,0 +1,51 @@
+package net.maku.quartz.controller;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.AllArgsConstructor;
+import net.maku.framework.common.page.PageResult;
+import net.maku.framework.common.utils.Result;
+import net.maku.quartz.convert.ScheduleJobLogConvert;
+import net.maku.quartz.entity.ScheduleJobLogEntity;
+import net.maku.quartz.query.ScheduleJobLogQuery;
+import net.maku.quartz.service.ScheduleJobLogService;
+import net.maku.quartz.vo.ScheduleJobLogVO;
+import org.springframework.security.access.prepost.PreAuthorize;
+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 javax.validation.Valid;
+
+/**
+* 定时任务日志
+*
+* @author 阿沐 babamu@126.com
+*/
+@RestController
+@RequestMapping("schedule/log")
+@Tag(name="定时任务日志")
+@AllArgsConstructor
+public class ScheduleJobLogController {
+ private final ScheduleJobLogService scheduleJobLogService;
+
+ @GetMapping("page")
+ @Operation(summary = "分页")
+ @PreAuthorize("hasAuthority('schedule:log')")
+ public Result> page(@Valid ScheduleJobLogQuery query){
+ PageResult page = scheduleJobLogService.page(query);
+
+ return Result.ok(page);
+ }
+
+ @GetMapping("{id}")
+ @Operation(summary = "信息")
+ @PreAuthorize("hasAuthority('schedule:log')")
+ public Result get(@PathVariable("id") Long id){
+ ScheduleJobLogEntity entity = scheduleJobLogService.getById(id);
+
+ return Result.ok(ScheduleJobLogConvert.INSTANCE.convert(entity));
+ }
+
+}
\ No newline at end of file
diff --git a/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/convert/ScheduleJobConvert.java b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/convert/ScheduleJobConvert.java
new file mode 100644
index 0000000..1ab7574
--- /dev/null
+++ b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/convert/ScheduleJobConvert.java
@@ -0,0 +1,25 @@
+package net.maku.quartz.convert;
+
+import net.maku.quartz.entity.ScheduleJobEntity;
+import net.maku.quartz.vo.ScheduleJobVO;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+
+/**
+* 定时任务
+*
+* @author 阿沐 babamu@126.com
+*/
+@Mapper
+public interface ScheduleJobConvert {
+ ScheduleJobConvert INSTANCE = Mappers.getMapper(ScheduleJobConvert.class);
+
+ ScheduleJobEntity convert(ScheduleJobVO vo);
+
+ ScheduleJobVO convert(ScheduleJobEntity entity);
+
+ List convertList(List list);
+
+}
\ No newline at end of file
diff --git a/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/convert/ScheduleJobLogConvert.java b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/convert/ScheduleJobLogConvert.java
new file mode 100644
index 0000000..b3620f6
--- /dev/null
+++ b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/convert/ScheduleJobLogConvert.java
@@ -0,0 +1,25 @@
+package net.maku.quartz.convert;
+
+import net.maku.quartz.entity.ScheduleJobLogEntity;
+import net.maku.quartz.vo.ScheduleJobLogVO;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+
+/**
+* 定时任务日志
+*
+* @author 阿沐 babamu@126.com
+*/
+@Mapper
+public interface ScheduleJobLogConvert {
+ ScheduleJobLogConvert INSTANCE = Mappers.getMapper(ScheduleJobLogConvert.class);
+
+ ScheduleJobLogEntity convert(ScheduleJobLogVO vo);
+
+ ScheduleJobLogVO convert(ScheduleJobLogEntity entity);
+
+ List convertList(List list);
+
+}
\ No newline at end of file
diff --git a/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/dao/ScheduleJobDao.java b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/dao/ScheduleJobDao.java
new file mode 100644
index 0000000..2328b47
--- /dev/null
+++ b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/dao/ScheduleJobDao.java
@@ -0,0 +1,15 @@
+package net.maku.quartz.dao;
+
+import net.maku.framework.common.dao.BaseDao;
+import net.maku.quartz.entity.ScheduleJobEntity;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+* 定时任务
+*
+* @author 阿沐 babamu@126.com
+*/
+@Mapper
+public interface ScheduleJobDao extends BaseDao {
+
+}
\ No newline at end of file
diff --git a/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/dao/ScheduleJobLogDao.java b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/dao/ScheduleJobLogDao.java
new file mode 100644
index 0000000..a03e425
--- /dev/null
+++ b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/dao/ScheduleJobLogDao.java
@@ -0,0 +1,15 @@
+package net.maku.quartz.dao;
+
+import net.maku.framework.common.dao.BaseDao;
+import net.maku.quartz.entity.ScheduleJobLogEntity;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+* 定时任务日志
+*
+* @author 阿沐 babamu@126.com
+*/
+@Mapper
+public interface ScheduleJobLogDao extends BaseDao {
+
+}
\ No newline at end of file
diff --git a/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/entity/ScheduleJobEntity.java b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/entity/ScheduleJobEntity.java
new file mode 100644
index 0000000..8a0d91f
--- /dev/null
+++ b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/entity/ScheduleJobEntity.java
@@ -0,0 +1,62 @@
+package net.maku.quartz.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import net.maku.framework.common.entity.BaseEntity;
+
+/**
+ * 定时任务
+ *
+ * @author 阿沐 babamu@126.com
+ */
+@Data
+@EqualsAndHashCode(callSuper=false)
+@TableName("schedule_job")
+public class ScheduleJobEntity extends BaseEntity {
+ /**
+ * 任务名称
+ */
+ private String jobName;
+
+ /**
+ * 任务组名
+ */
+ private String jobGroup;
+
+ /**
+ * bean名称
+ */
+ private String beanName;
+
+ /**
+ * 执行方法
+ */
+ private String method;
+
+ /**
+ * 方法参数
+ */
+ private String params;
+
+ /**
+ * cron表达式
+ */
+ private String cronExpression;
+
+ /**
+ * 状态
+ */
+ private Integer status;
+
+ /**
+ * 是否并发 0:禁止 1:允许
+ */
+ private Integer concurrent;
+
+ /**
+ * 备注
+ */
+ private String remark;
+
+}
\ No newline at end of file
diff --git a/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/entity/ScheduleJobLogEntity.java b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/entity/ScheduleJobLogEntity.java
new file mode 100644
index 0000000..e5f5734
--- /dev/null
+++ b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/entity/ScheduleJobLogEntity.java
@@ -0,0 +1,76 @@
+package net.maku.quartz.entity;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 定时任务日志
+ *
+ * @author 阿沐 babamu@126.com
+ */
+@Data
+@TableName("schedule_job_log")
+public class ScheduleJobLogEntity {
+ /**
+ * id
+ */
+ @TableId
+ private Long id;
+
+ /**
+ * 任务id
+ */
+ private Long jobId;
+
+ /**
+ * 任务名称
+ */
+ private String jobName;
+
+ /**
+ * 任务组名
+ */
+ private String jobGroup;
+
+ /**
+ * spring bean名称
+ */
+ private String beanName;
+
+ /**
+ * 执行方法
+ */
+ private String method;
+
+ /**
+ * 参数
+ */
+ private String params;
+
+ /**
+ * 任务状态
+ */
+ private Integer status;
+
+ /**
+ * 异常信息
+ */
+ private String error;
+
+ /**
+ * 耗时(单位:毫秒)
+ */
+ private Long times;
+
+ /**
+ * 创建时间
+ */
+ @TableField(fill = FieldFill.INSERT)
+ private Date createTime;
+
+}
\ No newline at end of file
diff --git a/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/enums/ScheduleConcurrentEnum.java b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/enums/ScheduleConcurrentEnum.java
new file mode 100644
index 0000000..436f375
--- /dev/null
+++ b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/enums/ScheduleConcurrentEnum.java
@@ -0,0 +1,24 @@
+package net.maku.quartz.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 定时任务并发枚举
+ *
+ * @author 阿沐 babamu@126.com
+ */
+@Getter
+@AllArgsConstructor
+public enum ScheduleConcurrentEnum {
+ /**
+ * 禁止
+ */
+ NO(0),
+ /**
+ * 允许
+ */
+ YES(1);
+
+ private final int value;
+}
diff --git a/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/enums/ScheduleStatusEnum.java b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/enums/ScheduleStatusEnum.java
new file mode 100644
index 0000000..1d27f46
--- /dev/null
+++ b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/enums/ScheduleStatusEnum.java
@@ -0,0 +1,24 @@
+package net.maku.quartz.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 定时任务状态枚举
+ *
+ * @author 阿沐 babamu@126.com
+ */
+@Getter
+@AllArgsConstructor
+public enum ScheduleStatusEnum {
+ /**
+ * 暂停
+ */
+ PAUSE(0),
+ /**
+ * 正常
+ */
+ NORMAL(1);
+
+ private final int value;
+}
diff --git a/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/query/ScheduleJobLogQuery.java b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/query/ScheduleJobLogQuery.java
new file mode 100644
index 0000000..4fa134a
--- /dev/null
+++ b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/query/ScheduleJobLogQuery.java
@@ -0,0 +1,26 @@
+package net.maku.quartz.query;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import net.maku.framework.common.query.Query;
+
+/**
+* 定时任务日志查询
+*
+* @author 阿沐 babamu@126.com
+*/
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Schema(description = "定时任务日志查询")
+public class ScheduleJobLogQuery extends Query {
+ @Schema(description = "任务id")
+ private Long jobId;
+
+ @Schema(description = "任务名称")
+ private String jobName;
+
+ @Schema(description = "任务组名")
+ private String jobGroup;
+
+}
\ No newline at end of file
diff --git a/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/query/ScheduleJobQuery.java b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/query/ScheduleJobQuery.java
new file mode 100644
index 0000000..061b793
--- /dev/null
+++ b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/query/ScheduleJobQuery.java
@@ -0,0 +1,26 @@
+package net.maku.quartz.query;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import net.maku.framework.common.query.Query;
+
+/**
+* 定时任务查询
+*
+* @author 阿沐 babamu@126.com
+*/
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Schema(description = "定时任务查询")
+public class ScheduleJobQuery extends Query {
+ @Schema(description = "任务名称")
+ private String jobName;
+
+ @Schema(description = "任务组名")
+ private String jobGroup;
+
+ @Schema(description = "状态")
+ private Integer status;
+
+}
\ No newline at end of file
diff --git a/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/service/ScheduleJobLogService.java b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/service/ScheduleJobLogService.java
new file mode 100644
index 0000000..71532de
--- /dev/null
+++ b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/service/ScheduleJobLogService.java
@@ -0,0 +1,18 @@
+package net.maku.quartz.service;
+
+import net.maku.framework.common.page.PageResult;
+import net.maku.framework.common.service.BaseService;
+import net.maku.quartz.entity.ScheduleJobLogEntity;
+import net.maku.quartz.query.ScheduleJobLogQuery;
+import net.maku.quartz.vo.ScheduleJobLogVO;
+
+/**
+ * 定时任务日志
+ *
+ * @author 阿沐 babamu@126.com
+ */
+public interface ScheduleJobLogService extends BaseService {
+
+ PageResult page(ScheduleJobLogQuery query);
+
+}
\ No newline at end of file
diff --git a/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/service/ScheduleJobService.java b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/service/ScheduleJobService.java
new file mode 100644
index 0000000..bf4ed9e
--- /dev/null
+++ b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/service/ScheduleJobService.java
@@ -0,0 +1,29 @@
+package net.maku.quartz.service;
+
+import net.maku.framework.common.page.PageResult;
+import net.maku.framework.common.service.BaseService;
+import net.maku.quartz.entity.ScheduleJobEntity;
+import net.maku.quartz.query.ScheduleJobQuery;
+import net.maku.quartz.vo.ScheduleJobVO;
+
+import java.util.List;
+
+/**
+ * 定时任务
+ *
+ * @author 阿沐 babamu@126.com
+ */
+public interface ScheduleJobService extends BaseService {
+
+ PageResult page(ScheduleJobQuery query);
+
+ void save(ScheduleJobVO vo);
+
+ void update(ScheduleJobVO vo);
+
+ void delete(List idList);
+
+ void run(ScheduleJobVO vo);
+
+ void changeStatus(ScheduleJobVO vo);
+}
\ No newline at end of file
diff --git a/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/service/impl/ScheduleJobLogServiceImpl.java b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/service/impl/ScheduleJobLogServiceImpl.java
new file mode 100644
index 0000000..ed5d816
--- /dev/null
+++ b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/service/impl/ScheduleJobLogServiceImpl.java
@@ -0,0 +1,43 @@
+package net.maku.quartz.service.impl;
+
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.AllArgsConstructor;
+import net.maku.framework.common.page.PageResult;
+import net.maku.framework.common.service.impl.BaseServiceImpl;
+import net.maku.quartz.convert.ScheduleJobLogConvert;
+import net.maku.quartz.dao.ScheduleJobLogDao;
+import net.maku.quartz.entity.ScheduleJobLogEntity;
+import net.maku.quartz.query.ScheduleJobLogQuery;
+import net.maku.quartz.service.ScheduleJobLogService;
+import net.maku.quartz.vo.ScheduleJobLogVO;
+import org.springframework.stereotype.Service;
+
+/**
+ * 定时任务日志
+ *
+ * @author 阿沐 babamu@126.com
+ */
+@Service
+@AllArgsConstructor
+public class ScheduleJobLogServiceImpl extends BaseServiceImpl implements ScheduleJobLogService {
+
+ @Override
+ public PageResult page(ScheduleJobLogQuery query) {
+ IPage page = baseMapper.selectPage(getPage(query), getWrapper(query));
+
+ return new PageResult<>(ScheduleJobLogConvert.INSTANCE.convertList(page.getRecords()), page.getTotal());
+ }
+
+ private LambdaQueryWrapper getWrapper(ScheduleJobLogQuery query){
+ LambdaQueryWrapper wrapper = Wrappers.lambdaQuery();
+ wrapper.like(StrUtil.isNotBlank(query.getJobName()), ScheduleJobLogEntity::getJobName, query.getJobName());
+ wrapper.like(StrUtil.isNotBlank(query.getJobGroup()), ScheduleJobLogEntity::getJobGroup, query.getJobGroup());
+ wrapper.eq(query.getJobId() != null, ScheduleJobLogEntity::getJobId, query.getJobId());
+ wrapper.orderByDesc(ScheduleJobLogEntity::getId);
+ return wrapper;
+ }
+
+}
\ No newline at end of file
diff --git a/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/service/impl/ScheduleJobServiceImpl.java b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/service/impl/ScheduleJobServiceImpl.java
new file mode 100644
index 0000000..df39824
--- /dev/null
+++ b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/service/impl/ScheduleJobServiceImpl.java
@@ -0,0 +1,126 @@
+package net.maku.quartz.service.impl;
+
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.AllArgsConstructor;
+import net.maku.framework.common.page.PageResult;
+import net.maku.framework.common.service.impl.BaseServiceImpl;
+import net.maku.quartz.convert.ScheduleJobConvert;
+import net.maku.quartz.dao.ScheduleJobDao;
+import net.maku.quartz.entity.ScheduleJobEntity;
+import net.maku.quartz.enums.ScheduleStatusEnum;
+import net.maku.quartz.query.ScheduleJobQuery;
+import net.maku.quartz.service.ScheduleJobService;
+import net.maku.quartz.utils.ScheduleUtils;
+import net.maku.quartz.vo.ScheduleJobVO;
+import org.quartz.Scheduler;
+import org.quartz.SchedulerException;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.PostConstruct;
+import java.util.List;
+
+/**
+ * 定时任务
+ *
+ * @author 阿沐 babamu@126.com
+ */
+@Service
+@AllArgsConstructor
+public class ScheduleJobServiceImpl extends BaseServiceImpl implements ScheduleJobService {
+ private final Scheduler scheduler;
+
+ /**
+ * 启动项目时,初始化定时器
+ */
+ @PostConstruct
+ public void init() throws SchedulerException {
+ scheduler.clear();
+ List scheduleJobList = baseMapper.selectList(null);
+ for (ScheduleJobEntity scheduleJob : scheduleJobList) {
+ ScheduleUtils.createScheduleJob(scheduler, scheduleJob);
+ }
+ }
+
+ @Override
+ public PageResult page(ScheduleJobQuery query) {
+ IPage page = baseMapper.selectPage(getPage(query), getWrapper(query));
+
+ return new PageResult<>(ScheduleJobConvert.INSTANCE.convertList(page.getRecords()), page.getTotal());
+ }
+
+ private LambdaQueryWrapper getWrapper(ScheduleJobQuery query){
+ LambdaQueryWrapper wrapper = Wrappers.lambdaQuery();
+ wrapper.like(StrUtil.isNotBlank(query.getJobName()), ScheduleJobEntity::getJobName, query.getJobName());
+ wrapper.like(StrUtil.isNotBlank(query.getJobGroup()), ScheduleJobEntity::getJobGroup, query.getJobGroup());
+ wrapper.eq(query.getStatus() != null, ScheduleJobEntity::getStatus, query.getStatus());
+
+ return wrapper;
+ }
+
+ @Override
+ public void save(ScheduleJobVO vo) {
+ ScheduleJobEntity entity = ScheduleJobConvert.INSTANCE.convert(vo);
+
+ entity.setStatus(ScheduleStatusEnum.PAUSE.getValue());
+ if(baseMapper.insert(entity) > 0) {
+ ScheduleUtils.createScheduleJob(scheduler, entity);
+ }
+ }
+
+ @Override
+ public void update(ScheduleJobVO vo) {
+ ScheduleJobEntity entity = ScheduleJobConvert.INSTANCE.convert(vo);
+
+ // 更新定时任务
+ if(updateById(entity)) {
+ ScheduleJobEntity scheduleJob = getById(entity.getId());
+ ScheduleUtils.updateSchedulerJob(scheduler, scheduleJob);
+ }
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void delete(List idList) {
+ for(Long id: idList){
+ ScheduleJobEntity scheduleJob = getById(id);
+
+ // 删除定时任务
+ if (removeById(id)) {
+ ScheduleUtils.deleteScheduleJob(scheduler, scheduleJob);
+ }
+ }
+ }
+
+ @Override
+ public void run(ScheduleJobVO vo) {
+ ScheduleJobEntity scheduleJob = getById(vo.getId());
+ if(scheduleJob == null) {
+ return;
+ }
+
+ ScheduleUtils.run(scheduler, scheduleJob);
+ }
+
+ @Override
+ public void changeStatus(ScheduleJobVO vo) {
+ ScheduleJobEntity scheduleJob = getById(vo.getId());
+ if(scheduleJob == null) {
+ return;
+ }
+
+ // 更新数据
+ scheduleJob.setStatus(vo.getStatus());
+ updateById(scheduleJob);
+
+ if(ScheduleStatusEnum.PAUSE.getValue() == vo.getStatus()) {
+ ScheduleUtils.pauseJob(scheduler, scheduleJob);
+ }else if(ScheduleStatusEnum.NORMAL.getValue() == vo.getStatus()) {
+ ScheduleUtils.resumeJob(scheduler, scheduleJob);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/task/TestTask.java b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/task/TestTask.java
new file mode 100644
index 0000000..27f9c0c
--- /dev/null
+++ b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/task/TestTask.java
@@ -0,0 +1,19 @@
+package net.maku.quartz.task;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+/**
+ * 测试定时任务
+ *
+ * @author 阿沐 babamu@126.com
+ */
+@Slf4j
+@Component
+public class TestTask {
+
+ public void run(String params) throws InterruptedException {
+ log.info("我是testTask.run(),参数:{},正在被执行。", params);
+ Thread.sleep(1000);
+ }
+}
diff --git a/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/utils/AbstractScheduleJob.java b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/utils/AbstractScheduleJob.java
new file mode 100644
index 0000000..5d54566
--- /dev/null
+++ b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/utils/AbstractScheduleJob.java
@@ -0,0 +1,83 @@
+package net.maku.quartz.utils;
+
+import cn.hutool.extra.spring.SpringUtil;
+import lombok.extern.slf4j.Slf4j;
+import net.maku.framework.common.utils.ExceptionUtils;
+import net.maku.quartz.entity.ScheduleJobEntity;
+import net.maku.quartz.entity.ScheduleJobLogEntity;
+import net.maku.quartz.enums.ScheduleStatusEnum;
+import net.maku.quartz.service.ScheduleJobLogService;
+import org.apache.commons.lang3.StringUtils;
+import org.quartz.Job;
+import org.quartz.JobExecutionContext;
+import org.springframework.beans.BeanUtils;
+
+import java.lang.reflect.Method;
+import java.util.Date;
+
+@Slf4j
+public abstract class AbstractScheduleJob implements Job {
+ private static final ThreadLocal threadLocal = new ThreadLocal<>();
+
+ @Override
+ public void execute(JobExecutionContext context) {
+ ScheduleJobEntity scheduleJob = new ScheduleJobEntity();
+ BeanUtils.copyProperties(context.getMergedJobDataMap().get(ScheduleUtils.JOB_PARAM_KEY), scheduleJob);
+
+ try {
+ threadLocal.set(new Date());
+ doExecute(scheduleJob);
+ saveLog(scheduleJob, null);
+ } catch (Exception e) {
+ log.error("任务执行失败,任务ID:{}", scheduleJob.getId(), e);
+ saveLog(scheduleJob, e);
+ }
+ }
+
+ /**
+ * 执行spring bean方法
+ */
+ protected void doExecute(ScheduleJobEntity scheduleJob) throws Exception {
+ log.info("准备执行任务,任务ID:{}", scheduleJob.getId());
+
+ Object bean = SpringUtil.getBean(scheduleJob.getBeanName());
+ Method method = bean.getClass().getDeclaredMethod(scheduleJob.getMethod(), String.class);
+ method.invoke(bean, scheduleJob.getParams());
+
+ log.info("任务执行完毕,任务ID:{}", scheduleJob.getId());
+ }
+
+ /**
+ * 保存 log
+ */
+ protected void saveLog(ScheduleJobEntity scheduleJob, Exception e) {
+ Date startTime = threadLocal.get();
+ threadLocal.remove();
+
+ // 执行总时长
+ long times = System.currentTimeMillis() - startTime.getTime();
+
+ // 保存执行记录
+ ScheduleJobLogEntity log = new ScheduleJobLogEntity();
+ log.setJobId(scheduleJob.getId());
+ log.setJobName(scheduleJob.getJobName());
+ log.setJobGroup(scheduleJob.getJobGroup());
+ log.setBeanName(scheduleJob.getBeanName());
+ log.setMethod(scheduleJob.getMethod());
+ log.setParams(scheduleJob.getParams());
+ log.setTimes(times);
+ log.setCreateTime(new Date());
+
+ if (e != null) {
+ log.setStatus(ScheduleStatusEnum.PAUSE.getValue());
+ String error = StringUtils.substring(ExceptionUtils.getExceptionMessage(e), 0, 2000);
+ log.setError(error);
+ } else {
+ log.setStatus(ScheduleStatusEnum.NORMAL.getValue());
+ }
+
+ // 保存日志
+ SpringUtil.getBean(ScheduleJobLogService.class).save(log);
+ }
+
+}
diff --git a/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/utils/CronUtils.java b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/utils/CronUtils.java
new file mode 100644
index 0000000..b5d5e13
--- /dev/null
+++ b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/utils/CronUtils.java
@@ -0,0 +1,33 @@
+package net.maku.quartz.utils;
+
+import org.quartz.CronExpression;
+
+import java.text.ParseException;
+import java.util.Date;
+
+/**
+ * cron 工具类
+ *
+ * @author 阿沐 babamu@126.com
+ *
+ */
+public class CronUtils {
+ /**
+ * 验证Cron表达式是否有效
+ */
+ public static boolean isValid(String cronExpression) {
+ return CronExpression.isValidExpression(cronExpression);
+ }
+
+ /**
+ * 根据给定的Cron表达式,返回下一个执行时间
+ */
+ public static Date getNextExecution(String cronExpression) {
+ try {
+ CronExpression cron = new CronExpression(cronExpression);
+ return cron.getNextValidTimeAfter(new Date(System.currentTimeMillis()));
+ } catch (ParseException e) {
+ throw new IllegalArgumentException(e.getMessage());
+ }
+ }
+}
diff --git a/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/utils/ScheduleConcurrentExecution.java b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/utils/ScheduleConcurrentExecution.java
new file mode 100644
index 0000000..2bdb7fb
--- /dev/null
+++ b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/utils/ScheduleConcurrentExecution.java
@@ -0,0 +1,11 @@
+package net.maku.quartz.utils;
+
+/**
+ * 允许并发(不会等待上一次任务执行完毕,只要时间到就会执行)
+ *
+ * @author 阿沐 babamu@126.com
+ *
+ */
+public class ScheduleConcurrentExecution extends AbstractScheduleJob {
+
+}
diff --git a/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/utils/ScheduleDisallowConcurrentExecution.java b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/utils/ScheduleDisallowConcurrentExecution.java
new file mode 100644
index 0000000..0d46460
--- /dev/null
+++ b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/utils/ScheduleDisallowConcurrentExecution.java
@@ -0,0 +1,14 @@
+package net.maku.quartz.utils;
+
+import org.quartz.DisallowConcurrentExecution;
+
+/**
+ * 禁止并发
+ *
+ * @author 阿沐 babamu@126.com
+ *
+ */
+@DisallowConcurrentExecution
+public class ScheduleDisallowConcurrentExecution extends AbstractScheduleJob {
+
+}
diff --git a/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/utils/ScheduleUtils.java b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/utils/ScheduleUtils.java
new file mode 100644
index 0000000..fd4f373
--- /dev/null
+++ b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/utils/ScheduleUtils.java
@@ -0,0 +1,161 @@
+package net.maku.quartz.utils;
+
+import net.maku.framework.common.exception.FastException;
+import net.maku.quartz.entity.ScheduleJobEntity;
+import net.maku.quartz.enums.ScheduleConcurrentEnum;
+import net.maku.quartz.enums.ScheduleStatusEnum;
+import org.quartz.*;
+
+/**
+ * 定时任务工具类
+ *
+ * @author 阿沐 babamu@126.com
+ */
+public class ScheduleUtils {
+ private final static String JOB_NAME = "TASK_NAME_";
+ /**
+ * 任务调度参数key
+ */
+ public static final String JOB_PARAM_KEY = "JOB_PARAM_KEY";
+
+ /**
+ * 获取quartz任务类
+ */
+ public static Class extends Job> getJobClass(ScheduleJobEntity scheduleJob) {
+ if(scheduleJob.getConcurrent().equals(ScheduleConcurrentEnum.NO.getValue())){
+ return ScheduleDisallowConcurrentExecution.class;
+ }else {
+ return ScheduleConcurrentExecution.class;
+ }
+ }
+
+ /**
+ * 获取触发器key
+ */
+ public static TriggerKey getTriggerKey(ScheduleJobEntity scheduleJob) {
+ return TriggerKey.triggerKey(JOB_NAME + scheduleJob.getId(), scheduleJob.getJobGroup());
+ }
+
+ /**
+ * 获取jobKey
+ */
+ public static JobKey getJobKey(ScheduleJobEntity scheduleJob) {
+ return JobKey.jobKey(JOB_NAME + scheduleJob.getId(), scheduleJob.getJobGroup());
+ }
+
+ /**
+ * 创建定时任务
+ */
+ public static void createScheduleJob(Scheduler scheduler, ScheduleJobEntity scheduleJob) {
+ try {
+ // job key
+ JobKey jobKey = getJobKey(scheduleJob);
+ // 构建job信息
+ JobDetail jobDetail = JobBuilder.newJob(getJobClass(scheduleJob)).withIdentity(jobKey).build();
+
+ // 表达式调度构建器
+ CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression())
+ .withMisfireHandlingInstructionDoNothing();
+
+ // 按新的cronExpression表达式构建一个新的trigger
+ CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(scheduleJob))
+ .withSchedule(scheduleBuilder).build();
+
+ // 放入参数,运行时的方法可以获取
+ jobDetail.getJobDataMap().put(JOB_PARAM_KEY, scheduleJob);
+
+ scheduler.scheduleJob(jobDetail, trigger);
+
+ // 判断是否存在
+ if (scheduler.checkExists(jobKey)){
+ // 防止创建时存在数据问题,先移除,然后再执行创建操作
+ scheduler.deleteJob(jobKey);
+ }
+
+ // 判断任务是否过期
+ if (CronUtils.getNextExecution(scheduleJob.getCronExpression()) != null){
+ // 执行调度任务
+ scheduler.scheduleJob(jobDetail, trigger);
+ }
+
+ // 暂停任务
+ if (scheduleJob.getStatus().equals(ScheduleStatusEnum.PAUSE.getValue())){
+ scheduler.pauseJob(jobKey);
+ }
+ } catch (SchedulerException e) {
+ throw new FastException("创建定时任务失败", e);
+ }
+ }
+
+
+
+ /**
+ * 立即执行任务
+ */
+ public static void run(Scheduler scheduler, ScheduleJobEntity scheduleJob) {
+ try {
+ // 参数
+ JobDataMap dataMap = new JobDataMap();
+ dataMap.put(JOB_PARAM_KEY, scheduleJob);
+
+ JobKey jobKey = getJobKey(scheduleJob);
+ if (scheduler.checkExists(jobKey)) {
+ scheduler.triggerJob(jobKey, dataMap);
+ }
+ } catch (SchedulerException e) {
+ throw new FastException("执行定时任务失败", e);
+ }
+ }
+
+ /**
+ * 暂停任务
+ */
+ public static void pauseJob(Scheduler scheduler, ScheduleJobEntity scheduleJob) {
+ try {
+ scheduler.pauseJob(getJobKey(scheduleJob));
+ } catch (SchedulerException e) {
+ throw new FastException("暂停定时任务失败", e);
+ }
+ }
+
+ /**
+ * 恢复任务
+ */
+ public static void resumeJob(Scheduler scheduler, ScheduleJobEntity scheduleJob) {
+ try {
+ scheduler.resumeJob(getJobKey(scheduleJob));
+ } catch (SchedulerException e) {
+ throw new FastException("恢复定时任务失败", e);
+ }
+ }
+
+ /**
+ * 更新定时任务
+ */
+ public static void updateSchedulerJob(Scheduler scheduler, ScheduleJobEntity scheduleJob) {
+ // 判断是否存在
+ JobKey jobKey = ScheduleUtils.getJobKey(scheduleJob);
+
+ try {
+ // 防止创建时存在数据问题,先移除,然后再执行创建操作
+ if (scheduler.checkExists(jobKey)) {
+ scheduler.deleteJob(jobKey);
+ }
+ }catch (SchedulerException e){
+ throw new FastException("更新定时任务失败", e);
+ }
+
+ ScheduleUtils.createScheduleJob(scheduler, scheduleJob);
+ }
+
+ /**
+ * 删除定时任务
+ */
+ public static void deleteScheduleJob(Scheduler scheduler, ScheduleJobEntity scheduleJob) {
+ try {
+ scheduler.deleteJob(getJobKey(scheduleJob));
+ } catch (SchedulerException e) {
+ throw new FastException("删除定时任务失败", e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/vo/ScheduleJobLogVO.java b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/vo/ScheduleJobLogVO.java
new file mode 100644
index 0000000..2701248
--- /dev/null
+++ b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/vo/ScheduleJobLogVO.java
@@ -0,0 +1,55 @@
+package net.maku.quartz.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import net.maku.framework.common.utils.DateUtils;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+* 定时任务日志
+*
+* @author 阿沐 babamu@126.com
+*/
+@Data
+@Schema(description = "定时任务日志")
+public class ScheduleJobLogVO implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "id")
+ private Long id;
+
+ @Schema(description = "任务id")
+ private Long jobId;
+
+ @Schema(description = "任务名称")
+ private String jobName;
+
+ @Schema(description = "任务组名")
+ private String jobGroup;
+
+ @Schema(description = "spring bean名称")
+ private String beanName;
+
+ @Schema(description = "执行方法")
+ private String method;
+
+ @Schema(description = "参数")
+ private String params;
+
+ @Schema(description = "任务状态")
+ private Integer status;
+
+ @Schema(description = "异常信息")
+ private String error;
+
+ @Schema(description = "耗时(单位:毫秒)")
+ private Integer times;
+
+ @Schema(description = "创建时间")
+ @JsonFormat(pattern = DateUtils.DATE_TIME_PATTERN)
+ private Date createTime;
+
+}
\ No newline at end of file
diff --git a/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/vo/ScheduleJobVO.java b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/vo/ScheduleJobVO.java
new file mode 100644
index 0000000..fea3674
--- /dev/null
+++ b/fast-boot-module/fast-boot-quartz/src/main/java/net/maku/quartz/vo/ScheduleJobVO.java
@@ -0,0 +1,55 @@
+package net.maku.quartz.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import net.maku.framework.common.utils.DateUtils;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+* 定时任务
+*
+* @author 阿沐 babamu@126.com
+*/
+@Data
+@Schema(description = "定时任务")
+public class ScheduleJobVO implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "id")
+ private Long id;
+
+ @Schema(description = "任务名称")
+ private String jobName;
+
+ @Schema(description = "任务组名")
+ private String jobGroup;
+
+ @Schema(description = "bean名称")
+ private String beanName;
+
+ @Schema(description = "执行方法")
+ private String method;
+
+ @Schema(description = "参数")
+ private String params;
+
+ @Schema(description = "cron表达式")
+ private String cronExpression;
+
+ @Schema(description = "状态 ")
+ private Integer status;
+
+ @Schema(description = "是否并发")
+ private Integer concurrent;
+
+ @Schema(description = "备注")
+ private String remark;
+
+ @Schema(description = "创建时间")
+ @JsonFormat(pattern = DateUtils.DATE_TIME_PATTERN)
+ private Date createTime;
+
+}
\ No newline at end of file
diff --git a/fast-boot-module/pom.xml b/fast-boot-module/pom.xml
new file mode 100644
index 0000000..dcd99c8
--- /dev/null
+++ b/fast-boot-module/pom.xml
@@ -0,0 +1,15 @@
+
+
+ net.maku
+ fast-boot
+ ${revision}
+
+ 4.0.0
+ fast-boot-module
+ pom
+
+
+ fast-boot-quartz
+
+
+
\ No newline at end of file
diff --git a/fast-boot-system/pom.xml b/fast-boot-system/pom.xml
index ab6e2e9..f78befb 100644
--- a/fast-boot-system/pom.xml
+++ b/fast-boot-system/pom.xml
@@ -20,6 +20,11 @@
${revision}
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
com.aliyun.oss
aliyun-sdk-oss
diff --git a/fast-framework/src/main/java/net/maku/framework/common/utils/ExceptionUtils.java b/fast-framework/src/main/java/net/maku/framework/common/utils/ExceptionUtils.java
new file mode 100644
index 0000000..fe91ea7
--- /dev/null
+++ b/fast-framework/src/main/java/net/maku/framework/common/utils/ExceptionUtils.java
@@ -0,0 +1,31 @@
+package net.maku.framework.common.utils;
+
+import cn.hutool.core.io.IoUtil;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * Exception工具类
+ *
+ * @author 阿沐 babamu@126.com
+ */
+public class ExceptionUtils {
+
+ /**
+ * 获取异常信息
+ * @param e 异常
+ * @return 返回异常信息
+ */
+ public static String getExceptionMessage(Exception e) {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw, true);
+ e.printStackTrace(pw);
+
+ // 关闭IO流
+ IoUtil.close(pw);
+ IoUtil.close(sw);
+
+ return sw.toString();
+ }
+}
\ No newline at end of file
diff --git a/fast-server/pom.xml b/fast-server/pom.xml
index edd20a3..d71530f 100644
--- a/fast-server/pom.xml
+++ b/fast-server/pom.xml
@@ -16,6 +16,11 @@
net.maku
+ fast-boot-quartz
+ ${revision}
+
+
+ net.maku
fast-boot-system
${revision}
diff --git a/pom.xml b/pom.xml
index a9e81cc..72ce6dd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -19,17 +19,20 @@
fast-boot-new
fast-boot-system
+ fast-boot-module
fast-boot-api
fast-framework
fast-server
- 1.2.0
+ 1.3.0
+ true
UTF-8
UTF-8
1.8
3.5.2
+ 8.1.2.79
3.0.3
1.6.8
5.7.22
@@ -72,6 +75,11 @@
${mybatisplus.version}
+ com.dameng
+ DmJdbcDriver18
+ ${dameng.version}
+
+
com.github.xiaoymin
knife4j-springdoc-ui
${knife4j.version}