修复暂停下载继续后不结束下载的问题

This commit is contained in:
2025-10-06 21:26:34 +08:00
parent 480d357fdb
commit 3181a198fd
7 changed files with 127 additions and 37 deletions
+8 -10
View File
@@ -309,6 +309,8 @@ class DownloadService {
// 获取更新后的任务
const updatedTask = this.taskManager.getTask(taskId);
// 立即通知状态更新
this.progressManager.notifyProgressUpdate(taskId, updatedTask);
logger.info('任务暂停完成', { taskId });
@@ -336,13 +338,6 @@ class DownloadService {
return { success: false, error: '任务不存在' };
}
// logger.info('尝试恢复任务', {
// taskId,
// currentStatus: task.status,
// type: task.type,
// artwork_id: task.artwork_id
// });
// 只允许恢复暂停的任务
if (task.status !== 'paused') {
logger.warn('恢复任务失败:任务状态不是暂停状态', {
@@ -357,14 +352,19 @@ class DownloadService {
logger.info('开始恢复任务执行', { taskId });
await this.downloadExecutor.resumeTask(taskId);
// 获取更新后的任务状态
// 确保状态已经更新后再返回
const updatedTask = this.taskManager.getTask(taskId);
// 立即通知状态更新
this.progressManager.notifyProgressUpdate(taskId, updatedTask);
logger.info('任务恢复成功', {
taskId,
newStatus: updatedTask.status
});
// 返回最新的任务状态
return { success: true, data: updatedTask };
} catch (error) {
logger.error('恢复任务执行失败', {
taskId,
@@ -374,8 +374,6 @@ class DownloadService {
// 如果恢复失败,保持暂停状态
return { success: false, error: `恢复任务失败: ${error.message}` };
}
return { success: true, data: this.taskManager.getTask(taskId) };
}
/**
+6 -5
View File
@@ -503,19 +503,20 @@ class FileManager {
throw error;
}
// 处理其他文件系统错误
const errorResult = ErrorHandler.handleFileSystemError(error, filePath, 'download');
// 检查是否是可重试的网络错误
const isRetryable = ErrorHandler.isRetryableError(error);
logger.error(`下载文件失败 (尝试 ${attempt}/${maxRetries}): ${filePath}`, {
error: error.message,
stack: error.stack,
url,
retryable: errorResult.retryable,
attempt
retryable: isRetryable,
attempt,
errorCode: error.code
});
// 如果不是可重试的错误,直接抛出
if (!errorResult.retryable) {
if (!isRetryable) {
throw error;
}
+29 -3
View File
@@ -305,11 +305,24 @@ class ErrorHandler {
}
/**
* 检查是否可重试的错误
* 判断错误是否可重试
*/
static isRetryableError(error) {
const retryableCodes = ['EPERM', 'EACCES', 'EBUSY', 'EAGAIN', 'ENOSPC'];
return retryableCodes.includes(error.code);
const retryableCodes = ['EPERM', 'EACCES', 'EBUSY', 'EAGAIN', 'ENOSPC', 'ECONNRESET', 'ETIMEDOUT', 'ENOTFOUND'];
const retryableMessages = ['aborted', 'socket hang up', 'network timeout'];
// 检查错误码
if (retryableCodes.includes(error.code)) {
return true;
}
// 检查错误消息
if (error.message) {
const message = error.message.toLowerCase();
return retryableMessages.some(msg => message.includes(msg));
}
return false;
}
/**
@@ -327,7 +340,20 @@ class ErrorHandler {
case 'EPERM':
case 'EACCES':
return Math.min(delay, 5000); // 权限错误,中等延迟
case 'ECONNRESET':
case 'ETIMEDOUT':
return Math.min(delay, 3000); // 网络错误,较短延迟
case 'ENOTFOUND':
return Math.min(delay, 5000); // DNS错误,中等延迟
default:
// 检查是否是网络相关的错误消息
if (error.message && (
error.message.toLowerCase().includes('aborted') ||
error.message.toLowerCase().includes('socket hang up') ||
error.message.toLowerCase().includes('network timeout')
)) {
return Math.min(delay, 3000); // 网络错误,较短延迟
}
return delay;
}
}
+5 -5
View File
@@ -49,10 +49,10 @@ set LOG_LEVEL=INFO
REM ========================================
REM Auto Open Browser Configuration - Options: true, false
REM true: Automatically open browser when server starts
REM false: Do not automatically open browser (default)
REM true: Automatically open browser when server starts (default)
REM false: Do not automatically open browser
REM ========================================
set AUTO_OPEN_BROWSER=false
set AUTO_OPEN_BROWSER=true
echo.
echo ========================================
@@ -120,8 +120,8 @@ pause
### 自动打开浏览器设置
修改(AUTO_OPEN_BROWSER=xxxx)的值来启用或禁用自动打开浏览器功能:
- true: 启动服务器后自动打开浏览器
- false: 不自动打开浏览器(默认)
- true: 启动服务器后自动打开浏览器(默认)
- false: 不自动打开浏览器
## 注意事项
+1 -1
View File
@@ -206,7 +206,7 @@ class DownloadService {
// 处理不同类型的SSE消息
if (data.type === 'connected') {
console.log('SSE连接已建立:', data.taskId);
// console.log('SSE连接已建立:', data.taskId);
} else if (data.type === 'progress') {
// 新的数据格式:data.task 包含任务信息
if (data.task) {
+75 -10
View File
@@ -13,6 +13,26 @@ export const useDownloadStore = defineStore('download', () => {
// SSE连接管理
const sseConnections = ref<Map<string, () => void>>(new Map());
// 延迟更新管理
const delayedUpdates = ref<Map<string, number[]>>(new Map());
// 清理指定任务的所有延迟更新
const clearAllDelayedUpdates = (taskId: string) => {
const timeouts = delayedUpdates.value.get(taskId);
if (timeouts) {
timeouts.forEach(timeout => clearTimeout(timeout));
delayedUpdates.value.delete(taskId);
}
};
// 添加延迟更新
const addDelayedUpdate = (taskId: string, timeout: number) => {
if (!delayedUpdates.value.has(taskId)) {
delayedUpdates.value.set(taskId, []);
}
delayedUpdates.value.get(taskId)!.push(timeout);
};
// 计算属性:显示活跃任务和暂停任务
const activeTasks = computed(() => {
return tasks.value.filter(task =>
@@ -131,7 +151,7 @@ export const useDownloadStore = defineStore('download', () => {
sseConnections.value.get(taskId)!();
}
console.log('开始SSE监听任务进度:', taskId);
// console.log('开始SSE监听任务进度:', taskId);
// 添加超时处理 - 增加到60秒以匹配后端
const timeoutId = setTimeout(() => {
@@ -183,10 +203,24 @@ export const useDownloadStore = defineStore('download', () => {
tasks.value.push(task);
}
// 如果任务完成或暂停,清理连接
// 如果任务完成或暂停,清理连接并触发额外的状态同步
if (['completed', 'failed', 'cancelled', 'partial', 'paused'].includes(task.status)) {
console.log('任务状态变更,关闭SSE连接:', taskId);
console.log('任务状态变更,关闭SSE连接:', taskId, task.status);
stopTaskStreaming(taskId);
// 如果任务完成,立即更新本地状态并停止所有延迟操作
if (['completed', 'failed', 'cancelled', 'partial'].includes(task.status)) {
console.log('任务完成,立即更新状态:', taskId, task.status);
// 立即更新本地任务状态,防止被其他操作覆盖
const index = tasks.value.findIndex(t => t.id === taskId);
if (index !== -1) {
tasks.value[index] = { ...task };
}
// 取消所有可能的延迟状态更新操作
clearAllDelayedUpdates(taskId);
}
}
},
() => {
@@ -221,12 +255,23 @@ export const useDownloadStore = defineStore('download', () => {
}
});
// 为正在下载的任务建立连接
// 为正在下载的任务建立连接,增加状态检查
activeTasks.value.forEach(task => {
if (task.status === 'downloading' && !sseConnections.value.has(task.id)) {
console.log('为下载任务建立SSE连接:', task.id, task.status);
startTaskStreaming(task.id);
}
});
// 清理已暂停或完成任务的连接
sseConnections.value.forEach((closeConnection, taskId) => {
const task = getTask(taskId);
if (task && ['paused', 'completed', 'failed', 'cancelled', 'partial'].includes(task.status)) {
console.log('清理非活跃任务的SSE连接:', taskId, task.status);
closeConnection();
sseConnections.value.delete(taskId);
}
});
};
// 清理所有SSE连接
@@ -366,12 +411,22 @@ export const useDownloadStore = defineStore('download', () => {
}
if (response.success) {
// 立即更新状态为下载中
// 清理可能存在的延迟更新
clearAllDelayedUpdates(taskId);
// 使用后端返回的最新状态,确保状态同步
if (response.data) {
const index = tasks.value.findIndex(t => t.id === taskId);
if (index !== -1) {
tasks.value[index] = { ...response.data };
}
} else {
// 如果后端没有返回数据,则手动更新状态
updateTask(taskId, { status: 'downloading' });
}
// 立即建立SSE连接
startTaskStreaming(taskId);
// 异步刷新任务列表以确保同步
setTimeout(() => fetchTasks(), 500);
} else {
// 如果恢复失败,恢复原状态
await fetchTasks();
@@ -422,12 +477,22 @@ export const useDownloadStore = defineStore('download', () => {
}
if (response.success) {
// 立即更新状态为已暂停
// 清理可能存在的延迟更新
clearAllDelayedUpdates(taskId);
// 使用后端返回的最新状态,确保状态同步
if (response.data) {
const index = tasks.value.findIndex(t => t.id === taskId);
if (index !== -1) {
tasks.value[index] = { ...response.data };
}
} else {
// 如果后端没有返回数据,则手动更新状态
updateTask(taskId, { status: 'paused' });
}
// 停止SSE连接
stopTaskStreaming(taskId);
// 异步刷新任务列表以确保同步
setTimeout(() => fetchTasks(), 500);
} else {
// 如果暂停失败,恢复原状态
await fetchTasks();
+1 -1
View File
@@ -265,7 +265,7 @@ const handleDownload = async () => {
});
if (response.success) {
console.log('下载响应:', response.data);
// console.log('下载响应:', response.data);
// 检查是否跳过下载
if (response.data.skipped) {