perf: 批量运行优化,支持普通运行和强制重新运行

This commit is contained in:
xiaojunnuo
2025-12-29 15:31:33 +08:00
parent 07f0aa45ef
commit 039c62b09b
7 changed files with 106 additions and 29 deletions

View File

@@ -159,6 +159,7 @@ export default {
selectedCount: "Selected {count} items",
batchDelete: "Batch Delete",
batchForceRerun: "Force Rerun",
batchRerun: "Rerun",
applyCertificate: "Apply for Certificate",
pipelineExecutionRecords: "Pipeline Execution Records",
confirm: "Confirm",

View File

@@ -165,6 +165,7 @@ export default {
selectedCount: "已选择 {count} 项",
batchDelete: "批量删除",
batchForceRerun: "强制重新运行",
batchRerun: "重新运行",
applyCertificate: "申请证书",
pipelineExecutionRecords: "流水线执行记录",
confirm: "确认",

View File

@@ -117,11 +117,11 @@ export async function BatchDelete(pipelineIds: number[]): Promise<void> {
data: { ids: pipelineIds },
});
}
export async function BatchRerun(pipelineIds: number[]): Promise<void> {
export async function BatchRerun(pipelineIds: number[], force: boolean): Promise<void> {
return await request({
url: apiPrefix + "/batchRerun",
method: "post",
data: { ids: pipelineIds },
data: { ids: pipelineIds, force },
});
}

View File

@@ -0,0 +1,86 @@
<template>
<fs-button icon="icon-park-outline:replay-music" class="need-plus" type="link" :text="t('certd.batchRerun')" @click="openFormDialog"></fs-button>
</template>
<script setup lang="ts">
import { compute, dict, useFormWrapper } from "@fast-crud/fast-crud";
import * as api from "../api";
import { useSettingStore } from "/@/store/settings";
import { useI18n } from "/src/locales";
import { computed } from "vue";
const { t } = useI18n();
const props = defineProps<{
selectedRowKeys: any[];
}>();
const emit = defineEmits<{
change: any;
}>();
async function batchUpdateRequest(form: any) {
await api.BatchRerun(props.selectedRowKeys, form.force ?? false);
emit("change");
}
const { openCrudFormDialog } = useFormWrapper();
const settingStore = useSettingStore();
async function openFormDialog() {
settingStore.checkPlus();
const crudOptions: any = {
columns: {
force: {
title: "运行模式",
form: {
value: false,
required: true,
helper: "强制重新运行:清除流水线所有状态,全部重新执行\n普通运行成功过的任务会跳过",
component: {
name: "fs-dict-radio",
vModel: "value",
style: {
marginTop: "5px",
},
dict: dict({
data: [
{
label: "普通运行",
value: false,
},
{
label: "强制重新运行",
value: true,
},
],
}),
},
},
},
},
form: {
mode: "edit",
initialForm: {
clear: false,
},
//@ts-ignore
async doSubmit({ form }) {
await batchUpdateRequest(form);
},
col: {
span: 22,
},
labelCol: {
style: {
width: "100px",
},
},
wrapper: {
title: t("certd.batchRerun"),
width: 600,
},
},
} as any;
await openCrudFormDialog({ crudOptions });
}
</script>

View File

@@ -8,7 +8,7 @@
<div class="batch-actions-inner">
<span>{{ t("certd.selectedCount", { count: selectedRowKeys.length }) }}</span>
<fs-button icon="ion:trash-outline" class="color-red" type="link" :text="t('certd.batchDelete')" @click="batchDelete"></fs-button>
<fs-button icon="icon-park-outline:replay-music" class="need-plus" type="link" :text="t('certd.batchForceRerun')" @click="batchRerun"></fs-button>
<batch-rerun :selected-row-keys="selectedRowKeys" @change="batchFinished"></batch-rerun>
<change-group :selected-row-keys="selectedRowKeys" @change="batchFinished"></change-group>
<change-notification :selected-row-keys="selectedRowKeys" @change="batchFinished"></change-notification>
<change-trigger :selected-row-keys="selectedRowKeys" @change="batchFinished"></change-trigger>
@@ -28,6 +28,7 @@ import { dict, useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud";
import ChangeGroup from "./components/change-group.vue";
import ChangeTrigger from "./components/change-trigger.vue";
import BatchRerun from "./components/batch-rerun.vue";
import { Modal, notification } from "ant-design-vue";
import * as api from "./api";
import { useI18n } from "/src/locales";
@@ -75,20 +76,6 @@ function batchDelete() {
},
});
}
function batchRerun() {
settingStore.checkPlus();
Modal.confirm({
title: "确认强制重新运行吗",
content: "确定要强制重新运行选中流水线吗?(20条一批执行)",
async onOk() {
await api.BatchRerun(selectedRowKeys.value);
notification.success({ message: "任务已提交" });
await crudExpose.doRefresh();
selectedRowKeys.value = [];
},
});
}
</script>
<style lang="less">
.batch-actions {

View File

@@ -192,8 +192,8 @@ export class PipelineController extends CrudController<PipelineService> {
}
@Post('/batchRerun', { summary: Constants.per.authOnly })
async batchRerun(@Body('ids') ids: number[]) {
await this.service.batchRerun(ids, this.getUserId());
async batchRerun(@Body('ids') ids: number[], @Body('force') force: boolean) {
await this.service.batchRerun(ids, this.getUserId(), force);
return this.ok({});
}
}

View File

@@ -898,7 +898,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
}
}
async batchRerun(ids: number[], userId: any) {
async batchRerun(ids: number[], userId: any, force: boolean) {
if (!isPlus()) {
throw new NeedVIPException("此功能需要升级专业版");
}
@@ -919,18 +919,20 @@ export class PipelineService extends BaseService<PipelineEntity> {
ids = list.map(item => item.id);
//异步执行
this.startBatchRerun(ids);
this.startBatchRerun(userId,ids, force);
}
async startBatchRerun(ids: number[]) {
//20条一批
const batchSize = 20;
for (let i = 0; i < ids.length; i += batchSize) {
const batchIds = ids.slice(i, i + batchSize);
const batchPromises = batchIds.map(async (id) => {
await this.run(id, null, "ALL");
startBatchRerun(userId:number, ids: number[], force: boolean) {
for (const id of ids) {
executorQueue.addTask(userId,{
task: async () => {
if (force) {
await this.run(id, null, "ALL");
} else {
await this.run(id, null);
}
}
});
await Promise.all(batchPromises);
}
}