最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

springboot?基于quartz?實現(xiàn)的單機可管理的調(diào)度任務(wù)(最新推薦)

 更新時間:2025年07月25日 10:31:31   作者:墨筆之風(fēng)  
本文介紹Spring Boot集成Quartz實現(xiàn)單機可管理調(diào)度任務(wù),通過自定義表替代默認11張表,涵蓋配置類、Controller、Service及前端TailwindCSS應(yīng)用,感興趣的朋友一起看看吧

先看效果圖:

由于是個單體項目走的【單體登錄】,屬于別的系統(tǒng)繼承的方式,所以將他設(shè)置為可訪問的html,便于管理,使用步驟是項目啟動后,需要先保存注冊內(nèi)部的quartz實現(xiàn),方可以實現(xiàn)調(diào)度任務(wù)運行

原本的 quartz 需要11 張表,但是我都用不到,我就用我自己的表,

  1. QRTZ_JOB_DETAILS
    存儲任務(wù)的基本信息(如任務(wù)名稱、所屬組、實現(xiàn)類等)。

  2. QRTZ_TRIGGERS
    存儲觸發(fā)器的基本信息(如觸發(fā)器名稱、所屬組、關(guān)聯(lián)的任務(wù)等)。

  3. QRTZ_SIMPLE_TRIGGERS
    存儲簡單觸發(fā)器(SimpleTrigger)的信息(如重復(fù)次數(shù)、間隔時間等)。

  4. QRTZ_CRON_TRIGGERS
    存儲 cron 表達式觸發(fā)器(CronTrigger)的信息(如 cron 表達式、時區(qū)等)。

  5. QRTZ_SIMPROP_TRIGGERS
    存儲復(fù)雜觸發(fā)器(SimplePropertiesTrigger)的信息(支持更多配置參數(shù))。

  6. QRTZ_BLOB_TRIGGERS
    存儲自定義觸發(fā)器的序列化數(shù)據(jù)(用于 Quartz 不直接支持的觸發(fā)器類型)。

  7. QRTZ_CALENDARS
    存儲日歷信息(用于排除特定日期,如節(jié)假日不執(zhí)行任務(wù))。

  8. QRTZ_PAUSED_TRIGGER_GRPS
    存儲被暫停的觸發(fā)器組信息。

  9. QRTZ_FIRED_TRIGGERS
    存儲正在執(zhí)行或已觸發(fā)的觸發(fā)器記錄(用于跟蹤任務(wù)執(zhí)行狀態(tài))。

  10. QRTZ_SCHEDULER_STATE
    存儲調(diào)度器的狀態(tài)信息(如當(dāng)前節(jié)點標(biāo)識、最后啟動時間等)。

  11. QRTZ_LOCKS
    存儲分布式調(diào)度時的鎖信息(用于避免多節(jié)點并發(fā)執(zhí)行同一任務(wù))。

這是我自己的業(yè)務(wù)所需要的表,獨此一張

