Springboot Vue可配置調(diào)度任務(wù)實現(xiàn)示例詳解
正文
Springboot + Vue,定時任務(wù)調(diào)度的全套實現(xiàn)方案。
這里用了quartz這個框架,實現(xiàn)分布式調(diào)度任務(wù)很不錯,關(guān)于quarz的使用方式改天補一篇。相當(dāng)簡單。
1、表結(jié)構(gòu):
sys_job | |||||
---|---|---|---|---|---|
列名 | 數(shù)據(jù)類型 | 長度 | 是否可空 | 是否主鍵 | 說明 |
job_id | bigint | 否 | 是 | 任務(wù)ID | |
job_name | varchar | 64 | 否 | 是 | 任務(wù)名稱 |
job_group | varchar | 64 | 否 | 是 | 任務(wù)組名 |
invoke_target | varchar | 500 | 否 | 否 | 調(diào)用目標字符串 |
cron_expression | varchar | 255 | 是 | 否 | cron執(zhí)行表達式 |
misfire_policy | varchar | 20 | 是 | 否 | 計劃執(zhí)行錯誤策略(1立即執(zhí)行 2執(zhí)行一次 3放棄執(zhí)行) |
concurrent | char | 1 | 是 | 否 | 是否并發(fā)執(zhí)行(0允許 1禁止) |
status | char | 1 | 是 | 否 | 狀態(tài)(0正常 1暫停) |
create_by | varchar | 64 | 是 | 否 | 創(chuàng)建者 |
create_time | datetime | 是 | 否 | 創(chuàng)建時間 | |
update_by | varchar | 64 | 是 | 否 | 更新者 |
update_time | datetime | 是 | 否 | 更新時間 | |
remark | varchar | 500 | 是 | 否 | 備注信息 |
2、接口:
@RestController @RequestMapping("/job") public class SysJobController extends BaseController { @Autowired private ISysJobService jobService; ? /** * 查詢定時任務(wù)列表 */ @GetMapping("/list") public TableDataInfo list(SysJob sysJob) { startPage(); List<SysJob> list = jobService.selectJobList(sysJob); return getDataTable(list); } ? /** * 導(dǎo)出定時任務(wù)列表 */ @PostMapping("/export") public void export(HttpServletResponse response, SysJob sysJob) { List<SysJob> list = jobService.selectJobList(sysJob); ExcelUtil<SysJob> util = new ExcelUtil<SysJob>(SysJob.class); util.exportExcel(response, list, "定時任務(wù)"); } ? /** * 獲取定時任務(wù)詳細信息 */ @GetMapping(value = "/{jobId}") public AjaxResult getInfo(@PathVariable("jobId") Long jobId) { return success(jobService.selectJobById(jobId)); } ? /** * 新增定時任務(wù) */ @PostMapping public AjaxResult add(@RequestBody SysJob job) throws SchedulerException, TaskException { if (!CronUtils.isValid(job.getCronExpression())) { return error("新增任務(wù)'" + job.getJobName() + "'失敗,Cron表達式不正確"); } else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_RMI)) { return error("新增任務(wù)'" + job.getJobName() + "'失敗,目標字符串不允許'rmi'調(diào)用"); } else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.LOOKUP_LDAP, Constants.LOOKUP_LDAPS })) { return error("新增任務(wù)'" + job.getJobName() + "'失敗,目標字符串不允許'ldap(s)'調(diào)用"); } else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.HTTP, Constants.HTTPS })) { return error("新增任務(wù)'" + job.getJobName() + "'失敗,目標字符串不允許'http(s)'調(diào)用"); } else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), Constants.JOB_ERROR_STR)) { return error("新增任務(wù)'" + job.getJobName() + "'失敗,目標字符串存在違規(guī)"); } else if (!ScheduleUtils.whiteList(job.getInvokeTarget())) { return error("新增任務(wù)'" + job.getJobName() + "'失敗,目標字符串不在白名單內(nèi)"); } job.setCreateBy(getUsername()); return toAjax(jobService.insertJob(job)); } ? /** * 修改定時任務(wù) */ @PutMapping public AjaxResult edit(@RequestBody SysJob job) throws SchedulerException, TaskException { if (!CronUtils.isValid(job.getCronExpression())) { return error("修改任務(wù)'" + job.getJobName() + "'失敗,Cron表達式不正確"); } else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_RMI)) { return error("修改任務(wù)'" + job.getJobName() + "'失敗,目標字符串不允許'rmi'調(diào)用"); } else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.LOOKUP_LDAP, Constants.LOOKUP_LDAPS })) { return error("修改任務(wù)'" + job.getJobName() + "'失敗,目標字符串不允許'ldap(s)'調(diào)用"); } else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.HTTP, Constants.HTTPS })) { return error("修改任務(wù)'" + job.getJobName() + "'失敗,目標字符串不允許'http(s)'調(diào)用"); } else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), Constants.JOB_ERROR_STR)) { return error("修改任務(wù)'" + job.getJobName() + "'失敗,目標字符串存在違規(guī)"); } else if (!ScheduleUtils.whiteList(job.getInvokeTarget())) { return error("修改任務(wù)'" + job.getJobName() + "'失敗,目標字符串不在白名單內(nèi)"); } job.setUpdateBy(getUsername()); return toAjax(jobService.updateJob(job)); } ? /** * 定時任務(wù)狀態(tài)修改 */ @PutMapping("/changeStatus") public AjaxResult changeStatus(@RequestBody SysJob job) throws SchedulerException { SysJob newJob = jobService.selectJobById(job.getJobId()); newJob.setStatus(job.getStatus()); return toAjax(jobService.changeStatus(newJob)); } ? /** * 定時任務(wù)立即執(zhí)行一次 */ @PutMapping("/run") public AjaxResult run(@RequestBody SysJob job) throws SchedulerException { boolean result = jobService.run(job); return result ? success() : error("任務(wù)不存在或已過期!"); } ? /** * 刪除定時任務(wù) */ @DeleteMapping("/{jobIds}") public AjaxResult remove(@PathVariable Long[] jobIds) throws SchedulerException, TaskException { jobService.deleteJobByIds(jobIds); return success(); } 復(fù)制代碼
3、業(yè)務(wù)層:
public interface ISysJobService { /** * 獲取quartz調(diào)度器的計劃任務(wù) * * @param job 調(diào)度信息 * @return 調(diào)度任務(wù)集合 */ public List<SysJob> selectJobList(SysJob job); ? /** * 通過調(diào)度任務(wù)ID查詢調(diào)度信息 * * @param jobId 調(diào)度任務(wù)ID * @return 調(diào)度任務(wù)對象信息 */ public SysJob selectJobById(Long jobId); ? /** * 暫停任務(wù) * * @param job 調(diào)度信息 * @return 結(jié)果 */ public int pauseJob(SysJob job) throws SchedulerException; ? /** * 恢復(fù)任務(wù) * * @param job 調(diào)度信息 * @return 結(jié)果 */ public int resumeJob(SysJob job) throws SchedulerException; ? /** * 刪除任務(wù)后,所對應(yīng)的trigger也將被刪除 * * @param job 調(diào)度信息 * @return 結(jié)果 */ public int deleteJob(SysJob job) throws SchedulerException; ? /** * 批量刪除調(diào)度信息 * * @param jobIds 需要刪除的任務(wù)ID * @return 結(jié)果 */ public void deleteJobByIds(Long[] jobIds) throws SchedulerException; ? /** * 任務(wù)調(diào)度狀態(tài)修改 * * @param job 調(diào)度信息 * @return 結(jié)果 */ public int changeStatus(SysJob job) throws SchedulerException; ? /** * 立即運行任務(wù) * * @param job 調(diào)度信息 * @return 結(jié)果 */ public boolean run(SysJob job) throws SchedulerException; ? /** * 新增任務(wù) * * @param job 調(diào)度信息 * @return 結(jié)果 */ public int insertJob(SysJob job) throws SchedulerException, TaskException; ? /** * 更新任務(wù) * * @param job 調(diào)度信息 * @return 結(jié)果 */ public int updateJob(SysJob job) throws SchedulerException, TaskException; ? /** * 校驗cron表達式是否有效 * * @param cronExpression 表達式 * @return 結(jié)果 */ public boolean checkCronExpressionIsValid(String cronExpression); } 復(fù)制代碼
@Service public class SysJobServiceImpl implements ISysJobService { @Autowired private Scheduler scheduler; ? @Autowired private SysJobMapper jobMapper; ? /** * 項目啟動時,初始化定時器 主要是防止手動修改數(shù)據(jù)庫導(dǎo)致未同步到定時任務(wù)處理(注:不能手動修改數(shù)據(jù)庫ID和任務(wù)組名,否則會導(dǎo)致臟數(shù)據(jù)) */ @PostConstruct public void init() throws SchedulerException, TaskException { scheduler.clear(); List<SysJob> jobList = jobMapper.selectJobAll(); for (SysJob job : jobList) { ScheduleUtils.createScheduleJob(scheduler, job); } } ? /** * 獲取quartz調(diào)度器的計劃任務(wù)列表 * * @param job 調(diào)度信息 * @return */ @Override public List<SysJob> selectJobList(SysJob job) { return jobMapper.selectJobList(job); } ? /** * 通過調(diào)度任務(wù)ID查詢調(diào)度信息 * * @param jobId 調(diào)度任務(wù)ID * @return 調(diào)度任務(wù)對象信息 */ @Override public SysJob selectJobById(Long jobId) { return jobMapper.selectJobById(jobId); } ? /** * 暫停任務(wù) * * @param job 調(diào)度信息 */ @Override @Transactional(rollbackFor = Exception.class) public int pauseJob(SysJob job) throws SchedulerException { Long jobId = job.getJobId(); String jobGroup = job.getJobGroup(); job.setStatus(ScheduleConstants.Status.PAUSE.getValue()); int rows = jobMapper.updateJob(job); if (rows > 0) { scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup)); } return rows; } ? /** * 恢復(fù)任務(wù) * * @param job 調(diào)度信息 */ @Override @Transactional(rollbackFor = Exception.class) public int resumeJob(SysJob job) throws SchedulerException { Long jobId = job.getJobId(); String jobGroup = job.getJobGroup(); job.setStatus(ScheduleConstants.Status.NORMAL.getValue()); int rows = jobMapper.updateJob(job); if (rows > 0) { scheduler.resumeJob(ScheduleUtils.getJobKey(jobId, jobGroup)); } return rows; } ? /** * 刪除任務(wù)后,所對應(yīng)的trigger也將被刪除 * * @param job 調(diào)度信息 */ @Override @Transactional(rollbackFor = Exception.class) public int deleteJob(SysJob job) throws SchedulerException { Long jobId = job.getJobId(); String jobGroup = job.getJobGroup(); int rows = jobMapper.deleteJobById(jobId); if (rows > 0) { scheduler.deleteJob(ScheduleUtils.getJobKey(jobId, jobGroup)); } return rows; } ? /** * 批量刪除調(diào)度信息 * * @param jobIds 需要刪除的任務(wù)ID * @return 結(jié)果 */ @Override @Transactional(rollbackFor = Exception.class) public void deleteJobByIds(Long[] jobIds) throws SchedulerException { for (Long jobId : jobIds) { SysJob job = jobMapper.selectJobById(jobId); deleteJob(job); } } ? /** * 任務(wù)調(diào)度狀態(tài)修改 * * @param job 調(diào)度信息 */ @Override @Transactional(rollbackFor = Exception.class) public int changeStatus(SysJob job) throws SchedulerException { int rows = 0; String status = job.getStatus(); if (ScheduleConstants.Status.NORMAL.getValue().equals(status)) { rows = resumeJob(job); } else if (ScheduleConstants.Status.PAUSE.getValue().equals(status)) { rows = pauseJob(job); } return rows; } ? /** * 立即運行任務(wù) * * @param job 調(diào)度信息 */ @Override @Transactional(rollbackFor = Exception.class) public boolean run(SysJob job) throws SchedulerException { boolean result = false; Long jobId = job.getJobId(); String jobGroup = job.getJobGroup(); SysJob properties = selectJobById(job.getJobId()); // 參數(shù) JobDataMap dataMap = new JobDataMap(); dataMap.put(ScheduleConstants.TASK_PROPERTIES, properties); JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup); if (scheduler.checkExists(jobKey)) { result = true; scheduler.triggerJob(jobKey, dataMap); } return result; } ? /** * 新增任務(wù) * * @param job 調(diào)度信息 調(diào)度信息 */ @Override @Transactional(rollbackFor = Exception.class) public int insertJob(SysJob job) throws SchedulerException, TaskException { job.setStatus(ScheduleConstants.Status.PAUSE.getValue()); int rows = jobMapper.insertJob(job); if (rows > 0) { ScheduleUtils.createScheduleJob(scheduler, job); } return rows; } ? /** * 更新任務(wù)的時間表達式 * * @param job 調(diào)度信息 */ @Override @Transactional(rollbackFor = Exception.class) public int updateJob(SysJob job) throws SchedulerException, TaskException { SysJob properties = selectJobById(job.getJobId()); int rows = jobMapper.updateJob(job); if (rows > 0) { updateSchedulerJob(job, properties.getJobGroup()); } return rows; } ? /** * 更新任務(wù) * * @param job 任務(wù)對象 * @param jobGroup 任務(wù)組名 */ public void updateSchedulerJob(SysJob job, String jobGroup) throws SchedulerException, TaskException { Long jobId = job.getJobId(); // 判斷是否存在 JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup); if (scheduler.checkExists(jobKey)) { // 防止創(chuàng)建時存在數(shù)據(jù)問題 先移除,然后在執(zhí)行創(chuàng)建操作 scheduler.deleteJob(jobKey); } ScheduleUtils.createScheduleJob(scheduler, job); } ? /** * 校驗cron表達式是否有效 * * @param cronExpression 表達式 * @return 結(jié)果 */ @Override public boolean checkCronExpressionIsValid(String cronExpression) { return CronUtils.isValid(cronExpression); } } 復(fù)制代碼
4、Mapper
public interface SysJobMapper { /** * 查詢調(diào)度任務(wù)日志集合 * * @param job 調(diào)度信息 * @return 操作日志集合 */ public List<SysJob> selectJobList(SysJob job); ? /** * 查詢所有調(diào)度任務(wù) * * @return 調(diào)度任務(wù)列表 */ public List<SysJob> selectJobAll(); ? /** * 通過調(diào)度ID查詢調(diào)度任務(wù)信息 * * @param jobId 調(diào)度ID * @return 角色對象信息 */ public SysJob selectJobById(Long jobId); ? /** * 通過調(diào)度ID刪除調(diào)度任務(wù)信息 * * @param jobId 調(diào)度ID * @return 結(jié)果 */ public int deleteJobById(Long jobId); ? /** * 批量刪除調(diào)度任務(wù)信息 * * @param ids 需要刪除的數(shù)據(jù)ID * @return 結(jié)果 */ public int deleteJobByIds(Long[] ids); ? /** * 修改調(diào)度任務(wù)信息 * * @param job 調(diào)度任務(wù)信息 * @return 結(jié)果 */ public int updateJob(SysJob job); ? /** * 新增調(diào)度任務(wù)信息 * * @param job 調(diào)度任務(wù)信息 * @return 結(jié)果 */ public int insertJob(SysJob job); } 復(fù)制代碼
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.job.quartz.mapper.SysJobMapper"> ? <resultMap type="SysJob" id="SysJobResult"> <id property="jobId" column="job_id" /> <result property="jobName" column="job_name" /> <result property="jobGroup" column="job_group" /> <result property="invokeTarget" column="invoke_target" /> <result property="cronExpression" column="cron_expression" /> <result property="misfirePolicy" column="misfire_policy" /> <result property="concurrent" column="concurrent" /> <result property="status" column="status" /> <result property="createBy" column="create_by" /> <result property="createTime" column="create_time" /> <result property="updateBy" column="update_by" /> <result property="updateTime" column="update_time" /> <result property="remark" column="remark" /> </resultMap> <sql id="selectJobVo"> select job_id, job_name, job_group, invoke_target, cron_expression, misfire_policy, concurrent, status, create_by, create_time, remark from sys_job </sql> <select id="selectJobList" parameterType="SysJob" resultMap="SysJobResult"> <include refid="selectJobVo"/> <where> <if test="jobName != null and jobName != ''"> AND job_name like concat('%', #{jobName}, '%') </if> <if test="jobGroup != null and jobGroup != ''"> AND job_group = #{jobGroup} </if> <if test="status != null and status != ''"> AND status = #{status} </if> <if test="invokeTarget != null and invokeTarget != ''"> AND invoke_target like concat('%', #{invokeTarget}, '%') </if> </where> </select> <select id="selectJobAll" resultMap="SysJobResult"> <include refid="selectJobVo"/> </select> <select id="selectJobById" parameterType="Long" resultMap="SysJobResult"> <include refid="selectJobVo"/> where job_id = #{jobId} </select> <delete id="deleteJobById" parameterType="Long"> delete from sys_job where job_id = #{jobId} </delete> <delete id="deleteJobByIds" parameterType="Long"> delete from sys_job where job_id in <foreach collection="array" item="jobId" open="(" separator="," close=")"> #{jobId} </foreach> </delete> <update id="updateJob" parameterType="SysJob"> update sys_job <set> <if test="jobName != null and jobName != ''">job_name = #{jobName},</if> <if test="jobGroup != null and jobGroup != ''">job_group = #{jobGroup},</if> <if test="invokeTarget != null and invokeTarget != ''">invoke_target = #{invokeTarget},</if> <if test="cronExpression != null and cronExpression != ''">cron_expression = #{cronExpression},</if> <if test="misfirePolicy != null and misfirePolicy != ''">misfire_policy = #{misfirePolicy},</if> <if test="concurrent != null and concurrent != ''">concurrent = #{concurrent},</if> <if test="status !=null">status = #{status},</if> <if test="remark != null and remark != ''">remark = #{remark},</if> <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if> update_time = sysdate() </set> where job_id = #{jobId} </update> <insert id="insertJob" parameterType="SysJob" useGeneratedKeys="true" keyProperty="jobId"> insert into sys_job( <if test="jobId != null and jobId != 0">job_id,</if> <if test="jobName != null and jobName != ''">job_name,</if> <if test="jobGroup != null and jobGroup != ''">job_group,</if> <if test="invokeTarget != null and invokeTarget != ''">invoke_target,</if> <if test="cronExpression != null and cronExpression != ''">cron_expression,</if> <if test="misfirePolicy != null and misfirePolicy != ''">misfire_policy,</if> <if test="concurrent != null and concurrent != ''">concurrent,</if> <if test="status != null and status != ''">status,</if> <if test="remark != null and remark != ''">remark,</if> <if test="createBy != null and createBy != ''">create_by,</if> create_time )values( <if test="jobId != null and jobId != 0">#{jobId},</if> <if test="jobName != null and jobName != ''">#{jobName},</if> <if test="jobGroup != null and jobGroup != ''">#{jobGroup},</if> <if test="invokeTarget != null and invokeTarget != ''">#{invokeTarget},</if> <if test="cronExpression != null and cronExpression != ''">#{cronExpression},</if> <if test="misfirePolicy != null and misfirePolicy != ''">#{misfirePolicy},</if> <if test="concurrent != null and concurrent != ''">#{concurrent},</if> <if test="status != null and status != ''">#{status},</if> <if test="remark != null and remark != ''">#{remark},</if> <if test="createBy != null and createBy != ''">#{createBy},</if> sysdate() ) </insert> ? </mapper> 復(fù)制代碼
5、前端(Vue):
import request from '@/utils/request' ? // 查詢定時任務(wù)調(diào)度列表 export function listJob(query) { return request({ url: '/monitor/job/list', method: 'get', params: query }) } ? // 查詢定時任務(wù)調(diào)度詳細 export function getJob(jobId) { return request({ url: '/monitor/job/' + jobId, method: 'get' }) } ? // 新增定時任務(wù)調(diào)度 export function addJob(data) { return request({ url: '/monitor/job', method: 'post', data: data }) } ? // 修改定時任務(wù)調(diào)度 export function updateJob(data) { return request({ url: '/monitor/job', method: 'put', data: data }) } ? // 刪除定時任務(wù)調(diào)度 export function delJob(jobId) { return request({ url: '/monitor/job/' + jobId, method: 'delete' }) } ? // 任務(wù)狀態(tài)修改 export function changeJobStatus(jobId, status) { const data = { jobId, status } return request({ url: '/monitor/job/changeStatus', method: 'put', data: data }) } ? ? // 定時任務(wù)立即執(zhí)行一次 export function runJob(jobId, jobGroup) { const data = { jobId, jobGroup } return request({ url: '/monitor/job/run', method: 'put', data: data }) } 復(fù)制代碼
import request from '@/utils/request' ? // 查詢調(diào)度日志列表 export function listJobLog(query) { return request({ url: '/monitor/jobLog/list', method: 'get', params: query }) } ? // 刪除調(diào)度日志 export function delJobLog(jobLogId) { return request({ url: '/monitor/jobLog/' + jobLogId, method: 'delete' }) } ? // 清空調(diào)度日志 export function cleanJobLog() { return request({ url: '/monitor/jobLog/clean', method: 'delete' }) } ? 復(fù)制代碼
<template> <div class="app-container"> <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px"> <el-form-item label="任務(wù)名稱" prop="jobName"> <el-input v-model="queryParams.jobName" placeholder="請輸入任務(wù)名稱" clearable @keyup.enter.native="handleQuery" /> </el-form-item> <el-form-item label="任務(wù)組名" prop="jobGroup"> <el-select v-model="queryParams.jobGroup" placeholder="請選擇任務(wù)組名" clearable> <el-option v-for="dict in dict.type.sys_job_group" :key="dict.value" :label="dict.label" :value="dict.value" /> </el-select> </el-form-item> <el-form-item label="任務(wù)狀態(tài)" prop="status"> <el-select v-model="queryParams.status" placeholder="請選擇任務(wù)狀態(tài)" clearable> <el-option v-for="dict in dict.type.sys_job_status" :key="dict.value" :label="dict.label" :value="dict.value" /> </el-select> </el-form-item> <el-form-item> <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button> </el-form-item> </el-form> ? <el-row :gutter="10" class="mb8"> <el-col :span="1.5"> <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" v-hasPermi="['monitor:job:add']" >新增</el-button> </el-col> <el-col :span="1.5"> <el-button type="success" plain icon="el-icon-edit" size="mini" :disabled="single" @click="handleUpdate" v-hasPermi="['monitor:job:edit']" >修改</el-button> </el-col> <el-col :span="1.5"> <el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete" v-hasPermi="['monitor:job:remove']" >刪除</el-button> </el-col> <el-col :span="1.5"> <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" v-hasPermi="['monitor:job:export']" >導(dǎo)出</el-button> </el-col> <el-col :span="1.5"> <el-button type="info" plain icon="el-icon-s-operation" size="mini" @click="handleJobLog" v-hasPermi="['monitor:job:query']" >日志</el-button> </el-col> <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> </el-row> ? <el-table v-loading="loading" :data="jobList" @selection-change="handleSelectionChange"> <el-table-column type="selection" width="55" align="center" /> <el-table-column label="任務(wù)編號" width="100" align="center" prop="jobId" /> <el-table-column label="任務(wù)名稱" align="center" prop="jobName" :show-overflow-tooltip="true" /> <el-table-column label="任務(wù)組名" align="center" prop="jobGroup"> <template slot-scope="scope"> <dict-tag :options="dict.type.sys_job_group" :value="scope.row.jobGroup"/> </template> </el-table-column> <el-table-column label="調(diào)用目標字符串" align="center" prop="invokeTarget" :show-overflow-tooltip="true" /> <el-table-column label="cron執(zhí)行表達式" align="center" prop="cronExpression" :show-overflow-tooltip="true" /> <el-table-column label="狀態(tài)" align="center"> <template slot-scope="scope"> <el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)" ></el-switch> </template> </el-table-column> <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> <template slot-scope="scope"> <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" v-hasPermi="['monitor:job:edit']" >修改</el-button> <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" v-hasPermi="['monitor:job:remove']" >刪除</el-button> <el-dropdown size="mini" @command="(command) => handleCommand(command, scope.row)" v-hasPermi="['monitor:job:changeStatus', 'monitor:job:query']"> <el-button size="mini" type="text" icon="el-icon-d-arrow-right">更多</el-button> <el-dropdown-menu slot="dropdown"> <el-dropdown-item command="handleRun" icon="el-icon-caret-right" v-hasPermi="['monitor:job:changeStatus']">執(zhí)行一次</el-dropdown-item> <el-dropdown-item command="handleView" icon="el-icon-view" v-hasPermi="['monitor:job:query']">任務(wù)詳細</el-dropdown-item> <el-dropdown-item command="handleJobLog" icon="el-icon-s-operation" v-hasPermi="['monitor:job:query']">調(diào)度日志</el-dropdown-item> </el-dropdown-menu> </el-dropdown> </template> </el-table-column> </el-table> ? <pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" /> ? <!-- 添加或修改定時任務(wù)對話框 --> <el-dialog :title="title" :visible.sync="open" width="800px" append-to-body> <el-form ref="form" :model="form" :rules="rules" label-width="120px"> <el-row> <el-col :span="12"> <el-form-item label="任務(wù)名稱" prop="jobName"> <el-input v-model="form.jobName" placeholder="請輸入任務(wù)名稱" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="任務(wù)分組" prop="jobGroup"> <el-select v-model="form.jobGroup" placeholder="請選擇任務(wù)分組"> <el-option v-for="dict in dict.type.sys_job_group" :key="dict.value" :label="dict.label" :value="dict.value" ></el-option> </el-select> </el-form-item> </el-col> <el-col :span="24"> <el-form-item prop="invokeTarget"> <span slot="label"> 調(diào)用方法 <el-tooltip placement="top"> <div slot="content"> Bean調(diào)用示例:ryTask.ryParams('ry') <br />Class類調(diào)用示例:com.ruoyi.quartz.task.RyTask.ryParams('ry') <br />參數(shù)說明:支持字符串,布爾類型,長整型,浮點型,整型 </div> <i class="el-icon-question"></i> </el-tooltip> </span> <el-input v-model="form.invokeTarget" placeholder="請輸入調(diào)用目標字符串" /> </el-form-item> </el-col> <el-col :span="24"> <el-form-item label="cron表達式" prop="cronExpression"> <el-input v-model="form.cronExpression" placeholder="請輸入cron執(zhí)行表達式"> <template slot="append"> <el-button type="primary" @click="handleShowCron"> 生成表達式 <i class="el-icon-time el-icon--right"></i> </el-button> </template> </el-input> </el-form-item> </el-col> <el-col :span="24"> <el-form-item label="執(zhí)行策略" prop="misfirePolicy"> <el-radio-group v-model="form.misfirePolicy" size="small"> <el-radio-button label="1">立即執(zhí)行</el-radio-button> <el-radio-button label="2">執(zhí)行一次</el-radio-button> <el-radio-button label="3">放棄執(zhí)行</el-radio-button> </el-radio-group> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="是否并發(fā)" prop="concurrent"> <el-radio-group v-model="form.concurrent" size="small"> <el-radio-button label="0">允許</el-radio-button> <el-radio-button label="1">禁止</el-radio-button> </el-radio-group> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="狀態(tài)"> <el-radio-group v-model="form.status"> <el-radio v-for="dict in dict.type.sys_job_status" :key="dict.value" :label="dict.value" >{{dict.label}}</el-radio> </el-radio-group> </el-form-item> </el-col> </el-row> </el-form> <div slot="footer" class="dialog-footer"> <el-button type="primary" @click="submitForm">確 定</el-button> <el-button @click="cancel">取 消</el-button> </div> </el-dialog> ? <el-dialog title="Cron表達式生成器" :visible.sync="openCron" append-to-body destroy-on-close class="scrollbar"> <crontab @hide="openCron=false" @fill="crontabFill" :expression="expression"></crontab> </el-dialog> ? <!-- 任務(wù)日志詳細 --> <el-dialog title="任務(wù)詳細" :visible.sync="openView" width="700px" append-to-body> <el-form ref="form" :model="form" label-width="120px" size="mini"> <el-row> <el-col :span="12"> <el-form-item label="任務(wù)編號:">{{ form.jobId }}</el-form-item> <el-form-item label="任務(wù)名稱:">{{ form.jobName }}</el-form-item> </el-col> <el-col :span="12"> <el-form-item label="任務(wù)分組:">{{ jobGroupFormat(form) }}</el-form-item> <el-form-item label="創(chuàng)建時間:">{{ form.createTime }}</el-form-item> </el-col> <el-col :span="12"> <el-form-item label="cron表達式:">{{ form.cronExpression }}</el-form-item> </el-col> <el-col :span="12"> <el-form-item label="下次執(zhí)行時間:">{{ parseTime(form.nextValidTime) }}</el-form-item> </el-col> <el-col :span="24"> <el-form-item label="調(diào)用目標方法:">{{ form.invokeTarget }}</el-form-item> </el-col> <el-col :span="12"> <el-form-item label="任務(wù)狀態(tài):"> <div v-if="form.status == 0">正常</div> <div v-else-if="form.status == 1">失敗</div> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="是否并發(fā):"> <div v-if="form.concurrent == 0">允許</div> <div v-else-if="form.concurrent == 1">禁止</div> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="執(zhí)行策略:"> <div v-if="form.misfirePolicy == 0">默認策略</div> <div v-else-if="form.misfirePolicy == 1">立即執(zhí)行</div> <div v-else-if="form.misfirePolicy == 2">執(zhí)行一次</div> <div v-else-if="form.misfirePolicy == 3">放棄執(zhí)行</div> </el-form-item> </el-col> </el-row> </el-form> <div slot="footer" class="dialog-footer"> <el-button @click="openView = false">關(guān) 閉</el-button> </div> </el-dialog> </div> </template> ? <script> import { listJob, getJob, delJob, addJob, updateJob, runJob, changeJobStatus } from "@/api/monitor/job"; import Crontab from '@/components/Crontab' ? export default { components: { Crontab }, name: "Job", dicts: ['sys_job_group', 'sys_job_status'], data() { return { // 遮罩層 loading: true, // 選中數(shù)組 ids: [], // 非單個禁用 single: true, // 非多個禁用 multiple: true, // 顯示搜索條件 showSearch: true, // 總條數(shù) total: 0, // 定時任務(wù)表格數(shù)據(jù) jobList: [], // 彈出層標題 title: "", // 是否顯示彈出層 open: false, // 是否顯示詳細彈出層 openView: false, // 是否顯示Cron表達式彈出層 openCron: false, // 傳入的表達式 expression: "", // 查詢參數(shù) queryParams: { pageNum: 1, pageSize: 10, jobName: undefined, jobGroup: undefined, status: undefined }, // 表單參數(shù) form: {}, // 表單校驗 rules: { jobName: [ { required: true, message: "任務(wù)名稱不能為空", trigger: "blur" } ], invokeTarget: [ { required: true, message: "調(diào)用目標字符串不能為空", trigger: "blur" } ], cronExpression: [ { required: true, message: "cron執(zhí)行表達式不能為空", trigger: "blur" } ] } }; }, created() { this.getList(); }, methods: { /** 查詢定時任務(wù)列表 */ getList() { this.loading = true; listJob(this.queryParams).then(response => { this.jobList = response.rows; this.total = response.total; this.loading = false; }); }, // 任務(wù)組名字典翻譯 jobGroupFormat(row, column) { return this.selectDictLabel(this.dict.type.sys_job_group, row.jobGroup); }, // 取消按鈕 cancel() { this.open = false; this.reset(); }, // 表單重置 reset() { this.form = { jobId: undefined, jobName: undefined, jobGroup: undefined, invokeTarget: undefined, cronExpression: undefined, misfirePolicy: 1, concurrent: 1, status: "0" }; this.resetForm("form"); }, /** 搜索按鈕操作 */ handleQuery() { this.queryParams.pageNum = 1; this.getList(); }, /** 重置按鈕操作 */ resetQuery() { this.resetForm("queryForm"); this.handleQuery(); }, // 多選框選中數(shù)據(jù) handleSelectionChange(selection) { this.ids = selection.map(item => item.jobId); this.single = selection.length != 1; this.multiple = !selection.length; }, // 更多操作觸發(fā) handleCommand(command, row) { switch (command) { case "handleRun": this.handleRun(row); break; case "handleView": this.handleView(row); break; case "handleJobLog": this.handleJobLog(row); break; default: break; } }, // 任務(wù)狀態(tài)修改 handleStatusChange(row) { let text = row.status === "0" ? "啟用" : "停用"; this.$modal.confirm('確認要"' + text + '""' + row.jobName + '"任務(wù)嗎?').then(function() { return changeJobStatus(row.jobId, row.status); }).then(() => { this.$modal.msgSuccess(text + "成功"); }).catch(function() { row.status = row.status === "0" ? "1" : "0"; }); }, /* 立即執(zhí)行一次 */ handleRun(row) { this.$modal.confirm('確認要立即執(zhí)行一次"' + row.jobName + '"任務(wù)嗎?').then(function() { return runJob(row.jobId, row.jobGroup); }).then(() => { this.$modal.msgSuccess("執(zhí)行成功"); }).catch(() => {}); }, /** 任務(wù)詳細信息 */ handleView(row) { getJob(row.jobId).then(response => { this.form = response.data; this.openView = true; }); }, /** cron表達式按鈕操作 */ handleShowCron() { this.expression = this.form.cronExpression; this.openCron = true; }, /** 確定后回傳值 */ crontabFill(value) { this.form.cronExpression = value; }, /** 任務(wù)日志列表查詢 */ handleJobLog(row) { const jobId = row.jobId || 0; this.$router.push({ path: '/monitor/job-log/index', query: { jobId: jobId } }) }, /** 新增按鈕操作 */ handleAdd() { this.reset(); this.open = true; this.title = "添加任務(wù)"; }, /** 修改按鈕操作 */ handleUpdate(row) { this.reset(); const jobId = row.jobId || this.ids; getJob(jobId).then(response => { this.form = response.data; this.open = true; this.title = "修改任務(wù)"; }); }, /** 提交按鈕 */ submitForm: function() { this.$refs["form"].validate(valid => { if (valid) { if (this.form.jobId != undefined) { updateJob(this.form).then(response => { this.$modal.msgSuccess("修改成功"); this.open = false; this.getList(); }); } else { addJob(this.form).then(response => { this.$modal.msgSuccess("新增成功"); this.open = false; this.getList(); }); } } }); }, /** 刪除按鈕操作 */ handleDelete(row) { const jobIds = row.jobId || this.ids; this.$modal.confirm('是否確認刪除定時任務(wù)編號為"' + jobIds + '"的數(shù)據(jù)項?').then(function() { return delJob(jobIds); }).then(() => { this.getList(); this.$modal.msgSuccess("刪除成功"); }).catch(() => {}); }, /** 導(dǎo)出按鈕操作 */ handleExport() { this.download('monitor/job/export', { ...this.queryParams }, `job_${new Date().getTime()}.xlsx`) } } }; </script> 復(fù)制代碼
<template> <div class="app-container"> <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px"> <el-form-item label="任務(wù)名稱" prop="jobName"> <el-input v-model="queryParams.jobName" placeholder="請輸入任務(wù)名稱" clearable style="width: 240px" @keyup.enter.native="handleQuery" /> </el-form-item> <el-form-item label="任務(wù)組名" prop="jobGroup"> <el-select v-model="queryParams.jobGroup" placeholder="請選擇任務(wù)組名" clearable style="width: 240px" > <el-option v-for="dict in dict.type.sys_job_group" :key="dict.value" :label="dict.label" :value="dict.value" /> </el-select> </el-form-item> <el-form-item label="執(zhí)行狀態(tài)" prop="status"> <el-select v-model="queryParams.status" placeholder="請選擇執(zhí)行狀態(tài)" clearable style="width: 240px" > <el-option v-for="dict in dict.type.sys_common_status" :key="dict.value" :label="dict.label" :value="dict.value" /> </el-select> </el-form-item> <el-form-item label="執(zhí)行時間"> <el-date-picker v-model="dateRange" style="width: 240px" value-format="yyyy-MM-dd" type="daterange" range-separator="-" start-placeholder="開始日期" end-placeholder="結(jié)束日期" ></el-date-picker> </el-form-item> <el-form-item> <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button> </el-form-item> </el-form> ? <el-row :gutter="10" class="mb8"> <el-col :span="1.5"> <el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete" v-hasPermi="['monitor:job:remove']" >刪除</el-button> </el-col> <el-col :span="1.5"> <el-button type="danger" plain icon="el-icon-delete" size="mini" @click="handleClean" v-hasPermi="['monitor:job:remove']" >清空</el-button> </el-col> <el-col :span="1.5"> <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" v-hasPermi="['monitor:job:export']" >導(dǎo)出</el-button> </el-col> <el-col :span="1.5"> <el-button type="warning" plain icon="el-icon-close" size="mini" @click="handleClose" >關(guān)閉</el-button> </el-col> <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> </el-row> ? <el-table v-loading="loading" :data="jobLogList" @selection-change="handleSelectionChange"> <el-table-column type="selection" width="55" align="center" /> <el-table-column label="日志編號" width="80" align="center" prop="jobLogId" /> <el-table-column label="任務(wù)名稱" align="center" prop="jobName" :show-overflow-tooltip="true" /> <el-table-column label="任務(wù)組名" align="center" prop="jobGroup" :show-overflow-tooltip="true"> <template slot-scope="scope"> <dict-tag :options="dict.type.sys_job_group" :value="scope.row.jobGroup"/> </template> </el-table-column> <el-table-column label="調(diào)用目標字符串" align="center" prop="invokeTarget" :show-overflow-tooltip="true" /> <el-table-column label="日志信息" align="center" prop="jobMessage" :show-overflow-tooltip="true" /> <el-table-column label="執(zhí)行狀態(tài)" align="center" prop="status"> <template slot-scope="scope"> <dict-tag :options="dict.type.sys_common_status" :value="scope.row.status"/> </template> </el-table-column> <el-table-column label="執(zhí)行時間" align="center" prop="createTime" width="180"> <template slot-scope="scope"> <span>{{ parseTime(scope.row.createTime) }}</span> </template> </el-table-column> <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> <template slot-scope="scope"> <el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)" v-hasPermi="['monitor:job:query']" >詳細</el-button> </template> </el-table-column> </el-table> ? <pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" /> ? <!-- 調(diào)度日志詳細 --> <el-dialog title="調(diào)度日志詳細" :visible.sync="open" width="700px" append-to-body> <el-form ref="form" :model="form" label-width="100px" size="mini"> <el-row> <el-col :span="12"> <el-form-item label="日志序號:">{{ form.jobLogId }}</el-form-item> <el-form-item label="任務(wù)名稱:">{{ form.jobName }}</el-form-item> </el-col> <el-col :span="12"> <el-form-item label="任務(wù)分組:">{{ form.jobGroup }}</el-form-item> <el-form-item label="執(zhí)行時間:">{{ form.createTime }}</el-form-item> </el-col> <el-col :span="24"> <el-form-item label="調(diào)用方法:">{{ form.invokeTarget }}</el-form-item> </el-col> <el-col :span="24"> <el-form-item label="日志信息:">{{ form.jobMessage }}</el-form-item> </el-col> <el-col :span="24"> <el-form-item label="執(zhí)行狀態(tài):"> <div v-if="form.status == 0">正常</div> <div v-else-if="form.status == 1">失敗</div> </el-form-item> </el-col> <el-col :span="24"> <el-form-item label="異常信息:" v-if="form.status == 1">{{ form.exceptionInfo }}</el-form-item> </el-col> </el-row> </el-form> <div slot="footer" class="dialog-footer"> <el-button @click="open = false">關(guān) 閉</el-button> </div> </el-dialog> </div> </template> ? <script> import { getJob} from "@/api/monitor/job"; import { listJobLog, delJobLog, cleanJobLog } from "@/api/monitor/jobLog"; ? export default { name: "JobLog", dicts: ['sys_common_status', 'sys_job_group'], data() { return { // 遮罩層 loading: true, // 選中數(shù)組 ids: [], // 非多個禁用 multiple: true, // 顯示搜索條件 showSearch: true, // 總條數(shù) total: 0, // 調(diào)度日志表格數(shù)據(jù) jobLogList: [], // 是否顯示彈出層 open: false, // 日期范圍 dateRange: [], // 表單參數(shù) form: {}, // 查詢參數(shù) queryParams: { pageNum: 1, pageSize: 10, jobName: undefined, jobGroup: undefined, status: undefined } }; }, created() { const jobId = this.$route.query.jobId; if (jobId !== undefined && jobId != 0) { getJob(jobId).then(response => { this.queryParams.jobName = response.data.jobName; this.queryParams.jobGroup = response.data.jobGroup; this.getList(); }); } else { this.getList(); } }, methods: { /** 查詢調(diào)度日志列表 */ getList() { this.loading = true; listJobLog(this.addDateRange(this.queryParams, this.dateRange)).then(response => { this.jobLogList = response.rows; this.total = response.total; this.loading = false; } ); }, // 返回按鈕 handleClose() { const obj = { path: "/monitor/job" }; this.$tab.closeOpenPage(obj); }, /** 搜索按鈕操作 */ handleQuery() { this.queryParams.pageNum = 1; this.getList(); }, /** 重置按鈕操作 */ resetQuery() { this.dateRange = []; this.resetForm("queryForm"); this.handleQuery(); }, // 多選框選中數(shù)據(jù) handleSelectionChange(selection) { this.ids = selection.map(item => item.jobLogId); this.multiple = !selection.length; }, /** 詳細按鈕操作 */ handleView(row) { this.open = true; this.form = row; }, /** 刪除按鈕操作 */ handleDelete(row) { const jobLogIds = this.ids; this.$modal.confirm('是否確認刪除調(diào)度日志編號為"' + jobLogIds + '"的數(shù)據(jù)項?').then(function() { return delJobLog(jobLogIds); }).then(() => { this.getList(); this.$modal.msgSuccess("刪除成功"); }).catch(() => {}); }, /** 清空按鈕操作 */ handleClean() { this.$modal.confirm('是否確認清空所有調(diào)度日志數(shù)據(jù)項?').then(function() { return cleanJobLog(); }).then(() => { this.getList(); this.$modal.msgSuccess("清空成功"); }).catch(() => {}); }, /** 導(dǎo)出按鈕操作 */ handleExport() { this.download('/monitor/jobLog/export', { ...this.queryParams }, `log_${new Date().getTime()}.xlsx`) } } }; </script>
以上就是Springboot Vue可配置調(diào)度任務(wù)實現(xiàn)示例詳解的詳細內(nèi)容,更多關(guān)于Springboot Vue配置調(diào)度任務(wù)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Spring MVC處理參數(shù)中的枚舉類型通用實現(xiàn)方法
這篇文章主要給大家介紹了關(guān)于Spring MVC處理參數(shù)中的枚舉類型通用實現(xiàn)方法的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起看看吧2018-11-11spring的同一定時任務(wù)上一次的任務(wù)未結(jié)束前不會啟動這次任務(wù)問題
這篇文章主要介紹了spring的同一定時任務(wù)上一次的任務(wù)未結(jié)束前不會啟動這次任務(wù)問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12猜你不知道Spring Boot的幾種部署方式(小結(jié))
這篇文章主要介紹了猜你不知道Spring Boot的幾種部署方式(小結(jié)),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-07-07