quartz实现动态任务管理系统

2023-12-14 15:42:10

我们在开发中经常要对任务进行系统性的管理,比如新加一个任务每天0点更新,修改任务,删除任务,暂停任务等,就需要写一个集中的任务管理器,这边实现一个简单的任务管理系统

没有简单了解请看?quartz入门详解

在数据库中创建任务表以及字段进行任务数据管理
实现任务实体类,调用quartz官方api,实现对任务管理的逻辑
书写对应的接口,主要为增加任务,删除任务,修改任务,任务详情,暂停任务
实现了简单的前端测试代码,可以对任务动态管理进行测试
在实际项目中一般使用数据库存储任务,使用quartz框架进行增删改查暂停开启,达到定时任务的管理

1.创建任务表,sql脚本为

CREATE TABLE scheduled_tasks (
    task_id INT AUTO_INCREMENT PRIMARY KEY, -- 任务ID,自增主键
    task_name VARCHAR(255) NOT NULL, -- 任务名称,不允许为空
    task_group VARCHAR(255), -- 任务分组
    cron_expression VARCHAR(255) NOT NULL, -- Cron表达式,不允许为空
    job_class_name VARCHAR(255) NOT NULL, -- 任务类名,不允许为空
    is_active BOOLEAN -- 是否启用任务
);

2.加上quartz依赖

<!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz -->
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.3.2</version>
</dependency>

3.编写yml配置

# 数据源配置
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/your_database  # 数据库连接URL
    username: your_username  # 数据库用户名
    password: your_password  # 数据库密码
    driver-class-name: com.mysql.cj.jdbc.Driver  # 数据库驱动类名

# Quartz调度器配置
quartz:
  job-store-type: jdbc  # Quartz作业存储类型为JDBC
  properties:
    org:
      quartz:
        scheduler:
          instanceName: your_instance_name  # Quartz调度器实例名称
          instanceId: your_instance_id  # Quartz调度器实例ID
        jobStore:
          class: org.quartz.impl.jdbcjobstore.JobStoreTX  # Quartz作业存储类型
          driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate  # Quartz数据库操作委托类
          dataSource: your_data_source_name  # 数据源名称,连接到Spring中的数据源
          tablePrefix: QRTZ_  # Quartz表格前缀
          isClustered: true  # 启用Quartz集群模式
          clusterCheckinInterval: 20000  # 集群检查间隔时间(毫秒)

4.编写实体任务类

创建一个Java实体类(ScheduledTask)来映射数据库表,并创建一个JPA Repository来执行CRUD操作
@Data
public class ScheduledTask {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long taskId;            // 主键自动生成的任务ID
    private String taskName;        // 任务名称
    private String taskGroup;       // 任务分组
    private String cronExpression;  // Cron表达式,用于调度任务执行时间
    private String jobClassName;    // 任务执行的类名
    private boolean isActive;       // 任务是否处于活动状态
}

5.实现一个ScheduledTaskRepository,对任务进行操作

public interface ScheduledTaskRepository extends JpaRepository<ScheduledTask, Long> {
    // 通过任务名称和分组查找任务
    Optional<ScheduledTask> findByTaskNameAndTaskGroup(String taskName, String taskGroup);

    // 查找所有处于指定活动状态的任务
    List<ScheduledTask> findAllByIsActive(boolean isActive);
}

6.实现任务服务逻辑

创建一个服务类,负责任务的增加、删除、修改、查询、暂停和开启等操作
@Service
public class ScheduledTaskService {

    @Autowired
    private ScheduledTaskRepository taskRepository; // 任务数据存储库

    @Autowired
    private Scheduler scheduler; // 调度器

    // 获取所有已计划任务
    public List<ScheduledTask> getAllTasks() {
        return taskRepository.findAll();
    }

    // 通过任务ID获取指定任务
    public Optional<ScheduledTask> getTaskById(Long taskId) {
        return taskRepository.findById(taskId);
    }

    // 添加新任务
    public void addTask(ScheduledTask task) {
        taskRepository.save(task); // 保存任务信息到数据库

        try {
            // 创建JobDetail,定义任务的执行类和唯一标识
            JobDetail jobDetail = JobBuilder.newJob()
                    .ofType((Class<? extends Job>) Class.forName(task.getJobClassName()))
                    .withIdentity(task.getTaskName(), task.getTaskGroup())
                    .build();

            // 创建CronTrigger,定义任务的调度策略
            CronTrigger trigger = TriggerBuilder.newTrigger()
                    .withIdentity(task.getTaskName(), task.getTaskGroup())
                    .withSchedule(CronScheduleBuilder.cronSchedule(task.getCronExpression()))
                    .build();

            // 将任务和触发器添加到调度器中
            scheduler.scheduleJob(jobDetail, trigger);
        } catch (SchedulerException | ClassNotFoundException e) {
            // 处理异常
        }
    }

