From 27db2a5aa23a9340713624a15cfa99eb71b89d2d Mon Sep 17 00:00:00 2001 From: hailin Date: Wed, 25 Feb 2026 01:30:44 -0800 Subject: [PATCH] =?UTF-8?q?fix(snapshot):=20=E4=BF=AE=E5=A4=8D=E5=A4=87?= =?UTF-8?q?=E4=BB=BD=E8=BF=9B=E8=A1=8C=E4=B8=AD=E8=A2=AB=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E5=AF=BC=E8=87=B4=E5=AE=B9=E5=99=A8=E5=B4=A9=E6=BA=83=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. checkDone 中 statSync 加 try-catch,文件被删时 reject 而非 uncaught crash 2. 删除 API 禁止删除 RUNNING 状态的任务,返回 409 Conflict 3. compose 补充 restart: unless-stopped,防止异常退出后服务不可用 Co-Authored-By: Claude Opus 4.6 --- backend/services/docker-compose.2.0-snapshot.yml | 1 + .../src/api/controllers/snapshot.controller.ts | 5 +++++ .../services/snapshot-orchestrator.service.ts | 4 ++++ .../backup/postgres-backup.handler.ts | 14 ++++++++++---- 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/backend/services/docker-compose.2.0-snapshot.yml b/backend/services/docker-compose.2.0-snapshot.yml index 3a30bb83..cd2d2525 100644 --- a/backend/services/docker-compose.2.0-snapshot.yml +++ b/backend/services/docker-compose.2.0-snapshot.yml @@ -14,6 +14,7 @@ services: context: ./snapshot-service dockerfile: Dockerfile container_name: rwa-snapshot-service-2 + restart: unless-stopped ports: - "3199:3199" environment: diff --git a/backend/services/snapshot-service/src/api/controllers/snapshot.controller.ts b/backend/services/snapshot-service/src/api/controllers/snapshot.controller.ts index 1ec151ea..efa3d23d 100644 --- a/backend/services/snapshot-service/src/api/controllers/snapshot.controller.ts +++ b/backend/services/snapshot-service/src/api/controllers/snapshot.controller.ts @@ -85,6 +85,11 @@ export class SnapshotController { @Delete(':id') @ApiOperation({ summary: '删除备份' }) async deleteSnapshot(@Param('id') id: string) { + const task = await this.repo.findById(id); + if (!task) throw new NotFoundException('备份任务不存在'); + if (task.status === 'RUNNING') { + throw new ConflictException('备份任务正在执行中,无法删除,请等待完成后再操作'); + } await this.orchestrator.deleteSnapshot(id); return { message: '备份已删除' }; } diff --git a/backend/services/snapshot-service/src/application/services/snapshot-orchestrator.service.ts b/backend/services/snapshot-service/src/application/services/snapshot-orchestrator.service.ts index 885ea12a..c48b32c3 100644 --- a/backend/services/snapshot-service/src/application/services/snapshot-orchestrator.service.ts +++ b/backend/services/snapshot-service/src/application/services/snapshot-orchestrator.service.ts @@ -185,6 +185,10 @@ export class SnapshotOrchestratorService implements OnModuleInit { const task = await this.repo.findById(taskId); if (!task) throw new Error(`任务不存在: ${taskId}`); + if (task.status === SnapshotStatus.RUNNING) { + throw new Error('备份任务正在执行中,无法删除,请等待完成后再操作'); + } + if (task.storageType === StorageType.LOCAL) { this.localStorage.deleteTask(taskId); } else if (task.storageType === StorageType.MINIO) { diff --git a/backend/services/snapshot-service/src/infrastructure/backup/postgres-backup.handler.ts b/backend/services/snapshot-service/src/infrastructure/backup/postgres-backup.handler.ts index aa27a683..968e68bb 100644 --- a/backend/services/snapshot-service/src/infrastructure/backup/postgres-backup.handler.ts +++ b/backend/services/snapshot-service/src/infrastructure/backup/postgres-backup.handler.ts @@ -119,10 +119,16 @@ export class PostgresBackupHandler implements BackupHandler { if (dumpExitCode === null || gzipExitCode === null) return; if (dumpExitCode === 0 && gzipExitCode === 0) { - const stat = fs.statSync(filePath); - this.logger.log(`PostgreSQL 备份完成: ${fileName}, 压缩后: ${stat.size} bytes, 原始输出: ${bytesRead} bytes`); - onProgress(100, 'PostgreSQL 备份完成'); - resolve({ fileName, filePath, fileSize: stat.size }); + try { + const stat = fs.statSync(filePath); + this.logger.log(`PostgreSQL 备份完成: ${fileName}, 压缩后: ${stat.size} bytes, 原始输出: ${bytesRead} bytes`); + onProgress(100, 'PostgreSQL 备份完成'); + resolve({ fileName, filePath, fileSize: stat.size }); + } catch (err) { + const msg = `备份文件不可访问 (可能已被删除): ${filePath}`; + this.logger.error(msg); + reject(new Error(msg)); + } } else { const error = `pg_dumpall 退出码: ${dumpExitCode}, gzip 退出码: ${gzipExitCode}, stderr: ${stderrBuffer.slice(-500)}`; this.logger.error(error);