Springboot Vue可配置调度任务实现示例详解
harhar 人气:0正文
Springboot + Vue,定时任务调度的全套实现方案。
这里用了quartz这个框架,实现分布式调度任务很不错,关于quarz的使用方式改天补一篇。相当简单。
1、表结构:
sys_job | |||||
---|---|---|---|---|---|
列名 | 数据类型 | 长度 | 是否可空 | 是否主键 | 说明 |
job_id | bigint | 否 | 是 | 任务ID | |
job_name | varchar | 64 | 否 | 是 | 任务名称 |
job_group | varchar | 64 | 否 | 是 | 任务组名 |
invoke_target | varchar | 500 | 否 | 否 | 调用目标字符串 |
cron_expression | varchar | 255 | 是 | 否 | cron执行表达式 |
misfire_policy | varchar | 20 | 是 | 否 | 计划执行错误策略(1立即执行 2执行一次 3放弃执行) |
concurrent | char | 1 | 是 | 否 | 是否并发执行(0允许 1禁止) |
status | char | 1 | 是 | 否 | 状态(0正常 1暂停) |
create_by | varchar | 64 | 是 | 否 | 创建者 |
create_time | datetime | 是 | 否 | 创建时间 | |
update_by | varchar | 64 | 是 | 否 | 更新者 |
update_time | datetime | 是 | 否 | 更新时间 | |
remark | varchar | 500 | 是 | 否 | 备注信息 |
2、接口:
@RestController @RequestMapping("/job") public class SysJobController extends BaseController { @Autowired private ISysJobService jobService; /** * 查询定时任务列表 */ @GetMapping("/list") public TableDataInfo list(SysJob sysJob) { startPage(); List<SysJob> list = jobService.selectJobList(sysJob); return getDataTable(list); } /** * 导出定时任务列表 */ @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, "定时任务"); } /** * 获取定时任务详细信息 */ @GetMapping(value = "/{jobId}") public AjaxResult getInfo(@PathVariable("jobId") Long jobId) { return success(jobService.selectJobById(jobId)); } /** * 新增定时任务 */ @PostMapping public AjaxResult add(@RequestBody SysJob job) throws SchedulerException, TaskException { if (!CronUtils.isValid(job.getCronExpression())) { return error("新增任务'" + job.getJobName() + "'失败,Cron表达式不正确"); } else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_RMI)) { return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'rmi'调用"); } else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.LOOKUP_LDAP, Constants.LOOKUP_LDAPS })) { return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'ldap(s)'调用"); } else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.HTTP, Constants.HTTPS })) { return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'http(s)'调用"); } else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), Constants.JOB_ERROR_STR)) { return error("新增任务'" + job.getJobName() + "'失败,目标字符串存在违规"); } else if (!ScheduleUtils.whiteList(job.getInvokeTarget())) { return error("新增任务'" + job.getJobName() + "'失败,目标字符串不在白名单内"); } job.setCreateBy(getUsername()); return toAjax(jobService.insertJob(job)); } /** * 修改定时任务 */ @PutMapping public AjaxResult edit(@RequestBody SysJob job) throws SchedulerException, TaskException { if (!CronUtils.isValid(job.getCronExpression())) { return error("修改任务'" + job.getJobName() + "'失败,Cron表达式不正确"); } else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_RMI)) { return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'rmi'调用"); } else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.LOOKUP_LDAP, Constants.LOOKUP_LDAPS })) { return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'ldap(s)'调用"); } else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.HTTP, Constants.HTTPS })) { return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'http(s)'调用"); } else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), Constants.JOB_ERROR_STR)) { return error("修改任务'" + job.getJobName() + "'失败,目标字符串存在违规"); } else if (!ScheduleUtils.whiteList(job.getInvokeTarget())) { return error("修改任务'" + job.getJobName() + "'失败,目标字符串不在白名单内"); } job.setUpdateBy(getUsername()); return toAjax(jobService.updateJob(job)); } /** * 定时任务状态修改 */ @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)); } /** * 定时任务立即执行一次 */ @PutMapping("/run") public AjaxResult run(@RequestBody SysJob job) throws SchedulerException { boolean result = jobService.run(job); return result ? success() : error("任务不存在或已过期!"); } /** * 删除定时任务 */ @DeleteMapping("/{jobIds}") public AjaxResult remove(@PathVariable Long[] jobIds) throws SchedulerException, TaskException { jobService.deleteJobByIds(jobIds); return success(); } 复制代码
3、业务层:
public interface ISysJobService { /** * 获取quartz调度器的计划任务 * * @param job 调度信息 * @return 调度任务集合 */ public List<SysJob> selectJobList(SysJob job); /** * 通过调度任务ID查询调度信息 * * @param jobId 调度任务ID * @return 调度任务对象信息 */ public SysJob selectJobById(Long jobId); /** * 暂停任务 * * @param job 调度信息 * @return 结果 */ public int pauseJob(SysJob job) throws SchedulerException; /** * 恢复任务 * * @param job 调度信息 * @return 结果 */ public int resumeJob(SysJob job) throws SchedulerException; /** * 删除任务后,所对应的trigger也将被删除 * * @param job 调度信息 * @return 结果 */ public int deleteJob(SysJob job) throws SchedulerException; /** * 批量删除调度信息 * * @param jobIds 需要删除的任务ID * @return 结果 */ public void deleteJobByIds(Long[] jobIds) throws SchedulerException; /** * 任务调度状态修改 * * @param job 调度信息 * @return 结果 */ public int changeStatus(SysJob job) throws SchedulerException; /** * 立即运行任务 * * @param job 调度信息 * @return 结果 */ public boolean run(SysJob job) throws SchedulerException; /** * 新增任务 * * @param job 调度信息 * @return 结果 */ public int insertJob(SysJob job) throws SchedulerException, TaskException; /** * 更新任务 * * @param job 调度信息 * @return 结果 */ public int updateJob(SysJob job) throws SchedulerException, TaskException; /** * 校验cron表达式是否有效 * * @param cronExpression 表达式 * @return 结果 */ public boolean checkCronExpressionIsValid(String cronExpression); } 复制代码
@Service public class SysJobServiceImpl implements ISysJobService { @Autowired private Scheduler scheduler; @Autowired private SysJobMapper jobMapper; /** * 项目启动时,初始化定时器 主要是防止手动修改数据库导致未同步到定时任务处理(注:不能手动修改数据库ID和任务组名,否则会导致脏数据) */ @PostConstruct public void init() throws SchedulerException, TaskException { scheduler.clear(); List<SysJob> jobList = jobMapper.selectJobAll(); for (SysJob job : jobList) { ScheduleUtils.createScheduleJob(scheduler, job); } } /** * 获取quartz调度器的计划任务列表 * * @param job 调度信息 * @return */ @Override public List<SysJob> selectJobList(SysJob job) { return jobMapper.selectJobList(job); } /** * 通过调度任务ID查询调度信息 * * @param jobId 调度任务ID * @return 调度任务对象信息 */ @Override public SysJob selectJobById(Long jobId) { return jobMapper.selectJobById(jobId); } /** * 暂停任务 * * @param job 调度信息 */ @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; } /** * 恢复任务 * * @param job 调度信息 */ @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; } /** * 删除任务后,所对应的trigger也将被删除 * * @param job 调度信息 */ @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; } /** * 批量删除调度信息 * * @param jobIds 需要删除的任务ID * @return 结果 */ @Override @Transactional(rollbackFor = Exception.class) public void deleteJobByIds(Long[] jobIds) throws SchedulerException { for (Long jobId : jobIds) { SysJob job = jobMapper.selectJobById(jobId); deleteJob(job); } } /** * 任务调度状态修改 * * @param job 调度信息 */ @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; } /** * 立即运行任务 * * @param job 调度信息 */ @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()); // 参数 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; } /** * 新增任务 * * @param job 调度信息 调度信息 */ @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; } /** * 更新任务的时间表达式 * * @param job 调度信息 */ @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; } /** * 更新任务 * * @param job 任务对象 * @param jobGroup 任务组名 */ public void updateSchedulerJob(SysJob job, String jobGroup) throws SchedulerException, TaskException { Long jobId = job.getJobId(); // 判断是否存在 JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup); if (scheduler.checkExists(jobKey)) { // 防止创建时存在数据问题 先移除,然后在执行创建操作 scheduler.deleteJob(jobKey); } ScheduleUtils.createScheduleJob(scheduler, job); } /** * 校验cron表达式是否有效 * * @param cronExpression 表达式 * @return 结果 */ @Override public boolean checkCronExpressionIsValid(String cronExpression) { return CronUtils.isValid(cronExpression); } } 复制代码
4、Mapper
public interface SysJobMapper { /** * 查询调度任务日志集合 * * @param job 调度信息 * @return 操作日志集合 */ public List<SysJob> selectJobList(SysJob job); /** * 查询所有调度任务 * * @return 调度任务列表 */ public List<SysJob> selectJobAll(); /** * 通过调度ID查询调度任务信息 * * @param jobId 调度ID * @return 角色对象信息 */ public SysJob selectJobById(Long jobId); /** * 通过调度ID删除调度任务信息 * * @param jobId 调度ID * @return 结果 */ public int deleteJobById(Long jobId); /** * 批量删除调度任务信息 * * @param ids 需要删除的数据ID * @return 结果 */ public int deleteJobByIds(Long[] ids); /** * 修改调度任务信息 * * @param job 调度任务信息 * @return 结果 */ public int updateJob(SysJob job); /** * 新增调度任务信息 * * @param job 调度任务信息 * @return 结果 */ public int insertJob(SysJob job); } 复制代码
<?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> 复制代码
5、前端(Vue):
import request from '@/utils/request' // 查询定时任务调度列表 export function listJob(query) { return request({ url: '/monitor/job/list', method: 'get', params: query }) } // 查询定时任务调度详细 export function getJob(jobId) { return request({ url: '/monitor/job/' + jobId, method: 'get' }) } // 新增定时任务调度 export function addJob(data) { return request({ url: '/monitor/job', method: 'post', data: data }) } // 修改定时任务调度 export function updateJob(data) { return request({ url: '/monitor/job', method: 'put', data: data }) } // 删除定时任务调度 export function delJob(jobId) { return request({ url: '/monitor/job/' + jobId, method: 'delete' }) } // 任务状态修改 export function changeJobStatus(jobId, status) { const data = { jobId, status } return request({ url: '/monitor/job/changeStatus', method: 'put', data: data }) } // 定时任务立即执行一次 export function runJob(jobId, jobGroup) { const data = { jobId, jobGroup } return request({ url: '/monitor/job/run', method: 'put', data: data }) } 复制代码
import request from '@/utils/request' // 查询调度日志列表 export function listJobLog(query) { return request({ url: '/monitor/jobLog/list', method: 'get', params: query }) } // 删除调度日志 export function delJobLog(jobLogId) { return request({ url: '/monitor/jobLog/' + jobLogId, method: 'delete' }) } // 清空调度日志 export function cleanJobLog() { return request({ url: '/monitor/jobLog/clean', method: 'delete' }) } 复制代码
<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="任务名称" prop="jobName"> <el-input v-model="queryParams.jobName" placeholder="请输入任务名称" clearable @keyup.enter.native="handleQuery" /> </el-form-item> <el-form-item label="任务组名" prop="jobGroup"> <el-select v-model="queryParams.jobGroup" placeholder="请选择任务组名" 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="任务状态" prop="status"> <el-select v-model="queryParams.status" placeholder="请选择任务状态" 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']" >导出</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="任务编号" width="100" align="center" prop="jobId" /> <el-table-column label="任务名称" align="center" prop="jobName" :show-overflow-tooltip="true" /> <el-table-column label="任务组名" 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="调用目标字符串" align="center" prop="invokeTarget" :show-overflow-tooltip="true" /> <el-table-column label="cron执行表达式" align="center" prop="cronExpression" :show-overflow-tooltip="true" /> <el-table-column label="状态" 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']">执行一次</el-dropdown-item> <el-dropdown-item command="handleView" icon="el-icon-view" v-hasPermi="['monitor:job:query']">任务详细</el-dropdown-item> <el-dropdown-item command="handleJobLog" icon="el-icon-s-operation" v-hasPermi="['monitor:job:query']">调度日志</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" /> <!-- 添加或修改定时任务对话框 --> <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="任务名称" prop="jobName"> <el-input v-model="form.jobName" placeholder="请输入任务名称" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="任务分组" prop="jobGroup"> <el-select v-model="form.jobGroup" placeholder="请选择任务分组"> <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"> 调用方法 <el-tooltip placement="top"> <div slot="content"> Bean调用示例:ryTask.ryParams('ry') <br />Class类调用示例:com.ruoyi.quartz.task.RyTask.ryParams('ry') <br />参数说明:支持字符串,布尔类型,长整型,浮点型,整型 </div> <i class="el-icon-question"></i> </el-tooltip> </span> <el-input v-model="form.invokeTarget" placeholder="请输入调用目标字符串" /> </el-form-item> </el-col> <el-col :span="24"> <el-form-item label="cron表达式" prop="cronExpression"> <el-input v-model="form.cronExpression" placeholder="请输入cron执行表达式"> <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="执行策略" prop="misfirePolicy"> <el-radio-group v-model="form.misfirePolicy" size="small"> <el-radio-button label="1">立即执行</el-radio-button> <el-radio-button label="2">执行一次</el-radio-button> <el-radio-button label="3">放弃执行</el-radio-button> </el-radio-group> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="是否并发" 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="状态"> <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> <!-- 任务日志详细 --> <el-dialog title="任务详细" :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="任务编号:">{{ form.jobId }}</el-form-item> <el-form-item label="任务名称:">{{ form.jobName }}</el-form-item> </el-col> <el-col :span="12"> <el-form-item label="任务分组:">{{ jobGroupFormat(form) }}</el-form-item> <el-form-item label="创建时间:">{{ 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="下次执行时间:">{{ parseTime(form.nextValidTime) }}</el-form-item> </el-col> <el-col :span="24"> <el-form-item label="调用目标方法:">{{ form.invokeTarget }}</el-form-item> </el-col> <el-col :span="12"> <el-form-item label="任务状态:"> <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="是否并发:"> <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="执行策略:"> <div v-if="form.misfirePolicy == 0">默认策略</div> <div v-else-if="form.misfirePolicy == 1">立即执行</div> <div v-else-if="form.misfirePolicy == 2">执行一次</div> <div v-else-if="form.misfirePolicy == 3">放弃执行</div> </el-form-item> </el-col> </el-row> </el-form> <div slot="footer" class="dialog-footer"> <el-button @click="openView = false">关 闭</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, // 选中数组 ids: [], // 非单个禁用 single: true, // 非多个禁用 multiple: true, // 显示搜索条件 showSearch: true, // 总条数 total: 0, // 定时任务表格数据 jobList: [], // 弹出层标题 title: "", // 是否显示弹出层 open: false, // 是否显示详细弹出层 openView: false, // 是否显示Cron表达式弹出层 openCron: false, // 传入的表达式 expression: "", // 查询参数 queryParams: { pageNum: 1, pageSize: 10, jobName: undefined, jobGroup: undefined, status: undefined }, // 表单参数 form: {}, // 表单校验 rules: { jobName: [ { required: true, message: "任务名称不能为空", trigger: "blur" } ], invokeTarget: [ { required: true, message: "调用目标字符串不能为空", trigger: "blur" } ], cronExpression: [ { required: true, message: "cron执行表达式不能为空", trigger: "blur" } ] } }; }, created() { this.getList(); }, methods: { /** 查询定时任务列表 */ getList() { this.loading = true; listJob(this.queryParams).then(response => { this.jobList = response.rows; this.total = response.total; this.loading = false; }); }, // 任务组名字典翻译 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(); }, // 多选框选中数据 handleSelectionChange(selection) { this.ids = selection.map(item => item.jobId); this.single = selection.length != 1; this.multiple = !selection.length; }, // 更多操作触发 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; } }, // 任务状态修改 handleStatusChange(row) { let text = row.status === "0" ? "启用" : "停用"; this.$modal.confirm('确认要"' + text + '""' + row.jobName + '"任务吗?').then(function() { return changeJobStatus(row.jobId, row.status); }).then(() => { this.$modal.msgSuccess(text + "成功"); }).catch(function() { row.status = row.status === "0" ? "1" : "0"; }); }, /* 立即执行一次 */ handleRun(row) { this.$modal.confirm('确认要立即执行一次"' + row.jobName + '"任务吗?').then(function() { return runJob(row.jobId, row.jobGroup); }).then(() => { this.$modal.msgSuccess("执行成功"); }).catch(() => {}); }, /** 任务详细信息 */ 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; }, /** 任务日志列表查询 */ 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 = "添加任务"; }, /** 修改按钮操作 */ handleUpdate(row) { this.reset(); const jobId = row.jobId || this.ids; getJob(jobId).then(response => { this.form = response.data; this.open = true; this.title = "修改任务"; }); }, /** 提交按钮 */ 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('是否确认删除定时任务编号为"' + jobIds + '"的数据项?').then(function() { return delJob(jobIds); }).then(() => { this.getList(); this.$modal.msgSuccess("删除成功"); }).catch(() => {}); }, /** 导出按钮操作 */ handleExport() { this.download('monitor/job/export', { ...this.queryParams }, `job_${new Date().getTime()}.xlsx`) } } }; </script> 复制代码
<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="任务名称" prop="jobName"> <el-input v-model="queryParams.jobName" placeholder="请输入任务名称" clearable style="width: 240px" @keyup.enter.native="handleQuery" /> </el-form-item> <el-form-item label="任务组名" prop="jobGroup"> <el-select v-model="queryParams.jobGroup" placeholder="请选择任务组名" 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="执行状态" prop="status"> <el-select v-model="queryParams.status" placeholder="请选择执行状态" 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="执行时间"> <el-date-picker v-model="dateRange" style="width: 240px" value-format="yyyy-MM-dd" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" ></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']" >导出</el-button> </el-col> <el-col :span="1.5"> <el-button type="warning" plain icon="el-icon-close" size="mini" @click="handleClose" >关闭</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="任务名称" align="center" prop="jobName" :show-overflow-tooltip="true" /> <el-table-column label="任务组名" 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="调用目标字符串" align="center" prop="invokeTarget" :show-overflow-tooltip="true" /> <el-table-column label="日志信息" align="center" prop="jobMessage" :show-overflow-tooltip="true" /> <el-table-column label="执行状态" 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="执行时间" 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" /> <!-- 调度日志详细 --> <el-dialog title="调度日志详细" :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="任务名称:">{{ form.jobName }}</el-form-item> </el-col> <el-col :span="12"> <el-form-item label="任务分组:">{{ form.jobGroup }}</el-form-item> <el-form-item label="执行时间:">{{ form.createTime }}</el-form-item> </el-col> <el-col :span="24"> <el-form-item label="调用方法:">{{ 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="执行状态:"> <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">关 闭</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, // 选中数组 ids: [], // 非多个禁用 multiple: true, // 显示搜索条件 showSearch: true, // 总条数 total: 0, // 调度日志表格数据 jobLogList: [], // 是否显示弹出层 open: false, // 日期范围 dateRange: [], // 表单参数 form: {}, // 查询参数 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: { /** 查询调度日志列表 */ 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(); }, // 多选框选中数据 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('是否确认删除调度日志编号为"' + jobLogIds + '"的数据项?').then(function() { return delJobLog(jobLogIds); }).then(() => { this.getList(); this.$modal.msgSuccess("删除成功"); }).catch(() => {}); }, /** 清空按钮操作 */ handleClean() { this.$modal.confirm('是否确认清空所有调度日志数据项?').then(function() { return cleanJobLog(); }).then(() => { this.getList(); this.$modal.msgSuccess("清空成功"); }).catch(() => {}); }, /** 导出按钮操作 */ handleExport() { this.download('/monitor/jobLog/export', { ...this.queryParams }, `log_${new Date().getTime()}.xlsx`) } } }; </script>
加载全部内容