diff --git a/easy-retry-client/easy-retry-client-job-core/src/main/java/com/aizuda/easy/retry/client/job/core/client/JobEndPoint.java b/easy-retry-client/easy-retry-client-job-core/src/main/java/com/aizuda/easy/retry/client/job/core/client/JobEndPoint.java index 2862a9a96..d2ecc94fd 100644 --- a/easy-retry-client/easy-retry-client-job-core/src/main/java/com/aizuda/easy/retry/client/job/core/client/JobEndPoint.java +++ b/easy-retry-client/easy-retry-client-job-core/src/main/java/com/aizuda/easy/retry/client/job/core/client/JobEndPoint.java @@ -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); } diff --git a/easy-retry-client/easy-retry-client-job-core/src/main/java/com/aizuda/easy/retry/client/job/core/dto/JobArgs.java b/easy-retry-client/easy-retry-client-job-core/src/main/java/com/aizuda/easy/retry/client/job/core/dto/JobArgs.java new file mode 100644 index 000000000..221616d95 --- /dev/null +++ b/easy-retry-client/easy-retry-client-job-core/src/main/java/com/aizuda/easy/retry/client/job/core/dto/JobArgs.java @@ -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; +} diff --git a/easy-retry-client/easy-retry-client-job-core/src/main/java/com/aizuda/easy/retry/client/job/core/dto/ShardingJobArgs.java b/easy-retry-client/easy-retry-client-job-core/src/main/java/com/aizuda/easy/retry/client/job/core/dto/ShardingJobArgs.java new file mode 100644 index 000000000..13978a118 --- /dev/null +++ b/easy-retry-client/easy-retry-client-job-core/src/main/java/com/aizuda/easy/retry/client/job/core/dto/ShardingJobArgs.java @@ -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; + + +} diff --git a/easy-retry-client/easy-retry-client-job-core/src/main/java/com/aizuda/easy/retry/client/job/core/executor/AbstractJobExecutor.java b/easy-retry-client/easy-retry-client-job-core/src/main/java/com/aizuda/easy/retry/client/job/core/executor/AbstractJobExecutor.java index ca1ba266b..8661e51bb 100644 --- a/easy-retry-client/easy-retry-client-job-core/src/main/java/com/aizuda/easy/retry/client/job/core/executor/AbstractJobExecutor.java +++ b/easy-retry-client/easy-retry-client-job-core/src/main/java/com/aizuda/easy/retry/client/job/core/executor/AbstractJobExecutor.java @@ -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); } diff --git a/easy-retry-client/easy-retry-client-job-core/src/main/java/com/aizuda/easy/retry/client/job/core/executor/AnnotationJobExecutor.java b/easy-retry-client/easy-retry-client-job-core/src/main/java/com/aizuda/easy/retry/client/job/core/executor/AnnotationJobExecutor.java index ed5903d9d..a4c804cdf 100644 --- a/easy-retry-client/easy-retry-client-job-core/src/main/java/com/aizuda/easy/retry/client/job/core/executor/AnnotationJobExecutor.java +++ b/easy-retry-client/easy-retry-client-job-core/src/main/java/com/aizuda/easy/retry/client/job/core/executor/AnnotationJobExecutor.java @@ -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); } } diff --git a/easy-retry-client/easy-retry-client-job-core/src/main/java/com/aizuda/easy/retry/client/job/core/executor/NormalJobExecutor.java b/easy-retry-client/easy-retry-client-job-core/src/main/java/com/aizuda/easy/retry/client/job/core/executor/NormalJobExecutor.java deleted file mode 100644 index a99c69f4e..000000000 --- a/easy-retry-client/easy-retry-client-job-core/src/main/java/com/aizuda/easy/retry/client/job/core/executor/NormalJobExecutor.java +++ /dev/null @@ -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 { - -} diff --git a/easy-retry-server/easy-retry-server-web/src/main/java/com/aizuda/easy/retry/server/web/controller/JobController.java b/easy-retry-server/easy-retry-server-web/src/main/java/com/aizuda/easy/retry/server/web/controller/JobController.java index 84255e7ba..f98fa856b 100644 --- a/easy-retry-server/easy-retry-server-web/src/main/java/com/aizuda/easy/retry/server/web/controller/JobController.java +++ b/easy-retry-server/easy-retry-server-web/src/main/java/com/aizuda/easy/retry/server/web/controller/JobController.java @@ -61,4 +61,10 @@ public class JobController { return jobService.deleteJobById(id); } + @GetMapping("/cron") + @LoginRequired + public List getTimeByCron(@RequestParam("cron") String cron) { + return jobService.getTimeByCron(cron); + } + } diff --git a/easy-retry-server/easy-retry-server-web/src/main/java/com/aizuda/easy/retry/server/web/model/response/JobResponseVO.java b/easy-retry-server/easy-retry-server-web/src/main/java/com/aizuda/easy/retry/server/web/model/response/JobResponseVO.java index 2d77ec82b..a7cd20e43 100644 --- a/easy-retry-server/easy-retry-server-web/src/main/java/com/aizuda/easy/retry/server/web/model/response/JobResponseVO.java +++ b/easy-retry-server/easy-retry-server-web/src/main/java/com/aizuda/easy/retry/server/web/model/response/JobResponseVO.java @@ -62,7 +62,7 @@ public class JobResponseVO { /** * 执行器名称 */ - private String executorName; + private String executorInfo; /** * 触发类型 1.CRON 表达式 2. 固定时间 diff --git a/easy-retry-server/easy-retry-server-web/src/main/java/com/aizuda/easy/retry/server/web/service/JobService.java b/easy-retry-server/easy-retry-server-web/src/main/java/com/aizuda/easy/retry/server/web/service/JobService.java index c528bd23e..b8ce1ea9a 100644 --- a/easy-retry-server/easy-retry-server-web/src/main/java/com/aizuda/easy/retry/server/web/service/JobService.java +++ b/easy-retry-server/easy-retry-server-web/src/main/java/com/aizuda/easy/retry/server/web/service/JobService.java @@ -25,4 +25,6 @@ public interface JobService { Boolean updateJobStatus(JobUpdateJobStatusRequestVO jobRequestVO); Boolean deleteJobById(Long id); + + List getTimeByCron(String cron); } diff --git a/easy-retry-server/easy-retry-server-web/src/main/java/com/aizuda/easy/retry/server/web/service/impl/JobServiceImpl.java b/easy-retry-server/easy-retry-server-web/src/main/java/com/aizuda/easy/retry/server/web/service/impl/JobServiceImpl.java index b4dbc9d16..ffa8da250 100644 --- a/easy-retry-server/easy-retry-server-web/src/main/java/com/aizuda/easy/retry/server/web/service/impl/JobServiceImpl.java +++ b/easy-retry-server/easy-retry-server-web/src/main/java/com/aizuda/easy/retry/server/web/service/impl/JobServiceImpl.java @@ -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 getTimeByCron(String cron) { + + List 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); diff --git a/frontend/package.json b/frontend/package.json index 778630d5b..7c353e0d2 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -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", diff --git a/frontend/src/api/jobApi.js b/frontend/src/api/jobApi.js index 82c5a17c2..a9b96d8c4 100644 --- a/frontend/src/api/jobApi.js +++ b/frontend/src/api/jobApi.js @@ -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, diff --git a/frontend/src/main.js b/frontend/src/main.js index 0356b6789..b3c034129 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -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 diff --git a/frontend/src/views/job/JobInfo.vue b/frontend/src/views/job/JobInfo.vue index b40e8c6e5..57bf242a8 100644 --- a/frontend/src/views/job/JobInfo.vue +++ b/frontend/src/views/job/JobInfo.vue @@ -58,7 +58,7 @@ - {{ jobInfo.executorName }} + {{ jobInfo.executorInfo }} diff --git a/frontend/src/views/job/JobList.vue b/frontend/src/views/job/JobList.vue index 9c6265ac3..c0d8c03f0 100644 --- a/frontend/src/views/job/JobList.vue +++ b/frontend/src/views/job/JobList.vue @@ -107,7 +107,7 @@ - {{ text }}(秒) + {{ text }} {{ text }}(秒) @@ -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: '阻塞策略', diff --git a/frontend/src/views/job/from/CronModal.vue b/frontend/src/views/job/from/CronModal.vue new file mode 100644 index 000000000..54220a80f --- /dev/null +++ b/frontend/src/views/job/from/CronModal.vue @@ -0,0 +1,54 @@ + + + + diff --git a/frontend/src/views/job/from/JobFrom.vue b/frontend/src/views/job/from/JobFrom.vue index ba97b953c..005f8aaba 100644 --- a/frontend/src/views/job/from/JobFrom.vue +++ b/frontend/src/views/job/from/JobFrom.vue @@ -101,6 +101,7 @@ + + @@ -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()