forked from 77media/video-flow
11 KiB
11 KiB
任务重试功能集成方案
📋 概述
本文档详细说明了如何将任务重试接口 POST https://77.smartvideo.py.qikongjian.com/task/retry_task 集成到Dashboard页面中,提供完整的用户体验和错误处理机制。
🏗️ 架构设计
1. API层设计
1.1 接口定义
// api/video_flow.ts
export const retryTask = async (request: {
/** 任务ID */
task_id: string;
}): Promise<ApiResponse<{
/** 任务ID */
task_id: string;
/** 重试状态 */
status: string;
/** 状态描述 */
message: string;
/** 是否成功启动重试 */
success: boolean;
}>>
1.2 请求示例
{
"task_id": "11ac5e0d-963d-4eb2-98e8-1eccf636f4f2"
}
1.3 响应示例
{
"code": 0,
"message": "success",
"data": {
"task_id": "11ac5e0d-963d-4eb2-98e8-1eccf636f4f2",
"status": "retrying",
"message": "任务重试已启动",
"success": true
}
}
2. Dashboard页面集成
2.1 导入重试API
import { getProjectTaskList, retryTask } from '@/api/video_flow';
2.2 onRetryTask优化实现(避免页面刷新)
onRetryTask={async (taskId: string) => {
console.log('重试任务:', taskId);
try {
// 1. 乐观更新:立即更新任务状态为重试中
setDashboardData(prevData => {
if (!Array.isArray(prevData)) return prevData;
return prevData.map(task => {
if (task.task_id === taskId) {
return { ...task, task_status: 'RETRYING' };
}
// 检查子任务
if (task.sub_tasks && Array.isArray(task.sub_tasks)) {
const updatedSubTasks = task.sub_tasks.map(subTask =>
subTask.task_id === taskId
? { ...subTask, task_status: 'RETRYING' }
: subTask
);
return { ...task, sub_tasks: updatedSubTasks };
}
return task;
});
});
// 2. 调用重试任务API
const retryResponse = await retryTask({ task_id: taskId });
if (retryResponse.code === 0 && retryResponse.data?.success) {
console.log('任务重试成功:', retryResponse.data);
// 3. 使用静默刷新,避免页面重载
setTimeout(() => {
refreshDataSilently();
}, 2000);
} else {
console.error('任务重试失败:', retryResponse.message);
setTimeout(() => {
refreshDataSilently();
}, 1000);
}
} catch (error) {
console.error('重试任务时发生错误:', error);
setTimeout(() => {
refreshDataSilently();
}, 1000);
}
}}
3. NetworkTimeline组件增强
3.1 状态管理
const [retryingTasks, setRetryingTasks] = useState<Set<string>>(new Set());
3.2 优化的重试处理函数
const handleRetryTask = async (taskId: string) => {
if (onRetryTask) {
try {
// 添加到重试中的任务集合
setRetryingTasks(prev => new Set(prev).add(taskId));
// 调用父组件的重试逻辑(包含乐观更新)
await onRetryTask(taskId);
console.log(`任务 ${taskId} 重试请求已发送`);
} catch (error) {
console.error('重试任务失败:', error);
// 重试失败时立即移除重试状态
setRetryingTasks(prev => {
const newSet = new Set(prev);
newSet.delete(taskId);
return newSet;
});
}
// 注意:成功情况下不立即移除重试状态
// 重试状态会在数据刷新后自然消失
}
};
// 自动清理重试状态
useEffect(() => {
if (retryingTasks.size === 0) return;
const currentRetryingTaskIds = Array.from(retryingTasks);
const tasksToRemove: string[] = [];
currentRetryingTaskIds.forEach(taskId => {
const isStillRetrying = tasks.some(task => {
if (task.task_id === taskId && task.task_status === 'RETRYING') {
return true;
}
if (task.sub_tasks && Array.isArray(task.sub_tasks)) {
return task.sub_tasks.some(subTask =>
subTask.task_id === taskId && subTask.task_status === 'RETRYING'
);
}
return false;
});
if (!isStillRetrying) {
tasksToRemove.push(taskId);
}
});
if (tasksToRemove.length > 0) {
setRetryingTasks(prev => {
const newSet = new Set(prev);
tasksToRemove.forEach(taskId => newSet.delete(taskId));
return newSet;
});
}
}, [tasks, retryingTasks]);
3.3 UI增强
{/* 重试按钮 - 列表视图 */}
<button
onClick={(e) => {
e.stopPropagation();
handleRetryTask(task.id);
}}
disabled={retryingTasks.has(task.id)}
className={cn(
"p-1 hover:bg-gray-700 rounded transition-colors",
retryingTasks.has(task.id)
? "text-yellow-400 cursor-not-allowed"
: "text-blue-400 hover:text-blue-300"
)}
title={retryingTasks.has(task.id) ? "重试中..." : "重试任务"}
>
<RefreshCw className={cn(
"w-4 h-4",
retryingTasks.has(task.id) && "animate-spin"
)} />
</button>
{/* 重试按钮 - 时间线视图 */}
<button
onClick={() => handleRetryTask(task.id)}
disabled={retryingTasks.has(task.id)}
className={cn(
"mt-2 flex items-center gap-1 px-2 py-1 text-white text-[10px] rounded transition-colors",
retryingTasks.has(task.id)
? "bg-yellow-600 cursor-not-allowed"
: "bg-blue-600 hover:bg-blue-700"
)}
>
<RefreshCw className={cn(
"w-3 h-3",
retryingTasks.has(task.id) && "animate-spin"
)} />
{retryingTasks.has(task.id) ? "重试中..." : "重试任务"}
</button>
🚀 核心优化要点
1. 避免页面刷新问题
问题: 原始实现使用 fetchInitialData() 导致整个页面重新加载,用户体验差
解决方案:
- 使用乐观更新立即反馈用户操作
- 采用
refreshDataSilently()进行静默数据刷新 - 保持现有的智能轮询机制不受干扰
2. 乐观更新策略
// 立即更新UI状态,给用户即时反馈
setDashboardData(prevData => {
return prevData.map(task => {
if (task.task_id === taskId) {
return { ...task, task_status: 'RETRYING' };
}
return task;
});
});
3. 智能状态管理
- 双重状态跟踪: 组件内部
retryingTasks+ 数据状态RETRYING - 自动状态清理: 当任务不再是RETRYING状态时自动清理UI状态
- 防重复操作: 重试过程中禁用按钮,防止重复点击
4. 渐进式数据更新
- 即时反馈: 点击重试立即显示重试状态
- API调用: 后台调用重试接口
- 静默刷新: 延迟获取真实状态,不影响用户体验
- 状态同步: 自动清理过期的UI状态
🎯 功能特性
1. 智能重试机制
- 状态检测: 只有失败状态的任务才显示重试按钮
- 防重复点击: 重试过程中按钮变为禁用状态
- 视觉反馈: 重试时显示旋转动画和"重试中..."文本
2. 错误处理
- API错误处理: 捕获并记录重试API调用失败
- 网络错误处理: 处理网络连接问题
- 状态恢复: 无论成功失败都会刷新数据获取最新状态
3. 用户体验优化
- 即时反馈: 点击重试按钮立即显示加载状态
- 状态同步: 重试后自动刷新任务列表
- 延迟恢复: 给用户足够的视觉反馈时间
🔧 技术实现细节
1. 状态枚举扩展
// app/model/enums.ts
export enum TaskStatusEnum {
PENDING = "pending",
PROCESSING = "processing",
COMPLETED = "completed",
FAILED = "failed",
RETRYING = "retrying", // 新增
CANCELLED = "cancelled", // 新增
}
2. 状态码映射
function getTaskStatusCode(status: string): number {
const statusMap: Record<string, number> = {
'RETRYING': 202, // 重试状态映射为进行中
// ... 其他状态
};
return statusMap[status] || 200;
}
3. 状态文本映射
function getTaskStatusText(status: string): string {
const statusTextMap: Record<string, string> = {
'RETRYING': '重试中', // 新增重试状态文本
// ... 其他状态
};
return statusTextMap[status] || status;
}
🚀 使用流程
1. 用户操作流程
- 用户在Dashboard页面查看任务列表
- 发现失败的任务(红色状态显示)
- 点击任务旁边的重试按钮(蓝色刷新图标)
- 按钮变为黄色并显示旋转动画
- 系统调用重试API
- 1秒后自动刷新任务列表
- 2秒后重试按钮恢复正常状态
2. 系统处理流程
- 前端调用
retryTaskAPI - 后端接收重试请求并启动任务重试
- 返回重试状态给前端
- 前端更新UI状态并刷新数据
- 后端异步处理任务重试
- 下次数据刷新时获取最新任务状态
📊 监控和日志
1. 前端日志
console.log('重试任务:', taskId);
console.log('任务重试成功:', retryResponse.data);
console.error('重试任务失败:', retryResponse.message);
console.error('重试任务时发生错误:', error);
2. 状态追踪
- 重试按钮状态变化
- API调用成功/失败
- 数据刷新时机
- 用户交互行为
🎨 UI/UX设计原则
1. 一致性
- 重试按钮在列表视图和时间线视图中保持一致的交互逻辑
- 状态颜色和图标使用统一的设计语言
2. 反馈性
- 立即的视觉反馈(按钮状态变化)
- 清晰的状态提示(重试中、重试任务)
- 动画效果增强用户感知
3. 容错性
- 防止重复点击
- 优雅的错误处理
- 自动状态恢复
🔍 测试建议
1. 功能测试
- 测试重试按钮的显示条件(只在失败任务上显示)
- 测试重试过程中的UI状态变化
- 测试重试成功后的数据刷新
2. 异常测试
- 测试网络错误时的处理
- 测试API返回错误时的处理
- 测试重复点击的防护机制
3. 性能测试
- 测试大量任务时的重试性能
- 测试并发重试的处理
- 测试内存泄漏问题
📈 未来扩展
1. 批量重试
- 支持选择多个失败任务进行批量重试
- 批量重试的进度显示
2. 重试策略
- 支持自动重试机制
- 可配置的重试次数和间隔
3. 重试历史
- 记录任务的重试历史
- 显示重试次数和时间
🎯 总结
通过以上设计和实现,任务重试功能已经完全集成到Dashboard页面中,提供了:
- 完整的API集成: 从接口定义到错误处理的完整链路
- 优秀的用户体验: 直观的UI反馈和流畅的交互
- 健壮的错误处理: 多层次的异常捕获和恢复机制
- 可扩展的架构: 支持未来功能扩展和优化
该实现充分体现了现代前端开发的最佳实践,是一个高质量的企业级功能实现。