    // 更新任务信息
    public void updateTask(ScheduledTask task) {
        taskRepository.save(task); // 更新任务信息到数据库

        try {
            // 获取原始任务的触发器标识
            TriggerKey triggerKey = new TriggerKey(task.getTaskName(), task.getTaskGroup());

            // 创建新的CronTrigger,更新任务的调度策略
            CronTrigger newTrigger = TriggerBuilder.newTrigger()
                    .withIdentity(task.getTaskName(), task.getTaskGroup())
                    .withSchedule(CronScheduleBuilder.cronSchedule(task.getCronExpression()))
                    .build();

            // 重新调度任务
            scheduler.rescheduleJob(triggerKey, newTrigger);
        } catch (SchedulerException e) {
            // 处理异常
        }
    }
   // 通过任务ID删除任务
   public void deleteTask(Long taskId) {
    
    Optional<ScheduledTask> task = taskRepository.findById(taskId);
    if (task.isPresent()) {
        // 从存储库中删除任务
        taskRepository.deleteById(taskId);

        try {
            // 根据任务信息创建作业键和触发器键
            JobKey jobKey = new JobKey(task.get().getTaskName(), task.get().getTaskGroup());
            TriggerKey triggerKey = new TriggerKey(task.get().getTaskName(), task.get().getTaskGroup());

            // 从调度程序中删除计划作业并取消其触发器
            scheduler.deleteJob(jobKey);
            scheduler.unscheduleJob(triggerKey);
        } catch (SchedulerException e) {
            // 处理调度程序操作可能引发的异常
        }
    }
}
   // 暂停开始任务
   public void toggleTaskStatus(Long taskId) {
    
    Optional<ScheduledTask> task = taskRepository.findById(taskId);
    if (task.isPresent()) {
        // 切换任务的活动状态
        task.get().setActive(!task.get().isActive());
        // 将更新后的任务信息保存到存储库中
        taskRepository.save(task.get());

        try {
            // 根据任务信息创建作业键
            JobKey jobKey = new JobKey(task.get().getTaskName(), task.get().getTaskGroup());

            // 检查任务状态并相应地恢复或暂停作业
            if (task.get().isActive()) {
                scheduler.resumeJob(jobKey);
            } else {
                scheduler.pauseJob(jobKey);
            }
        } catch (SchedulerException e) {
            // 处理调度程序操作可能引发的异常
        }
    }
}

7.实现控制器

@RestController
@RequestMapping("/api/tasks")
public class ScheduledTaskController {

    @Autowired
    private ScheduledTaskService taskService;

    // 获取所有计划任务的端点
    @GetMapping
    public ResponseEntity<List<ScheduledTask>> getAllTasks() {
        List<ScheduledTask> tasks = taskService.getAllTasks();
        return ResponseEntity.ok(tasks);
    }

    // 通过任务ID获取单个计划任务的端点
    @GetMapping("/{taskId}")
    public ResponseEntity<ScheduledTask> getTaskById(@PathVariable Long taskId) {
        Optional<ScheduledTask> task = taskService.getTaskById(taskId);
        return task.map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build());
    }

    // 添加计划任务的端点
    @PostMapping
    public ResponseEntity<Void> addTask(@RequestBody ScheduledTask task) {
        taskService.addTask(task);
        return ResponseEntity.created(null).build();
    }

    // 更新计划任务的端点
    @PutMapping("/{taskId}")
    public ResponseEntity<Void> updateTask(@PathVariable Long taskId, @RequestBody ScheduledTask task) {
        if (!taskService.getTaskById(taskId).isPresent()) {
            return ResponseEntity.notFound().build();
        }

        taskService.updateTask(task);
        return ResponseEntity.noContent().build();
    }

    // 删除计划任务的端点
    @DeleteMapping("/{taskId}")
    public ResponseEntity<Void> deleteTask(@PathVariable Long taskId) {
        if (!taskService.getTaskById(taskId).isPresent()) {
            return ResponseEntity.notFound().build();
        }

        taskService.deleteTask(taskId);
        return ResponseEntity.noContent().build();
    }

