perf: 优化流水线列表页面、详情页面性能,精简返回数据

v2-dev-order
xiaojunnuo 2025-07-14 01:36:40 +08:00
parent 79f2367472
commit 609ac9c9a2
9 changed files with 93 additions and 50 deletions

View File

@ -164,8 +164,11 @@ export abstract class BaseService<T> {
} }
private buildListQuery(listReq: ListReq<T>) { private buildListQuery(listReq: ListReq<T>) {
const { query, sort, buildQuery } = listReq; const { query, sort, buildQuery,select } = listReq;
const qb = this.getRepository().createQueryBuilder('main'); const qb = this.getRepository().createQueryBuilder('main');
if (select) {
qb.setFindOptions({select});
}
if (query) { if (query) {
const keys = Object.keys(query); const keys = Object.keys(query);
for (const key of keys) { for (const key of keys) {
@ -191,6 +194,7 @@ export abstract class BaseService<T> {
if (buildQuery) { if (buildQuery) {
buildQuery(qb); buildQuery(qb);
} }
return qb; return qb;
} }

View File

@ -335,7 +335,7 @@ export default function ({ crudExpose, context: { groupDictRef, selectedRowKeys
} }
}, },
}, },
_triggerCount: { triggerCount: {
title: t("certd.fields.scheduledTaskCount"), title: t("certd.fields.scheduledTaskCount"),
type: "number", type: "number",
column: { column: {
@ -346,7 +346,7 @@ export default function ({ crudExpose, context: { groupDictRef, selectedRowKeys
show: false, show: false,
}, },
}, },
_stepCount: { stepCount: {
title: t("certd.fields.deployTaskCount"), title: t("certd.fields.deployTaskCount"),
type: "number", type: "number",
form: { show: false }, form: { show: false },

View File

@ -4,7 +4,7 @@
<fs-icon v-bind="status" :color="status.iconColor || status.color" /> <fs-icon v-bind="status" :color="status.iconColor || status.color" />
</template> </template>
<p> <p>
<fs-date-format :model-value="runnable.status?.startTime"></fs-date-format> <fs-date-format :model-value="runnable.createTime"></fs-date-format>
<a-tag class="ml-5" :color="status.color" :closable="status.value === 'start'" @close="cancelTask"> <a-tag class="ml-5" :color="status.color" :closable="status.value === 'start'" @close="cancelTask">
{{ status.label }} {{ status.label }}
</a-tag> </a-tag>
@ -46,7 +46,7 @@ export default defineComponent({
emits: ["view", "cancel"], emits: ["view", "cancel"],
setup(props: any, ctx: any) { setup(props: any, ctx: any) {
const status = computed(() => { const status = computed(() => {
return statusUtil.get(props.runnable?.status?.result); return statusUtil.get(props.runnable?.status);
}); });
function view() { function view() {

View File

@ -258,7 +258,7 @@
<a-timeline class="mt-10"> <a-timeline class="mt-10">
<template v-for="item of histories" :key="item.id"> <template v-for="item of histories" :key="item.id">
<pi-history-timeline-item <pi-history-timeline-item
:runnable="item.pipeline" :runnable="item"
:history-id="item.id" :history-id="item.id"
:is-current="currentHistory?.id === item.id" :is-current="currentHistory?.id === item.id"
:edit-mode="editMode" :edit-mode="editMode"
@ -371,7 +371,7 @@ export default defineComponent({
const loadCurrentHistoryDetail = async () => { const loadCurrentHistoryDetail = async () => {
const detail: RunHistory = await props.options?.getHistoryDetail({ historyId: currentHistory.value.id }); const detail: RunHistory = await props.options?.getHistoryDetail({ historyId: currentHistory.value.id });
currentHistory.value.logs = detail.logs; currentHistory.value.logs = detail.logs;
merge(currentHistory.value.pipeline, detail.pipeline); currentHistory.value.pipeline = detail.pipeline;
}; };
const changeCurrentHistory = async (history?: RunHistory) => { const changeCurrentHistory = async (history?: RunHistory) => {
if (!history) { if (!history) {
@ -382,7 +382,8 @@ export default defineComponent({
} }
currentHistory.value = history; currentHistory.value = history;
await loadCurrentHistoryDetail(); await loadCurrentHistoryDetail();
pipeline.value = history.pipeline; pipeline.value = currentHistory.value.pipeline;
currentPipeline.value = cloneDeep(pipeline.value);
}; };
async function loadHistoryList(reload = false) { async function loadHistoryList(reload = false) {
@ -413,7 +414,8 @@ export default defineComponent({
return true; return true;
} }
} }
if (historyList[0].pipeline?.version === pipeline.value.version) { //@ts-ignore
if (historyList[0]?.version === pipeline.value.version) {
await changeCurrentHistory(historyList[0]); await changeCurrentHistory(historyList[0]);
} }
} }
@ -486,7 +488,6 @@ export default defineComponent({
}, },
detail.pipeline detail.pipeline
); );
debugger;
pipeline.value = currentPipeline.value; pipeline.value = currentPipeline.value;
await loadHistoryList(true); await loadHistoryList(true);
}, },
@ -707,7 +708,6 @@ export default defineComponent({
title: "确认", title: "确认",
content: `确定要手动触发运行吗?`, content: `确定要手动触发运行吗?`,
async onOk() { async onOk() {
debugger;
//@ts-ignore //@ts-ignore
await changeCurrentHistory(null); await changeCurrentHistory(null);
await props.options.doTrigger({ pipelineId: pipeline.value.id, stepId: stepId }); await props.options.doTrigger({ pipelineId: pipeline.value.id, stepId: stepId });
@ -787,7 +787,6 @@ export default defineComponent({
pipeline.value.version = 0; pipeline.value.version = 0;
} }
pipeline.value.version++; pipeline.value.version++;
debugger;
currentPipeline.value = pipeline.value; currentPipeline.value = pipeline.value;
// //

View File

@ -214,6 +214,7 @@ function transformStatusCount() {
{ name: "error", label: "失败" }, { name: "error", label: "失败" },
{ name: "canceled", label: "已取消" }, { name: "canceled", label: "已取消" },
{ name: null, label: "未执行" }, { name: null, label: "未执行" },
{ name: "skip", label: "跳过" },
]; ];
const result = []; const result = [];
for (const item of sorted) { for (const item of sorted) {

View File

@ -43,7 +43,7 @@ const slots = defineSlots();
.data-item { .data-item {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 180px; height: 188px;
.header { .header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;

View File

@ -1,16 +1,15 @@
import { ALL, Body, Controller, Get, Inject, Post, Provide, Query } from '@midwayjs/core'; import { ALL, Body, Controller, Get, Inject, Post, Provide, Query } from "@midwayjs/core";
import { CommonException, Constants, CrudController, PermissionException } from '@certd/lib-server'; import { CommonException, Constants, CrudController, PermissionException, SysSettingsService } from "@certd/lib-server";
import { PipelineEntity } from '../../../modules/pipeline/entity/pipeline.js'; import { PipelineEntity } from "../../../modules/pipeline/entity/pipeline.js";
import { HistoryService } from '../../../modules/pipeline/service/history-service.js'; import { HistoryService } from "../../../modules/pipeline/service/history-service.js";
import { HistoryLogService } from '../../../modules/pipeline/service/history-log-service.js'; import { HistoryLogService } from "../../../modules/pipeline/service/history-log-service.js";
import { HistoryEntity } from '../../../modules/pipeline/entity/history.js'; import { HistoryEntity } from "../../../modules/pipeline/entity/history.js";
import { HistoryLogEntity } from '../../../modules/pipeline/entity/history-log.js'; import { HistoryLogEntity } from "../../../modules/pipeline/entity/history-log.js";
import { PipelineService } from '../../../modules/pipeline/service/pipeline-service.js'; import { PipelineService } from "../../../modules/pipeline/service/pipeline-service.js";
import * as fs from 'fs'; import * as fs from "fs";
import { logger } from '@certd/basic'; import { logger } from "@certd/basic";
import { AuthService } from '../../../modules/sys/authority/service/auth-service.js'; import { AuthService } from "../../../modules/sys/authority/service/auth-service.js";
import { SysSettingsService } from '@certd/lib-server'; import { In } from "typeorm";
import { In } from 'typeorm';
/** /**
* *
@ -88,11 +87,30 @@ export class HistoryController extends CrudController<HistoryService> {
const buildQuery = qb => { const buildQuery = qb => {
qb.limit(20); qb.limit(20);
}; };
const withDetail = body.withDetail;
delete body.withDetail;
let select:any = null
if (!withDetail) {
select = {
pipeline: true, // 后面这里改成false
id: true,
userId: true,
pipelineId: true,
status: true,
// startTime: true,
triggerType: true,
endTime: true,
createTime: true,
updateTime: true
};
}
const listRet = await this.getService().list({ const listRet = await this.getService().list({
query: body, query: body,
sort: { prop: 'id', asc: false }, sort: { prop: 'id', asc: false },
buildQuery, buildQuery,
select
}); });
for (const item of listRet) { for (const item of listRet) {
if (!item.pipeline) { if (!item.pipeline) {
continue; continue;
@ -100,7 +118,13 @@ export class HistoryController extends CrudController<HistoryService> {
const json = JSON.parse(item.pipeline); const json = JSON.parse(item.pipeline);
delete json.stages; delete json.stages;
item.pipeline = json; item.pipeline = json;
//@ts-ignore
item.version = json.version;
item.status = json.status.result
delete item.pipeline;
} }
return this.ok(listRet); return this.ok(listRet);
} }

View File

@ -1,22 +1,21 @@
import {IServiceGetter} from "@certd/pipeline"; import { IServiceGetter } from "@certd/pipeline";
import {Inject, Provide, Scope, ScopeEnum} from "@midwayjs/core"; import { ApplicationContext, IMidwayContainer, Provide, Scope, ScopeEnum } from "@midwayjs/core";
import {AccessGetter, AccessService} from "@certd/lib-server"; import { AccessGetter, AccessService } from "@certd/lib-server";
import {CnameProxyService} from "./cname-proxy-service.js"; import { CnameProxyService } from "./cname-proxy-service.js";
import {NotificationGetter} from "./notification-getter.js"; import { NotificationGetter } from "./notification-getter.js";
import {NotificationService} from "../notification-service.js"; import { NotificationService } from "../notification-service.js";
import {CnameRecordService} from "../../../cname/service/cname-record-service.js"; import { CnameRecordService } from "../../../cname/service/cname-record-service.js";
import {SubDomainsGetter} from './sub-domain-getter.js' import { SubDomainsGetter } from "./sub-domain-getter.js";
import {DomainVerifierGetter} from "./domain-verifier-getter.js"; import { DomainVerifierGetter } from "./domain-verifier-getter.js";
import {Context} from "@midwayjs/koa"; import { DomainService } from "../../../cert/service/domain-service.js";
import {DomainService} from "../../../cert/service/domain-service.js"; import { SubDomainService } from "../sub-domain-service.js";
import {SubDomainService} from "../sub-domain-service.js";
export class TaskServiceGetter implements IServiceGetter{ export class TaskServiceGetter implements IServiceGetter{
private userId: number; private userId: number;
private ctx : Context; private appCtx : IMidwayContainer;
constructor(userId:number,ctx:Context) { constructor(userId:number,appCtx:IMidwayContainer) {
this.userId = userId; this.userId = userId;
this.ctx = ctx this.appCtx = appCtx
} }
async get<T>(serviceName: string): Promise<T> { async get<T>(serviceName: string): Promise<T> {
@ -36,27 +35,27 @@ export class TaskServiceGetter implements IServiceGetter{
} }
async getSubDomainsGetter(): Promise<SubDomainsGetter> { async getSubDomainsGetter(): Promise<SubDomainsGetter> {
const subDomainsService:SubDomainService = await this.ctx.requestContext.getAsync("subDomainService") const subDomainsService:SubDomainService = await this.appCtx.getAsync("subDomainService")
return new SubDomainsGetter(this.userId, subDomainsService) return new SubDomainsGetter(this.userId, subDomainsService)
} }
async getAccessService(): Promise<AccessGetter> { async getAccessService(): Promise<AccessGetter> {
const accessService:AccessService = await this.ctx.requestContext.getAsync("accessService") const accessService:AccessService = await this.appCtx.getAsync("accessService")
return new AccessGetter(this.userId, accessService.getById.bind(accessService)); return new AccessGetter(this.userId, accessService.getById.bind(accessService));
} }
async getCnameProxyService(): Promise<CnameProxyService> { async getCnameProxyService(): Promise<CnameProxyService> {
const cnameRecordService:CnameRecordService = await this.ctx.requestContext.getAsync("cnameRecordService") const cnameRecordService:CnameRecordService = await this.appCtx.getAsync("cnameRecordService")
return new CnameProxyService(this.userId, cnameRecordService.getWithAccessByDomain.bind(cnameRecordService)); return new CnameProxyService(this.userId, cnameRecordService.getWithAccessByDomain.bind(cnameRecordService));
} }
async getNotificationService(): Promise<NotificationGetter> { async getNotificationService(): Promise<NotificationGetter> {
const notificationService:NotificationService = await this.ctx.requestContext.getAsync("notificationService") const notificationService:NotificationService = await this.appCtx.getAsync("notificationService")
return new NotificationGetter(this.userId, notificationService); return new NotificationGetter(this.userId, notificationService);
} }
async getDomainVerifierGetter(): Promise<DomainVerifierGetter> { async getDomainVerifierGetter(): Promise<DomainVerifierGetter> {
const domainService:DomainService = await this.ctx.requestContext.getAsync("domainService") const domainService:DomainService = await this.appCtx.getAsync("domainService")
return new DomainVerifierGetter(this.userId, domainService); return new DomainVerifierGetter(this.userId, domainService);
} }
} }
@ -67,12 +66,12 @@ export type TaskServiceCreateReq = {
@Provide() @Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true }) @Scope(ScopeEnum.Request, { allowDowngrade: true })
export class TaskServiceBuilder { export class TaskServiceBuilder {
@Inject() @ApplicationContext()
ctx: Context; appCtx: IMidwayContainer;
create(req:TaskServiceCreateReq){ create(req:TaskServiceCreateReq){
const userId = req.userId; const userId = req.userId;
return new TaskServiceGetter(userId,this.ctx) return new TaskServiceGetter(userId,this.appCtx)
} }
} }

View File

@ -123,6 +123,22 @@ export class PipelineService extends BaseService<PipelineEntity> {
const result = await super.page(pageReq); const result = await super.page(pageReq);
await this.fillLastVars(result.records); await this.fillLastVars(result.records);
for (const item of result.records) {
if (!item.content){
continue
}
const pipeline = JSON.parse(item.content);
let stepCount = 0
RunnableCollection.each(pipeline.stages, (runnable: any) => {
stepCount++
})
// @ts-ignore
item.stepCount = stepCount
// @ts-ignore
item.triggerCount = pipeline.triggers.length
delete item.content
}
return result; return result;
} }
@ -637,7 +653,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
//修改pipeline状态 //修改pipeline状态
const pipelineEntity = new PipelineEntity(); const pipelineEntity = new PipelineEntity();
pipelineEntity.id = parseInt(history.pipeline.id); pipelineEntity.id = parseInt(history.pipeline.id);
pipelineEntity.status = history.pipeline.status.status + ''; pipelineEntity.status = history.pipeline.status.result + '';
pipelineEntity.lastHistoryTime = history.pipeline.status.startTime; pipelineEntity.lastHistoryTime = history.pipeline.status.startTime;
await this.update(pipelineEntity); await this.update(pipelineEntity);