数据迁移并发提升性能

This commit is contained in:
mtvpls
2026-02-24 21:03:18 +08:00
parent ef5656cde5
commit ac68bca649
5 changed files with 557 additions and 206 deletions

View File

@@ -144,6 +144,18 @@ const DataMigration = ({ onRefreshConfig }: DataMigrationProps) => {
const [selectedFile, setSelectedFile] = useState<File | null>(null);
const [isExporting, setIsExporting] = useState(false);
const [isImporting, setIsImporting] = useState(false);
const [exportProgress, setExportProgress] = useState<{
phase: string;
current: number;
total: number;
message: string;
} | null>(null);
const [importProgress, setImportProgress] = useState<{
phase: string;
current: number;
total: number;
message: string;
} | null>(null);
const [alertModal, setAlertModal] = useState<{
isOpen: boolean;
type: 'success' | 'error' | 'warning';
@@ -180,8 +192,22 @@ const DataMigration = ({ onRefreshConfig }: DataMigrationProps) => {
return;
}
let eventSource: EventSource | null = null;
try {
setIsExporting(true);
setExportProgress(null);
// 连接到进度 SSE 端点
eventSource = new EventSource('/api/admin/data_migration/progress?operation=export');
eventSource.onmessage = (event) => {
try {
const progress = JSON.parse(event.data);
setExportProgress(progress);
} catch (e) {
console.error('Failed to parse progress:', e);
}
};
const response = await fetch('/api/admin/data_migration/export', {
method: 'POST',
@@ -234,6 +260,10 @@ const DataMigration = ({ onRefreshConfig }: DataMigrationProps) => {
});
} finally {
setIsExporting(false);
setExportProgress(null);
if (eventSource) {
eventSource.close();
}
}
};
@@ -265,8 +295,22 @@ const DataMigration = ({ onRefreshConfig }: DataMigrationProps) => {
return;
}
let eventSource: EventSource | null = null;
try {
setIsImporting(true);
setImportProgress(null);
// 连接到进度 SSE 端点
eventSource = new EventSource('/api/admin/data_migration/progress?operation=import');
eventSource.onmessage = (event) => {
try {
const progress = JSON.parse(event.data);
setImportProgress(progress);
} catch (e) {
console.error('Failed to parse progress:', e);
}
};
const formData = new FormData();
formData.append('file', selectedFile);
@@ -322,6 +366,10 @@ const DataMigration = ({ onRefreshConfig }: DataMigrationProps) => {
});
} finally {
setIsImporting(false);
setImportProgress(null);
if (eventSource) {
eventSource.close();
}
}
};
@@ -393,9 +441,24 @@ const DataMigration = ({ onRefreshConfig }: DataMigrationProps) => {
}`}
>
{isExporting ? (
<div className="flex items-center justify-center gap-2">
<div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin"></div>
...
<div className="space-y-2">
<div className="flex items-center justify-center gap-2">
<div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin"></div>
...
</div>
{exportProgress && (
<div className="bg-gray-100 dark:bg-gray-700 rounded-lg p-3 space-y-2">
<div className="text-xs text-gray-900 dark:text-gray-100 font-medium">{exportProgress.message}</div>
{exportProgress.total > 0 && (
<div className="w-full bg-gray-300 dark:bg-gray-600 rounded-full h-3">
<div
className="bg-yellow-500 h-3 rounded-full transition-all duration-300"
style={{ width: `${(exportProgress.current / exportProgress.total) * 100}%` }}
></div>
</div>
)}
</div>
)}
</div>
) : (
<div className="flex items-center justify-center gap-2">
@@ -469,9 +532,24 @@ const DataMigration = ({ onRefreshConfig }: DataMigrationProps) => {
}`}
>
{isImporting ? (
<div className="flex items-center justify-center gap-2">
<div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin"></div>
...
<div className="space-y-2">
<div className="flex items-center justify-center gap-2">
<div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin"></div>
...
</div>
{importProgress && (
<div className="bg-gray-100 dark:bg-gray-700 rounded-lg p-3 space-y-2">
<div className="text-xs text-gray-900 dark:text-gray-100 font-medium">{importProgress.message}</div>
{importProgress.total > 0 && (
<div className="w-full bg-gray-300 dark:bg-gray-600 rounded-full h-3">
<div
className="bg-yellow-500 h-3 rounded-full transition-all duration-300"
style={{ width: `${(importProgress.current / importProgress.total) * 100}%` }}
></div>
</div>
)}
</div>
)}
</div>
) : (
<div className="flex items-center justify-center gap-2">