    // 切换计划任务状态的端点
    @PatchMapping("/{taskId}/toggle-status")
    public ResponseEntity<Void> toggleTaskStatus(@PathVariable Long taskId) {
        if (!taskService.getTaskById(taskId).isPresent()) {
            return ResponseEntity.notFound().build();
        }

        taskService.toggleTaskStatus(taskId);
        return ResponseEntity.noContent().build();
    }
}

前端简单的html如下

<!DOCTYPE html>
<html>
<head>
    <title>Scheduled Task Management</title>
</head>
<body>
    <h1>Scheduled Task Management</h1>

    <!-- 列出所有任务 -->
    <h2>List of Tasks</h2>
    <ul id="taskList">
    </ul>

    <!-- 添加任务表单 -->
    <h2>Add a Task</h2>
    <form id="addTaskForm">
        <label for="taskName">Task Name:</label>
        <input type="text" id="taskName" name="taskName" required>
        <button type="submit">Add Task</button>
    </form>

    <!-- 更新任务表单 -->
    <h2>Update a Task</h2>
    <form id="updateTaskForm">
        <label for="taskIdToUpdate">Task ID to Update:</label>
        <input type="text" id="taskIdToUpdate" name="taskIdToUpdate" required>
        <label for="updatedTaskName">Updated Task Name:</label>
        <input type="text" id="updatedTaskName" name="updatedTaskName" required>
        <button type="submit">Update Task</button>
    </form>

    <!-- 删除任务表单 -->
    <h2>Delete a Task</h2>
    <form id="deleteTaskForm">
        <label for="taskIdToDelete">Task ID to Delete:</label>
        <input type="text" id="taskIdToDelete" name="taskIdToDelete" required>
        <button type="submit">Delete Task</button>
    </form>

    <!-- 切换任务状态表单 -->
    <h2>Toggle Task Status</h2>
    <form id="toggleTaskStatusForm">
        <label for="taskIdToToggle">Task ID to Toggle Status:</label>
        <input type="text" id="taskIdToToggle" name="taskIdToToggle" required>
        <button type="submit">Toggle Status</button>
    </form>

    <script>
        // JavaScript代码,用于与Spring Boot服务进行交互

        // 列出所有任务
        function listTasks() {
            fetch('/api/tasks')
                .then(response => response.json())
                .then(data => {
                    const taskList = document.getElementById('taskList');
                    taskList.innerHTML = '';
                    data.forEach(task => {
                        const listItem = document.createElement('li');
                        listItem.textContent = `Task ID: ${task.id}, Task Name: ${task.name}, Status: ${task.status}`;
                        taskList.appendChild(listItem);
                    });
                });
        }

        // 添加任务
        document.getElementById('addTaskForm').addEventListener('submit', function (event) {
            event.preventDefault();
            const taskName = document.getElementById('taskName').value;
            fetch('/api/tasks', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({ name: taskName })
            })
                .then(() => {
                    listTasks();
                });
        });

        // 更新任务
        document.getElementById('updateTaskForm').addEventListener('submit', function (event) {
            event.preventDefault();
            const taskId = document.getElementById('taskIdToUpdate').value;
            const updatedTaskName = document.getElementById('updatedTaskName').value;
            fetch(`/api/tasks/${taskId}`, {
                method: 'PUT',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({ id: taskId, name: updatedTaskName })
            })
                .then(() => {
                    listTasks();
                });
        });

        // 删除任务
        document.getElementById('deleteTaskForm').addEventListener('submit', function (event) {
            event.preventDefault();
            const taskId = document.getElementById('taskIdToDelete').value;
            fetch(`/api/tasks/${taskId}`, {
                method: 'DELETE'
            })
                .then(() => {
                    listTasks();
                });
        });

        // 切换任务状态
        document.getElementById('toggleTaskStatusForm').addEventListener('submit', function (event) {
            event.preventDefault();
            const taskId = document.getElementById('taskIdToToggle').value;
            fetch(`/api/tasks/${taskId}/toggle-status`, {
                method: 'PATCH'
            })
                .then(() => {
                    listTasks();
                });
        });

        // 页面加载时列出所有任务
        listTasks();
    </script>
</body>
</html>

这样子的话就可以实现一个简单的动态定时任务管理系统

文章来源:https://blog.csdn.net/m0_69156017/article/details/134996019
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。