CREATE TABLE `schedule_job` (
  `id` varchar(255) NOT NULL COMMENT '主鍵',
  `task_id` varchar(255) NOT NULL COMMENT ' 任務(wù)id',
  `job_class` varchar(255) DEFAULT NULL COMMENT '任務(wù)調(diào)度類名稱',
  `job_name` varchar(255) DEFAULT NULL COMMENT '工程名稱',
  `cron_expression` varchar(255) DEFAULT NULL COMMENT 'Cron表達式',
  `status` varchar(1) DEFAULT NULL COMMENT '是否開啟(0:未開,1:開啟)',
  `create_time` varchar(255) DEFAULT NULL COMMENT '創(chuàng)建時間',
  `update_time` varchar(255) DEFAULT NULL COMMENT '更新時間',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

下面上代碼:

配置類:

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
 * 請求跨域配置
 */
@Configuration
public class WebsConfig implements WebMvcConfigurer {
    /**
     * 允許訪問靜態(tài)資源
     *
     * @param registry
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        // 允許靜態(tài)文件直接訪問excel上傳頁面
        registry.addResourceHandler("/html/**")
                .addResourceLocations("classpath:/html/");
    }
    /**
     * 允許跨域訪問
     *
     * @param registry
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowedMethods("GET", "POST", "PUT", "DELETE")
                .allowedHeaders("Content-Type", "Authorization")
                .allowedHeaders("*")
                .maxAge(3600);
    }
}

controller 接口層面:

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.njcky.entity.ScheduleJob;
import com.njcky.respones.CommonResult;
import com.njcky.service.ScheduleJobService;
import com.njcky.util.ToolCollectionUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.quartz.SchedulerException;
import org.springframework.web.bind.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Date;
import java.util.List;
/**
 * (schedule_job)表控制層
 */
@RestController
@RequestMapping("/schedule_job")
@Api(tags = "定時任務(wù)類管理")
public class ScheduleJobController {
    /**
     * 服務(wù)對象
     */
    @Autowired
    private ScheduleJobService scheduleJobService;
    /**
     * 查詢所有定時任務(wù)
     *
     * @return 單條數(shù)據(jù)
     */
    @GetMapping("/list")
    @ApiOperation("查詢所有定時任務(wù)")
    public CommonResult selectOne() {
        return CommonResult.success(scheduleJobService.list());
    }
    /**
     * 新增定時任務(wù)
     *
     * @param scheduleJob 定時任務(wù)對象
     * @return 新增結(jié)果
     */
    @PostMapping("/add")
    @ApiOperation("新增定時任務(wù)")
    public CommonResult add(@RequestBody ScheduleJob scheduleJob) {
        scheduleJob.setCreateTime(ToolCollectionUtil.dataStr(new Date()));
        scheduleJob.setTaskId(ToolCollectionUtil.UUID());
        scheduleJob.setId(ToolCollectionUtil.UUID());
        return CommonResult.success(scheduleJobService.createJob(scheduleJob));
    }
    /**
     * 更新定時任務(wù)
     *
     * @param scheduleJob 更新后的任務(wù)信息
     * @return 更新結(jié)果
     */
    @PutMapping("/update")
    @ApiOperation("更新定時任務(wù)")
    public CommonResult update(@RequestBody ScheduleJob scheduleJob) {
        return CommonResult.success(scheduleJobService.updateJob(scheduleJob));
    }
    /**
     * 刪除定時任務(wù)
     *
     * @param taskId 任務(wù)ID
     * @return 刪除結(jié)果
     */
    @GetMapping("/delete")
    @ApiOperation("刪除定時任務(wù)")
    public CommonResult delete(@RequestParam String taskId) {
        return CommonResult.success(scheduleJobService.deleteJob(taskId));
    }
    /**
     * 查詢所有定時任務(wù)(分頁)
     *
     * @param page 當(dāng)前頁
     * @param size 每頁大小
     * @return 分頁數(shù)據(jù)
     */
    @GetMapping("/page")
    @ApiOperation("獲取定時任務(wù)列表分頁")
    public CommonResult list(@RequestParam int page,
                             @RequestParam int size,
                             @RequestParam(required = false) String search) {
        Page<ScheduleJob> jobPage = new Page<>();
        jobPage.setCurrent(page);
        jobPage.setSize(size);
        QueryWrapper<ScheduleJob> queryWrapper = new QueryWrapper<>();
        if (search != null) {
            queryWrapper.like("job_name", search);
        } else {
            queryWrapper = new QueryWrapper<>();
        }
        Page<ScheduleJob> scheduleJobs = scheduleJobService.page(jobPage, queryWrapper);
        return CommonResult.success(scheduleJobs);
    }
    /**
     * 啟動定時任務(wù)
     *
     * @param taskId 任務(wù)ID
     * @return 啟動定時任務(wù)
     */
    @GetMapping("/startTask")
    @ApiOperation("啟動定時任務(wù)")
    public CommonResult startTask(@RequestParam String taskId) {
        return CommonResult.success(scheduleJobService.startJob(taskId));
    }
    /**
     * 停止定時任務(wù)
     *
     * @param taskId 任務(wù)ID
     * @return 停止定時任務(wù)
     */
    @GetMapping("/stopTask")
    @ApiOperation("停止定時任務(wù)")
    public CommonResult stopTask(@RequestParam String taskId) {
        return CommonResult.success(scheduleJobService.stopJob(taskId));
    }
}

service 層面:

import com.baomidou.mybatisplus.extension.service.IService;
import org.quartz.SchedulerException;
import org.springframework.stereotype.Service;
import com.njcky.entity.ScheduleJob;
public interface ScheduleJobService extends IService<ScheduleJob> {
    /**
     * 創(chuàng)建定時任務(wù)
     *
     * @param job
     */
    boolean createJob(ScheduleJob job);
    /**
     * 更新定時任務(wù)
     *
     * @param job
     */
    boolean updateJob(ScheduleJob job);
    /**
     * 啟動定時任務(wù)
     *
     * @param taskId
     */
    boolean startJob(String taskId);
    /**
     * 停止定時任務(wù)
     *
     * @param taskId
     */
    boolean stopJob(String taskId);
    /**
     * 刪除定時任務(wù)
     *
     * @param taskId
     */
    boolean deleteJob(String taskId);
}

業(yè)務(wù)實現(xiàn):impl

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.njcky.entity.ScheduleJob;
import com.njcky.mapper.ScheduleJobMapper;
import com.njcky.service.ScheduleJobService;
import com.njcky.util.ToolCollectionUtil;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.List;
/**
 * (ScheduleJob)表服務(wù)實現(xiàn)類
 */
@Slf4j
@Service
public class ScheduleJobServiceImpl extends ServiceImpl<ScheduleJobMapper, ScheduleJob> implements ScheduleJobService {
    private static final String JOB_GROUP_NAME = "DEFAULT_JOB_GROUP";
    private static final String TRIGGER_GROUP_NAME = "DEFAULT_TRIGGER_GROUP";
    @Autowired
    private SchedulerFactoryBean schedulerFactoryBean;
    @Autowired
    private ScheduleJobMapper jobMapper;
    /**
     * 創(chuàng)建定時任務(wù)
     */
    @Transactional
    public boolean createJob(ScheduleJob job) {
        try {
            Scheduler scheduler = schedulerFactoryBean.getScheduler();
            // 檢查任務(wù)是否已存在
            JobKey jobKey = new JobKey(job.getTaskId(), JOB_GROUP_NAME);
            if (scheduler.checkExists(jobKey)) {
                log.error("任務(wù)創(chuàng)建失敗,任務(wù)已存在,taskId: {}", job.getTaskId());
                return false;
            }
            // 設(shè)置默認值
            job.setCreateTime(ToolCollectionUtil.dataStr(new Date()));
            job.setStatus("0");
            // 保存到數(shù)據(jù)庫
            int insertResult = jobMapper.insert(job);
            if (insertResult <= 0) {
                log.error("數(shù)據(jù)庫插入失敗,taskId: {}", job.getTaskId());
                return false;
            }
            // 創(chuàng)建JobDetail
            Class<? extends Job> jobClass = getJobClass(job.getJobClass());
            JobDetail jobDetail = JobBuilder.newJob(jobClass)
                    .withIdentity(job.getTaskId(), JOB_GROUP_NAME)
                    .usingJobData("taskId", job.getTaskId())
                    .build();
            // 創(chuàng)建Trigger
            CronTrigger trigger = TriggerBuilder.newTrigger()
                    .withIdentity(job.getTaskId(), TRIGGER_GROUP_NAME)
                    .withSchedule(CronScheduleBuilder.cronSchedule(job.getCronExpression()))
                    .build();
            // 注冊任務(wù)和觸發(fā)器
            scheduler.scheduleJob(jobDetail, trigger);
            log.info("任務(wù)創(chuàng)建成功,taskId: {}", job.getTaskId());
            return true;
        } catch (SchedulerException e) {
            log.error("調(diào)度器異常,任務(wù)創(chuàng)建失敗,taskId: {}", job.getTaskId(), e);
            // 回滾數(shù)據(jù)庫操作(如果已執(zhí)行)
            return false;
        } catch (Exception e) {
            log.error("系統(tǒng)異常,任務(wù)創(chuàng)建失敗,taskId: {}", job.getTaskId(), e);
            return false;
        }
    }
    /**
     * 更新定時任務(wù)
     * 更新后自動停止定時任務(wù)
     */
    @Transactional
    public boolean updateJob(ScheduleJob job) {
        try {
            Scheduler scheduler = schedulerFactoryBean.getScheduler();
            QueryWrapper<ScheduleJob> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("task_id", job.getTaskId());
            ScheduleJob jobResp = jobMapper.selectOne(queryWrapper);
            if (jobResp == null) {
                log.error("任務(wù)不存在,taskId: {}", job.getTaskId());
                return false;
            }
            job.setId(jobResp.getId());
            job.setStatus("0");
            job.setCreateTime(jobResp.getCreateTime());
            job.setUpdateTime(ToolCollectionUtil.dataStr(new Date()));
            log.info("JOB:{}", job);
            int rowsAffected = jobMapper.updateById(job);
            if (rowsAffected <= 0) {
                log.error("數(shù)據(jù)庫更新失敗,taskId: {}", job.getTaskId());
                return false;
            }
            // 更新觸發(fā)器
            TriggerKey triggerKey = new TriggerKey(job.getTaskId(), TRIGGER_GROUP_NAME);
            CronTrigger oldTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            if (oldTrigger == null) {
                log.error("觸發(fā)器不存在,無法更新,taskId: {}", job.getTaskId());
                return false;
            }
            // 構(gòu)建新的觸發(fā)器
            CronTrigger newTrigger = oldTrigger.getTriggerBuilder()
                    .withIdentity(triggerKey)
                    .withSchedule(CronScheduleBuilder.cronSchedule(job.getCronExpression()))
                    .build();
            // 重新調(diào)度任務(wù)
            Date scheduleTime = scheduler.rescheduleJob(triggerKey, newTrigger);
            if (scheduleTime == null) {
                log.error("任務(wù)重新調(diào)度失敗,taskId: {}", job.getTaskId());
                return false;
            }
            log.info("任務(wù)更新成功,taskId: {}", job.getTaskId());
            return true;
        } catch (SchedulerException e) {
            log.error("調(diào)度器異常,任務(wù)更新失敗,taskId: {}", job.getTaskId(), e);
            return false;
        } catch (Exception e) {
            log.error("系統(tǒng)異常,任務(wù)更新失敗,taskId: {}", job.getTaskId(), e);
            return false;
        }
    }
    /**
     * 啟動任務(wù)
     */
    @Transactional
    public boolean startJob(String taskId) {
        try {
            Scheduler scheduler = schedulerFactoryBean.getScheduler();
            JobKey jobKey = new JobKey(taskId, JOB_GROUP_NAME);
            // 先檢查數(shù)據(jù)庫中是否存在任務(wù)
            QueryWrapper<ScheduleJob> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("task_id", taskId);
            ScheduleJob job = jobMapper.selectOne(queryWrapper);
            if (job == null) {
                log.error("任務(wù)不存在,taskId: {}", taskId);
                return false;
            }
            // 如果調(diào)度器中不存在任務(wù),但數(shù)據(jù)庫存在,則重新注冊
            if (!scheduler.checkExists(jobKey)) {
                log.info("任務(wù)在調(diào)度器中不存在,重新注冊,taskId: {}", taskId);
                JobDetail jobDetail = JobBuilder.newJob(getJobClass(job.getJobClass()))
                        .withIdentity(taskId, JOB_GROUP_NAME)
                        .usingJobData("taskId", taskId)
                        .build();
                CronTrigger trigger = TriggerBuilder.newTrigger()
                        .withIdentity(taskId, TRIGGER_GROUP_NAME)
                        .withSchedule(CronScheduleBuilder.cronSchedule(job.getCronExpression()))
                        .build();
                scheduler.scheduleJob(jobDetail, trigger);
            }
            // 更新狀態(tài)并啟動任務(wù)
            if (!"1".equals(job.getStatus())) {
                job.setStatus("1");
                job.setUpdateTime(ToolCollectionUtil.dataStr(new Date()));
                jobMapper.updateById(job);
            }
            scheduler.resumeJob(jobKey);
            log.info("任務(wù)啟動成功,taskId: {}", taskId);
            return true;
        } catch (Exception e) {
            log.error("任務(wù)啟動失敗,taskId: {}", taskId, e);
            return false;
        }
    }
    /**
     * 暫停任務(wù)
     */
    @Transactional
    public boolean stopJob(String taskId) {
        try {
            Scheduler scheduler = schedulerFactoryBean.getScheduler();
            JobKey jobKey = new JobKey(taskId, JOB_GROUP_NAME);
            // 檢查任務(wù)是否存在
            if (!scheduler.checkExists(jobKey)) {
                log.error("任務(wù)停止失敗,任務(wù)不存在,taskId: {}", taskId);
                return false;
            }
            // 查詢?nèi)蝿?wù)信息
            QueryWrapper<ScheduleJob> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("task_id", taskId);
            ScheduleJob job = jobMapper.selectOne(queryWrapper);
            // 更新任務(wù)狀態(tài)(如果需要)
            if (job != null && !"0".equals(job.getStatus())) {
                job.setStatus("0");
                job.setUpdateTime(new Date().toString());
                jobMapper.updateById(job);
            }
            // 暫停任務(wù)
            scheduler.pauseJob(jobKey);
            log.info("任務(wù)停止成功,taskId: {}", taskId);
            return true;
        } catch (SchedulerException e) {
            log.error("任務(wù)停止異常,taskId: {}", taskId, e);
            return false;
        } catch (Exception e) {
            log.error("系統(tǒng)異常,taskId: {}", taskId, e);
            return false;
        }
    }
    /**
     * 刪除任務(wù)
     */
    @Transactional
    public boolean deleteJob(String taskId) {
        try {
            Scheduler scheduler = schedulerFactoryBean.getScheduler();
            JobKey jobKey = new JobKey(taskId, JOB_GROUP_NAME);
            // 嘗試停止任務(wù) (忽略任務(wù)不存在的錯誤)
            try {
                scheduler.pauseJob(jobKey);
            } catch (SchedulerException e) {
                log.debug("嘗試暫停任務(wù)失敗,可能任務(wù)不存在: {}", taskId);
            }
            // 嘗試刪除觸發(fā)器 (忽略觸發(fā)器不存在的錯誤)
            try {
                TriggerKey triggerKey = new TriggerKey(taskId, TRIGGER_GROUP_NAME);
                scheduler.unscheduleJob(triggerKey);
            } catch (SchedulerException e) {
                log.debug("嘗試刪除觸發(fā)器失敗,可能觸發(fā)器不存在: {}", taskId);
            }
            // 嘗試刪除任務(wù) (忽略任務(wù)不存在的錯誤)
            try {
                scheduler.deleteJob(jobKey);
            } catch (SchedulerException e) {
                log.error("刪除任務(wù)時發(fā)生異常,taskId: {}", taskId, e);
                // 這里選擇不中斷流程,繼續(xù)嘗試刪除數(shù)據(jù)庫記錄
            }
            // 刪除數(shù)據(jù)庫記錄
            QueryWrapper<ScheduleJob> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("task_id", taskId);
            int rowsAffected = jobMapper.delete(queryWrapper);
            if (rowsAffected > 0) {
                log.info("數(shù)據(jù)庫記錄刪除成功,taskId: {}", taskId);
                return true;
            } else {
                log.warn("數(shù)據(jù)庫記錄刪除失敗或未找到記錄,taskId: {}", taskId);
                return false;
            }
        } catch (Exception e) {
            log.error("系統(tǒng)異常,taskId: {}", taskId, e);
            return false;
        }
    }
    /**
     * 根據(jù)類名獲取Job實現(xiàn)類
     */
    private Class<? extends Job> getJobClass(String className) throws SchedulerException {
        try {
            return (Class<? extends Job>) Class.forName(className);
        } catch (ClassNotFoundException e) {
            throw new SchedulerException("找不到任務(wù)類: " + className, e);
        }
    }
}

好像忘了什么!?。。?! 哦,原來是 前端代碼【前端代碼有點燒火棍哈。。。 。。?!?/p>

對于咱這種菜鳥來說,作為后臺自己的頁面我感覺,前端差不多就行了

這是官網(wǎng)地址:

Tailwind CSS - 只需書寫 HTML 代碼,無需書寫 CSS,即可快速構(gòu)建美觀的網(wǎng)站。 | TailwindCSS中文文檔 | TailwindCSS中文網(wǎng)

https://cdn.tailwindcss.com/3.4.16

這是:tailwindcss可下載的js直接訪問,復(fù)制下來保存就行,

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>定時任務(wù)管理系統(tǒng)</title>
    <script src="../html/js/tailwindcss.js"></script>
    <script>
        tailwind.config = {
            theme: {
                extend: {
                    colors: {
                        primary: '#3b82f6',
                        secondary: '#10b981',
                        danger: '#ef4444',
                        warning: '#f59e0b',
                        dark: '#1e293b',
                        light: '#f8fafc'
                    },
                    fontFamily: {
                        sans: ['Inter', 'system-ui', 'sans-serif'],
                    },
                }
            }
        }
    </script>
    <style type="text/tailwindcss">
        @layer utilities {
            .content-auto {
                content-visibility: auto;
            }
            .card-shadow {
                box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
            }
            .transition-bg {
                transition: background-color 0.2s ease;
            }
        }
    </style>
</head>
<body class="bg-gray-50 font-sans text-gray-800 min-h-screen flex flex-col">
<!-- 頂部導(dǎo)航 -->
<header class="bg-white shadow-md sticky top-0 z-50">
    <div class="container mx-auto px-4 py-3 flex justify-between items-center">
        <div class="flex items-center space-x-2">
            <span class="text-primary text-2xl">??</span>
            <h1 class="text-xl font-bold text-dark">定時任務(wù)管理系統(tǒng)</h1>
        </div>
        <div class="flex items-center space-x-4">
            <button id="refreshBtn" class="flex items-center text-gray-600 hover:text-primary transition-colors">
                <span>? 刷新</span>
            </button>
            <button id="addJobBtn"
                    class="bg-primary hover:bg-primary/90 text-white px-4 py-2 rounded-md flex items-center transition-bg">
                <span>+ 新增任務(wù)</span>
            </button>
        </div>
    </div>
</header>
<!-- 主內(nèi)容區(qū) -->
<main class="flex-grow container mx-auto px-4 py-6">
    <!-- 搜索和篩選 -->
    <div class="bg-white rounded-lg p-4 mb-6 card-shadow">
        <div class="flex flex-col md:flex-row md:items-center md:justify-between gap-4">
            <div class="relative w-full md:w-1/3">
                <input type="text" id="searchInput" placeholder="搜索任務(wù)名稱..."
                       class="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary">
                <span class="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400">??</span>
            </div>
            <div class="flex items-center space-x-3">
                <div class="flex items-center">
                    <span class="text-gray-600 mr-2">每頁顯示:</span>
                    <select id="pageSizeSelect"
                            class="border border-gray-300 rounded-md px-2 py-1 focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary">
                        <option value="5">5條</option>
                        <option value="10">10條</option>
                        <option value="20">20條</option>
                    </select>
                </div>
                <button id="searchBtn"
                        class="bg-primary hover:bg-primary/90 text-white px-4 py-2 rounded-md flex items-center transition-bg">
                    <span>搜索</span>
                </button>
            </div>
        </div>
    </div>
    <!-- 任務(wù)列表 -->
    <div class="bg-white rounded-lg overflow-hidden card-shadow mb-6">
        <div class="overflow-x-auto">
            <table class="w-full">
                <thead>
                <tr class="bg-gray-50 border-b border-gray-200">
                    <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">標(biāo)識
                    </th>
                    <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">任務(wù)編碼
                    </th>
                    <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">啟動類名稱
                    </th>
                    <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                        任務(wù)名稱
                    </th>
                    <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                        Cron表達式
                    </th>
                    <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                        狀態(tài)
                    </th>
                    <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                        創(chuàng)建時間
                    </th>
                    <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                        更新時間
                    </th>
                    <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">操作</th>
                </tr>
                </thead>
                <tbody id="jobTableBody" class="divide-y divide-gray-200">
                <!-- 任務(wù)數(shù)據(jù)將通過JavaScript動態(tài)填充 -->
                <tr class="text-center">
                    <td colspan="8" class="px-4 py-8 text-gray-500">
                        <span>加載中...</span>
                    </td>
                </tr>
                </tbody>
            </table>
        </div>
        <!-- 分頁控件 -->
        <div class="px-4 py-3 flex items-center justify-between border-t border-gray-200">
            <div class="hidden sm:flex-1 sm:flex sm:items-center sm:justify-between">
                <div>
                    <p class="text-sm text-gray-700">
                        顯示 <span id="pageStart">0</span> 到 <span id="pageEnd">0</span> 條,共 <span
                            id="totalCount">0</span> 條
                    </p>
                </div>
                <div>
                    <nav class="relative z-0 inline-flex rounded-md shadow-sm -space-x-px" aria-label="Pagination">
                        <button id="prevPageBtn"
                                class="relative inline-flex items-center px-2 py-2 rounded-l-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50">
                            <span>← 上一頁</span>
                        </button>
                        <div id="pageNumbers" class="flex">
                            <!-- 頁碼將通過JavaScript動態(tài)填充 -->
                        </div>
                        <button id="nextPageBtn"
                                class="relative inline-flex items-center px-2 py-2 rounded-r-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50">
                            <span>下一頁 →</span>
                        </button>
                    </nav>
                </div>
            </div>
        </div>
    </div>
</main>
<!-- 頁腳 -->
<footer class="bg-white border-t border-gray-200 py-4">
    <div class="container mx-auto px-4 text-center text-gray-500 text-sm">
        <p>? 2025 定時任務(wù)管理系統(tǒng) - 版權(quán)所有</p>
    </div>
</footer>
<!-- 新增/編輯任務(wù)模態(tài)框 -->
<div id="jobModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
    <div class="bg-white rounded-lg w-full max-w-md mx-4 overflow-hidden shadow-xl">
        <div class="px-6 py-4 border-b border-gray-200 flex justify-between items-center">
            <h3 id="modalTitle" class="text-lg font-medium text-gray-900">新增任務(wù)</h3>
            <button id="closeModalBtn" class="text-gray-400 hover:text-gray-500 focus:outline-none">
                <span>×</span>
            </button>
        </div>
        <div class="px-6 py-4">
            <form id="jobForm" class="space-y-4">
                <input type="hidden" id="jobTaskId"> <!-- 改為taskId -->
                <div>
                    <label for="jobName" class="block text-sm font-medium text-gray-700 mb-1">任務(wù)名稱</label>
                    <input type="text" id="jobName" name="jobName" required
                           class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary">
                    <p class="mt-1 text-xs text-gray-500">例如: 游客數(shù)據(jù)統(tǒng)計(任務(wù)執(zhí)行任務(wù)名稱)</p>
                </div>
                <div>
                    <label for="jobClass" class="block text-sm font-medium text-gray-700 mb-1">啟動類名稱</label>
                    <input type="text" id="jobClass" name="jobClass" required
                           class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary">
                    <p class="mt-1 text-xs text-gray-500">例如: com.njcky.task.TestJob(任務(wù)執(zhí)行的全類名)</p>
                </div>
                <div>
                    <label for="cronExpression" class="block text-sm font-medium text-gray-700 mb-1">Cron表達式</label>
                    <input type="text" id="cronExpression" name="cronExpression" required
                           class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary">
                    <p class="mt-1 text-xs text-gray-500">例如: 0 0/10 * * * ? 表示每10分鐘執(zhí)行一次</p>
                </div>
            </form>
        </div>
        <div class="px-6 py-4 bg-gray-50 flex justify-end space-x-3">
            <button id="cancelModalBtn"
                    class="px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary">
                取消
            </button>
            <button id="saveJobBtn"
                    class="px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-primary hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary">
                保存
            </button>
        </div>
    </div>
</div>
<!-- 刪除確認模態(tài)框 -->
<div id="deleteModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
    <div class="bg-white rounded-lg w-full max-w-md mx-4 overflow-hidden shadow-xl">
        <div class="px-6 py-4 border-b border-gray-200">
            <h3 class="text-lg font-medium text-gray-900">確認刪除</h3>
        </div>
        <div class="px-6 py-4">
            <p class="text-sm text-gray-700">你確定要刪除這個定時任務(wù)嗎?此操作不可撤銷。</p>
            <input type="hidden" id="deleteJobTaskId"> <!-- 改為taskId -->
        </div>
        <div class="px-6 py-4 bg-gray-50 flex justify-end space-x-3">
            <button id="cancelDeleteBtn"
                    class="px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary">
                取消
            </button>
            <button id="confirmDeleteBtn"
                    class="px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-danger hover:bg-danger/90 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-danger">
                確認刪除
            </button>
        </div>
    </div>
</div>
<!-- 消息提示 -->
<div id="toast"
     class="fixed bottom-4 right-4 bg-dark text-white px-4 py-3 rounded-md shadow-lg transform translate-y-10 opacity-0 transition-all duration-300 flex items-center z-50">
    <span id="toastIcon" class="mr-2">?</span>
    <span id="toastMessage"></span>
</div>
<script>
    // API基礎(chǔ)URL
    const API_BASE_URL = 'http://192.168.2.102:8889/schedule_job';
    // 當(dāng)前頁碼和每頁大小
    let currentPage = 1;
    let pageSize = 5;
    let searchTerm = '';
    let totalPages = 1;
    // DOM元素(更新了部分ID)
    const jobTableBody = document.getElementById('jobTableBody');
    const pageNumbers = document.getElementById('pageNumbers');
    const prevPageBtn = document.getElementById('prevPageBtn');
    const nextPageBtn = document.getElementById('nextPageBtn');
    const pageStart = document.getElementById('pageStart');
    const pageEnd = document.getElementById('pageEnd');
    const totalCount = document.getElementById('totalCount');
    const refreshBtn = document.getElementById('refreshBtn');
    const addJobBtn = document.getElementById('addJobBtn');
    const jobModal = document.getElementById('jobModal');
    const closeModalBtn = document.getElementById('closeModalBtn');
    const cancelModalBtn = document.getElementById('cancelModalBtn');
    const saveJobBtn = document.getElementById('saveJobBtn');
    const modalTitle = document.getElementById('modalTitle');
    const jobTaskIdInput = document.getElementById('jobTaskId'); // 改為taskId
    const jobNameInput = document.getElementById('jobName');
    const jobClassInput = document.getElementById('jobClass');
    const cronExpressionInput = document.getElementById('cronExpression');
    const deleteModal = document.getElementById('deleteModal');
    const cancelDeleteBtn = document.getElementById('cancelDeleteBtn');
    const confirmDeleteBtn = document.getElementById('confirmDeleteBtn');
    const deleteJobTaskIdInput = document.getElementById('deleteJobTaskId'); // 改為taskId
    const toast = document.getElementById('toast');
    const toastIcon = document.getElementById('toastIcon');
    const toastMessage = document.getElementById('toastMessage');
    const searchBtn = document.getElementById('searchBtn');
    const searchInput = document.getElementById('searchInput');
    const pageSizeSelect = document.getElementById('pageSizeSelect');
    // 初始化
    document.addEventListener('DOMContentLoaded', () => {
        loadJobs();
        setupEventListeners();
    });
    // 設(shè)置事件監(jiān)聽器
    function setupEventListeners() {
        // 分頁控制
        prevPageBtn.addEventListener('click', () => {
            if (currentPage > 1) {
                currentPage--;
                loadJobs();
            }
        });
        nextPageBtn.addEventListener('click', () => {
            if (currentPage < totalPages) {
                currentPage++;
                loadJobs();
            }
        });
        // 刷新
        refreshBtn.addEventListener('click', loadJobs);
        // 新增任務(wù)
        addJobBtn.addEventListener('click', () => {
            modalTitle.textContent = '新增任務(wù)';
            jobTaskIdInput.value = ''; // 清空taskId
            jobNameInput.value = '';
            jobClassInput.value = '';
            cronExpressionInput.value = '';
            jobModal.classList.remove('hidden');
        });
        // 關(guān)閉模態(tài)框
        closeModalBtn.addEventListener('click', () => {
            jobModal.classList.add('hidden');
        });
        cancelModalBtn.addEventListener('click', () => {
            jobModal.classList.add('hidden');
        });
        // 保存任務(wù)
        saveJobBtn.addEventListener('click', saveJob);
        // 刪除任務(wù)
        cancelDeleteBtn.addEventListener('click', () => {
            deleteModal.classList.add('hidden');
        });
        confirmDeleteBtn.addEventListener('click', deleteJob);
        // 搜索
        searchBtn.addEventListener('click', () => {
            searchTerm = searchInput.value.trim();
            currentPage = 1;
            loadJobs();
        });
        // 回車鍵搜索
        searchInput.addEventListener('keyup', (e) => {
            if (e.key === 'Enter') {
                searchTerm = searchInput.value.trim();
                currentPage = 1;
                loadJobs();
            }
        });
        // 每頁顯示數(shù)量變化
        pageSizeSelect.addEventListener('change', () => {
            pageSize = parseInt(pageSizeSelect.value);
            currentPage = 1;
            loadJobs();
        });
    }
    // 加載任務(wù)列表
    function loadJobs() {
        jobTableBody.innerHTML = `
                <tr class="text-center">
                    <td colspan="8" class="px-4 py-8 text-gray-500">
                        <span>加載中...</span>
                    </td>
                </tr>
            `;
        // 構(gòu)建請求URL(支持搜索)
        let url = `${API_BASE_URL}/page?page=${currentPage}&size=${pageSize}`;
        if (searchTerm) {
            url += `&search=${encodeURIComponent(searchTerm)}`;
        }
        fetch(url)
            .then(response => {
                if (!response.ok) {
                    throw new Error('加載任務(wù)列表失敗');
                }
                return response.json();
            })
            .then(data => {
                if (data.code !== 200) {
                    throw new Error(data.message || '加載任務(wù)列表失敗');
                }
                const jobs = data.data.records;
                const total = data.data.total;
                totalPages = data.data.pages;
                renderJobs(jobs);
                updatePagination(total);
            })
            .catch(error => {
                showToast('error', error.message || '加載任務(wù)列表失敗');
                jobTableBody.innerHTML = `
                        <tr class="text-center">
                            <td colspan="8" class="px-4 py-8 text-gray-500">
                                <span>加載失敗,請重試</span>
                            </td>
                        </tr>
                    `;
            });
    }
    // 渲染任務(wù)列表(新增狀態(tài)列和啟動/停止按鈕)
    function renderJobs(jobs) {
        if (jobs.length === 0) {
            jobTableBody.innerHTML = `
                    <tr class="text-center">
                        <td colspan="8" class="px-4 py-8 text-gray-500">
                            <span>沒有找到匹配的任務(wù)</span>
                        </td>
                    </tr>
                `;
            return;
        }
        jobTableBody.innerHTML = '';
        jobs.forEach(job => {
            const row = document.createElement('tr');
            // 狀態(tài)顯示:1=運行中,0=已停止
            const statusText = job.status === '1' ? '運行中' : '已停止';
            const statusClass = job.status === '1' ? 'text-secondary' : 'text-gray-500';
            // 操作按鈕:根據(jù)狀態(tài)顯示啟動/停止
            const actionButtons = job.status === '1'
                ? `<button class="stop-btn text-warning hover:text-warning/80 transition-colors mr-2" data-taskid="${job.taskId}">
                        <span>停止</span>
                   </button>`
                : `<button class="start-btn text-secondary hover:text-secondary/80 transition-colors mr-2" data-taskid="${job.taskId}">
                        <span>啟動</span>
                   </button>`;
            row.innerHTML = `
                    <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500">${job.id}</td>
                    <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500">${job.taskId}</td>
                     <td class="px-4 py-3 whitespace-nowrap text-sm font-medium text-gray-900">${job.jobName}</td>
                    <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500">${job.jobClass}</td>
                    <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500">${job.cronExpression}</td>
                    <td class="px-4 py-3 whitespace-nowrap text-sm ${statusClass}">${statusText}</td>
                    <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500">${formatDateTime(job.createTime)}</td>
                    <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500">${formatDateTime(job.updateTime)}</td>
                    <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500">
                        <div class="flex space-x-2">
                            ${actionButtons}
                            <button class="edit-btn text-primary hover:text-primary/80 transition-colors mr-2" data-taskid="${job.taskId}">
                                <span>編輯</span>
                            </button>
                            <button class="delete-btn text-danger hover:text-danger/80 transition-colors" data-taskid="${job.taskId}">
                                <span>刪除</span>
                            </button>
                        </div>
                    </td>
                `;
            row.className = 'hover:bg-gray-50 transition-bg';
            jobTableBody.appendChild(row);
            // 啟動按鈕事件
            if (row.querySelector('.start-btn')) {
                row.querySelector('.start-btn').addEventListener('click', (e) => {
                    const taskId = e.currentTarget.dataset.taskid;
                    startJob(taskId);
                });
            }
            // 停止按鈕事件
            if (row.querySelector('.stop-btn')) {
                row.querySelector('.stop-btn').addEventListener('click', (e) => {
                    const taskId = e.currentTarget.dataset.taskid;
                    stopJob(taskId);
                });
            }
            // 編輯按鈕事件
            row.querySelector('.edit-btn').addEventListener('click', (e) => {
                const taskId = e.currentTarget.dataset.taskid;
                editJob(taskId);
            });
            // 刪除按鈕事件
            row.querySelector('.delete-btn').addEventListener('click', (e) => {
                const taskId = e.currentTarget.dataset.taskid;
                deleteJobTaskIdInput.value = taskId;
                deleteModal.classList.remove('hidden');
            });
        });
    }
    // 更新分頁控件
    function updatePagination(total) {
        // 更新頁碼信息
        const start = (currentPage - 1) * pageSize + 1;
        const end = Math.min(currentPage * pageSize, total);
        pageStart.textContent = start;
        pageEnd.textContent = end;
        totalCount.textContent = total;
        // 更新頁碼按鈕
        pageNumbers.innerHTML = '';
        // 計算顯示的頁碼范圍
        let startPage = Math.max(1, currentPage - 2);
        let endPage = Math.min(startPage + 4, totalPages);
        if (endPage - startPage < 4 && startPage > 1) {
            startPage = Math.max(1, endPage - 4);
        }
        // 添加第一頁按鈕
        if (startPage > 1) {
            addPageButton(1);
            if (startPage > 2) {
                addEllipsis();
            }
        }
        // 添加頁碼按鈕
        for (let i = startPage; i <= endPage; i++) {
            addPageButton(i);
        }
        // 添加最后一頁按鈕
        if (endPage < totalPages) {
            if (endPage < totalPages - 1) {
                addEllipsis();
            }
            addPageButton(totalPages);
        }
        // 禁用/啟用上一頁、下一頁按鈕
        prevPageBtn.disabled = currentPage === 1;
        prevPageBtn.classList.toggle('opacity-50', currentPage === 1);
        nextPageBtn.disabled = currentPage === totalPages;
        nextPageBtn.classList.toggle('opacity-50', currentPage === totalPages);
    }
    // 添加頁碼按鈕
    function addPageButton(pageNum) {
        const button = document.createElement('button');
        button.className = `relative inline-flex items-center px-4 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 ${currentPage === pageNum ? 'bg-primary text-white border-primary' : ''}`;
        button.textContent = pageNum;
        button.addEventListener('click', () => {
            if (currentPage !== pageNum) {
                currentPage = pageNum;
                loadJobs();
            }
        });
        pageNumbers.appendChild(button);
    }
    // 添加省略號
    function addEllipsis() {
        const ellipsis = document.createElement('span');
        ellipsis.className = 'relative inline-flex items-center px-4 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-700';
        ellipsis.textContent = '...';
        ellipsis.style.pointerEvents = 'none';
        pageNumbers.appendChild(ellipsis);
    }
    // 編輯任務(wù)(基于taskId)
    function editJob(taskId) {
        // 通過taskId查找表格行
        const row = document.querySelector(`tr:has(.edit-btn[data-taskid="${taskId}"])`);
        if (!row) {
            showToast('error', '找不到該任務(wù)');
            return;
        }
        // 從表格行提取數(shù)據(jù)(根據(jù)列索引)
        const jobClass = row.querySelector('td:nth-child(4)').textContent;
        const jobName = row.querySelector('td:nth-child(3)').textContent;
        const cronExpression = row.querySelector('td:nth-child(5)').textContent;
        // 填充表單
        modalTitle.textContent = '編輯任務(wù)';
        jobTaskIdInput.value = taskId;
        jobNameInput.value = jobName;
        jobClassInput.value = jobClass;
        cronExpressionInput.value = cronExpression;
        jobModal.classList.remove('hidden');
    }
    // 保存任務(wù)(使用taskId)
    function saveJob() {
        const taskId = jobTaskIdInput.value;
        const jobName = jobNameInput.value.trim();
        const jobClass = jobClassInput.value.trim();
        const cronExpression = cronExpressionInput.value.trim();
        // 簡單驗證
        if (!jobName) {
            showToast('error', '請輸入任務(wù)名稱');
            return;
        }
        if (!jobClass) {
            showToast('error', '請輸入啟動類名稱');
            return;
        }
        if (!cronExpression) {
            showToast('error', '請輸入Cron表達式');
            return;
        }
        // 驗證Cron表達式格式 - 支持步進值和特殊字符,修正步進值范圍
        const cronPattern = /^(\*|([0-5]?[0-9]|([0-5]?[0-9]\/[1-9][0-9]*)))(\s+|\b)(\*|([01]?[0-9]|2[0-3]|([01]?[0-9]\/[1-9][0-9]*)))(\s+|\b)(\*|([1-9]|[12][0-9]|3[01]|([1-9]\/[1-9][0-9]*)))(\s+|\b)(\*|([1-9]|1[012]|([1-9]\/[1-9][0-9]*)))(\s+|\b)(\*|([0-6]|([0-6]\/[1-9][0-9]*)))(\s+|\b)(\?|\*)$/;
        if (!cronPattern.test(cronExpression)) {
            showToast('error', 'Cron表達式格式不正確');
            return;
        }
        // 準備請求數(shù)據(jù)
        const jobData = {
            taskId: taskId,
            jobClass: jobClass,
            jobName: jobName,
            cronExpression: cronExpression
        };
        // 確定請求方法和URL
        const method = taskId ? 'PUT' : 'POST';
        const url = taskId ? `${API_BASE_URL}/update` : `${API_BASE_URL}/add`;
        // 顯示加載狀態(tài)
        saveJobBtn.disabled = true;
        saveJobBtn.textContent = '保存中...';
        // 發(fā)送請求
        fetch(url, {
            method: method,
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(jobData)
        })
            .then(response => {
                // 先檢查HTTP狀態(tài)碼
                if (!response.ok) {
                    // 嘗試解析錯誤信息
                    return response.json().then(errData => {
                        throw new Error(errData.message || `HTTP錯誤 ${response.status}`);
                    }).catch(() => {
                        // 如果無法解析JSON,使用默認錯誤信息
                        throw new Error(`HTTP錯誤 ${response.status}`);
                    });
                }
                return response.json();
            })
            .then(data => {
                // 檢查業(yè)務(wù)狀態(tài)碼
                if (data.code !== 200) {
                    throw new Error(data.message || '保存任務(wù)失敗');
                }
                showToast('success', taskId ? '任務(wù)更新成功' : '任務(wù)創(chuàng)建成功');
                jobModal.classList.add('hidden');
                loadJobs(); // 刷新列表
            })
            .catch(error => {
                // 顯示更詳細的錯誤信息
                showToast('error', `操作失敗: ${error.message}`);
                console.error('保存任務(wù)時出錯:', error);
            })
            .finally(() => {
                saveJobBtn.disabled = false;
                saveJobBtn.textContent = '保存';
            });
    }
    // 刪除任務(wù)(使用taskId)
    function deleteJob() {
        const taskId = deleteJobTaskIdInput.value;
        fetch(`${API_BASE_URL}/delete?taskId=${taskId}`, { // 調(diào)整為taskId參數(shù)
            method: 'GET'
        })
            .then(response => {
                if (!response.ok) {
                    throw new Error('刪除任務(wù)失敗');
                }
                return response.json();
            })
            .then(data => {
                if (data.code !== 200) {
                    throw new Error(data.message || '刪除任務(wù)失敗');
                }
                showToast('success', '任務(wù)刪除成功');
                deleteModal.classList.add('hidden');
                loadJobs();
            })
            .catch(error => {
                showToast('error', error.message || '刪除任務(wù)失敗');
            });
    }
    // 啟動任務(wù)(對接后端startTask接口)
    function startJob(taskId) {
        fetch(`${API_BASE_URL}/startTask?taskId=${taskId}`, {
            method: 'GET'
        })
            .then(response => {
                if (!response.ok) {
                    throw new Error('啟動任務(wù)失敗');
                }
                return response.json();
            })
            .then(data => {
                if (data.code !== 200 || !data.data) { // 后端返回true表示成功
                    throw new Error(data.message || '啟動任務(wù)失敗');
                }
                showToast('success', '任務(wù)啟動成功');
                loadJobs(); // 刷新列表
            })
            .catch(error => {
                showToast('error', error.message || '啟動任務(wù)失敗');
            });
    }
    // 停止任務(wù)(對接后端stopTask接口)
    function stopJob(taskId) {
        fetch(`${API_BASE_URL}/stopTask?taskId=${taskId}`, {
            method: 'GET'
        })
            .then(response => {
                if (!response.ok) {
                    throw new Error('停止任務(wù)失敗');
                }
                return response.json();
            })
            .then(data => {
                if (data.code !== 200 || !data.data) { // 后端返回true表示成功
                    throw new Error(data.message || '停止任務(wù)失敗');
                }
                showToast('success', '任務(wù)停止成功');
                loadJobs(); // 刷新列表
            })
            .catch(error => {
                showToast('error', error.message || '停止任務(wù)失敗');
            });
    }
    // 顯示提示消息
    function showToast(type, message) {
        toastMessage.textContent = message;
        if (type === 'success') {
            toastIcon.textContent = '?';
            toast.classList.remove('bg-danger');
            toast.classList.add('bg-secondary');
        } else {
            toastIcon.textContent = '?';
            toast.classList.remove('bg-secondary');
            toast.classList.add('bg-danger');
        }
        toast.classList.remove('translate-y-10', 'opacity-0');
        toast.classList.add('translate-y-0', 'opacity-100');
        setTimeout(() => {
            toast.classList.remove('translate-y-0', 'opacity-100');
            toast.classList.add('translate-y-10', 'opacity-0');
        }, 3000);
    }
    // 格式化日期時間
    function formatDateTime(dateTimeStr) {
        if (!dateTimeStr) return '';
        // 處理時間戳或字符串格式
        const date = new Date(dateTimeStr);
        if (isNaN(date.getTime())) {
            return dateTimeStr; // 無法解析時返回原始字符串
        }
        return date.toLocaleString('zh-CN', {
            year: 'numeric',
            month: '2-digit',
            day: '2-digit',
            hour: '2-digit',
            minute: '2-digit',
            second: '2-digit'
        });
    }
</script>
</body>
</html>

還等什么【罩起來】。。。。。。

到此這篇關(guān)于springboot 基于quartz 實現(xiàn)的單機可管理的調(diào)度任務(wù)的文章就介紹到這了,更多相關(guān)springboot  quartz調(diào)度任務(wù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java_Spring之XML?的?AOP?配置

    Java_Spring之XML?的?AOP?配置

    這篇文章主要介紹了Java_Spring中基于XML的AOP配置,上篇講到的是基于注解的AOP配置,對XML感興趣的同學(xué)可以參考閱讀本文
    2023-04-04
  • java 文件目錄讀寫刪除操作詳細實現(xiàn)代碼

    java 文件目錄讀寫刪除操作詳細實現(xiàn)代碼

    這篇文章主要介紹了java 文件讀寫刪操作詳細實現(xiàn)代碼,需要的朋友可以參考下
    2017-09-09
  • RabbitMQ工作模式中的RPC通信模式詳解

    RabbitMQ工作模式中的RPC通信模式詳解

    在RabbitMQ中,RPC模式通過消息隊列實現(xiàn)遠程調(diào)用功能,這篇文章給大家介紹RabbitMQ工作模式之RPC通信模式,感興趣的朋友一起看看吧
    2025-06-06
  • Java實現(xiàn)限定時間CountDownLatch并行場景

    Java實現(xiàn)限定時間CountDownLatch并行場景

    本文將結(jié)合實例代碼,介紹Java實現(xiàn)限定時間CountDownLatch并行場景,文中通過示例代碼介紹的非常詳細,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-07-07
  • SpringBoot實現(xiàn)Excel文件批量上傳導(dǎo)入數(shù)據(jù)庫

    SpringBoot實現(xiàn)Excel文件批量上傳導(dǎo)入數(shù)據(jù)庫

    這篇文章主要為大家詳細介紹了SpringBoot實現(xiàn)Excel文件批量上傳導(dǎo)入數(shù)據(jù)庫,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-11-11
  • 為何Java8需要引入新的日期與時間庫

    為何Java8需要引入新的日期與時間庫

    這篇文章主要給大家介紹了關(guān)于Java8為什么需要引入新的日期與時間庫的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • netty中的IO、NIO、AIO使用詳解

    netty中的IO、NIO、AIO使用詳解

    這篇文章主要介紹了netty中的IO、NIO、AIO使用詳解,本文會說明各種IO的特點、分別解決了什么樣的問題做一個分析闡述,并結(jié)合Java代碼例子來輔助理解,像這些的歷史演進和詳細的底層原理網(wǎng)上很多,所以我們只站在應(yīng)用層,使用者的角度去分析,需要的朋友可以參考下
    2023-12-12
  • springboot攔截器Interceptor的使用,你都了解嗎

    springboot攔截器Interceptor的使用,你都了解嗎

    springmvc 中的攔截器可以對請求進行判別,在請求到達控制器之前,把非法的請求給攔截掉下面來說一說, 它在springboot中的使用,感興趣的朋友一起看看吧
    2021-07-07
  • 一個依賴搞定?Spring?Boot?接口防盜刷的流程分析

    一個依賴搞定?Spring?Boot?接口防盜刷的流程分析

    kk-anti-reptile 是適用于基于 spring-boot 開發(fā)的分布式系統(tǒng)的反爬蟲組件,這篇文章主要介紹了一個依賴搞定?Spring?Boot?接口防盜刷,需要的朋友可以參考下
    2022-06-06
  • 深入理解Spring Cloud Zuul過濾器

    深入理解Spring Cloud Zuul過濾器

    這篇文章主要給大家介紹了關(guān)于Spring Cloud Zuul過濾器的相關(guān)資料,通過閱讀本文您將了解:Zuul過濾器類型與請求生命周期、如何編寫Zuul過濾器、如何禁用Zuul過濾器和Spring Cloud為Zuul編寫的過濾器及其功能,需要的朋友可以參考下。
    2017-02-02

最新評論

泽库县| 中超| 乐都县| 武义县| 梓潼县| 宝鸡市| 高淳县| 和政县| 偃师市| 广元市| 浦城县| 平南县| 蓝山县| 乌拉特中旗| 梅州市| 凤城市| 盐山县| 宣武区| 平阳县| 集贤县| 安泽县| 余庆县| 马边| 灵石县| 青阳县| 新闻| 从化市| 郓城县| 和平区| 宁明县| 庐江县| 上虞市| 钟祥市| 吉林省| 马山县| 池州市| 驻马店市| 资源县| 铁岭市| 洛川县| 祁阳县|