正文
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
{
public List<SysJob> selectJobList(SysJob job);
public SysJob selectJobById(Long jobId);
public int pauseJob(SysJob job) throws SchedulerException;
public int resumeJob(SysJob job) throws SchedulerException;
public int deleteJob(SysJob job) throws SchedulerException;
public void deleteJobByIds(Long[] jobIds) throws SchedulerException;
public int changeStatus(SysJob job) throws SchedulerException;
public boolean run(SysJob job) throws SchedulerException;
public int insertJob(SysJob job) throws SchedulerException, TaskException;
public int updateJob(SysJob job) throws SchedulerException, TaskException;
public boolean checkCronExpressionIsValid(String cronExpression);
}
复制代码
@Service
public class SysJobServiceImpl implements ISysJobService
{
@Autowired
private Scheduler scheduler;
@Autowired
private SysJobMapper jobMapper;
@PostConstruct
public void init() throws SchedulerException, TaskException
{
scheduler.clear();
List<SysJob> jobList = jobMapper.selectJobAll();
for (SysJob job : jobList)
{
ScheduleUtils.createScheduleJob(scheduler, job);
}
}
@Override
public List<SysJob> selectJobList(SysJob job)
{
return jobMapper.selectJobList(job);
}
@Override
public SysJob selectJobById(Long jobId)
{
return jobMapper.selectJobById(jobId);
}
@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;
}
@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;
}
@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;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteJobByIds(Long[] jobIds) throws SchedulerException
{
for (Long jobId : jobIds)
{
SysJob job = jobMapper.selectJobById(jobId);
deleteJob(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;
}
@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;
}
@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;
}
@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;
}
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);
}
@Override
public boolean checkCronExpressionIsValid(String cronExpression)
{
return CronUtils.isValid(cronExpression);
}
}
复制代码
4、Mapper
public interface SysJobMapper
{
public List<SysJob> selectJobList(SysJob job);
public List<SysJob> selectJobAll();
public SysJob selectJobById(Long jobId);
public int deleteJobById(Long jobId);
public int deleteJobByIds(Long[] ids);
public int updateJob(SysJob job);
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;
});
},
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>
以上就是Springboot Vue可配置调度任务实现示例详解的详细内容,更多关于Springboot Vue配置调度任务的资料请关注编程网其它相关文章!