perf: 流水线支持名称模糊查询

pull/148/head
xiaojunnuo 2024-08-04 02:35:45 +08:00
parent a9717b9a0d
commit 59897c4cea
14 changed files with 157 additions and 28 deletions

View File

@ -246,18 +246,26 @@ export class Executor {
await instance.onInstance(); await instance.onInstance();
await instance.execute(); await instance.execute();
//执行结果处理
if (instance._result.clearLastStatus) { if (instance._result.clearLastStatus) {
//是否需要清除所有状态
this.lastStatusMap.clear(); this.lastStatusMap.clear();
} }
//输出到output context //输出上下文变量到output context
_.forEach(define.output, (item: any, key: any) => { _.forEach(define.output, (item: any, key: any) => {
step.status!.output[key] = instance[key]; step.status!.output[key] = instance[key];
const stepOutputKey = `step.${step.id}.${key}`; const stepOutputKey = `step.${step.id}.${key}`;
this.runtime.context[stepOutputKey] = instance[key]; this.runtime.context[stepOutputKey] = instance[key];
}); });
step.status!.files = instance.getFiles(); step.status!.files = instance.getFiles();
//更新pipeline vars
if (Object.keys(instance._result.pipelineVars).length > 0) {
// 判断 pipelineVars 有值时更新
const vars = this.pipelineContext.getObj("vars");
_.merge(vars, instance._result.pipelineVars);
await this.pipelineContext.setObj("vars", vars);
}
} }
async notification(when: NotificationWhen, error?: any) { async notification(when: NotificationWhen, error?: any) {

View File

@ -51,6 +51,7 @@ export type ITaskPlugin = {
export type TaskResult = { export type TaskResult = {
clearLastStatus?: boolean; clearLastStatus?: boolean;
files?: FileItem[]; files?: FileItem[];
pipelineVars: Record<string, any>;
}; };
export type TaskInstanceContext = { export type TaskInstanceContext = {
pipeline: Pipeline; pipeline: Pipeline;
@ -66,7 +67,7 @@ export type TaskInstanceContext = {
}; };
export abstract class AbstractTaskPlugin implements ITaskPlugin { export abstract class AbstractTaskPlugin implements ITaskPlugin {
_result: TaskResult = { clearLastStatus: false, files: [] }; _result: TaskResult = { clearLastStatus: false, files: [], pipelineVars: {} };
ctx!: TaskInstanceContext; ctx!: TaskInstanceContext;
clearLastStatus() { clearLastStatus() {
this._result.clearLastStatus = true; this._result.clearLastStatus = true;
@ -83,12 +84,6 @@ export abstract class AbstractTaskPlugin implements ITaskPlugin {
randomFileId() { randomFileId() {
return Math.random().toString(36).substring(2, 9); return Math.random().toString(36).substring(2, 9);
} }
linkFile(file: FileItem) {
this._result.files?.push({
...file,
id: this.randomFileId(),
});
}
saveFile(filename: string, file: Buffer) { saveFile(filename: string, file: Buffer) {
const filePath = this.ctx.fileStore.writeFile(filename, file); const filePath = this.ctx.fileStore.writeFile(filename, file);
logger.info(`saveFile:${filePath}`); logger.info(`saveFile:${filePath}`);

View File

@ -138,6 +138,8 @@ export abstract class CertApplyBasePlugin extends AbstractTaskPlugin {
const cert: CertInfo = certReader.toCertInfo(); const cert: CertInfo = certReader.toCertInfo();
this.cert = cert; this.cert = cert;
this._result.pipelineVars.certExpiresTime = dayjs(certReader.detail.validity.notAfter).unix();
if (isNew) { if (isNew) {
const applyTime = dayjs(certReader.detail.validity.notBefore).format("YYYYMMDD_HHmmss"); const applyTime = dayjs(certReader.detail.validity.notBefore).format("YYYYMMDD_HHmmss");
await this.zipCert(cert, applyTime); await this.zipCert(cert, applyTime);

View File

@ -61,7 +61,7 @@ function install(app: App, options: any = {}) {
}, },
size: "small", size: "small",
pagination: false, pagination: false,
onResizeColumn: (w: number, col: any) => { onResizeColumn: (w: number | string, col: any) => {
if (crudBinding.value?.table?.columnsMap && crudBinding.value?.table?.columnsMap[col.key]) { if (crudBinding.value?.table?.columnsMap && crudBinding.value?.table?.columnsMap[col.key]) {
crudBinding.value.table.columnsMap[col.key].width = w; crudBinding.value.table.columnsMap[col.key].width = w;
} }
@ -347,6 +347,8 @@ function install(app: App, options: any = {}) {
columnProps.column.resizable = true; columnProps.column.resizable = true;
if (!columnProps.column.width) { if (!columnProps.column.width) {
columnProps.column.width = 100; columnProps.column.width = 100;
} else if (typeof columnProps.column?.width === "string" && columnProps.column.width.indexOf("px") > -1) {
columnProps.column.width = parseInt(columnProps.column.width.replace("px", ""));
} }
return columnProps; return columnProps;
} }

View File

@ -8,6 +8,7 @@ import { nanoid } from "nanoid";
import { message, Modal } from "ant-design-vue"; import { message, Modal } from "ant-design-vue";
import { env } from "/@/utils/util.env"; import { env } from "/@/utils/util.env";
import { useUserStore } from "/@/store/modules/user"; import { useUserStore } from "/@/store/modules/user";
import dayjs from "dayjs";
export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOptionsProps): CreateCrudOptionsRet { export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const router = useRouter(); const router = useRouter();
@ -125,6 +126,8 @@ export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOp
} }
}, },
rowHandle: { rowHandle: {
minWidth: 200,
fixed: "right",
buttons: { buttons: {
view: { view: {
click({ row }) { click({ row }) {
@ -200,6 +203,7 @@ export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOp
}, },
column: { column: {
width: 300, width: 300,
sorter: true,
component: { component: {
on: { on: {
// 注意必须要on前缀 // 注意必须要on前缀
@ -210,11 +214,35 @@ export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOp
} }
} }
}, },
lastVars: {
title: "到期剩余",
type: "number",
form: {
show: false
},
column: {
cellRender({ row }) {
if (!row.lastVars?.certExpiresTime) {
return "-";
}
const leftDays = dayjs(row.lastVars.certExpiresTime).diff(dayjs(), "day");
const color = leftDays < 20 ? "red" : "#389e0d";
const percent = (leftDays / 90) * 100;
return <a-progress percent={percent} strokeColor={color} format={(percent: number) => `${leftDays}`} />;
},
width: 110
}
},
lastHistoryTime: { lastHistoryTime: {
title: "最后运行", title: "最后运行",
type: "datetime", type: "datetime",
form: { form: {
show: false show: false
},
column: {
sorter: true,
width: 120,
align: "center"
} }
}, },
status: { status: {
@ -225,6 +253,11 @@ export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOp
}), }),
form: { form: {
show: false show: false
},
column: {
sorter: true,
width: 80,
align: "center"
} }
}, },
@ -242,6 +275,9 @@ export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOp
show: false show: false
}, },
column: { column: {
sorter: true,
width: 80,
align: "center",
component: { component: {
name: "fs-dict-switch", name: "fs-dict-switch",
vModel: "checked" vModel: "checked"
@ -254,12 +290,25 @@ export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOp
} }
} }
}, },
keepHistoryCount: { keepHistoryCount: {
title: "历史记录保持数", title: "历史记录保持数",
type: "number", type: "number",
form: { form: {
value: 30, value: 30,
helper: "历史记录保持条数,多余的会被删除" helper: "历史记录保持条数,多余的会被删除"
},
column: {
show: false
}
},
order: {
title: "排序号",
type: "number",
column: {
sorter: true,
align: "center",
width: 80
} }
}, },
createTime: { createTime: {
@ -267,6 +316,11 @@ export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOp
type: "datetime", type: "datetime",
form: { form: {
show: false show: false
},
column: {
sorter: true,
width: 125,
align: "center"
} }
}, },
updateTime: { updateTime: {
@ -274,6 +328,9 @@ export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOp
type: "datetime", type: "datetime",
form: { form: {
show: false show: false
},
column: {
show: false
} }
} }
} }

View File

@ -1 +1,5 @@
alter table pi_pipeline alter column title type varchar(100) using title::varchar(100); alter table pi_pipeline alter column title type varchar(100) using title::varchar(100);
alter table pi_pipeline alter column content type text using content::text;
alter table pi_storage alter column value type text using value::text;
alter table pi_pipeline add "order" integer default 0;

View File

@ -0,0 +1,3 @@
alter table pi_pipeline add COLUMN "order" integer default 0;

View File

@ -118,10 +118,9 @@ export abstract class BaseService<T> {
} }
const qb = this.getRepository().createQueryBuilder('main'); const qb = this.getRepository().createQueryBuilder('main');
if (order && order.prop) { if (order && order.prop) {
qb.orderBy('main.' + order.prop, order.asc ? 'ASC' : 'DESC'); qb.addOrderBy('main.' + order.prop, order.asc ? 'ASC' : 'DESC');
} else {
qb.orderBy('id', 'DESC');
} }
qb.addOrderBy('id', 'DESC');
qb.offset(page.offset).limit(page.limit); qb.offset(page.offset).limit(page.limit);
//根据bean query //根据bean query
if (query) { if (query) {

View File

@ -9,12 +9,7 @@ export abstract class CrudController<T> extends BaseController {
@Body(ALL) @Body(ALL)
body body
) { ) {
const pageRet = await this.getService().page( const pageRet = await this.getService().page(body?.query, body?.page, body?.sort, null);
body?.query,
body?.page,
body?.sort,
null
);
return this.ok(pageRet); return this.ok(pageRet);
} }
@ -54,6 +49,7 @@ export abstract class CrudController<T> extends BaseController {
await this.getService().update(bean); await this.getService().update(bean);
return this.ok(null); return this.ok(null);
} }
@Post('/delete') @Post('/delete')
async delete( async delete(
@Query('id') @Query('id')

View File

@ -23,9 +23,19 @@ export class PipelineController extends CrudController<PipelineService> {
@Post('/page', { summary: Constants.per.authOnly }) @Post('/page', { summary: Constants.per.authOnly })
async page(@Body(ALL) body) { async page(@Body(ALL) body) {
body.query.userId = this.ctx.user.id; body.query.userId = this.ctx.user.id;
const title = body.query.title;
delete body.query.title;
const buildQuery = qb => { const buildQuery = qb => {
qb.where({}); if (title) {
qb.where('title like :title', { title: `%${title}%` });
}
}; };
if (!body.sort || !body.sort?.prop) {
body.sort = { prop: 'order', asc: false };
}
return super.page({ ...body, buildQuery }); return super.page({ ...body, buildQuery });
} }
@ -48,7 +58,6 @@ export class PipelineController extends CrudController<PipelineService> {
await this.service.checkUserId(bean.id, this.ctx.user.id); await this.service.checkUserId(bean.id, this.ctx.user.id);
} }
await this.service.save(bean); await this.service.save(bean);
await this.service.registerTriggerById(bean.id);
return this.ok(bean.id); return this.ok(bean.id);
} }

View File

@ -37,6 +37,16 @@ export class PipelineEntity {
}) })
lastHistoryTime: number; lastHistoryTime: number;
// 变量
lastVars: any;
@Column({
name: 'order',
comment: '排序',
nullable: true,
})
order: number;
@Column({ @Column({
name: 'create_time', name: 'create_time',
comment: '创建时间', comment: '创建时间',

View File

@ -12,7 +12,7 @@ export class DbStorage implements IStorage {
this.storageService = storageService; this.storageService = storageService;
} }
remove(scope: string, namespace: string, version: string, key: string): Promise<void> { async remove(scope: string, namespace: string, version: string, key: string): Promise<void> {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }

View File

@ -47,8 +47,23 @@ export class PipelineService extends BaseService<PipelineEntity> {
return this.repository; return this.repository;
} }
async update(entity) { async page(query: any, page: { offset: number; limit: number }, order: any, buildQuery: any) {
await super.update(entity); const result = await super.page(query, page, order, buildQuery);
const pipelineIds: number[] = [];
const recordMap = {};
for (const record of result.records) {
pipelineIds.push(record.id);
recordMap[record.id] = record;
}
const vars = await this.storageService.findPipelineVars(pipelineIds);
for (const varEntity of vars) {
const record = recordMap[varEntity.namespace];
if (record) {
const value = JSON.parse(varEntity.value);
record.lastVars = value.value;
}
}
return result;
} }
public async registerTriggerById(pipelineId) { public async registerTriggerById(pipelineId) {
@ -71,10 +86,18 @@ export class PipelineService extends BaseService<PipelineEntity> {
return new PipelineDetail(pipeline); return new PipelineDetail(pipeline);
} }
async update(bean: PipelineEntity) {
await this.clearTriggers(bean.id);
await super.update(bean);
await this.registerTriggerById(bean.id);
}
async save(bean: PipelineEntity) { async save(bean: PipelineEntity) {
await this.clearTriggers(bean.id);
const pipeline = JSON.parse(bean.content); const pipeline = JSON.parse(bean.content);
bean.title = pipeline.title; bean.title = pipeline.title;
await this.addOrUpdate(bean); await this.addOrUpdate(bean);
await this.registerTriggerById(bean.id);
} }
/** /**
@ -153,6 +176,14 @@ export class PipelineService extends BaseService<PipelineEntity> {
} }
async delete(id: number) { async delete(id: number) {
await this.clearTriggers(id);
//TODO 删除storage
// const storage = new DbStorage(pipeline.userId, this.storageService);
// await storage.remove(pipeline.id);
await super.delete([id]);
}
async clearTriggers(id: number) {
const pipeline = await this.info(id); const pipeline = await this.info(id);
if (!pipeline) { if (!pipeline) {
return; return;
@ -163,7 +194,6 @@ export class PipelineService extends BaseService<PipelineEntity> {
this.removeCron(id, trigger); this.removeCron(id, trigger);
} }
} }
await super.delete([id]);
} }
removeCron(pipelineId, trigger) { removeCron(pipelineId, trigger) {
@ -176,6 +206,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
if (cron == null) { if (cron == null) {
return; return;
} }
cron = cron.trim();
if (cron.startsWith('*')) { if (cron.startsWith('*')) {
cron = '0' + cron.substring(1, cron.length); cron = '0' + cron.substring(1, cron.length);
} }
@ -183,7 +214,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
this.cron.remove(name); this.cron.remove(name);
this.cron.register({ this.cron.register({
name, name,
cron: cron, cron,
job: async () => { job: async () => {
logger.info('定时任务触发:', pipelineId, trigger.id); logger.info('定时任务触发:', pipelineId, trigger.id);
try { try {
@ -198,6 +229,9 @@ export class PipelineService extends BaseService<PipelineEntity> {
async run(id: number, triggerId: string) { async run(id: number, triggerId: string) {
const entity: PipelineEntity = await this.info(id); const entity: PipelineEntity = await this.info(id);
if (entity.disabled) {
return;
}
const pipeline = JSON.parse(entity.content); const pipeline = JSON.parse(entity.content);
if (!pipeline.stages || pipeline.stages.length === 0) { if (!pipeline.stages || pipeline.stages.length === 0) {

View File

@ -1,6 +1,6 @@
import { Provide, Scope, ScopeEnum } from '@midwayjs/core'; import { Provide, Scope, ScopeEnum } from '@midwayjs/core';
import { InjectEntityModel } from '@midwayjs/typeorm'; import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm'; import { In, Repository } from 'typeorm';
import { BaseService } from '../../../basic/base-service.js'; import { BaseService } from '../../../basic/base-service.js';
import { StorageEntity } from '../entity/storage.js'; import { StorageEntity } from '../entity/storage.js';
@ -41,4 +41,14 @@ export class StorageService extends BaseService<StorageEntity> {
} }
return; return;
} }
async findPipelineVars(pipelineIds: number[]) {
return await this.repository.find({
where: {
scope: 'pipeline',
namespace: In(pipelineIds),
key: 'vars',
},
});
}
} }