diff --git a/api/video_flow.ts b/api/video_flow.ts index 990c629..194d2fe 100644 --- a/api/video_flow.ts +++ b/api/video_flow.ts @@ -823,6 +823,116 @@ export const retryTask = async (request: { return post("/task/retry_task", request); }; +/** + * 任务统计数据接口定义 + */ +export interface TaskStatistics { + /** 项目ID */ + project_id: string; + /** 项目名称 */ + project_name?: string; + /** 总任务数 */ + total_tasks: number; + /** 各状态任务统计 */ + status_stats: { + /** 已完成任务数 */ + completed: number; + /** 进行中任务数 */ + in_progress: number; + /** 等待中任务数 */ + pending: number; + /** 失败任务数 */ + failed: number; + /** 阻塞任务数 */ + blocked: number; + }; + /** 成功率 */ + success_rate: number; + /** 平均执行时间(秒) */ + avg_execution_time: number; + /** 最长执行时间(秒) */ + max_execution_time: number; + /** 最短执行时间(秒) */ + min_execution_time: number; + /** 最近更新时间 */ + last_updated: string; +} + +/** + * 消息队列状态接口定义 + */ +export interface MessageQueueStatus { + /** 队列名称 */ + queue_name: string; + /** 队列状态 */ + status: 'healthy' | 'warning' | 'error'; + /** 队列中消息数量 */ + message_count: number; + /** 消费者数量 */ + consumer_count: number; + /** 最后心跳时间 */ + last_heartbeat: string; + /** 错误信息 */ + error_message?: string; +} + +/** + * 获取项目任务统计数据 + * @param request - 请求参数 + * @returns Promise> + */ +export const getProjectTaskStatistics = async (request: { + /** 项目ID */ + project_id: string; + /** 时间范围(小时),默认24小时 */ + time_range?: number; +}): Promise> => { + return post("/task/get_project_statistics", request); +}; + +/** + * 获取多个项目的任务统计数据 + * @param request - 请求参数 + * @returns Promise> + */ +export const getMultiProjectTaskStatistics = async (request: { + /** 项目ID列表 */ + project_ids: string[]; + /** 时间范围(小时),默认24小时 */ + time_range?: number; +}): Promise> => { + return post("/task/get_multi_project_statistics", request); +}; + +/** + * 获取消息队列状态 + * @returns Promise> + */ +export const getMessageQueueStatus = async (): Promise> => { + return post("/system/get_queue_status", {}); +}; + +/** + * 重启消息队列 + * @param request - 请求参数 + * @returns Promise> + */ +export const restartMessageQueue = async (request: { + /** 队列名称 */ + queue_name: string; +}): Promise> => { + return post("/system/restart_queue", request); +}; + export const resumePlanFlow = async (request: { /** 项目ID */ project_id: string; diff --git a/app/task-monitor/page.tsx b/app/task-monitor/page.tsx new file mode 100644 index 0000000..81910cf --- /dev/null +++ b/app/task-monitor/page.tsx @@ -0,0 +1,289 @@ +'use client'; + +import React, { useState, useEffect, useRef } from 'react'; +import { useSearchParams } from 'next/navigation'; +import { + getProjectTaskStatistics, + getMultiProjectTaskStatistics, + getMessageQueueStatus, + restartMessageQueue, + TaskStatistics, + MessageQueueStatus +} from '@/api/video_flow'; +import { cn } from '@/public/lib/utils'; +import TaskStatisticsCards from '@/components/task-monitor/task-statistics-cards'; +import TaskStatusChart from '@/components/task-monitor/task-status-chart'; +import TaskTimelineChart from '@/components/task-monitor/task-timeline-chart'; +import MessageQueuePanel from '@/components/task-monitor/message-queue-panel'; +import TaskDetailTable from '@/components/task-monitor/task-detail-table'; +import ProjectSelector from '@/components/task-monitor/project-selector'; + +export default function TaskMonitorPage() { + const searchParams = useSearchParams(); + const initialProjectId = searchParams.get('project_id') || 'bc43bc81-c781-4caa-8256-9710fd5bee80'; + + // 状态管理 + const [selectedProjectIds, setSelectedProjectIds] = useState([initialProjectId]); + const [timeRange, setTimeRange] = useState(24); // 24小时 + const [taskStatistics, setTaskStatistics] = useState([]); + const [queueStatus, setQueueStatus] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [lastUpdateTime, setLastUpdateTime] = useState(null); + const [autoRefresh, setAutoRefresh] = useState(true); + + // 刷新控制 + const refreshIntervalRef = useRef(null); + + // 获取任务统计数据 + const fetchTaskStatistics = async () => { + try { + setError(null); + + if (selectedProjectIds.length === 1) { + const response = await getProjectTaskStatistics({ + project_id: selectedProjectIds[0], + time_range: timeRange + }); + + if (response.code === 0 && response.data) { + setTaskStatistics([response.data]); + } else { + throw new Error(response.message || '获取任务统计失败'); + } + } else if (selectedProjectIds.length > 1) { + const response = await getMultiProjectTaskStatistics({ + project_ids: selectedProjectIds, + time_range: timeRange + }); + + if (response.code === 0 && response.data) { + setTaskStatistics(response.data); + } else { + throw new Error(response.message || '获取多项目统计失败'); + } + } + + setLastUpdateTime(new Date()); + } catch (err: any) { + console.error('获取任务统计失败:', err); + setError(err.message || '获取数据失败'); + } + }; + + // 获取消息队列状态 + const fetchQueueStatus = async () => { + try { + const response = await getMessageQueueStatus(); + + if (response.code === 0 && response.data) { + setQueueStatus(response.data); + } else { + console.warn('获取队列状态失败:', response.message); + } + } catch (err: any) { + console.error('获取队列状态失败:', err); + } + }; + + // 初始化数据加载 + const initializeData = async () => { + setLoading(true); + try { + await Promise.all([ + fetchTaskStatistics(), + fetchQueueStatus() + ]); + } finally { + setLoading(false); + } + }; + + // 手动刷新 + const handleRefresh = async () => { + await initializeData(); + }; + + // 重启消息队列 + const handleRestartQueue = async (queueName: string) => { + try { + const response = await restartMessageQueue({ queue_name: queueName }); + + if (response.code === 0 && response.data?.success) { + console.log('队列重启成功:', response.data); + // 延迟刷新队列状态 + setTimeout(() => { + fetchQueueStatus(); + }, 2000); + } else { + throw new Error(response.message || '重启失败'); + } + } catch (err: any) { + console.error('重启队列失败:', err); + alert(`重启队列失败: ${err.message}`); + } + }; + + // 项目选择变化 + const handleProjectChange = (projectIds: string[]) => { + setSelectedProjectIds(projectIds); + }; + + // 时间范围变化 + const handleTimeRangeChange = (range: number) => { + setTimeRange(range); + }; + + // 自动刷新控制 + useEffect(() => { + if (autoRefresh) { + refreshIntervalRef.current = setInterval(() => { + fetchTaskStatistics(); + fetchQueueStatus(); + }, 30000); // 30秒刷新一次 + } else { + if (refreshIntervalRef.current) { + clearInterval(refreshIntervalRef.current); + refreshIntervalRef.current = null; + } + } + + return () => { + if (refreshIntervalRef.current) { + clearInterval(refreshIntervalRef.current); + } + }; + }, [autoRefresh, selectedProjectIds, timeRange]); + + // 初始化和依赖变化时重新加载数据 + useEffect(() => { + initializeData(); + }, [selectedProjectIds, timeRange]); + + if (loading) { + return ( +
+
+
+

加载任务监控数据...

+
+
+ ); + } + + return ( +
+ {/* 顶部导航栏 */} +
+
+
+
+
+

任务监控中心

+
+
+ 实时监控项目任务状态和系统健康度 +
+
+ + {/* 控制面板 */} +
+ {/* 自动刷新开关 */} + + + {/* 手动刷新按钮 */} + + + {/* 最后更新时间 */} + {lastUpdateTime && ( +
+ 更新于 {lastUpdateTime.toLocaleTimeString()} +
+ )} +
+
+
+ + {/* 错误提示 */} + {error && ( +
+
+ + + + {error} +
+
+ )} + + {/* 主要内容区域 */} +
+ {/* 项目选择和时间范围控制 */} +
+ + +
+ + +
+
+ + {/* 核心指标卡片 */} + + + {/* 数据可视化和控制面板 */} +
+ {/* 图表区域 */} +
+ + +
+ + {/* 控制面板 */} +
+ +
+
+ + {/* 任务详情表格 */} + +
+
+ ); +} diff --git a/components/SmartChatBox/InputBar.tsx b/components/SmartChatBox/InputBar.tsx index ea3cde8..0f70fe0 100644 --- a/components/SmartChatBox/InputBar.tsx +++ b/components/SmartChatBox/InputBar.tsx @@ -268,7 +268,7 @@ export function InputBar({ onSend, setVideoPreview, initialVideoUrl, initialVide