feat(snapshot): 进度精度升级 — Float百分比 + MB消息存DB
- schema: progress Int→Float,新增 progressMsg 字段 - PG handler: 百分比保留2位小数(toFixed(2)),不再 Math.floor - orchestrator: 每2秒写DB时同时写 progressMsg (含MB信息) - 前端: 百分比显示 toFixed(1),message 优先读 progressMsg 效果: 113GB库每次轮询进度条和MB数都有变化,不再卡在整数百分比 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
9cbc0ba580
commit
8855491637
|
|
@ -30,7 +30,8 @@ model SnapshotDetail {
|
||||||
taskId String
|
taskId String
|
||||||
target String
|
target String
|
||||||
status String @default("PENDING")
|
status String @default("PENDING")
|
||||||
progress Int @default(0)
|
progress Float @default(0)
|
||||||
|
progressMsg String?
|
||||||
fileSize BigInt @default(0)
|
fileSize BigInt @default(0)
|
||||||
fileName String?
|
fileName String?
|
||||||
error String?
|
error String?
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,7 @@ export class SnapshotOrchestratorService implements OnModuleInit {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
if (now - lastDbWriteTime >= 2000) {
|
if (now - lastDbWriteTime >= 2000) {
|
||||||
lastDbWriteTime = now;
|
lastDbWriteTime = now;
|
||||||
this.repo.updateDetailProgress(taskId, target, percent).catch(() => {});
|
this.repo.updateDetailProgress(taskId, target, percent, msg).catch(() => {});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ export class PostgresBackupHandler implements BackupHandler {
|
||||||
if (readMB > lastReportedMB) {
|
if (readMB > lastReportedMB) {
|
||||||
lastReportedMB = readMB;
|
lastReportedMB = readMB;
|
||||||
const totalMB = Math.floor(totalSize / (1024 * 1024));
|
const totalMB = Math.floor(totalSize / (1024 * 1024));
|
||||||
const percent = Math.min(99, Math.floor((bytesRead / totalSize) * 100));
|
const percent = Math.min(99, parseFloat(((bytesRead / totalSize) * 100).toFixed(2)));
|
||||||
onProgress(percent, `PostgreSQL 备份中... ${readMB}MB / ~${totalMB}MB`);
|
onProgress(percent, `PostgreSQL 备份中... ${readMB}MB / ~${totalMB}MB`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -79,14 +79,16 @@ export class SnapshotRepository {
|
||||||
return this.prisma.snapshotDetail.update({ where: { id: detail.id }, data });
|
return this.prisma.snapshotDetail.update({ where: { id: detail.id }, data });
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateDetailProgress(taskId: string, target: string, progress: number) {
|
async updateDetailProgress(taskId: string, target: string, progress: number, progressMsg?: string) {
|
||||||
const detail = await this.prisma.snapshotDetail.findFirst({
|
const detail = await this.prisma.snapshotDetail.findFirst({
|
||||||
where: { taskId, target },
|
where: { taskId, target },
|
||||||
});
|
});
|
||||||
if (!detail) return;
|
if (!detail) return;
|
||||||
|
const data: Record<string, unknown> = { progress };
|
||||||
|
if (progressMsg !== undefined) data.progressMsg = progressMsg;
|
||||||
return this.prisma.snapshotDetail.update({
|
return this.prisma.snapshotDetail.update({
|
||||||
where: { id: detail.id },
|
where: { id: detail.id },
|
||||||
data: { progress },
|
data,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -255,7 +255,7 @@ export default function SnapshotsPage() {
|
||||||
{BACKUP_TARGET_LABELS[p.target] || p.target}
|
{BACKUP_TARGET_LABELS[p.target] || p.target}
|
||||||
</span>
|
</span>
|
||||||
<span style={{ color: statusColor(p.status.toUpperCase() as SnapshotStatus) }}>
|
<span style={{ color: statusColor(p.status.toUpperCase() as SnapshotStatus) }}>
|
||||||
{p.status === 'completed' ? '100%' : p.status === 'failed' ? '失败' : `${p.percent}%`}
|
{p.status === 'completed' ? '100%' : p.status === 'failed' ? '失败' : `${p.percent.toFixed(1)}%`}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.progressBar}>
|
<div className={styles.progressBar}>
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ export function useSnapshotPolling(taskId: string | null): UseSnapshotPollingRet
|
||||||
: d.status === 'COMPLETED'
|
: d.status === 'COMPLETED'
|
||||||
? '完成'
|
? '完成'
|
||||||
: d.status === 'RUNNING'
|
: d.status === 'RUNNING'
|
||||||
? `备份中... ${d.progress}%`
|
? (d.progressMsg || `备份中... ${d.progress.toFixed(1)}%`)
|
||||||
: '等待中',
|
: '等待中',
|
||||||
status: statusMap[d.status] || 'pending',
|
status: statusMap[d.status] || 'pending',
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ export interface SnapshotDetail {
|
||||||
target: BackupTarget;
|
target: BackupTarget;
|
||||||
status: SnapshotStatus;
|
status: SnapshotStatus;
|
||||||
progress: number;
|
progress: number;
|
||||||
|
progressMsg: string | null;
|
||||||
fileSize: string;
|
fileSize: string;
|
||||||
fileName: string | null;
|
fileName: string | null;
|
||||||
error: string | null;
|
error: string | null;
|
||||||
|
|
|
||||||
|
|
@ -238,7 +238,7 @@ export default function SnapshotsPage() {
|
||||||
: 'text-blue-500'
|
: 'text-blue-500'
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{p.status === 'completed' ? '100%' : p.status === 'failed' ? '失败' : `${p.percent}%`}
|
{p.status === 'completed' ? '100%' : p.status === 'failed' ? '失败' : `${p.percent.toFixed(1)}%`}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="h-2 overflow-hidden rounded-full bg-muted">
|
<div className="h-2 overflow-hidden rounded-full bg-muted">
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ export function useSnapshotPolling(taskId: string | null): UseSnapshotPollingRet
|
||||||
: d.status === 'COMPLETED'
|
: d.status === 'COMPLETED'
|
||||||
? '完成'
|
? '完成'
|
||||||
: d.status === 'RUNNING'
|
: d.status === 'RUNNING'
|
||||||
? `备份中... ${d.progress}%`
|
? (d.progressMsg || `备份中... ${d.progress.toFixed(1)}%`)
|
||||||
: '等待中',
|
: '等待中',
|
||||||
status: statusMap[d.status] || 'pending',
|
status: statusMap[d.status] || 'pending',
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ export interface SnapshotDetail {
|
||||||
target: BackupTarget;
|
target: BackupTarget;
|
||||||
status: SnapshotStatus;
|
status: SnapshotStatus;
|
||||||
progress: number;
|
progress: number;
|
||||||
|
progressMsg: string | null;
|
||||||
fileSize: string;
|
fileSize: string;
|
||||||
fileName: string | null;
|
fileName: string | null;
|
||||||
error: string | null;
|
error: string | null;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue