feat:2.4.0

1. 任务调度日使用嵌套表格的形式,减少一次页面跳转
2. 修复重复任务问题
3. 修复新增JOb没有进行分桶
This commit is contained in:
byteblogs168 2023-10-29 13:11:21 +08:00
parent f8e04959b7
commit b4836413f2
60 changed files with 169 additions and 201 deletions

View File

@ -53,6 +53,11 @@ public class JobTaskBatchGenerator {
Assert.isTrue(1 == jobTaskBatchMapper.insert(jobTaskBatch), () -> new EasyRetryServerException("新增调度任务失败.jobId:[{}]", context.getJobId()));
// 非待处理状态无需进入时间轮中
if (JobTaskBatchStatusEnum.WAITING.getStatus() != jobTaskBatch.getTaskBatchStatus()) {
return;
}
// 进入时间轮
long delay = context.getNextTriggerAt().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()
- System.currentTimeMillis();

View File

@ -2,6 +2,7 @@ package com.aizuda.easy.retry.server.job.task.support.prepare;
import com.aizuda.easy.retry.common.core.enums.JobOperationReasonEnum;
import com.aizuda.easy.retry.common.core.enums.JobTaskBatchStatusEnum;
import com.aizuda.easy.retry.common.core.util.JsonUtil;
import com.aizuda.easy.retry.server.job.task.support.BlockStrategy;
import com.aizuda.easy.retry.server.job.task.support.JobTaskConverter;
import com.aizuda.easy.retry.server.job.task.dto.JobTaskPrepareDTO;
@ -38,7 +39,7 @@ public class RunningJobPrepareHandler extends AbstractJobPrePareHandler {
@Override
protected void doHandler(JobTaskPrepareDTO prepare) {
log.info("存在运行中的任务. taskBatchId:[{}]", prepare.getTaskBatchId());
log.info("存在运行中的任务. prepare:[{}]", JsonUtil.toJsonString(prepare));
// 若存在所有的任务都是完成但是批次上的状态为运行中则是并发导致的未把批次状态变成为终态此处做一次兜底处理
int blockStrategy = prepare.getBlockStrategy();

View File

@ -25,6 +25,8 @@ public abstract class AbstractJobTaskStopHandler implements JobTaskStopHandler,
@Autowired
private JobTaskMapper jobTaskMapper;
protected abstract void doStop(TaskStopJobContext context);
@Override
public void stop(TaskStopJobContext context) {
@ -59,8 +61,6 @@ public abstract class AbstractJobTaskStopHandler implements JobTaskStopHandler,
}
}
protected abstract void doStop(TaskStopJobContext context);
@Override
public void afterPropertiesSet() throws Exception {
JobTaskStopFactory.registerTaskStop(getTaskType(), this);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0aa660"],{"119c":function(t,a,e){"use strict";e.r(a);e("b0c0");var o=function(){var t=this,a=t._self._c;return a("div",[a("page-header-wrapper",{staticStyle:{margin:"-24px -1px 0"},on:{back:function(){return t.$router.go(-1)}}},[a("div")]),null!==t.jobBatchInfo?a("a-card",{attrs:{bordered:!1}},[a("a-descriptions",{attrs:{title:"",column:3,bordered:""}},[a("a-descriptions-item",{attrs:{label:"组名称"}},[t._v(" "+t._s(t.jobBatchInfo.groupName)+" ")]),a("a-descriptions-item",{attrs:{label:"任务名称"}},[t._v(" "+t._s(t.jobBatchInfo.jobName)+" ")]),a("a-descriptions-item",{attrs:{label:"状态"}},[a("a-tag",{attrs:{color:t.taskBatchStatus[t.jobBatchInfo.taskBatchStatus].color}},[t._v(" "+t._s(t.taskBatchStatus[t.jobBatchInfo.taskBatchStatus].name)+" ")])],1),a("a-descriptions-item",{attrs:{label:"执行器类型"}},[a("a-tag",{attrs:{color:t.executorType[t.jobBatchInfo.executorType].color}},[t._v(" "+t._s(t.executorType[t.jobBatchInfo.executorType].name)+" ")])],1),a("a-descriptions-item",{attrs:{label:"操作原因"}},[a("a-tag",{attrs:{color:t.operationReason[t.jobBatchInfo.operationReason].color}},[t._v(" "+t._s(t.operationReason[t.jobBatchInfo.operationReason].name)+" ")])],1),a("a-descriptions-item",{attrs:{label:"开始执行时间"}},[t._v(" "+t._s(t.jobBatchInfo.executionAt)+" ")]),a("a-descriptions-item",{attrs:{label:"执行器名称",span:"4"}},[t._v(" "+t._s(t.jobBatchInfo.executorInfo)+" ")]),a("a-descriptions-item",{attrs:{label:"创建时间"}},[t._v(" "+t._s(t.jobBatchInfo.createDt)+" ")])],1)],1):t._e(),a("div",{staticStyle:{margin:"20px 0","border-left":"#f5222d 5px solid","font-size":"medium","font-weight":"bold"}},[t._v("    任务项列表 ")]),a("JobTaskList",{ref:"JobTaskListRef"})],1)},r=[],s=e("3b7a"),n=e("c1df"),c=e.n(n),i=e("38b7"),u=e.n(i),p=e("36e8"),b={name:"JobInfo",components:{JobTaskList:p["default"]},data:function(){return{jobBatchInfo:null,taskBatchStatus:u.a.taskBatchStatus,operationReason:u.a.operationReason,taskType:u.a.taskType,triggerType:u.a.triggerType,blockStrategy:u.a.blockStrategy,executorType:u.a.executorType}},created:function(){var t=this,a=this.$route.query.id,e=this.$route.query.groupName;a&&e?Object(s["d"])(a).then((function(e){t.jobBatchInfo=e.data,t.queryParam={groupName:t.jobBatchInfo.groupName,taskBatchId:a},t.$refs.JobTaskListRef.refreshTable(t.queryParam)})):this.$router.push({path:"/404"})},methods:{jobTaskList:s["h"],parseDate:function(t){return c()(t).format("YYYY-MM-DD HH:mm:ss")}}},l=b,f=e("2877"),h=Object(f["a"])(l,o,r,!1,null,"1a941578",null);a["default"]=h.exports}}]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -14,4 +14,5 @@ import lombok.EqualsAndHashCode;
public class JobLogQueryVO extends BaseQueryVO {
private Long jobId;
private Long taskBatchId;
private Long taskId;
}

View File

@ -14,6 +14,8 @@ public class JobLogResponseVO {
private Long id;
private Long key;
/**
* 组名称
*/

View File

@ -14,6 +14,8 @@ public class JobTaskResponseVO {
private Long id;
private Long key;
/**
* 组名称
*/

View File

@ -3,6 +3,8 @@ package com.aizuda.easy.retry.server.web.service.convert;
import com.aizuda.easy.retry.server.web.model.response.JobTaskResponseVO;
import com.aizuda.easy.retry.template.datasource.persistence.po.JobTask;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
import java.util.List;
@ -17,5 +19,8 @@ public interface JobTaskResponseVOConverter {
JobTaskResponseVOConverter INSTANCE = Mappers.getMapper(JobTaskResponseVOConverter.class);
@Mappings(
@Mapping(source = "id", target = "key")
)
List<JobTaskResponseVO> toJobTaskResponseVOs(List<JobTask> jobTasks);
}

View File

@ -40,6 +40,10 @@ public class JobLogServiceImpl implements JobLogService {
queryWrapper.eq(JobLogMessage::getTaskBatchId, queryVO.getTaskBatchId());
}
if (Objects.nonNull(queryVO.getTaskId())) {
queryWrapper.eq(JobLogMessage::getTaskId, queryVO.getTaskId());
}
queryWrapper.orderByDesc(JobLogMessage::getId);
PageDTO<JobLogMessage> selectPage = jobLogMessageMapper.selectPage(pageDTO, queryWrapper);

View File

@ -45,6 +45,10 @@ public class JobTaskServiceImpl implements JobTaskService {
List<JobTaskResponseVO> jobTaskResponseVOs = JobTaskResponseVOConverter.INSTANCE.toJobTaskResponseVOs(
selectPage.getRecords());
for (JobTaskResponseVO jobTaskResponseVO : jobTaskResponseVOs) {
jobTaskResponseVO.setKey(jobTaskResponseVO.getId());
}
return new PageResult<>(pageDTO, jobTaskResponseVOs);
}
}

View File

@ -67,23 +67,18 @@
</div>
<div class="table-operator">
<a-dropdown v-action:edit v-if="selectedRowKeys.length > 0">
<a-menu slot="overlay" @click="onClick">
<a-menu-item key="1"><a-icon type="delete" />删除</a-menu-item>
<a-menu-item key="2"><a-icon type="edit" />更新</a-menu-item>
</a-menu>
<a-button style="margin-left: 8px"> 批量操作 <a-icon type="down" /> </a-button>
</a-dropdown>
</div>
<s-table
ref="table"
size="default"
:rowKey="(record) => record.id"
<a-table
:columns="columns"
:data="loadData"
:alert="options.alert"
:rowSelection="options.rowSelection"
:scroll="{ x: 2000 }"
:dataSource="data"
:pagination="pagination"
:loading="memberLoading"
:scroll="{ x: 1200 }"
rowKey="id"
@expand="getRows"
>
<span slot="serial" slot-scope="text, record">
{{ record.id }}
@ -96,17 +91,20 @@
<span slot="clientInfo" slot-scope="text">
{{ text !== '' ? text.split('@')[1] : '' }}
</span>
<!-- <p slot="expandedRowRender" style="margin: 0" slot-scope="record">-->
<!-- 执行结果: {{ record.resultMessage }}<br/>-->
<!-- 参数: {{ record.argsStr }}-->
<!-- </p>-->
<span slot="action" slot-scope="text, record">
<template>
<a @click="handleLog(record)">日志</a>
</template>
</span>
</s-table>
<a-table
slot="expandedRowRender"
slot-scope="record"
:columns="logColumns"
:data-source="loadData(record)"
:pagination="false"
rowKey="id"
>
<span slot="serial" slot-scope="text, record">
{{ record.id }}
</span>
</a-table>
</a-table>
</a-card>
</template>
@ -114,10 +112,11 @@
import ATextarea from 'ant-design-vue/es/input/TextArea'
import AInput from 'ant-design-vue/es/input/Input'
import { STable } from '@/components'
import { jobTaskList } from '@/api/jobApi'
import { getAllGroupNameList } from '@/api/manage'
import { jobLogList, jobTaskList } from '@/api/jobApi'
import enums from '@/utils/jobEnum'
import JobLogMessageList from './JobLogMessageList'
import moment from 'moment/moment'
export default {
name: 'JobTaskList',
components: {
@ -129,13 +128,13 @@ export default {
data () {
return {
currentComponet: 'List',
record: '',
mdl: {},
visible: false,
// /
advanced: false,
//
queryParam: {},
data: [],
logData: [],
taskStatus: enums.taskStatus,
//
columns: [
@ -177,27 +176,29 @@ export default {
dataIndex: 'createDt',
sorter: true,
width: '10%'
},
{
title: '操作',
fixed: 'right',
dataIndex: 'action',
width: '180px',
scopedSlots: { customRender: 'action' }
}
],
// Promise
loadData: (parameter) => {
if (this.queryParam.taskBatchId === null || this.queryParam.taskBatchId === undefined) {
return []
logColumns: [
{
title: '#',
scopedSlots: { customRender: 'serial' },
width: '5%'
},
{
title: '信息',
dataIndex: 'message',
width: '50%'
},
{
title: '执行时间',
dataIndex: 'createDt',
sorter: true,
customRender: (text) => moment(text).format('YYYY-MM-DD HH:mm:ss'),
width: '10%'
}
return jobTaskList(Object.assign(parameter, this.queryParam)).then((res) => {
return res
})
},
],
selectedRowKeys: [],
selectedRows: [],
// custom table alert & rowSelection
options: {
alert: {
@ -213,125 +214,66 @@ export default {
},
optionAlertShow: false,
groupNameList: [],
sceneList: []
sceneList: [],
memberLoading: false,
pagination: {},
logPagination: {}
}
},
created () {
getAllGroupNameList().then((res) => {
this.groupNameList = res.data
if (this.groupNameList !== null && this.groupNameList.length > 0) {
this.queryParam['groupName'] = this.groupNameList[0]
this.$refs.table.refresh(true)
this.handleChange(this.groupNameList[0])
}
})
},
methods: {
handleNew () {
this.$refs.saveRetryTask.isShow(true, null)
},
handleBatchNew () {
this.$refs.batchSaveRetryTask.isShow(true, null)
loadData (record) {
const foundItem = this.logData.filter(item => item.taskId === record.id)
console.log(record)
console.log(foundItem)
return foundItem
},
handleChange (value) {
},
toggleAdvanced () {
this.advanced = !this.advanced
},
handleLog (record) {
this.$router.push({ path: '/job/log/list', query: { taskBatchId: record.taskBatchId, jobId: record.jobId } })
getRows (expanded, record) {
console.log(record)
if (expanded) {
this.fetchLog({
taskBatchId: record.taskBatchId,
jobId: record.jobId,
taskId: record.id
}, record)
}
},
handleOk (record) {},
handleSuspend (record) {
// updateRetryTaskStatus({ id: record.id, groupName: record.groupName, retryStatus: 3 }).then((res) => {
// const { status } = res
// if (status === 0) {
// this.$message.error('')
// } else {
// this.$refs.table.refresh(true)
// this.$message.success('')
// }
// })
queryChange () {
this.fetch()
},
handleRecovery (record) {
// updateRetryTaskStatus({ id: record.id, groupName: record.groupName, retryStatus: 0 }).then((res) => {
// const { status } = res
// if (status === 0) {
// this.$message.error('')
// } else {
// this.$refs.table.refresh(true)
// this.$message.success('')
// }
// })
},
handleFinish (record) {
// updateRetryTaskStatus({ id: record.id, groupName: record.groupName, retryStatus: 1 }).then((res) => {
// const { status } = res
// if (status === 0) {
// this.$message.error('')
// } else {
// this.$refs.table.refresh(true)
// this.$message.success('')
// }
// })
},
handleTrigger (record) {
// if (record.taskType === 1) {
// manualTriggerRetryTask({ groupName: record.groupName, uniqueIds: [ record.uniqueId ] }).then(res => {
// const { status } = res
// if (status === 0) {
// this.$message.error('')
// } else {
// this.$refs.table.refresh(true)
// this.$message.success('')
// }
// })
// } else {
// manualTriggerCallbackTask({ groupName: record.groupName, uniqueIds: [ record.uniqueId ] }).then(res => {
// const { status } = res
// if (status === 0) {
// this.$message.error('')
// } else {
// this.$refs.table.refresh(true)
// this.$message.success('')
// }
// })
// }
fetch () {
this.loading = true
jobTaskList(this.queryParam).then(res => {
this.data = res.data
const pagination = { ...this.pagination }
pagination.pageSize = res.size
pagination.current = res.page
pagination.total = res.total
this.pagination = pagination
this.loading = false
})
},
refreshTable (v) {
this.queryParam = v
this.$refs.table.refresh(true)
this.queryChange()
},
async fetchLog (queryParam, record) {
const res = await jobLogList(queryParam)
console.log(this.logData)
this.logData.push(...res.data)
},
onSelectChange (selectedRowKeys, selectedRows) {
this.selectedRowKeys = selectedRowKeys
this.selectedRows = selectedRows
},
handlerDel () {
// var that = this
// this.$confirm({
// title: '?',
// content: h => <div style="color:red;">!</div>,
// onOk () {
// batchDelete({ groupName: that.selectedRows[0].groupName, ids: that.selectedRowKeys }).then(res => {
// that.$refs.table.refresh(true)
// that.$message.success(`${res.data}`)
// that.selectedRowKeys = []
// })
// },
// onCancel () {
// },
// class: 'test'
// })
},
onClick ({ key }) {
if (key === '2') {
this.$refs.batchUpdateRetryTaskInfo.isShow(true, this.selectedRows, this.selectedRowKeys)
return
}
if (key === '1') {
this.handlerDel()
}
}
}
}