From 485e1b8be4ef7d5c338e99967642fd0388daccf6 Mon Sep 17 00:00:00 2001 From: wangys <3401275564@qq.com> Date: Sat, 25 Oct 2025 18:46:54 +0800 Subject: [PATCH] =?UTF-8?q?serv-=E5=AE=9A=E6=97=B6=E4=BB=BB=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../.bin/mysql/sql/createTableCrontab.sql | 59 +++ schoolNewsServ/.bin/mysql/sql/initAll.sql | 2 + .../.bin/mysql/sql/initMenuData.sql | 29 +- schoolNewsServ/admin/pom.xml | 5 + .../admin/src/main/java/org/xyzh/App.java | 4 +- schoolNewsServ/api/api-all/pom.xml | 4 + schoolNewsServ/api/api-crontab/pom.xml | 33 ++ .../org/xyzh/api/crontab/CrontabService.java | 169 ++++++ schoolNewsServ/api/pom.xml | 6 + .../xyzh/common/dto/crontab/TbCrontabLog.java | 188 +++++++ .../common/dto/crontab/TbCrontabTask.java | 201 +++++++ .../java/org/xyzh/common/vo/CrontabLogVO.java | 212 ++++++++ .../org/xyzh/common/vo/CrontabTaskVO.java | 238 +++++++++ schoolNewsServ/crontab/README.md | 248 +++++++++ schoolNewsServ/crontab/pom.xml | 90 ++++ .../xyzh/crontab/config/SchedulerConfig.java | 45 ++ .../crontab/controller/CrontabController.java | 230 ++++++++ .../xyzh/crontab/mapper/CrontabLogMapper.java | 124 +++++ .../crontab/mapper/CrontabTaskMapper.java | 104 ++++ .../scheduler/SchedulerInitializer.java | 57 ++ .../crontab/scheduler/SchedulerManager.java | 157 ++++++ .../xyzh/crontab/scheduler/TaskExecutor.java | 128 +++++ .../service/impl/CrontabServiceImpl.java | 494 ++++++++++++++++++ .../org/xyzh/crontab/task/DataBackupTask.java | 60 +++ .../org/xyzh/crontab/task/LogCleanTask.java | 68 +++ .../crontab/task/SystemStatisticsTask.java | 54 ++ .../resources/mapper/CrontabLogMapper.xml | 194 +++++++ .../resources/mapper/CrontabTaskMapper.xml | 188 +++++++ schoolNewsServ/pom.xml | 6 + 29 files changed, 3391 insertions(+), 6 deletions(-) create mode 100644 schoolNewsServ/.bin/mysql/sql/createTableCrontab.sql create mode 100644 schoolNewsServ/api/api-crontab/pom.xml create mode 100644 schoolNewsServ/api/api-crontab/src/main/java/org/xyzh/api/crontab/CrontabService.java create mode 100644 schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/dto/crontab/TbCrontabLog.java create mode 100644 schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/dto/crontab/TbCrontabTask.java create mode 100644 schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/CrontabLogVO.java create mode 100644 schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/CrontabTaskVO.java create mode 100644 schoolNewsServ/crontab/README.md create mode 100644 schoolNewsServ/crontab/pom.xml create mode 100644 schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/config/SchedulerConfig.java create mode 100644 schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/controller/CrontabController.java create mode 100644 schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/mapper/CrontabLogMapper.java create mode 100644 schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/mapper/CrontabTaskMapper.java create mode 100644 schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/scheduler/SchedulerInitializer.java create mode 100644 schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/scheduler/SchedulerManager.java create mode 100644 schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/scheduler/TaskExecutor.java create mode 100644 schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/service/impl/CrontabServiceImpl.java create mode 100644 schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/task/DataBackupTask.java create mode 100644 schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/task/LogCleanTask.java create mode 100644 schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/task/SystemStatisticsTask.java create mode 100644 schoolNewsServ/crontab/src/main/resources/mapper/CrontabLogMapper.xml create mode 100644 schoolNewsServ/crontab/src/main/resources/mapper/CrontabTaskMapper.xml diff --git a/schoolNewsServ/.bin/mysql/sql/createTableCrontab.sql b/schoolNewsServ/.bin/mysql/sql/createTableCrontab.sql new file mode 100644 index 0000000..7bdd6e0 --- /dev/null +++ b/schoolNewsServ/.bin/mysql/sql/createTableCrontab.sql @@ -0,0 +1,59 @@ +-- ==================================================== +-- 定时任务表 +-- ==================================================== +DROP TABLE IF EXISTS `tb_crontab_task`; +CREATE TABLE `tb_crontab_task` ( + `id` VARCHAR(64) NOT NULL COMMENT '主键ID', + `task_id` VARCHAR(64) NOT NULL COMMENT '任务ID', + `task_name` VARCHAR(100) NOT NULL COMMENT '任务名称', + `task_group` VARCHAR(50) NOT NULL DEFAULT 'DEFAULT' COMMENT '任务分组', + `bean_name` VARCHAR(100) NOT NULL COMMENT 'Bean名称', + `method_name` VARCHAR(100) NOT NULL COMMENT '方法名称', + `method_params` VARCHAR(500) DEFAULT NULL COMMENT '方法参数', + `cron_expression` VARCHAR(100) NOT NULL COMMENT 'Cron表达式', + `status` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '任务状态(0:暂停 1:运行中)', + `description` VARCHAR(500) DEFAULT NULL COMMENT '任务描述', + `concurrent` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否允许并发执行(0:否 1:是)', + `misfire_policy` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '错过执行策略(1:立即执行 2:执行一次 3:放弃执行)', + `creator` VARCHAR(64) DEFAULT NULL COMMENT '创建者', + `updater` VARCHAR(64) DEFAULT NULL COMMENT '更新者', + `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `delete_time` DATETIME DEFAULT NULL COMMENT '删除时间', + `deleted` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否删除(0:否 1:是)', + PRIMARY KEY (`id`), + KEY `idx_task_name` (`task_name`), + KEY `idx_bean_name` (`bean_name`), + KEY `idx_status` (`status`), + KEY `idx_deleted` (`deleted`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='定时任务配置表'; + +-- ==================================================== +-- 定时任务执行日志表 +-- ==================================================== +DROP TABLE IF EXISTS `tb_crontab_log`; +CREATE TABLE `tb_crontab_log` ( + `id` VARCHAR(64) NOT NULL COMMENT '主键ID', + `task_id` VARCHAR(64) NOT NULL COMMENT '任务ID', + `task_name` VARCHAR(100) NOT NULL COMMENT '任务名称', + `task_group` VARCHAR(50) NOT NULL DEFAULT 'DEFAULT' COMMENT '任务分组', + `bean_name` VARCHAR(100) NOT NULL COMMENT 'Bean名称', + `method_name` VARCHAR(100) NOT NULL COMMENT '方法名称', + `method_params` VARCHAR(500) DEFAULT NULL COMMENT '方法参数', + `execute_status` TINYINT(1) NOT NULL COMMENT '执行状态(0:失败 1:成功)', + `execute_message` TEXT DEFAULT NULL COMMENT '执行结果信息', + `exception_info` TEXT DEFAULT NULL COMMENT '异常信息', + `start_time` DATETIME NOT NULL COMMENT '开始时间', + `end_time` DATETIME DEFAULT NULL COMMENT '结束时间', + `execute_duration` INT DEFAULT NULL COMMENT '执行时长(毫秒)', + `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `delete_time` DATETIME DEFAULT NULL COMMENT '删除时间', + `deleted` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否删除(0:否 1:是)', + PRIMARY KEY (`id`), + KEY `idx_task_id` (`task_id`), + KEY `idx_task_name` (`task_name`), + KEY `idx_execute_status` (`execute_status`), + KEY `idx_start_time` (`start_time`), + KEY `idx_deleted` (`deleted`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='定时任务执行日志表'; diff --git a/schoolNewsServ/.bin/mysql/sql/initAll.sql b/schoolNewsServ/.bin/mysql/sql/initAll.sql index 50207fc..7735b92 100644 --- a/schoolNewsServ/.bin/mysql/sql/initAll.sql +++ b/schoolNewsServ/.bin/mysql/sql/initAll.sql @@ -37,6 +37,8 @@ SOURCE createTableSystem.sql; SOURCE createTableAchievement.sql; +SOURCE createTableCrontab.sql; + -- ===================================================== -- 插入初始数据 -- ===================================================== diff --git a/schoolNewsServ/.bin/mysql/sql/initMenuData.sql b/schoolNewsServ/.bin/mysql/sql/initMenuData.sql index 92b69b1..7f2a11e 100644 --- a/schoolNewsServ/.bin/mysql/sql/initMenuData.sql +++ b/schoolNewsServ/.bin/mysql/sql/initMenuData.sql @@ -25,7 +25,8 @@ INSERT INTO `tb_sys_module` (id, module_id, name, code, description, icon, order ('3', 'module_study', '学习管理', 'study', '学习管理模块', 'el-icon-reading', 3, 1, '1', now()), ('4', 'module_ai', 'AI管理', 'ai', 'AI管理模块', 'el-icon-cpu', 4, 1, '1', now()), ('5', 'module_usercenter', '用户中心', 'usercenter', '用户中心模块', 'el-icon-user', 5, 1, '1', now()), -('6', 'module_file', '文件管理', 'file', '文件管理模块', 'el-icon-folder', 6, 1, '1', now()); +('6', 'module_file', '文件管理', 'file', '文件管理模块', 'el-icon-folder', 6, 1, '1', now()), +('7', 'module_crontab', '定时任务', 'crontab', '定时任务管理模块', 'el-icon-alarm-clock', 7, 1, '1', now()); -- 插入权限数据 INSERT INTO `tb_sys_permission` (id,permission_id, name, code, description, module_id, creator, create_time) VALUES @@ -43,7 +44,9 @@ INSERT INTO `tb_sys_permission` (id,permission_id, name, code, description, modu ('10.1','perm_achievement_manage', '成就管理', 'achievement:manage', '成就管理权限', 'module_study', '1', now()), ('11','perm_ai_manage', 'AI管理', 'ai:manage', 'AI管理权限', 'module_ai', '1', now()), ('12','perm_usercenter_manage', '用户中心管理', 'usercenter:manage', '用户中心管理权限', 'module_usercenter', '1', now()), -('13','perm_file_manage', '文件管理', 'file:manage', '文件管理权限', 'module_file', '1', now()); +('13','perm_file_manage', '文件管理', 'file:manage', '文件管理权限', 'module_file', '1', now()), +('14','perm_crontab_manage', '定时任务管理', 'crontab:manage', '定时任务管理权限', 'module_crontab', '1', now()), +('15','perm_crontab_execute', '定时任务执行', 'crontab:execute', '定时任务执行权限', 'module_crontab', '1', now()); -- 插入角色-权限关联数据 INSERT INTO `tb_sys_role_permission` (id, role_id, permission_id, creator, create_time) VALUES @@ -62,7 +65,11 @@ INSERT INTO `tb_sys_role_permission` (id, role_id, permission_id, creator, creat ('11', 'superadmin', 'perm_ai_manage', '1', now()), ('12', 'superadmin', 'perm_usercenter_manage', '1', now()), ('13', 'superadmin', 'perm_file_manage', '1', now()), -('14', 'freedom', 'perm_default', '1', now()); +('14', 'freedom', 'perm_default', '1', now()), +('15', 'superadmin', 'perm_crontab_manage', '1', now()), +('16', 'superadmin', 'perm_crontab_execute', '1', now()), +('17', 'admin', 'perm_crontab_manage', '1', now()), +('18', 'admin', 'perm_crontab_execute', '1', now()); -- 插入前端菜单数据 INSERT INTO `tb_sys_menu` (id, menu_id, name, parent_id, url, component, icon, order_num, type, layout, creator, create_time) VALUES @@ -138,7 +145,13 @@ INSERT INTO `tb_sys_menu` (id, menu_id, name, parent_id, url, component, icon, o ('7001', 'menu_admin_system_logs', '系统日志', 'menu_admin_logs_manage', '/admin/manage/logs/system', 'admin/manage/logs/SystemLogsView', 'el-icon-document', 1, 1, 'NavigationLayout', '1', now()), ('7002', 'menu_admin_login_logs', '登录日志', 'menu_admin_logs_manage', '/admin/manage/logs/login', 'admin/manage/logs/LoginLogsView', 'el-icon-key', 2, 1, 'NavigationLayout', '1', now()), ('7003', 'menu_admin_operation_logs', '操作日志', 'menu_admin_logs_manage', '/admin/manage/logs/operation', 'admin/manage/logs/OperationLogsView', 'el-icon-s-operation', 3, 1, 'NavigationLayout', '1', now()), -('7004', 'menu_admin_system_config', '系统配置', 'menu_admin_logs_manage', '/admin/manage/logs/config', 'admin/manage/logs/SystemConfigView', 'el-icon-setting', 4, 1, 'NavigationLayout', '1', now()); +('7004', 'menu_admin_system_config', '系统配置', 'menu_admin_logs_manage', '/admin/manage/logs/config', 'admin/manage/logs/SystemConfigView', 'el-icon-setting', 4, 1, 'NavigationLayout', '1', now()), + +-- 定时任务管理 +('8000', 'menu_admin_crontab_manage', '定时任务管理', NULL, '', '', 'el-icon-alarm-clock', 8, 1, '', '1', now()), +('8001', 'menu_admin_crontab_task', '任务管理', 'menu_admin_crontab_manage', '/admin/manage/crontab/task', 'admin/manage/crontab/TaskManagementView', 'el-icon-s-order', 1, 1, 'NavigationLayout', '1', now()), +('8002', 'menu_admin_crontab_log', '执行日志', 'menu_admin_crontab_manage', '/admin/manage/crontab/log', 'admin/manage/crontab/LogManagementView', 'el-icon-document', 2, 1, 'NavigationLayout', '1', now()), +('8003', 'menu_admin_news_crawler', '新闻爬虫配置', 'menu_admin_crontab_manage', '/admin/manage/crontab/news-crawler', 'admin/manage/crontab/NewsCrawlerView', 'el-icon-share', 3, 1, 'NavigationLayout', '1', now()); -- 插入菜单权限关联数据 INSERT INTO `tb_sys_menu_permission` (id, permission_id, menu_id, creator, create_time) VALUES @@ -195,4 +208,10 @@ INSERT INTO `tb_sys_menu_permission` (id, permission_id, menu_id, creator, creat ('228', 'perm_system_manage', 'menu_admin_system_logs', '1', now()), ('229', 'perm_system_manage', 'menu_admin_login_logs', '1', now()), ('230', 'perm_system_manage', 'menu_admin_operation_logs', '1', now()), -('231', 'perm_system_manage', 'menu_admin_system_config', '1', now()); +('231', 'perm_system_manage', 'menu_admin_system_config', '1', now()), + +-- 定时任务管理菜单权限关联 +('232', 'perm_crontab_manage', 'menu_admin_crontab_manage', '1', now()), +('233', 'perm_crontab_manage', 'menu_admin_crontab_task', '1', now()), +('234', 'perm_crontab_manage', 'menu_admin_crontab_log', '1', now()), +('235', 'perm_crontab_manage', 'menu_admin_news_crawler', '1', now()); diff --git a/schoolNewsServ/admin/pom.xml b/schoolNewsServ/admin/pom.xml index 6ce664d..06e9f77 100644 --- a/schoolNewsServ/admin/pom.xml +++ b/schoolNewsServ/admin/pom.xml @@ -69,6 +69,11 @@ ai ${school-news.version} + + org.xyzh + crontab + ${school-news.version} + diff --git a/schoolNewsServ/admin/src/main/java/org/xyzh/App.java b/schoolNewsServ/admin/src/main/java/org/xyzh/App.java index 9d5b3f0..c12cee7 100644 --- a/schoolNewsServ/admin/src/main/java/org/xyzh/App.java +++ b/schoolNewsServ/admin/src/main/java/org/xyzh/App.java @@ -3,6 +3,7 @@ package org.xyzh; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.transaction.annotation.EnableTransactionManagement; /** @@ -14,8 +15,9 @@ import org.springframework.transaction.annotation.EnableTransactionManagement; */ @EnableTransactionManagement @SpringBootApplication(scanBasePackages = "org.xyzh") +@EnableScheduling @MapperScan({"org.xyzh.system.mapper", "org.xyzh.file.mapper", "org.xyzh.news.mapper", "org.xyzh.study.mapper", - "org.xyzh.usercenter.mapper", "org.xyzh.ai.mapper", "org.xyzh.achievement.mapper"}) + "org.xyzh.usercenter.mapper", "org.xyzh.ai.mapper", "org.xyzh.achievement.mapper", "org.xyzh.crontab.mapper"}) public class App { public static void main(String[] args) { diff --git a/schoolNewsServ/api/api-all/pom.xml b/schoolNewsServ/api/api-all/pom.xml index 7bb5571..2350034 100644 --- a/schoolNewsServ/api/api-all/pom.xml +++ b/schoolNewsServ/api/api-all/pom.xml @@ -53,5 +53,9 @@ org.xyzh api-file + + org.xyzh + api-crontab + \ No newline at end of file diff --git a/schoolNewsServ/api/api-crontab/pom.xml b/schoolNewsServ/api/api-crontab/pom.xml new file mode 100644 index 0000000..9eb26f6 --- /dev/null +++ b/schoolNewsServ/api/api-crontab/pom.xml @@ -0,0 +1,33 @@ + + + 4.0.0 + + org.xyzh + api + ${school-news.version} + + + org.xyzh + api-crontab + ${school-news.version} + + + 21 + 21 + + + + + org.xyzh + common-core + ${school-news.version} + + + org.xyzh + common-dto + ${school-news.version} + + + diff --git a/schoolNewsServ/api/api-crontab/src/main/java/org/xyzh/api/crontab/CrontabService.java b/schoolNewsServ/api/api-crontab/src/main/java/org/xyzh/api/crontab/CrontabService.java new file mode 100644 index 0000000..3aa2751 --- /dev/null +++ b/schoolNewsServ/api/api-crontab/src/main/java/org/xyzh/api/crontab/CrontabService.java @@ -0,0 +1,169 @@ +package org.xyzh.api.crontab; + +import org.xyzh.common.core.domain.ResultDomain; +import org.xyzh.common.core.page.PageParam; +import org.xyzh.common.dto.crontab.TbCrontabTask; +import org.xyzh.common.dto.crontab.TbCrontabLog; +import org.xyzh.common.vo.CrontabTaskVO; +import org.xyzh.common.vo.CrontabLogVO; + +/** + * @description 定时任务服务接口 + * @filename CrontabService.java + * @author yslg + * @copyright xyzh + * @since 2025-10-25 + */ +public interface CrontabService { + + // ----------------定时任务管理-------------------------------- + + /** + * @description 创建定时任务 + * @param task 任务对象 + * @return ResultDomain 创建结果 + * @author yslg + * @since 2025-10-25 + */ + ResultDomain createTask(TbCrontabTask task); + + /** + * @description 更新定时任务 + * @param task 任务对象 + * @return ResultDomain 更新结果 + * @author yslg + * @since 2025-10-25 + */ + ResultDomain updateTask(TbCrontabTask task); + + /** + * @description 删除定时任务 + * @param taskId 任务ID + * @return ResultDomain 删除结果 + * @author yslg + * @since 2025-10-25 + */ + ResultDomain deleteTask(String taskId); + + /** + * @description 根据ID查询任务 + * @param taskId 任务ID + * @return ResultDomain 任务信息 + * @author yslg + * @since 2025-10-25 + */ + ResultDomain getTaskById(String taskId); + + /** + * @description 根据过滤条件查询任务列表 + * @param filter 过滤条件 + * @return ResultDomain 任务列表 + * @author yslg + * @since 2025-10-25 + */ + ResultDomain getTaskList(TbCrontabTask filter); + + /** + * @description 分页查询任务列表 + * @param filter 过滤条件 + * @param pageParam 分页参数 + * @return ResultDomain 任务列表 + * @author yslg + * @since 2025-10-25 + */ + ResultDomain getTaskPage(TbCrontabTask filter, PageParam pageParam); + + /** + * @description 启动定时任务 + * @param taskId 任务ID + * @return ResultDomain 启动结果 + * @author yslg + * @since 2025-10-25 + */ + ResultDomain startTask(String taskId); + + /** + * @description 暂停定时任务 + * @param taskId 任务ID + * @return ResultDomain 暂停结果 + * @author yslg + * @since 2025-10-25 + */ + ResultDomain pauseTask(String taskId); + + /** + * @description 立即执行一次任务 + * @param taskId 任务ID + * @return ResultDomain 执行结果 + * @author yslg + * @since 2025-10-25 + */ + ResultDomain executeTaskOnce(String taskId); + + /** + * @description 验证Cron表达式 + * @param cronExpression Cron表达式 + * @return ResultDomain 验证结果(返回下次执行时间) + * @author yslg + * @since 2025-10-25 + */ + ResultDomain validateCronExpression(String cronExpression); + + // ----------------定时任务日志-------------------------------- + + /** + * @description 根据任务ID查询执行日志 + * @param taskId 任务ID + * @return ResultDomain 日志列表 + * @author yslg + * @since 2025-10-25 + */ + ResultDomain getLogsByTaskId(String taskId); + + /** + * @description 根据过滤条件查询日志列表 + * @param filter 过滤条件 + * @return ResultDomain 日志列表 + * @author yslg + * @since 2025-10-25 + */ + ResultDomain getLogList(TbCrontabLog filter); + + /** + * @description 分页查询日志列表 + * @param filter 过滤条件 + * @param pageParam 分页参数 + * @return ResultDomain 日志列表 + * @author yslg + * @since 2025-10-25 + */ + ResultDomain getLogPage(TbCrontabLog filter, PageParam pageParam); + + /** + * @description 根据ID查询日志详情 + * @param logId 日志ID + * @return ResultDomain 日志详情 + * @author yslg + * @since 2025-10-25 + */ + ResultDomain getLogById(String logId); + + /** + * @description 清理指定天数之前的日志 + * @param days 天数 + * @return ResultDomain 清理结果(返回清理条数) + * @author yslg + * @since 2025-10-25 + */ + ResultDomain cleanLogs(Integer days); + + /** + * @description 删除任务执行日志 + * @param logId 日志ID + * @return ResultDomain 删除结果 + * @author yslg + * @since 2025-10-25 + */ + ResultDomain deleteLog(String logId); +} + diff --git a/schoolNewsServ/api/pom.xml b/schoolNewsServ/api/pom.xml index 2798bc2..472f3ea 100644 --- a/schoolNewsServ/api/pom.xml +++ b/schoolNewsServ/api/pom.xml @@ -23,6 +23,7 @@ api-study api-news api-file + api-crontab @@ -82,6 +83,11 @@ api-file ${school-news.version} + + org.xyzh + api-crontab + ${school-news.version} + diff --git a/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/dto/crontab/TbCrontabLog.java b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/dto/crontab/TbCrontabLog.java new file mode 100644 index 0000000..4ac36b9 --- /dev/null +++ b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/dto/crontab/TbCrontabLog.java @@ -0,0 +1,188 @@ +package org.xyzh.common.dto.crontab; + +import org.xyzh.common.dto.BaseDTO; + +import java.util.Date; + +/** + * @description 定时任务执行日志表 + * @filename TbCrontabLog.java + * @author yslg + * @copyright xyzh + * @since 2025-10-25 + */ +public class TbCrontabLog extends BaseDTO { + + private static final long serialVersionUID = 1L; + + /** + * @description 任务ID + */ + private String taskId; + + /** + * @description 任务名称 + */ + private String taskName; + + /** + * @description 任务分组 + */ + private String taskGroup; + + /** + * @description Bean名称 + */ + private String beanName; + + /** + * @description 方法名称 + */ + private String methodName; + + /** + * @description 方法参数 + */ + private String methodParams; + + /** + * @description 执行状态(0:失败 1:成功) + */ + private Integer executeStatus; + + /** + * @description 执行结果信息 + */ + private String executeMessage; + + /** + * @description 异常信息 + */ + private String exceptionInfo; + + /** + * @description 开始时间 + */ + private Date startTime; + + /** + * @description 结束时间 + */ + private Date endTime; + + /** + * @description 执行时长(毫秒) + */ + private Integer executeDuration; + + public String getTaskId() { + return taskId; + } + + public void setTaskId(String taskId) { + this.taskId = taskId; + } + + public String getTaskName() { + return taskName; + } + + public void setTaskName(String taskName) { + this.taskName = taskName; + } + + public String getTaskGroup() { + return taskGroup; + } + + public void setTaskGroup(String taskGroup) { + this.taskGroup = taskGroup; + } + + public String getBeanName() { + return beanName; + } + + public void setBeanName(String beanName) { + this.beanName = beanName; + } + + public String getMethodName() { + return methodName; + } + + public void setMethodName(String methodName) { + this.methodName = methodName; + } + + public String getMethodParams() { + return methodParams; + } + + public void setMethodParams(String methodParams) { + this.methodParams = methodParams; + } + + public Integer getExecuteStatus() { + return executeStatus; + } + + public void setExecuteStatus(Integer executeStatus) { + this.executeStatus = executeStatus; + } + + public String getExecuteMessage() { + return executeMessage; + } + + public void setExecuteMessage(String executeMessage) { + this.executeMessage = executeMessage; + } + + public String getExceptionInfo() { + return exceptionInfo; + } + + public void setExceptionInfo(String exceptionInfo) { + this.exceptionInfo = exceptionInfo; + } + + public Date getStartTime() { + return startTime; + } + + public void setStartTime(Date startTime) { + this.startTime = startTime; + } + + public Date getEndTime() { + return endTime; + } + + public void setEndTime(Date endTime) { + this.endTime = endTime; + } + + public Integer getExecuteDuration() { + return executeDuration; + } + + public void setExecuteDuration(Integer executeDuration) { + this.executeDuration = executeDuration; + } + + @Override + public String toString() { + return "TbCrontabLog{" + + "id=" + getID() + + ", taskId='" + taskId + '\'' + + ", taskName='" + taskName + '\'' + + ", taskGroup='" + taskGroup + '\'' + + ", executeStatus=" + executeStatus + + ", startTime=" + startTime + + ", endTime=" + endTime + + ", executeDuration=" + executeDuration + + '}'; + } +} + diff --git a/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/dto/crontab/TbCrontabTask.java b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/dto/crontab/TbCrontabTask.java new file mode 100644 index 0000000..97be520 --- /dev/null +++ b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/dto/crontab/TbCrontabTask.java @@ -0,0 +1,201 @@ +package org.xyzh.common.dto.crontab; + +import org.xyzh.common.dto.BaseDTO; + +/** + * @description 定时任务配置表 + * @filename TbCrontabTask.java + * @author yslg + * @copyright xyzh + * @since 2025-10-25 + */ +public class TbCrontabTask extends BaseDTO { + + private static final long serialVersionUID = 1L; + + /** + * @description 任务ID + */ + private String taskId; + + /** + * @description 任务名称 + */ + private String taskName; + + /** + * @description 任务分组 + */ + private String taskGroup; + + /** + * @description Bean名称 + */ + private String beanName; + + /** + * @description 方法名称 + */ + private String methodName; + + /** + * @description 方法参数 + */ + private String methodParams; + + /** + * @description Cron表达式 + */ + private String cronExpression; + + /** + * @description 任务状态(0:暂停 1:运行中) + */ + private Integer status; + + /** + * @description 任务描述 + */ + private String description; + + /** + * @description 是否允许并发执行(0:否 1:是) + */ + private Integer concurrent; + + /** + * @description 错过执行策略(1:立即执行 2:执行一次 3:放弃执行) + */ + private Integer misfirePolicy; + + /** + * @description 创建者 + */ + private String creator; + + /** + * @description 更新者 + */ + private String updater; + + public String getTaskId() { + return taskId; + } + + public void setTaskId(String taskId) { + this.taskId = taskId; + } + + public String getTaskName() { + return taskName; + } + + public void setTaskName(String taskName) { + this.taskName = taskName; + } + + public String getTaskGroup() { + return taskGroup; + } + + public void setTaskGroup(String taskGroup) { + this.taskGroup = taskGroup; + } + + public String getBeanName() { + return beanName; + } + + public void setBeanName(String beanName) { + this.beanName = beanName; + } + + public String getMethodName() { + return methodName; + } + + public void setMethodName(String methodName) { + this.methodName = methodName; + } + + public String getMethodParams() { + return methodParams; + } + + public void setMethodParams(String methodParams) { + this.methodParams = methodParams; + } + + public String getCronExpression() { + return cronExpression; + } + + public void setCronExpression(String cronExpression) { + this.cronExpression = cronExpression; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Integer getConcurrent() { + return concurrent; + } + + public void setConcurrent(Integer concurrent) { + this.concurrent = concurrent; + } + + public Integer getMisfirePolicy() { + return misfirePolicy; + } + + public void setMisfirePolicy(Integer misfirePolicy) { + this.misfirePolicy = misfirePolicy; + } + + public String getCreator() { + return creator; + } + + public void setCreator(String creator) { + this.creator = creator; + } + + public String getUpdater() { + return updater; + } + + public void setUpdater(String updater) { + this.updater = updater; + } + + @Override + public String toString() { + return "TbCrontabTask{" + + "id=" + getID() + + ", taskId='" + taskId + '\'' + + ", taskName='" + taskName + '\'' + + ", taskGroup='" + taskGroup + '\'' + + ", beanName='" + beanName + '\'' + + ", methodName='" + methodName + '\'' + + ", cronExpression='" + cronExpression + '\'' + + ", status=" + status + + ", concurrent=" + concurrent + + ", misfirePolicy=" + misfirePolicy + + '}'; + } +} + diff --git a/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/CrontabLogVO.java b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/CrontabLogVO.java new file mode 100644 index 0000000..ce90e76 --- /dev/null +++ b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/CrontabLogVO.java @@ -0,0 +1,212 @@ +package org.xyzh.common.vo; + +import java.io.Serializable; +import java.util.Date; + +/** + * @description 定时任务日志视图对象 + * @filename CrontabLogVO.java + * @author yslg + * @copyright xyzh + * @since 2025-10-25 + */ +public class CrontabLogVO implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * @description 日志ID + */ + private String id; + + /** + * @description 任务ID + */ + private String taskId; + + /** + * @description 任务名称 + */ + private String taskName; + + /** + * @description 任务分组 + */ + private String taskGroup; + + /** + * @description Bean名称 + */ + private String beanName; + + /** + * @description 方法名称 + */ + private String methodName; + + /** + * @description 方法参数 + */ + private String methodParams; + + /** + * @description 执行状态(0:失败 1:成功) + */ + private Integer executeStatus; + + /** + * @description 执行结果信息 + */ + private String executeMessage; + + /** + * @description 异常信息 + */ + private String exceptionInfo; + + /** + * @description 开始时间 + */ + private Date startTime; + + /** + * @description 结束时间 + */ + private Date endTime; + + /** + * @description 执行时长(毫秒) + */ + private Integer executeDuration; + + /** + * @description 创建时间 + */ + private Date createTime; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getTaskId() { + return taskId; + } + + public void setTaskId(String taskId) { + this.taskId = taskId; + } + + public String getTaskName() { + return taskName; + } + + public void setTaskName(String taskName) { + this.taskName = taskName; + } + + public String getTaskGroup() { + return taskGroup; + } + + public void setTaskGroup(String taskGroup) { + this.taskGroup = taskGroup; + } + + public String getBeanName() { + return beanName; + } + + public void setBeanName(String beanName) { + this.beanName = beanName; + } + + public String getMethodName() { + return methodName; + } + + public void setMethodName(String methodName) { + this.methodName = methodName; + } + + public String getMethodParams() { + return methodParams; + } + + public void setMethodParams(String methodParams) { + this.methodParams = methodParams; + } + + public Integer getExecuteStatus() { + return executeStatus; + } + + public void setExecuteStatus(Integer executeStatus) { + this.executeStatus = executeStatus; + } + + public String getExecuteMessage() { + return executeMessage; + } + + public void setExecuteMessage(String executeMessage) { + this.executeMessage = executeMessage; + } + + public String getExceptionInfo() { + return exceptionInfo; + } + + public void setExceptionInfo(String exceptionInfo) { + this.exceptionInfo = exceptionInfo; + } + + public Date getStartTime() { + return startTime; + } + + public void setStartTime(Date startTime) { + this.startTime = startTime; + } + + public Date getEndTime() { + return endTime; + } + + public void setEndTime(Date endTime) { + this.endTime = endTime; + } + + public Integer getExecuteDuration() { + return executeDuration; + } + + public void setExecuteDuration(Integer executeDuration) { + this.executeDuration = executeDuration; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + @Override + public String toString() { + return "CrontabLogVO{" + + "id='" + id + '\'' + + ", taskId='" + taskId + '\'' + + ", taskName='" + taskName + '\'' + + ", executeStatus=" + executeStatus + + ", startTime=" + startTime + + ", endTime=" + endTime + + ", executeDuration=" + executeDuration + + '}'; + } +} + diff --git a/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/CrontabTaskVO.java b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/CrontabTaskVO.java new file mode 100644 index 0000000..88e9097 --- /dev/null +++ b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/CrontabTaskVO.java @@ -0,0 +1,238 @@ +package org.xyzh.common.vo; + +import java.io.Serializable; +import java.util.Date; + +/** + * @description 定时任务视图对象 + * @filename CrontabTaskVO.java + * @author yslg + * @copyright xyzh + * @since 2025-10-25 + */ +public class CrontabTaskVO implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * @description 主键ID + */ + private String id; + + /** + * @description 任务ID + */ + private String taskId; + + /** + * @description 任务名称 + */ + private String taskName; + + /** + * @description 任务分组 + */ + private String taskGroup; + + /** + * @description Bean名称 + */ + private String beanName; + + /** + * @description 方法名称 + */ + private String methodName; + + /** + * @description 方法参数 + */ + private String methodParams; + + /** + * @description Cron表达式 + */ + private String cronExpression; + + /** + * @description 任务状态(0:暂停 1:运行中) + */ + private Integer status; + + /** + * @description 任务描述 + */ + private String description; + + /** + * @description 是否允许并发执行(0:否 1:是) + */ + private Integer concurrent; + + /** + * @description 错过执行策略(1:立即执行 2:执行一次 3:放弃执行) + */ + private Integer misfirePolicy; + + /** + * @description 下次执行时间 + */ + private Date nextExecuteTime; + + /** + * @description 创建者 + */ + private String creator; + + /** + * @description 创建时间 + */ + private Date createTime; + + /** + * @description 更新时间 + */ + private Date updateTime; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getTaskId() { + return taskId; + } + + public void setTaskId(String taskId) { + this.taskId = taskId; + } + + public String getTaskName() { + return taskName; + } + + public void setTaskName(String taskName) { + this.taskName = taskName; + } + + public String getTaskGroup() { + return taskGroup; + } + + public void setTaskGroup(String taskGroup) { + this.taskGroup = taskGroup; + } + + public String getBeanName() { + return beanName; + } + + public void setBeanName(String beanName) { + this.beanName = beanName; + } + + public String getMethodName() { + return methodName; + } + + public void setMethodName(String methodName) { + this.methodName = methodName; + } + + public String getMethodParams() { + return methodParams; + } + + public void setMethodParams(String methodParams) { + this.methodParams = methodParams; + } + + public String getCronExpression() { + return cronExpression; + } + + public void setCronExpression(String cronExpression) { + this.cronExpression = cronExpression; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Integer getConcurrent() { + return concurrent; + } + + public void setConcurrent(Integer concurrent) { + this.concurrent = concurrent; + } + + public Integer getMisfirePolicy() { + return misfirePolicy; + } + + public void setMisfirePolicy(Integer misfirePolicy) { + this.misfirePolicy = misfirePolicy; + } + + public Date getNextExecuteTime() { + return nextExecuteTime; + } + + public void setNextExecuteTime(Date nextExecuteTime) { + this.nextExecuteTime = nextExecuteTime; + } + + public String getCreator() { + return creator; + } + + public void setCreator(String creator) { + this.creator = creator; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public Date getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + @Override + public String toString() { + return "CrontabTaskVO{" + + "id='" + id + '\'' + + ", taskId='" + taskId + '\'' + + ", taskName='" + taskName + '\'' + + ", taskGroup='" + taskGroup + '\'' + + ", cronExpression='" + cronExpression + '\'' + + ", status=" + status + + ", nextExecuteTime=" + nextExecuteTime + + '}'; + } +} + diff --git a/schoolNewsServ/crontab/README.md b/schoolNewsServ/crontab/README.md new file mode 100644 index 0000000..53ec63c --- /dev/null +++ b/schoolNewsServ/crontab/README.md @@ -0,0 +1,248 @@ +# 定时任务模块 (Crontab Module) + +## 模块简介 + +定时任务模块是基于Spring Boot和Quartz框架实现的动态定时任务管理系统,支持任务的动态创建、修改、启动、暂停和删除,并提供完整的任务执行日志记录功能。 + +## 主要功能 + +### 1. 任务管理 +- **创建任务**: 动态创建定时任务,支持Cron表达式 +- **更新任务**: 修改任务配置,自动重新调度 +- **删除任务**: 删除任务并停止调度 +- **启动/暂停**: 控制任务的运行状态 +- **立即执行**: 手动触发任务执行一次 + +### 2. 任务配置 +- **Bean名称和方法名**: 指定要执行的Spring Bean和方法 +- **Cron表达式**: 灵活的时间调度配置 +- **并发控制**: 支持控制任务是否允许并发执行 +- **错过执行策略**: 配置任务错过执行时间后的处理策略 + +### 3. 日志记录 +- **执行记录**: 记录每次任务执行的详细信息 +- **执行时长**: 统计任务执行耗时 +- **异常信息**: 详细记录执行失败的异常堆栈 +- **日志清理**: 支持定期清理过期日志 + +## 数据库表结构 + +### tb_crontab_task (定时任务配置表) +```sql +- id: 主键ID +- task_id: 任务ID +- task_name: 任务名称 +- task_group: 任务分组 +- bean_name: Bean名称 +- method_name: 方法名称 +- method_params: 方法参数 +- cron_expression: Cron表达式 +- status: 任务状态(0:暂停 1:运行中) +- description: 任务描述 +- concurrent: 是否允许并发执行 +- misfire_policy: 错过执行策略 +- creator: 创建者 +- updater: 更新者 +- create_time: 创建时间 +- update_time: 更新时间 +- delete_time: 删除时间 +- deleted: 是否删除 +``` + +### tb_crontab_log (定时任务执行日志表) +```sql +- id: 主键ID +- task_id: 任务ID +- task_name: 任务名称 +- task_group: 任务分组 +- bean_name: Bean名称 +- method_name: 方法名称 +- method_params: 方法参数 +- execute_status: 执行状态(0:失败 1:成功) +- execute_message: 执行结果信息 +- exception_info: 异常信息 +- start_time: 开始时间 +- end_time: 结束时间 +- execute_duration: 执行时长(毫秒) +- create_time: 创建时间 +- update_time: 更新时间 +- delete_time: 删除时间 +- deleted: 是否删除 +``` + +## API接口 + +### 任务管理接口 + +#### 1. 创建定时任务 +``` +POST /crontab/task +Body: TbCrontabTask对象 +``` + +#### 2. 更新定时任务 +``` +PUT /crontab/task +Body: TbCrontabTask对象 +``` + +#### 3. 删除定时任务 +``` +DELETE /crontab/task +Body: TbCrontabTask对象 +``` + +#### 4. 查询任务详情 +``` +GET /crontab/task/{taskId} +``` + +#### 5. 查询任务列表 +``` +POST /crontab/task/list +Body: 过滤条件 +``` + +#### 6. 分页查询任务 +``` +POST /crontab/task/page +Body: PageRequest +``` + +#### 7. 启动任务 +``` +POST /crontab/task/start/{taskId} +``` + +#### 8. 暂停任务 +``` +POST /crontab/task/pause/{taskId} +``` + +#### 9. 立即执行任务 +``` +POST /crontab/task/execute/{taskId} +``` + +#### 10. 验证Cron表达式 +``` +GET /crontab/task/validate?cronExpression={expression} +``` + +### 日志管理接口 + +#### 1. 根据任务ID查询日志 +``` +GET /crontab/log/task/{taskId} +``` + +#### 2. 查询日志列表 +``` +POST /crontab/log/list +Body: 过滤条件 +``` + +#### 3. 分页查询日志 +``` +POST /crontab/log/page +Body: PageRequest +``` + +#### 4. 查询日志详情 +``` +GET /crontab/log/{logId} +``` + +#### 5. 清理过期日志 +``` +DELETE /crontab/log/clean/{days} +``` + +#### 6. 删除日志 +``` +DELETE /crontab/log +Body: TbCrontabLog对象 +``` + +## 示例任务 + +模块内置了三个示例任务: + +### 1. SystemStatisticsTask (系统数据统计任务) +- Bean名称: `systemStatisticsTask` +- 方法名称: `execute` +- 功能: 执行系统数据统计 + +### 2. LogCleanTask (清理过期日志任务) +- Bean名称: `logCleanTask` +- 方法名称: `execute` +- 功能: 清理指定天数之前的日志 +- 参数: 天数(默认30天) + +### 3. DataBackupTask (数据备份任务) +- Bean名称: `dataBackupTask` +- 方法名称: `execute` +- 功能: 执行数据备份 +- 参数: 备份类型(full-全量,incremental-增量) + +## 自定义任务 + +要创建自定义任务,按照以下步骤: + +1. 创建一个Spring Bean类 +```java +@Component("myCustomTask") +public class MyCustomTask { + + public void execute() { + // 任务执行逻辑 + } + + public void execute(String params) { + // 带参数的任务执行逻辑 + } +} +``` + +2. 在数据库中添加任务配置 +```sql +INSERT INTO tb_crontab_task (id, task_id, task_name, bean_name, method_name, cron_expression, status) +VALUES ('xxx', 'xxx', '我的自定义任务', 'myCustomTask', 'execute', '0 0 * * * ?', 1); +``` + +3. 或通过API接口创建任务 + +## Cron表达式示例 + +``` +0 0 1 * * ? 每天凌晨1点执行 +0 */5 * * * ? 每5分钟执行一次 +0 0 0 1 * ? 每月1号凌晨执行 +0 0 9-18 * * ? 每天9点到18点,每小时执行 +0 0 * * * ? 每小时执行 +``` + +## 注意事项 + +1. **Cron表达式格式**: 使用Spring的Cron表达式格式(6位或7位) +2. **并发控制**: 设置`concurrent=0`可防止同一任务并发执行 +3. **异常处理**: 任务执行失败会记录详细的异常信息到日志表 +4. **任务初始化**: 系统启动时会自动加载所有状态为"运行中"的任务 +5. **线程池配置**: 默认线程池大小为10,可在`SchedulerConfig`中调整 + +## 技术栈 + +- Spring Boot 3.5.6 +- Spring Scheduling +- MyBatis Plus 3.5.14 +- MySQL 9.4.0 +- Java 21 + +## 作者 + +yslg @ xyzh + +## 更新日期 + +2025-10-25 + diff --git a/schoolNewsServ/crontab/pom.xml b/schoolNewsServ/crontab/pom.xml new file mode 100644 index 0000000..c38662e --- /dev/null +++ b/schoolNewsServ/crontab/pom.xml @@ -0,0 +1,90 @@ + + + 4.0.0 + + org.xyzh + school-news + ${school-news.version} + + + org.xyzh + crontab + ${school-news.version} + + + 21 + 21 + + + + + + org.xyzh + api-crontab + ${school-news.version} + + + + + org.xyzh + common-all + ${school-news.version} + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-logging + + + + + + + com.baomidou + mybatis-plus-spring-boot3-starter + + + + + com.mysql + mysql-connector-j + + + + + com.zaxxer + HikariCP + + + + + org.springframework.boot + spring-boot-starter-log4j2 + + + + + org.springframework.boot + spring-boot-starter-quartz + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + true + + + + + diff --git a/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/config/SchedulerConfig.java b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/config/SchedulerConfig.java new file mode 100644 index 0000000..4e4aaeb --- /dev/null +++ b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/config/SchedulerConfig.java @@ -0,0 +1,45 @@ +package org.xyzh.crontab.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.TaskScheduler; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; + +/** + * @description 定时任务配置 + * @filename SchedulerConfig.java + * @author yslg + * @copyright xyzh + * @since 2025-10-25 + */ +@Configuration +@EnableScheduling +public class SchedulerConfig { + + /** + * @description 配置任务调度线程池 + * @return TaskScheduler + * @author yslg + * @since 2025-10-25 + */ + @Bean + public TaskScheduler taskScheduler() { + ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); + // 设置线程池大小 + scheduler.setPoolSize(10); + // 设置线程名称前缀 + scheduler.setThreadNamePrefix("crontab-task-"); + // 设置是否等待所有任务完成后再关闭 + scheduler.setWaitForTasksToCompleteOnShutdown(true); + // 设置等待时间(秒) + scheduler.setAwaitTerminationSeconds(60); + // 设置拒绝策略 + scheduler.setRejectedExecutionHandler(new java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy()); + // 初始化 + scheduler.initialize(); + + return scheduler; + } +} + diff --git a/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/controller/CrontabController.java b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/controller/CrontabController.java new file mode 100644 index 0000000..ef30cc6 --- /dev/null +++ b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/controller/CrontabController.java @@ -0,0 +1,230 @@ +package org.xyzh.crontab.controller; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.xyzh.api.crontab.CrontabService; +import org.xyzh.common.core.domain.ResultDomain; +import org.xyzh.common.core.page.PageParam; +import org.xyzh.common.core.page.PageRequest; +import org.xyzh.common.dto.crontab.TbCrontabTask; +import org.xyzh.common.dto.crontab.TbCrontabLog; + +/** + * @description 定时任务控制器 + * @filename CrontabController.java + * @author yslg + * @copyright xyzh + * @since 2025-10-25 + */ +@RestController +@RequestMapping("/crontab") +public class CrontabController { + + private static final Logger logger = LoggerFactory.getLogger(CrontabController.class); + + @Autowired + private CrontabService crontabService; + + // ----------------定时任务管理-------------------------------- + + /** + * @description 创建定时任务 + * @param task 任务对象 + * @return ResultDomain + * @author yslg + * @since 2025-10-25 + */ + @PostMapping("/task") + public ResultDomain createTask(@RequestBody TbCrontabTask task) { + return crontabService.createTask(task); + } + + /** + * @description 更新定时任务 + * @param task 任务对象 + * @return ResultDomain + * @author yslg + * @since 2025-10-25 + */ + @PutMapping("/task") + public ResultDomain updateTask(@RequestBody TbCrontabTask task) { + return crontabService.updateTask(task); + } + + /** + * @description 删除定时任务 + * @param task 任务对象 + * @return ResultDomain + * @author yslg + * @since 2025-10-25 + */ + @DeleteMapping("/task") + public ResultDomain deleteTask(@RequestBody TbCrontabTask task) { + return crontabService.deleteTask(task.getID()); + } + + /** + * @description 根据ID查询任务 + * @param taskId 任务ID + * @return ResultDomain + * @author yslg + * @since 2025-10-25 + */ + @GetMapping("/task/{taskId}") + public ResultDomain getTaskById(@PathVariable String taskId) { + return crontabService.getTaskById(taskId); + } + + /** + * @description 查询任务列表 + * @param filter 过滤条件 + * @return ResultDomain + * @author yslg + * @since 2025-10-25 + */ + @PostMapping("/task/list") + public ResultDomain getTaskList(@RequestBody TbCrontabTask filter) { + return crontabService.getTaskList(filter); + } + + /** + * @description 分页查询任务列表 + * @param pageRequest 分页请求对象 + * @return ResultDomain + * @author yslg + * @since 2025-10-25 + */ + @PostMapping("/task/page") + public ResultDomain getTaskPage(@RequestBody PageRequest pageRequest) { + TbCrontabTask filter = pageRequest.getFilter(); + PageParam pageParam = pageRequest.getPageParam(); + return crontabService.getTaskPage(filter, pageParam); + } + + /** + * @description 启动定时任务 + * @param taskId 任务ID + * @return ResultDomain + * @author yslg + * @since 2025-10-25 + */ + @PostMapping("/task/start/{taskId}") + public ResultDomain startTask(@PathVariable String taskId) { + return crontabService.startTask(taskId); + } + + /** + * @description 暂停定时任务 + * @param taskId 任务ID + * @return ResultDomain + * @author yslg + * @since 2025-10-25 + */ + @PostMapping("/task/pause/{taskId}") + public ResultDomain pauseTask(@PathVariable String taskId) { + return crontabService.pauseTask(taskId); + } + + /** + * @description 立即执行一次任务 + * @param taskId 任务ID + * @return ResultDomain + * @author yslg + * @since 2025-10-25 + */ + @PostMapping("/task/execute/{taskId}") + public ResultDomain executeTaskOnce(@PathVariable String taskId) { + return crontabService.executeTaskOnce(taskId); + } + + /** + * @description 验证Cron表达式 + * @param cronExpression Cron表达式 + * @return ResultDomain + * @author yslg + * @since 2025-10-25 + */ + @GetMapping("/task/validate") + public ResultDomain validateCronExpression(@RequestParam String cronExpression) { + return crontabService.validateCronExpression(cronExpression); + } + + // ----------------定时任务日志-------------------------------- + + /** + * @description 根据任务ID查询日志 + * @param taskId 任务ID + * @return ResultDomain + * @author yslg + * @since 2025-10-25 + */ + @GetMapping("/log/task/{taskId}") + public ResultDomain getLogsByTaskId(@PathVariable String taskId) { + return crontabService.getLogsByTaskId(taskId); + } + + /** + * @description 查询日志列表 + * @param filter 过滤条件 + * @return ResultDomain + * @author yslg + * @since 2025-10-25 + */ + @PostMapping("/log/list") + public ResultDomain getLogList(@RequestBody TbCrontabLog filter) { + return crontabService.getLogList(filter); + } + + /** + * @description 分页查询日志列表 + * @param pageRequest 分页请求对象 + * @return ResultDomain + * @author yslg + * @since 2025-10-25 + */ + @PostMapping("/log/page") + public ResultDomain getLogPage(@RequestBody PageRequest pageRequest) { + TbCrontabLog filter = pageRequest.getFilter(); + PageParam pageParam = pageRequest.getPageParam(); + return crontabService.getLogPage(filter, pageParam); + } + + /** + * @description 根据ID查询日志详情 + * @param logId 日志ID + * @return ResultDomain + * @author yslg + * @since 2025-10-25 + */ + @GetMapping("/log/{logId}") + public ResultDomain getLogById(@PathVariable String logId) { + return crontabService.getLogById(logId); + } + + /** + * @description 清理指定天数之前的日志 + * @param days 天数 + * @return ResultDomain + * @author yslg + * @since 2025-10-25 + */ + @DeleteMapping("/log/clean/{days}") + public ResultDomain cleanLogs(@PathVariable Integer days) { + return crontabService.cleanLogs(days); + } + + /** + * @description 删除日志 + * @param log 日志对象 + * @return ResultDomain + * @author yslg + * @since 2025-10-25 + */ + @DeleteMapping("/log") + public ResultDomain deleteLog(@RequestBody TbCrontabLog log) { + return crontabService.deleteLog(log.getID()); + } +} + diff --git a/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/mapper/CrontabLogMapper.java b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/mapper/CrontabLogMapper.java new file mode 100644 index 0000000..9620508 --- /dev/null +++ b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/mapper/CrontabLogMapper.java @@ -0,0 +1,124 @@ +package org.xyzh.crontab.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.xyzh.common.core.page.PageParam; +import org.xyzh.common.dto.crontab.TbCrontabLog; + +import java.util.Date; +import java.util.List; + +/** + * @description 定时任务日志数据访问层 + * @filename CrontabLogMapper.java + * @author yslg + * @copyright xyzh + * @since 2025-10-25 + */ +@Mapper +public interface CrontabLogMapper extends BaseMapper { + + /** + * @description 插入日志 + * @param log 日志信息 + * @return int 影响行数 + * @author yslg + * @since 2025-10-25 + */ + int insertLog(@Param("log") TbCrontabLog log); + + /** + * @description 根据ID查询日志 + * @param logId 日志ID + * @return TbCrontabLog 日志信息 + * @author yslg + * @since 2025-10-25 + */ + TbCrontabLog selectLogById(@Param("logId") String logId); + + /** + * @description 根据任务ID查询日志列表 + * @param taskId 任务ID + * @return List 日志列表 + * @author yslg + * @since 2025-10-25 + */ + List selectLogsByTaskId(@Param("taskId") String taskId); + + /** + * @description 根据过滤条件查询日志列表 + * @param filter 过滤条件 + * @return List 日志列表 + * @author yslg + * @since 2025-10-25 + */ + List selectLogList(@Param("filter") TbCrontabLog filter); + + /** + * @description 分页查询日志列表 + * @param filter 过滤条件 + * @param pageParam 分页参数 + * @return List 日志列表 + * @author yslg + * @since 2025-10-25 + */ + List selectLogPage(@Param("filter") TbCrontabLog filter, @Param("pageParam") PageParam pageParam); + + /** + * @description 统计日志总数 + * @param filter 过滤条件 + * @return long 总数 + * @author yslg + * @since 2025-10-25 + */ + long countLogs(@Param("filter") TbCrontabLog filter); + + /** + * @description 删除日志(逻辑删除) + * @param logId 日志ID + * @return int 影响行数 + * @author yslg + * @since 2025-10-25 + */ + int deleteLog(@Param("logId") String logId); + + /** + * @description 批量删除日志 + * @param ids 日志ID列表 + * @return int 影响行数 + * @author yslg + * @since 2025-10-25 + */ + int batchDeleteLogs(@Param("ids") List ids); + + /** + * @description 清理指定时间之前的日志 + * @param beforeDate 指定时间 + * @return int 影响行数 + * @author yslg + * @since 2025-10-25 + */ + int cleanLogsByDate(@Param("beforeDate") Date beforeDate); + + /** + * @description 根据任务ID和执行状态查询日志 + * @param taskId 任务ID + * @param executeStatus 执行状态 + * @return List 日志列表 + * @author yslg + * @since 2025-10-25 + */ + List selectLogsByTaskIdAndStatus(@Param("taskId") String taskId, @Param("executeStatus") Integer executeStatus); + + /** + * @description 查询最近的执行日志 + * @param taskId 任务ID + * @param limit 限制数量 + * @return List 日志列表 + * @author yslg + * @since 2025-10-25 + */ + List selectRecentLogs(@Param("taskId") String taskId, @Param("limit") Integer limit); +} + diff --git a/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/mapper/CrontabTaskMapper.java b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/mapper/CrontabTaskMapper.java new file mode 100644 index 0000000..8ca15c1 --- /dev/null +++ b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/mapper/CrontabTaskMapper.java @@ -0,0 +1,104 @@ +package org.xyzh.crontab.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.xyzh.common.core.page.PageParam; +import org.xyzh.common.dto.crontab.TbCrontabTask; + +import java.util.List; + +/** + * @description 定时任务数据访问层 + * @filename CrontabTaskMapper.java + * @author yslg + * @copyright xyzh + * @since 2025-10-25 + */ +@Mapper +public interface CrontabTaskMapper extends BaseMapper { + + /** + * @description 插入任务 + * @param task 任务信息 + * @return int 影响行数 + * @author yslg + * @since 2025-10-25 + */ + int insertTask(@Param("task") TbCrontabTask task); + + /** + * @description 更新任务 + * @param task 任务信息 + * @return int 影响行数 + * @author yslg + * @since 2025-10-25 + */ + int updateTask(@Param("task") TbCrontabTask task); + + /** + * @description 删除任务(逻辑删除) + * @param taskId 任务ID + * @return int 影响行数 + * @author yslg + * @since 2025-10-25 + */ + int deleteTask(@Param("taskId") String taskId); + + /** + * @description 根据ID查询任务 + * @param taskId 任务ID + * @return TbCrontabTask 任务信息 + * @author yslg + * @since 2025-10-25 + */ + TbCrontabTask selectTaskById(@Param("taskId") String taskId); + + /** + * @description 根据过滤条件查询任务列表 + * @param filter 过滤条件 + * @return List 任务列表 + * @author yslg + * @since 2025-10-25 + */ + List selectTaskList(@Param("filter") TbCrontabTask filter); + + /** + * @description 分页查询任务列表 + * @param filter 过滤条件 + * @param pageParam 分页参数 + * @return List 任务列表 + * @author yslg + * @since 2025-10-25 + */ + List selectTaskPage(@Param("filter") TbCrontabTask filter, @Param("pageParam") PageParam pageParam); + + /** + * @description 查询所有运行中的任务 + * @return List 任务列表 + * @author yslg + * @since 2025-10-25 + */ + List selectRunningTasks(); + + /** + * @description 更新任务状态 + * @param taskId 任务ID + * @param status 状态 + * @return int 影响行数 + * @author yslg + * @since 2025-10-25 + */ + int updateTaskStatus(@Param("taskId") String taskId, @Param("status") Integer status); + + /** + * @description 根据Bean名称和方法名称查询任务 + * @param beanName Bean名称 + * @param methodName 方法名称 + * @return TbCrontabTask 任务信息 + * @author yslg + * @since 2025-10-25 + */ + TbCrontabTask selectTaskByBeanAndMethod(@Param("beanName") String beanName, @Param("methodName") String methodName); +} + diff --git a/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/scheduler/SchedulerInitializer.java b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/scheduler/SchedulerInitializer.java new file mode 100644 index 0000000..b2ca69c --- /dev/null +++ b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/scheduler/SchedulerInitializer.java @@ -0,0 +1,57 @@ +package org.xyzh.crontab.scheduler; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; +import org.xyzh.common.dto.crontab.TbCrontabTask; +import org.xyzh.crontab.mapper.CrontabTaskMapper; + +import java.util.List; + +/** + * @description 定时任务初始化器,启动时加载所有运行中的任务 + * @filename SchedulerInitializer.java + * @author yslg + * @copyright xyzh + * @since 2025-10-25 + */ +@Component +public class SchedulerInitializer implements CommandLineRunner { + + private static final Logger logger = LoggerFactory.getLogger(SchedulerInitializer.class); + + @Autowired + private CrontabTaskMapper taskMapper; + + @Autowired + private SchedulerManager schedulerManager; + + @Override + public void run(String... args) throws Exception { + try { + logger.info("开始初始化定时任务..."); + + // 查询所有运行中的任务 + List runningTasks = taskMapper.selectRunningTasks(); + + if (runningTasks != null && !runningTasks.isEmpty()) { + for (TbCrontabTask task : runningTasks) { + try { + schedulerManager.scheduleTask(task); + logger.info("加载定时任务: {} [{}]", task.getTaskName(), task.getCronExpression()); + } catch (Exception e) { + logger.error("加载定时任务失败: {}", task.getTaskName(), e); + } + } + logger.info("定时任务初始化完成,共加载 {} 个任务", runningTasks.size()); + } else { + logger.info("没有需要加载的定时任务"); + } + } catch (Exception e) { + logger.error("定时任务初始化异常: ", e); + } + } +} + diff --git a/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/scheduler/SchedulerManager.java b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/scheduler/SchedulerManager.java new file mode 100644 index 0000000..38d4b7a --- /dev/null +++ b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/scheduler/SchedulerManager.java @@ -0,0 +1,157 @@ +package org.xyzh.crontab.scheduler; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.scheduling.TaskScheduler; +import org.springframework.scheduling.support.CronTrigger; +import org.springframework.stereotype.Component; +import org.xyzh.common.dto.crontab.TbCrontabTask; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledFuture; + +/** + * @description 定时任务调度管理器 + * @filename SchedulerManager.java + * @author yslg + * @copyright xyzh + * @since 2025-10-25 + */ +@Component +public class SchedulerManager { + + private static final Logger logger = LoggerFactory.getLogger(SchedulerManager.class); + + @Autowired + private TaskScheduler taskScheduler; + + @Autowired + private ApplicationContext applicationContext; + + @Autowired + private TaskExecutor taskExecutor; + + /** + * 存储已调度的任务 + * Key: 任务ID, Value: ScheduledFuture + */ + private final Map> scheduledTasks = new ConcurrentHashMap<>(); + + /** + * @description 调度任务 + * @param task 任务对象 + * @author yslg + * @since 2025-10-25 + */ + public void scheduleTask(TbCrontabTask task) { + try { + String taskId = task.getID(); + + // 如果任务已经在调度中,先取消 + if (scheduledTasks.containsKey(taskId)) { + unscheduleTask(task); + } + + // 创建Cron触发器 + CronTrigger cronTrigger = new CronTrigger(task.getCronExpression()); + + // 调度任务 + ScheduledFuture future = taskScheduler.schedule( + () -> taskExecutor.executeTask(task), + cronTrigger + ); + + // 保存调度信息 + scheduledTasks.put(taskId, future); + + logger.info("任务调度成功: {} [{}]", task.getTaskName(), task.getCronExpression()); + } catch (Exception e) { + logger.error("任务调度失败: {}", task.getTaskName(), e); + } + } + + /** + * @description 取消任务调度 + * @param task 任务对象 + * @author yslg + * @since 2025-10-25 + */ + public void unscheduleTask(TbCrontabTask task) { + try { + String taskId = task.getID(); + ScheduledFuture future = scheduledTasks.get(taskId); + + if (future != null) { + future.cancel(false); + scheduledTasks.remove(taskId); + logger.info("任务取消调度成功: {}", task.getTaskName()); + } + } catch (Exception e) { + logger.error("任务取消调度失败: {}", task.getTaskName(), e); + } + } + + /** + * @description 重新调度任务 + * @param task 任务对象 + * @author yslg + * @since 2025-10-25 + */ + public void rescheduleTask(TbCrontabTask task) { + unscheduleTask(task); + scheduleTask(task); + } + + /** + * @description 立即执行一次任务 + * @param task 任务对象 + * @author yslg + * @since 2025-10-25 + */ + public void executeTaskOnce(TbCrontabTask task) { + try { + taskExecutor.executeTask(task); + logger.info("立即执行任务: {}", task.getTaskName()); + } catch (Exception e) { + logger.error("立即执行任务失败: {}", task.getTaskName(), e); + } + } + + /** + * @description 检查任务是否在调度中 + * @param taskId 任务ID + * @return boolean + * @author yslg + * @since 2025-10-25 + */ + public boolean isTaskScheduled(String taskId) { + return scheduledTasks.containsKey(taskId); + } + + /** + * @description 获取所有调度中的任务数量 + * @return int + * @author yslg + * @since 2025-10-25 + */ + public int getScheduledTaskCount() { + return scheduledTasks.size(); + } + + /** + * @description 清除所有任务调度 + * @author yslg + * @since 2025-10-25 + */ + public void clearAllSchedules() { + scheduledTasks.forEach((taskId, future) -> { + future.cancel(false); + }); + scheduledTasks.clear(); + logger.info("已清除所有任务调度"); + } +} + diff --git a/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/scheduler/TaskExecutor.java b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/scheduler/TaskExecutor.java new file mode 100644 index 0000000..e4dc9ba --- /dev/null +++ b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/scheduler/TaskExecutor.java @@ -0,0 +1,128 @@ +package org.xyzh.crontab.scheduler; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Component; +import org.xyzh.common.dto.crontab.TbCrontabLog; +import org.xyzh.common.dto.crontab.TbCrontabTask; +import org.xyzh.common.utils.IDUtils; +import org.xyzh.crontab.mapper.CrontabLogMapper; + +import java.lang.reflect.Method; +import java.util.Date; + +/** + * @description 任务执行器 + * @filename TaskExecutor.java + * @author yslg + * @copyright xyzh + * @since 2025-10-25 + */ +@Component +public class TaskExecutor { + + private static final Logger logger = LoggerFactory.getLogger(TaskExecutor.class); + + @Autowired + private ApplicationContext applicationContext; + + @Autowired + private CrontabLogMapper logMapper; + + /** + * @description 执行任务 + * @param task 任务对象 + * @author yslg + * @since 2025-10-25 + */ + public void executeTask(TbCrontabTask task) { + Date startTime = new Date(); + TbCrontabLog log = new TbCrontabLog(); + log.setID(IDUtils.generateID()); + log.setTaskId(task.getTaskId()); + log.setTaskName(task.getTaskName()); + log.setTaskGroup(task.getTaskGroup()); + log.setBeanName(task.getBeanName()); + log.setMethodName(task.getMethodName()); + log.setMethodParams(task.getMethodParams()); + log.setStartTime(startTime); + log.setCreateTime(new Date()); + log.setDeleted(false); + + try { + // 检查是否允许并发执行 + if (task.getConcurrent() == 0) { + // TODO: 可以添加分布式锁来防止并发执行 + } + + // 获取Bean实例 + Object bean = applicationContext.getBean(task.getBeanName()); + + // 获取方法 + Method method; + if (task.getMethodParams() != null && !task.getMethodParams().isEmpty()) { + // 如果有参数,需要解析参数类型 + method = bean.getClass().getMethod(task.getMethodName(), String.class); + method.invoke(bean, task.getMethodParams()); + } else { + // 无参方法 + method = bean.getClass().getMethod(task.getMethodName()); + method.invoke(bean); + } + + // 执行成功 + Date endTime = new Date(); + log.setEndTime(endTime); + log.setExecuteDuration((int) (endTime.getTime() - startTime.getTime())); + log.setExecuteStatus(1); + log.setExecuteMessage("执行成功"); + + logger.info("任务执行成功: {} [{}ms]", task.getTaskName(), log.getExecuteDuration()); + } catch (Exception e) { + // 执行失败 + Date endTime = new Date(); + log.setEndTime(endTime); + log.setExecuteDuration((int) (endTime.getTime() - startTime.getTime())); + log.setExecuteStatus(0); + log.setExecuteMessage("执行失败"); + log.setExceptionInfo(getExceptionStackTrace(e)); + + logger.error("任务执行失败: {}", task.getTaskName(), e); + } finally { + // 保存执行日志 + try { + logMapper.insertLog(log); + } catch (Exception e) { + logger.error("保存任务执行日志失败: {}", task.getTaskName(), e); + } + } + } + + /** + * @description 获取异常堆栈信息 + * @param e 异常 + * @return String + * @author yslg + * @since 2025-10-25 + */ + private String getExceptionStackTrace(Exception e) { + StringBuilder sb = new StringBuilder(); + sb.append(e.getClass().getName()).append(": ").append(e.getMessage()).append("\n"); + + StackTraceElement[] stackTrace = e.getStackTrace(); + int limit = Math.min(stackTrace.length, 10); // 只保留前10行 + + for (int i = 0; i < limit; i++) { + sb.append("\tat ").append(stackTrace[i].toString()).append("\n"); + } + + if (stackTrace.length > limit) { + sb.append("\t... ").append(stackTrace.length - limit).append(" more\n"); + } + + return sb.toString(); + } +} + diff --git a/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/service/impl/CrontabServiceImpl.java b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/service/impl/CrontabServiceImpl.java new file mode 100644 index 0000000..0ff3e44 --- /dev/null +++ b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/service/impl/CrontabServiceImpl.java @@ -0,0 +1,494 @@ +package org.xyzh.crontab.service.impl; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.support.CronExpression; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.xyzh.api.crontab.CrontabService; +import org.xyzh.common.core.domain.ResultDomain; +import org.xyzh.common.core.page.PageParam; +import org.xyzh.common.dto.crontab.TbCrontabTask; +import org.xyzh.common.dto.crontab.TbCrontabLog; +import org.xyzh.common.utils.IDUtils; +import org.xyzh.crontab.mapper.CrontabTaskMapper; +import org.xyzh.crontab.mapper.CrontabLogMapper; +import org.xyzh.crontab.scheduler.SchedulerManager; + +import java.time.LocalDateTime; +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +/** + * @description 定时任务服务实现类 + * @filename CrontabServiceImpl.java + * @author yslg + * @copyright xyzh + * @since 2025-10-25 + */ +@Service +public class CrontabServiceImpl implements CrontabService { + + private static final Logger logger = LoggerFactory.getLogger(CrontabServiceImpl.class); + + @Autowired + private CrontabTaskMapper taskMapper; + + @Autowired + private CrontabLogMapper logMapper; + + @Autowired + private SchedulerManager schedulerManager; + + // ----------------定时任务管理-------------------------------- + + @Override + @Transactional(rollbackFor = Exception.class) + public ResultDomain createTask(TbCrontabTask task) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + // 验证Cron表达式 + if (!CronExpression.isValidExpression(task.getCronExpression())) { + resultDomain.fail("Cron表达式格式不正确"); + return resultDomain; + } + + // 生成ID + task.setID(IDUtils.generateID()); + task.setTaskId(IDUtils.generateID()); + task.setCreateTime(new Date()); + task.setDeleted(false); + + // 默认值 + if (task.getStatus() == null) { + task.setStatus(0); // 默认暂停 + } + if (task.getConcurrent() == null) { + task.setConcurrent(0); // 默认不允许并发 + } + if (task.getMisfirePolicy() == null) { + task.setMisfirePolicy(1); // 默认立即执行 + } + + int result = taskMapper.insertTask(task); + if (result > 0) { + logger.info("创建定时任务成功: {}", task.getTaskName()); + + // 如果任务状态为启动,则立即调度 + if (task.getStatus() == 1) { + schedulerManager.scheduleTask(task); + } + + resultDomain.success("创建定时任务成功", task); + } else { + resultDomain.fail("创建定时任务失败"); + } + } catch (Exception e) { + logger.error("创建定时任务异常: ", e); + resultDomain.fail("创建定时任务异常: " + e.getMessage()); + } + return resultDomain; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public ResultDomain updateTask(TbCrontabTask task) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + if (task.getID() == null) { + resultDomain.fail("任务ID不能为空"); + return resultDomain; + } + + // 验证Cron表达式 + if (task.getCronExpression() != null && !CronExpression.isValidExpression(task.getCronExpression())) { + resultDomain.fail("Cron表达式格式不正确"); + return resultDomain; + } + + task.setUpdateTime(new Date()); + int result = taskMapper.updateTask(task); + + if (result > 0) { + logger.info("更新定时任务成功: {}", task.getTaskName()); + + // 重新调度任务 + TbCrontabTask dbTask = taskMapper.selectTaskById(task.getID()); + if (dbTask != null && dbTask.getStatus() == 1) { + schedulerManager.rescheduleTask(dbTask); + } + + resultDomain.success("更新定时任务成功", task); + } else { + resultDomain.fail("更新定时任务失败"); + } + } catch (Exception e) { + logger.error("更新定时任务异常: ", e); + resultDomain.fail("更新定时任务异常: " + e.getMessage()); + } + return resultDomain; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public ResultDomain deleteTask(String taskId) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + if (taskId == null || taskId.isEmpty()) { + resultDomain.fail("任务ID不能为空"); + return resultDomain; + } + + // 先停止任务调度 + TbCrontabTask task = taskMapper.selectTaskById(taskId); + if (task != null && task.getStatus() == 1) { + schedulerManager.unscheduleTask(task); + } + + int result = taskMapper.deleteTask(taskId); + if (result > 0) { + logger.info("删除定时任务成功,任务ID: {}", taskId); + resultDomain.success("删除定时任务成功", (TbCrontabTask) null); + } else { + resultDomain.fail("删除定时任务失败"); + } + } catch (Exception e) { + logger.error("删除定时任务异常: ", e); + resultDomain.fail("删除定时任务异常: " + e.getMessage()); + } + return resultDomain; + } + + @Override + public ResultDomain getTaskById(String taskId) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + if (taskId == null || taskId.isEmpty()) { + resultDomain.fail("任务ID不能为空"); + return resultDomain; + } + + TbCrontabTask task = taskMapper.selectTaskById(taskId); + if (task != null) { + resultDomain.success("查询成功", task); + } else { + resultDomain.fail("任务不存在"); + } + } catch (Exception e) { + logger.error("查询定时任务异常: ", e); + resultDomain.fail("查询定时任务异常: " + e.getMessage()); + } + return resultDomain; + } + + @Override + public ResultDomain getTaskList(TbCrontabTask filter) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + if (filter == null) { + filter = new TbCrontabTask(); + } + filter.setDeleted(false); + + List list = taskMapper.selectTaskList(filter); + resultDomain.success("查询成功", list); + } catch (Exception e) { + logger.error("查询定时任务列表异常: ", e); + resultDomain.fail("查询定时任务列表异常: " + e.getMessage()); + } + return resultDomain; + } + + @Override + public ResultDomain getTaskPage(TbCrontabTask filter, PageParam pageParam) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + if (filter == null) { + filter = new TbCrontabTask(); + } + filter.setDeleted(false); + + if (pageParam == null) { + pageParam = new PageParam(); + } + + List list = taskMapper.selectTaskPage(filter, pageParam); + resultDomain.success("查询成功", list); + } catch (Exception e) { + logger.error("分页查询定时任务异常: ", e); + resultDomain.fail("分页查询定时任务异常: " + e.getMessage()); + } + return resultDomain; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public ResultDomain startTask(String taskId) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + if (taskId == null || taskId.isEmpty()) { + resultDomain.fail("任务ID不能为空"); + return resultDomain; + } + + TbCrontabTask task = taskMapper.selectTaskById(taskId); + if (task == null) { + resultDomain.fail("任务不存在"); + return resultDomain; + } + + if (task.getStatus() == 1) { + resultDomain.fail("任务已在运行中"); + return resultDomain; + } + + // 更新状态为运行中 + int result = taskMapper.updateTaskStatus(taskId, 1); + if (result > 0) { + task.setStatus(1); + // 调度任务 + schedulerManager.scheduleTask(task); + + logger.info("启动定时任务成功: {}", task.getTaskName()); + resultDomain.success("启动定时任务成功", task); + } else { + resultDomain.fail("启动定时任务失败"); + } + } catch (Exception e) { + logger.error("启动定时任务异常: ", e); + resultDomain.fail("启动定时任务异常: " + e.getMessage()); + } + return resultDomain; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public ResultDomain pauseTask(String taskId) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + if (taskId == null || taskId.isEmpty()) { + resultDomain.fail("任务ID不能为空"); + return resultDomain; + } + + TbCrontabTask task = taskMapper.selectTaskById(taskId); + if (task == null) { + resultDomain.fail("任务不存在"); + return resultDomain; + } + + if (task.getStatus() == 0) { + resultDomain.fail("任务已暂停"); + return resultDomain; + } + + // 取消调度 + schedulerManager.unscheduleTask(task); + + // 更新状态为暂停 + int result = taskMapper.updateTaskStatus(taskId, 0); + if (result > 0) { + task.setStatus(0); + logger.info("暂停定时任务成功: {}", task.getTaskName()); + resultDomain.success("暂停定时任务成功", task); + } else { + resultDomain.fail("暂停定时任务失败"); + } + } catch (Exception e) { + logger.error("暂停定时任务异常: ", e); + resultDomain.fail("暂停定时任务异常: " + e.getMessage()); + } + return resultDomain; + } + + @Override + public ResultDomain executeTaskOnce(String taskId) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + if (taskId == null || taskId.isEmpty()) { + resultDomain.fail("任务ID不能为空"); + return resultDomain; + } + + TbCrontabTask task = taskMapper.selectTaskById(taskId); + if (task == null) { + resultDomain.fail("任务不存在"); + return resultDomain; + } + + // 立即执行一次 + schedulerManager.executeTaskOnce(task); + + logger.info("立即执行定时任务: {}", task.getTaskName()); + resultDomain.success("立即执行定时任务成功", task); + } catch (Exception e) { + logger.error("立即执行定时任务异常: ", e); + resultDomain.fail("立即执行定时任务异常: " + e.getMessage()); + } + return resultDomain; + } + + @Override + public ResultDomain validateCronExpression(String cronExpression) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + if (cronExpression == null || cronExpression.isEmpty()) { + resultDomain.fail("Cron表达式不能为空"); + return resultDomain; + } + + if (!CronExpression.isValidExpression(cronExpression)) { + resultDomain.fail("Cron表达式格式不正确"); + return resultDomain; + } + + // 计算下次执行时间 + CronExpression cron = CronExpression.parse(cronExpression); + LocalDateTime nextExecution = cron.next(LocalDateTime.now()); + + if (nextExecution != null) { + String message = "Cron表达式有效,下次执行时间: " + nextExecution; + resultDomain.success("验证成功", message); + } else { + resultDomain.fail("无法计算下次执行时间"); + } + } catch (Exception e) { + logger.error("验证Cron表达式异常: ", e); + resultDomain.fail("验证Cron表达式异常: " + e.getMessage()); + } + return resultDomain; + } + + // ----------------定时任务日志-------------------------------- + + @Override + public ResultDomain getLogsByTaskId(String taskId) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + if (taskId == null || taskId.isEmpty()) { + resultDomain.fail("任务ID不能为空"); + return resultDomain; + } + + List list = logMapper.selectLogsByTaskId(taskId); + resultDomain.success("查询成功", list); + } catch (Exception e) { + logger.error("查询任务日志异常: ", e); + resultDomain.fail("查询任务日志异常: " + e.getMessage()); + } + return resultDomain; + } + + @Override + public ResultDomain getLogList(TbCrontabLog filter) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + if (filter == null) { + filter = new TbCrontabLog(); + } + filter.setDeleted(false); + + List list = logMapper.selectLogList(filter); + resultDomain.success("查询成功", list); + } catch (Exception e) { + logger.error("查询日志列表异常: ", e); + resultDomain.fail("查询日志列表异常: " + e.getMessage()); + } + return resultDomain; + } + + @Override + public ResultDomain getLogPage(TbCrontabLog filter, PageParam pageParam) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + if (filter == null) { + filter = new TbCrontabLog(); + } + filter.setDeleted(false); + + if (pageParam == null) { + pageParam = new PageParam(); + } + + List list = logMapper.selectLogPage(filter, pageParam); + resultDomain.success("查询成功", list); + } catch (Exception e) { + logger.error("分页查询日志异常: ", e); + resultDomain.fail("分页查询日志异常: " + e.getMessage()); + } + return resultDomain; + } + + @Override + public ResultDomain getLogById(String logId) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + if (logId == null || logId.isEmpty()) { + resultDomain.fail("日志ID不能为空"); + return resultDomain; + } + + TbCrontabLog log = logMapper.selectLogById(logId); + if (log != null) { + resultDomain.success("查询成功", log); + } else { + resultDomain.fail("日志不存在"); + } + } catch (Exception e) { + logger.error("查询日志详情异常: ", e); + resultDomain.fail("查询日志详情异常: " + e.getMessage()); + } + return resultDomain; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public ResultDomain cleanLogs(Integer days) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + if (days == null || days <= 0) { + resultDomain.fail("天数参数无效"); + return resultDomain; + } + + Calendar calendar = Calendar.getInstance(); + calendar.add(Calendar.DAY_OF_MONTH, -days); + Date beforeDate = calendar.getTime(); + + int count = logMapper.cleanLogsByDate(beforeDate); + logger.info("清理{}天前的日志,共{}条", days, count); + + resultDomain.success("清理成功", count); + } catch (Exception e) { + logger.error("清理日志异常: ", e); + resultDomain.fail("清理日志异常: " + e.getMessage()); + } + return resultDomain; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public ResultDomain deleteLog(String logId) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + if (logId == null || logId.isEmpty()) { + resultDomain.fail("日志ID不能为空"); + return resultDomain; + } + + int result = logMapper.deleteLog(logId); + if (result > 0) { + logger.info("删除日志成功,日志ID: {}", logId); + resultDomain.success("删除日志成功", (TbCrontabLog) null); + } else { + resultDomain.fail("删除日志失败"); + } + } catch (Exception e) { + logger.error("删除日志异常: ", e); + resultDomain.fail("删除日志异常: " + e.getMessage()); + } + return resultDomain; + } +} diff --git a/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/task/DataBackupTask.java b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/task/DataBackupTask.java new file mode 100644 index 0000000..18126ed --- /dev/null +++ b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/task/DataBackupTask.java @@ -0,0 +1,60 @@ +package org.xyzh.crontab.task; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * @description 数据备份任务 + * @filename DataBackupTask.java + * @author yslg + * @copyright xyzh + * @since 2025-10-25 + */ +@Component("dataBackupTask") +public class DataBackupTask { + + private static final Logger logger = LoggerFactory.getLogger(DataBackupTask.class); + + /** + * @description 执行数据备份 + * @author yslg + * @since 2025-10-25 + */ + public void execute() { + logger.info("开始执行数据备份任务..."); + + try { + SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss"); + String backupTime = sdf.format(new Date()); + + // TODO: 实现数据备份逻辑 + // 1. 备份数据库 + // 2. 备份文件 + // 3. 压缩备份文件 + // 4. 上传到备份服务器或云存储 + + Thread.sleep(2000); // 模拟执行 + + logger.info("数据备份任务执行完成,备份标识: {}", backupTime); + } catch (Exception e) { + logger.error("数据备份任务执行失败: ", e); + throw new RuntimeException("数据备份任务执行失败", e); + } + } + + /** + * @description 执行带参数的备份任务 + * @param params 参数(备份类型:full-全量,incremental-增量) + * @author yslg + * @since 2025-10-25 + */ + public void execute(String params) { + logger.info("开始执行数据备份任务,备份类型: {}", params); + execute(); + } +} + diff --git a/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/task/LogCleanTask.java b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/task/LogCleanTask.java new file mode 100644 index 0000000..d20f007 --- /dev/null +++ b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/task/LogCleanTask.java @@ -0,0 +1,68 @@ +package org.xyzh.crontab.task; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.xyzh.crontab.mapper.CrontabLogMapper; + +import java.util.Calendar; +import java.util.Date; + +/** + * @description 清理过期日志任务 + * @filename LogCleanTask.java + * @author yslg + * @copyright xyzh + * @since 2025-10-25 + */ +@Component("logCleanTask") +public class LogCleanTask { + + private static final Logger logger = LoggerFactory.getLogger(LogCleanTask.class); + + @Autowired + private CrontabLogMapper logMapper; + + /** + * @description 执行日志清理,默认清理30天前的日志 + * @author yslg + * @since 2025-10-25 + */ + public void execute() { + execute("30"); + } + + /** + * @description 执行日志清理 + * @param params 天数参数 + * @author yslg + * @since 2025-10-25 + */ + public void execute(String params) { + logger.info("开始执行日志清理任务..."); + + try { + int days = 30; // 默认30天 + if (params != null && !params.isEmpty()) { + try { + days = Integer.parseInt(params); + } catch (NumberFormatException e) { + logger.warn("参数格式错误,使用默认值30天"); + } + } + + Calendar calendar = Calendar.getInstance(); + calendar.add(Calendar.DAY_OF_MONTH, -days); + Date beforeDate = calendar.getTime(); + + int count = logMapper.cleanLogsByDate(beforeDate); + + logger.info("日志清理任务执行完成,共清理{}条日志", count); + } catch (Exception e) { + logger.error("日志清理任务执行失败: ", e); + throw new RuntimeException("日志清理任务执行失败", e); + } + } +} + diff --git a/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/task/SystemStatisticsTask.java b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/task/SystemStatisticsTask.java new file mode 100644 index 0000000..65547fe --- /dev/null +++ b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/task/SystemStatisticsTask.java @@ -0,0 +1,54 @@ +package org.xyzh.crontab.task; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +/** + * @description 系统数据统计任务 + * @filename SystemStatisticsTask.java + * @author yslg + * @copyright xyzh + * @since 2025-10-25 + */ +@Component("systemStatisticsTask") +public class SystemStatisticsTask { + + private static final Logger logger = LoggerFactory.getLogger(SystemStatisticsTask.class); + + /** + * @description 执行系统数据统计 + * @author yslg + * @since 2025-10-25 + */ + public void execute() { + logger.info("开始执行系统数据统计任务..."); + + try { + // TODO: 实现系统数据统计逻辑 + // 1. 统计用户数据 + // 2. 统计资源数据 + // 3. 统计访问数据 + // 4. 生成统计报告 + + Thread.sleep(1000); // 模拟执行 + + logger.info("系统数据统计任务执行完成"); + } catch (Exception e) { + logger.error("系统数据统计任务执行失败: ", e); + throw new RuntimeException("系统数据统计任务执行失败", e); + } + } + + /** + * @description 执行带参数的统计任务 + * @param params 参数 + * @author yslg + * @since 2025-10-25 + */ + public void execute(String params) { + logger.info("开始执行系统数据统计任务,参数: {}", params); + execute(); + } +} + diff --git a/schoolNewsServ/crontab/src/main/resources/mapper/CrontabLogMapper.xml b/schoolNewsServ/crontab/src/main/resources/mapper/CrontabLogMapper.xml new file mode 100644 index 0000000..64a13c1 --- /dev/null +++ b/schoolNewsServ/crontab/src/main/resources/mapper/CrontabLogMapper.xml @@ -0,0 +1,194 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + id, task_id, task_name, task_group, bean_name, method_name, method_params, + execute_status, execute_message, exception_info, start_time, end_time, + execute_duration, create_time, update_time, delete_time, deleted + + + + + + + + AND id = #{filter.ID} + + + AND task_id = #{filter.taskId} + + + AND task_name LIKE CONCAT('%', #{filter.taskName}, '%') + + + AND task_group = #{filter.taskGroup} + + + AND bean_name = #{filter.beanName} + + + AND execute_status = #{filter.executeStatus} + + + AND deleted = #{filter.deleted} + + + + + + + + INSERT INTO tb_crontab_log + + id, + task_id, + task_name, + task_group, + bean_name, + method_name, + method_params, + execute_status, + execute_message, + exception_info, + start_time, + end_time, + execute_duration, + create_time, + deleted + + VALUES + + #{log.ID}, + #{log.taskId}, + #{log.taskName}, + #{log.taskGroup}, + #{log.beanName}, + #{log.methodName}, + #{log.methodParams}, + #{log.executeStatus}, + #{log.executeMessage}, + #{log.exceptionInfo}, + #{log.startTime}, + #{log.endTime}, + #{log.executeDuration}, + #{log.createTime}, + 0 + + + + + + + + + + + + + + + + + + + + + UPDATE tb_crontab_log + SET deleted = 1, + delete_time = NOW() + WHERE id = #{logId} AND deleted = 0 + + + + + UPDATE tb_crontab_log + SET deleted = 1, + delete_time = NOW() + WHERE deleted = 0 + AND id IN + + #{id} + + + + + + UPDATE tb_crontab_log + SET deleted = 1, + delete_time = NOW() + WHERE deleted = 0 + AND create_time < #{beforeDate} + + + + + + + + diff --git a/schoolNewsServ/crontab/src/main/resources/mapper/CrontabTaskMapper.xml b/schoolNewsServ/crontab/src/main/resources/mapper/CrontabTaskMapper.xml new file mode 100644 index 0000000..5cae457 --- /dev/null +++ b/schoolNewsServ/crontab/src/main/resources/mapper/CrontabTaskMapper.xml @@ -0,0 +1,188 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + id, task_id, task_name, task_group, bean_name, method_name, method_params, + cron_expression, status, description, concurrent, misfire_policy, + creator, updater, create_time, update_time, delete_time, deleted + + + + + + + + AND id = #{filter.ID} + + + AND task_id = #{filter.taskId} + + + AND task_name LIKE CONCAT('%', #{filter.taskName}, '%') + + + AND task_group = #{filter.taskGroup} + + + AND bean_name = #{filter.beanName} + + + AND method_name = #{filter.methodName} + + + AND status = #{filter.status} + + + AND deleted = #{filter.deleted} + + + + + + + + INSERT INTO tb_crontab_task + + id, + task_id, + task_name, + task_group, + bean_name, + method_name, + method_params, + cron_expression, + status, + description, + concurrent, + misfire_policy, + creator, + create_time, + deleted + + VALUES + + #{task.ID}, + #{task.taskId}, + #{task.taskName}, + #{task.taskGroup}, + #{task.beanName}, + #{task.methodName}, + #{task.methodParams}, + #{task.cronExpression}, + #{task.status}, + #{task.description}, + #{task.concurrent}, + #{task.misfirePolicy}, + #{task.creator}, + #{task.createTime}, + 0 + + + + + + UPDATE tb_crontab_task + + task_name = #{task.taskName}, + task_group = #{task.taskGroup}, + bean_name = #{task.beanName}, + method_name = #{task.methodName}, + method_params = #{task.methodParams}, + cron_expression = #{task.cronExpression}, + status = #{task.status}, + description = #{task.description}, + concurrent = #{task.concurrent}, + misfire_policy = #{task.misfirePolicy}, + updater = #{task.updater}, + update_time = NOW() + + WHERE id = #{task.ID} AND deleted = 0 + + + + + UPDATE tb_crontab_task + SET deleted = 1, + delete_time = NOW() + WHERE id = #{taskId} AND deleted = 0 + + + + + + + + + + + + + + + + + UPDATE tb_crontab_task + SET status = #{status}, + update_time = NOW() + WHERE id = #{taskId} AND deleted = 0 + + + + + diff --git a/schoolNewsServ/pom.xml b/schoolNewsServ/pom.xml index fa7ecc0..e7c4460 100644 --- a/schoolNewsServ/pom.xml +++ b/schoolNewsServ/pom.xml @@ -20,6 +20,7 @@ achievement ai file + crontab @@ -117,6 +118,11 @@ org.xyzh ai ${school-news.version} + + + org.xyzh + crontab + ${school-news.version}