# 任务重试功能集成方案

## 📋 概述

本文档详细说明了如何将任务重试接口 `POST https://77.smartvideo.py.qikongjian.com/task/retry_task` 集成到Dashboard页面中，提供完整的用户体验和错误处理机制。

## 🏗️ 架构设计

### 1. API层设计

#### 1.1 接口定义
```typescript
// 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 请求示例
```json
{
  "task_id": "11ac5e0d-963d-4eb2-98e8-1eccf636f4f2"
}
```

#### 1.3 响应示例
```json
{
  "code": 0,
  "message": "success",
  "data": {
    "task_id": "11ac5e0d-963d-4eb2-98e8-1eccf636f4f2",
    "status": "retrying",
    "message": "任务重试已启动",
    "success": true
  }
}
```

### 2. Dashboard页面集成

#### 2.1 导入重试API
```typescript
import { getProjectTaskList, retryTask } from '@/api/video_flow';
```

#### 2.2 onRetryTask优化实现（避免页面刷新）
```typescript
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 状态管理
```typescript
const [retryingTasks, setRetryingTasks] = useState<Set<string>>(new Set());
```

#### 3.2 优化的重试处理函数
```typescript
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增强
```typescript
{/* 重试按钮 - 列表视图 */}
<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. 乐观更新策略
```typescript
// 立即更新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. 状态枚举扩展
```typescript
// app/model/enums.ts
export enum TaskStatusEnum {
  PENDING = "pending",
  PROCESSING = "processing", 
  COMPLETED = "completed",
  FAILED = "failed",
  RETRYING = "retrying",     // 新增
  CANCELLED = "cancelled",   // 新增
}
```

### 2. 状态码映射
```typescript
function getTaskStatusCode(status: string): number {
  const statusMap: Record<string, number> = {
    'RETRYING': 202,  // 重试状态映射为进行中
    // ... 其他状态
  };
  return statusMap[status] || 200;
}
```

### 3. 状态文本映射
```typescript
function getTaskStatusText(status: string): string {
  const statusTextMap: Record<string, string> = {
    'RETRYING': '重试中',  // 新增重试状态文本
    // ... 其他状态
  };
  return statusTextMap[status] || status;
}
```

## 🚀 使用流程

### 1. 用户操作流程
1. 用户在Dashboard页面查看任务列表
2. 发现失败的任务（红色状态显示）
3. 点击任务旁边的重试按钮（蓝色刷新图标）
4. 按钮变为黄色并显示旋转动画
5. 系统调用重试API
6. 1秒后自动刷新任务列表
7. 2秒后重试按钮恢复正常状态

### 2. 系统处理流程
1. 前端调用 `retryTask` API
2. 后端接收重试请求并启动任务重试
3. 返回重试状态给前端
4. 前端更新UI状态并刷新数据
5. 后端异步处理任务重试
6. 下次数据刷新时获取最新任务状态

## 📊 监控和日志

### 1. 前端日志
```typescript
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页面中，提供了：

1. **完整的API集成**: 从接口定义到错误处理的完整链路
2. **优秀的用户体验**: 直观的UI反馈和流畅的交互
3. **健壮的错误处理**: 多层次的异常捕获和恢复机制
4. **可扩展的架构**: 支持未来功能扩展和优化

该实现充分体现了现代前端开发的最佳实践，是一个高质量的企业级功能实现。
