feat: 2.4.0
1. 前端新增cron表达式组件
This commit is contained in:
parent
01eefc2774
commit
69e0fe0a48
@ -3,8 +3,8 @@ package com.aizuda.easy.retry.client.job.core.client;
|
||||
import com.aizuda.easy.retry.client.job.core.IJobExecutor;
|
||||
import com.aizuda.easy.retry.client.job.core.cache.JobExecutorInfoCache;
|
||||
import com.aizuda.easy.retry.client.job.core.cache.ThreadPoolCache;
|
||||
import com.aizuda.easy.retry.client.job.core.executor.AbstractJobExecutor;
|
||||
import com.aizuda.easy.retry.client.job.core.executor.AnnotationJobExecutor;
|
||||
import com.aizuda.easy.retry.client.job.core.executor.NormalJobExecutor;
|
||||
import com.aizuda.easy.retry.client.job.core.dto.JobContext;
|
||||
import com.aizuda.easy.retry.client.job.core.dto.JobExecutorInfo;
|
||||
import com.aizuda.easy.retry.client.model.request.DispatchJobRequest;
|
||||
@ -48,7 +48,7 @@ public class JobEndPoint {
|
||||
IJobExecutor iJobExecutor = SpringContext.getBeanByType(AnnotationJobExecutor.class);
|
||||
iJobExecutor.jobExecute(jobContext);
|
||||
} else {
|
||||
NormalJobExecutor normalJobExecutor = (NormalJobExecutor) jobExecutorInfo.getExecutor();
|
||||
AbstractJobExecutor normalJobExecutor = (AbstractJobExecutor) jobExecutorInfo.getExecutor();
|
||||
normalJobExecutor.jobExecute(jobContext);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,14 @@
|
||||
package com.aizuda.easy.retry.client.job.core.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author: www.byteblogs.com
|
||||
* @date : 2023-10-18 16:53
|
||||
* @since : 2.4.0
|
||||
*/
|
||||
@Data
|
||||
public class JobArgs {
|
||||
|
||||
private String executorInfo;
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package com.aizuda.easy.retry.client.job.core.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* @author: www.byteblogs.com
|
||||
* @date : 2023-10-18 16:53
|
||||
* @since : 2.4.0
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class ShardingJobArgs extends JobArgs {
|
||||
|
||||
private Integer shardingTotal;
|
||||
|
||||
private Integer shardingIndex;
|
||||
|
||||
|
||||
}
|
@ -3,6 +3,7 @@ package com.aizuda.easy.retry.client.job.core.executor;
|
||||
import com.aizuda.easy.retry.client.job.core.IJobExecutor;
|
||||
import com.aizuda.easy.retry.client.job.core.cache.FutureCache;
|
||||
import com.aizuda.easy.retry.client.job.core.cache.ThreadPoolCache;
|
||||
import com.aizuda.easy.retry.client.job.core.dto.JobArgs;
|
||||
import com.aizuda.easy.retry.client.job.core.dto.JobContext;
|
||||
import com.aizuda.easy.retry.client.job.core.timer.StopTaskTimerTask;
|
||||
import com.aizuda.easy.retry.client.job.core.timer.TimerManager;
|
||||
@ -23,7 +24,7 @@ import java.util.concurrent.TimeUnit;
|
||||
* @date : 2023-09-27 09:48
|
||||
* @since 2.4.0
|
||||
*/
|
||||
abstract class AbstractJobExecutor implements IJobExecutor {
|
||||
public abstract class AbstractJobExecutor implements IJobExecutor {
|
||||
|
||||
@Override
|
||||
public void jobExecute(JobContext jobContext) {
|
||||
@ -43,6 +44,6 @@ abstract class AbstractJobExecutor implements IJobExecutor {
|
||||
|
||||
}
|
||||
|
||||
protected abstract ExecuteResult doJobExecute(JobContext jobContext);
|
||||
protected abstract ExecuteResult doJobExecute(JobArgs jobArgs);
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.aizuda.easy.retry.client.job.core.executor;
|
||||
|
||||
import com.aizuda.easy.retry.client.job.core.cache.JobExecutorInfoCache;
|
||||
import com.aizuda.easy.retry.client.job.core.dto.JobArgs;
|
||||
import com.aizuda.easy.retry.client.job.core.dto.JobContext;
|
||||
import com.aizuda.easy.retry.client.job.core.dto.JobExecutorInfo;
|
||||
import com.aizuda.easy.retry.client.model.ExecuteResult;
|
||||
@ -18,8 +19,8 @@ import org.springframework.util.ReflectionUtils;
|
||||
public class AnnotationJobExecutor extends AbstractJobExecutor {
|
||||
|
||||
@Override
|
||||
protected ExecuteResult doJobExecute(final JobContext jobContext) {
|
||||
JobExecutorInfo jobExecutorInfo = JobExecutorInfoCache.get(jobContext.getExecutorInfo());
|
||||
return (ExecuteResult) ReflectionUtils.invokeMethod(jobExecutorInfo.getMethod(), jobExecutorInfo.getExecutor(), jobContext);
|
||||
protected ExecuteResult doJobExecute(final JobArgs jobArgs) {
|
||||
JobExecutorInfo jobExecutorInfo = JobExecutorInfoCache.get(jobArgs.getExecutorInfo());
|
||||
return (ExecuteResult) ReflectionUtils.invokeMethod(jobExecutorInfo.getMethod(), jobExecutorInfo.getExecutor(), jobArgs);
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +0,0 @@
|
||||
package com.aizuda.easy.retry.client.job.core.executor;
|
||||
|
||||
/**
|
||||
* @author: www.byteblogs.com
|
||||
* @date : 2023-10-08 17:02
|
||||
* @since : 2.4.0
|
||||
*/
|
||||
public abstract class NormalJobExecutor extends AbstractJobExecutor {
|
||||
|
||||
}
|
@ -61,4 +61,10 @@ public class JobController {
|
||||
return jobService.deleteJobById(id);
|
||||
}
|
||||
|
||||
@GetMapping("/cron")
|
||||
@LoginRequired
|
||||
public List<String> getTimeByCron(@RequestParam("cron") String cron) {
|
||||
return jobService.getTimeByCron(cron);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ public class JobResponseVO {
|
||||
/**
|
||||
* 执行器名称
|
||||
*/
|
||||
private String executorName;
|
||||
private String executorInfo;
|
||||
|
||||
/**
|
||||
* 触发类型 1.CRON 表达式 2. 固定时间
|
||||
|
@ -25,4 +25,6 @@ public interface JobService {
|
||||
Boolean updateJobStatus(JobUpdateJobStatusRequestVO jobRequestVO);
|
||||
|
||||
Boolean deleteJobById(Long id);
|
||||
|
||||
List<String> getTimeByCron(String cron);
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package com.aizuda.easy.retry.server.web.service.impl;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.aizuda.easy.retry.common.core.enums.StatusEnum;
|
||||
import com.aizuda.easy.retry.common.core.util.CronExpression;
|
||||
import com.aizuda.easy.retry.server.common.exception.EasyRetryServerException;
|
||||
import com.aizuda.easy.retry.server.job.task.support.WaitStrategy;
|
||||
import com.aizuda.easy.retry.server.job.task.support.strategy.WaitStrategies.WaitStrategyContext;
|
||||
@ -22,10 +23,15 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author www.byteblogs.com
|
||||
@ -35,6 +41,8 @@ import java.util.concurrent.TimeUnit;
|
||||
@Service
|
||||
public class JobServiceImpl implements JobService {
|
||||
|
||||
private static final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
@Autowired
|
||||
private JobMapper jobMapper;
|
||||
|
||||
@ -72,6 +80,26 @@ public class JobServiceImpl implements JobService {
|
||||
return JobResponseVOConverter.INSTANCE.toJobResponseVO(job);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTimeByCron(String cron) {
|
||||
|
||||
List<String> list = new ArrayList<>();
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
for (int i = 0; i < 5; i++) {
|
||||
Date nextValidTime;
|
||||
try {
|
||||
ZonedDateTime zdt = now.atZone(ZoneOffset.ofHours(8));
|
||||
nextValidTime = new CronExpression(cron).getNextValidTimeAfter(Date.from(zdt.toInstant()));
|
||||
now = LocalDateTime.ofEpochSecond( nextValidTime.getTime() / 1000,0, ZoneOffset.ofHours(8));
|
||||
list.add(dateTimeFormatter.format(now));
|
||||
} catch (ParseException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean saveJob(JobRequestVO jobRequestVO) {
|
||||
Job job = JobConverter.INSTANCE.toJob(jobRequestVO);
|
||||
|
@ -14,8 +14,9 @@
|
||||
"@antv/data-set": "^0.10.2",
|
||||
"@antv/g2": "^3.5.1",
|
||||
"ant-design-vue": "^1.7.6",
|
||||
"antv-cron": "^1.0.9",
|
||||
"axios": ">=0.21.1",
|
||||
"core-js": "^3.1.2",
|
||||
"core-js": "^3.8.3",
|
||||
"enquire.js": "^2.1.6",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"lodash.get": "^4.4.2",
|
||||
|
@ -7,6 +7,7 @@ const jobApi = {
|
||||
updateJob: '/job/',
|
||||
updateJobStatus: '/job/status',
|
||||
delJob: '/job/',
|
||||
timeByCron: '/job/cron',
|
||||
|
||||
// 任务批次
|
||||
jobBatchList: '/job/batch/list',
|
||||
@ -21,6 +22,14 @@ const jobApi = {
|
||||
|
||||
export default jobApi
|
||||
|
||||
export function timeByCron (parameter) {
|
||||
return request({
|
||||
url: jobApi.timeByCron,
|
||||
method: 'get',
|
||||
params: parameter
|
||||
})
|
||||
}
|
||||
|
||||
export function delJob (id) {
|
||||
return request({
|
||||
url: jobApi.delJob + id,
|
||||
|
@ -20,7 +20,8 @@ import './core/lazy_use' // use lazy load components
|
||||
import './permission' // permission control
|
||||
import './utils/filter' // global filter
|
||||
import './global.less' // global style
|
||||
|
||||
import CronInput from 'antv-cron/packages/index'
|
||||
import 'antv-cron/lib/antv-cron.css'
|
||||
Vue.config.productionTip = false
|
||||
|
||||
// mount axios to `Vue.$http` and `this.$http`
|
||||
@ -29,6 +30,7 @@ Vue.use(VueAxios)
|
||||
Vue.component('pro-layout', ProLayout)
|
||||
Vue.component('page-container', PageHeaderWrapper)
|
||||
Vue.component('page-header-wrapper', PageHeaderWrapper)
|
||||
Vue.use(CronInput)
|
||||
|
||||
window.umi_plugin_ant_themeVar = themePluginConfig.theme
|
||||
|
||||
|
@ -58,7 +58,7 @@
|
||||
</a-tag>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="执行器名称" span="4">
|
||||
{{ jobInfo.executorName }}
|
||||
{{ jobInfo.executorInfo }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="任务类型">
|
||||
<a-tag :color="taskType[jobInfo.taskType].color">
|
||||
|
@ -107,7 +107,7 @@
|
||||
</a-tag>
|
||||
</span>
|
||||
<span slot="triggerInterval" slot-scope="text">
|
||||
<span>{{ text }}(秒)</span>
|
||||
<span>{{ text }}</span>
|
||||
</span>
|
||||
<span slot="executorTimeout" slot-scope="text">
|
||||
<span>{{ text }}(秒)</span>
|
||||
@ -199,7 +199,8 @@ export default {
|
||||
},
|
||||
{
|
||||
title: '触发时间',
|
||||
dataIndex: 'nextTriggerAt'
|
||||
dataIndex: 'nextTriggerAt',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
@ -219,7 +220,8 @@ export default {
|
||||
{
|
||||
title: '间隔时长',
|
||||
dataIndex: 'triggerInterval',
|
||||
scopedSlots: { customRender: 'triggerInterval' }
|
||||
scopedSlots: { customRender: 'triggerInterval' },
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '阻塞策略',
|
||||
|
54
frontend/src/views/job/from/CronModal.vue
Normal file
54
frontend/src/views/job/from/CronModal.vue
Normal file
@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<div>
|
||||
<a-modal :visible="visible" title="Cron表达式" @ok="handleOk" @cancel="handlerCancel" width="650px">
|
||||
<cron-input v-model="cron" :item="cronItem" @change="showFive"/>
|
||||
<a-input placeholder="请输入cron表达式" v-model="cron"/>
|
||||
<div style="margin: 20px 0; border-left: #f5222d 5px solid; font-size: medium; font-weight: bold">
|
||||
近5次的运行时间:
|
||||
</div>
|
||||
<div v-for="(item, index) in list" :key="item" style="margin-top: 10px"> 第{{ index + 1 }}次: {{ item }}</div>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { timeByCron } from '@/api/jobApi'
|
||||
|
||||
export default {
|
||||
name: 'CronModal',
|
||||
data () {
|
||||
return {
|
||||
visible: false,
|
||||
cronItem: ['second', 'minute', 'hour', 'day', 'month', 'week', 'year'],
|
||||
cron: '',
|
||||
list: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleOk () {
|
||||
this.visible = false
|
||||
this.$emit('getCron', this.cron)
|
||||
},
|
||||
handlerCancel () {
|
||||
this.visible = false
|
||||
},
|
||||
isShow (cron) {
|
||||
this.cron = cron
|
||||
this.visible = true
|
||||
},
|
||||
showFive (cron) {
|
||||
this.cron = cron
|
||||
timeByCron({ 'cron': cron }).then(res => {
|
||||
this.list = res.data
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.ant-cron-result{
|
||||
display: none;
|
||||
}
|
||||
|
||||
</style>
|
@ -101,6 +101,7 @@
|
||||
<a-col :lg="16" :md="12" :sm="12">
|
||||
<a-form-item label="间隔时长">
|
||||
<a-input
|
||||
@click="handlerCron"
|
||||
placeholder="请输入间隔时长"
|
||||
v-decorator="[
|
||||
'triggerInterval',
|
||||
@ -250,7 +251,7 @@
|
||||
</a-form>
|
||||
</a-card>
|
||||
|
||||
<a-modal :visible="visible" title="添加配置" @ok="handleOk" @cancel="handlerCancel" width="500px">
|
||||
<a-modal :visible="visible" title="分片参数" @ok="handleOk" @cancel="handlerCancel" width="500px">
|
||||
<a-form :form="dynamicForm" @submit="handleSubmit" :body-style="{padding: '0px 0px'}" v-bind="formItemLayout" >
|
||||
<a-form-item
|
||||
v-for="(k, index) in dynamicForm.getFieldValue('keys')"
|
||||
@ -298,6 +299,7 @@
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
<cron-modal ref="cronModalRef" @getCron="getCron"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -305,11 +307,18 @@
|
||||
import { getAllGroupNameList } from '@/api/manage'
|
||||
import { getJobDetail, saveJob, updateJob } from '@/api/jobApi'
|
||||
import pick from 'lodash.pick'
|
||||
import CronModal from '@/views/job/from/CronModal'
|
||||
|
||||
const enums = require('@/utils/enum')
|
||||
|
||||
let id = 0
|
||||
export default {
|
||||
name: 'JobFrom',
|
||||
components: { CronModal },
|
||||
props: {},
|
||||
comments: {
|
||||
CronModal
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
form: this.$form.createForm(this),
|
||||
@ -336,7 +345,6 @@ export default {
|
||||
}
|
||||
},
|
||||
beforeCreate () {
|
||||
console.log('beforeCreate')
|
||||
this.dynamicForm = this.$form.createForm(this, { name: 'dynamic_form_item' })
|
||||
this.dynamicForm.getFieldDecorator('keys', { initialValue: [], preserve: true })
|
||||
},
|
||||
@ -357,6 +365,16 @@ export default {
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
handlerCron () {
|
||||
const triggerType = this.form.getFieldValue('triggerType')
|
||||
if (triggerType === '1') {
|
||||
let triggerInterval = this.form.getFieldValue('triggerInterval')
|
||||
if (triggerInterval === null || triggerInterval === '') {
|
||||
triggerInterval = '* * * * * ?'
|
||||
}
|
||||
this.$refs.cronModalRef.isShow(triggerInterval)
|
||||
}
|
||||
},
|
||||
remove (k) {
|
||||
const { dynamicForm } = this
|
||||
// can use data-binding to get
|
||||
@ -414,6 +432,11 @@ export default {
|
||||
})
|
||||
}
|
||||
},
|
||||
getCron (cron) {
|
||||
this.form.setFieldsValue({
|
||||
triggerInterval: cron
|
||||
})
|
||||
},
|
||||
handleOk (e) {
|
||||
const { form } = this
|
||||
e.preventDefault()
|
||||
@ -461,7 +484,7 @@ export default {
|
||||
setTimeout(resolve, 100)
|
||||
}).then(() => {
|
||||
const formData = pick(data, [
|
||||
'id', 'jobName', 'groupName', 'jobStatus', 'executorName', 'argsStr', 'executorTimeout', 'description',
|
||||
'id', 'jobName', 'groupName', 'jobStatus', 'executorInfo', 'argsStr', 'executorTimeout', 'description',
|
||||
'maxRetryTimes', 'parallelNum', 'retryInterval', 'triggerType', 'blockStrategy', 'executorType', 'taskType', 'triggerInterval'])
|
||||
formData.jobStatus = formData.jobStatus.toString()
|
||||
formData.taskType = formData.taskType.toString()
|
||||
|
Loading…
Reference in New Issue
Block a user