From dea6cfd7fe73505ef88644fb0314a991bfb1c553 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B9=9B=E5=85=AE?= Date: Fri, 12 Jun 2026 05:31:51 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E9=81=BF=E5=85=8D=E5=B9=82=E7=AD=89?= =?UTF-8?q?=E5=8F=91=E5=B8=83=E9=87=8D=E5=A4=8D=E6=89=A7=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - src/deploy-runs/deploy-run.repository.ts: 返回发布单是否新建,唯一键竞争时回读已有记录 - src/deploy-runs/deploy-runs.service.ts: 重复 idempotencyKey 只返回已有发布单并记录复用审计,不再重复触发 Jenkins --- src/deploy-runs/deploy-run.repository.ts | 48 +++++++++++++++++++----- src/deploy-runs/deploy-runs.service.ts | 9 ++++- 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/src/deploy-runs/deploy-run.repository.ts b/src/deploy-runs/deploy-run.repository.ts index ae360fb..118aa8a 100644 --- a/src/deploy-runs/deploy-run.repository.ts +++ b/src/deploy-runs/deploy-run.repository.ts @@ -49,6 +49,14 @@ type DeployRunRecord = Prisma.DeployRunGetPayload<{ }; }>; type DeployRunDatabaseClient = PrismaService | Prisma.TransactionClient; +type CreateDeployRunResult = { + run: DeployRunSummary; + created: boolean; +}; +type CreateDatabaseDeployRunResult = { + record: DeployRunRecord; + created: boolean; +}; type DeployRunMetadata = Prisma.InputJsonObject & { jenkinsBuildUrl?: string; @@ -143,16 +151,19 @@ export class DeployRunRepository { return run ? this.toSummary(run) : null; } - async create(input: CreateDeployRunInput): Promise { + async create(input: CreateDeployRunInput): Promise { const idempotencyKey = this.resolveIdempotencyKey(input); if (!this.useDatabase()) { return this.createMemoryRun(input, idempotencyKey); } - const created = await this.createDatabaseRun(input, idempotencyKey); + const result = await this.createDatabaseRun(input, idempotencyKey); - return this.toSummary(created); + return { + run: this.toSummary(result.record), + created: result.created, + }; } async updateStatus( @@ -437,7 +448,7 @@ export class DeployRunRepository { private async createDatabaseRun( input: CreateDeployRunInput, idempotencyKey: string, - ): Promise { + ): Promise { try { return await this.prisma.$transaction(async (tx) => { const existing = await this.findRecordByIdempotencyKey( @@ -446,14 +457,17 @@ export class DeployRunRepository { ); if (existing) { - return existing; + return { + record: existing, + created: false, + }; } const target = await this.findTargetProjectEnvironment(input, tx); const initialRun = this.buildInitialRun(input, idempotencyKey); const metadata = this.inputMetadata(input); - return tx.deployRun.create({ + const record = await tx.deployRun.create({ data: { projectId: target.project.id, environmentId: target.environment.id, @@ -483,13 +497,21 @@ export class DeployRunRepository { }, }, }); + + return { + record, + created: true, + }; }); } catch (error) { if (this.isIdempotencyUniqueConflict(error)) { const existing = await this.findRecordByIdempotencyKey(idempotencyKey); if (existing) { - return existing; + return { + record: existing, + created: false, + }; } } @@ -565,16 +587,22 @@ export class DeployRunRepository { private createMemoryRun( input: CreateDeployRunInput, idempotencyKey: string, - ): DeployRunSummary { + ): CreateDeployRunResult { const existing = this.memoryRuns.find((run) => run.id === idempotencyKey); if (existing) { - return existing; + return { + run: existing, + created: false, + }; } const run = this.buildInitialRun(input, idempotencyKey); this.memoryRuns.unshift(run); - return run; + return { + run, + created: true, + }; } private buildInitialRun( diff --git a/src/deploy-runs/deploy-runs.service.ts b/src/deploy-runs/deploy-runs.service.ts index 95ca20a..167cf3c 100644 --- a/src/deploy-runs/deploy-runs.service.ts +++ b/src/deploy-runs/deploy-runs.service.ts @@ -114,11 +114,12 @@ export class DeployRunsService { const project = await this.projectsService.getProject(input.projectKey); await this.validateReleaseRequest(input, project); - const run = await this.deployRunRepository.create(input); + const creation = await this.deployRunRepository.create(input); + const run = creation.run; const confirmation = this.productionConfirmationDigest(input); await this.auditService.record({ - action: 'DEPLOY_REQUESTED', + action: creation.created ? 'DEPLOY_REQUESTED' : 'DEPLOY_REQUEST_REUSED', resourceType: 'deploy_run', resourceId: run.id, actorName: input.operator, @@ -132,6 +133,10 @@ export class DeployRunsService { }, }); + if (!creation.created) { + return run; + } + return this.deployExecutionService.execute(run, project); }