import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { App } from 'antd'; import apiClient from '../utils/apiClient'; import { buildApiUrl, API_ENDPOINTS } from '../config/api'; const AUTO_REFRESH_INTERVAL = 30; export default function useAdminDashboardPage() { const { message, modal } = App.useApp(); const inFlightRef = useRef(false); const mountedRef = useRef(true); const [stats, setStats] = useState(null); const [onlineUsers, setOnlineUsers] = useState([]); const [usersList, setUsersList] = useState([]); const [tasks, setTasks] = useState([]); const [resources, setResources] = useState(null); const [loading, setLoading] = useState(true); const [taskLoading, setTaskLoading] = useState(false); const [lastUpdatedAt, setLastUpdatedAt] = useState(null); const [taskType, setTaskType] = useState('all'); const [taskStatus, setTaskStatus] = useState('all'); const [autoRefresh, setAutoRefresh] = useState(true); const [countdown, setCountdown] = useState(AUTO_REFRESH_INTERVAL); const [showMeetingModal, setShowMeetingModal] = useState(false); const [meetingDetails, setMeetingDetails] = useState(null); const [meetingLoading, setMeetingLoading] = useState(false); useEffect(() => { mountedRef.current = true; return () => { mountedRef.current = false; }; }, []); const fetchStats = useCallback(async () => { const response = await apiClient.get(buildApiUrl(API_ENDPOINTS.ADMIN.DASHBOARD_STATS)); if (response.code === '200') { setStats(response.data); } }, []); const fetchOnlineUsers = useCallback(async () => { const response = await apiClient.get(buildApiUrl(API_ENDPOINTS.ADMIN.ONLINE_USERS)); if (response.code === '200') { setOnlineUsers(response.data.users || []); } }, []); const fetchUsersList = useCallback(async () => { const response = await apiClient.get(buildApiUrl(API_ENDPOINTS.ADMIN.USER_STATS)); if (response.code === '200') { setUsersList(response.data.users || []); } }, []); const fetchTasks = useCallback(async () => { try { setTaskLoading(true); const params = new URLSearchParams(); if (taskType !== 'all') params.append('task_type', taskType); if (taskStatus !== 'all') params.append('status', taskStatus); params.append('limit', '20'); const response = await apiClient.get(buildApiUrl(`${API_ENDPOINTS.ADMIN.TASKS_MONITOR}?${params.toString()}`)); if (response.code === '200') { setTasks(response.data.tasks || []); } } finally { setTaskLoading(false); } }, [taskStatus, taskType]); const fetchResources = useCallback(async () => { const response = await apiClient.get(buildApiUrl(API_ENDPOINTS.ADMIN.SYSTEM_RESOURCES)); if (response.code === '200') { setResources(response.data); } }, []); const fetchAllData = useCallback(async ({ silent = false } = {}) => { if (inFlightRef.current) { return; } inFlightRef.current = true; try { if (!silent && mountedRef.current) { setLoading(true); } await Promise.all([fetchStats(), fetchOnlineUsers(), fetchUsersList(), fetchTasks(), fetchResources()]); if (mountedRef.current) { setLastUpdatedAt(new Date()); setCountdown(AUTO_REFRESH_INTERVAL); } } catch (error) { console.error('获取数据失败:', error); if (mountedRef.current && !silent) { message.error('加载数据失败,请稍后重试'); } } finally { inFlightRef.current = false; if (mountedRef.current && !silent) { setLoading(false); } } }, [fetchOnlineUsers, fetchResources, fetchStats, fetchTasks, fetchUsersList, message]); useEffect(() => { fetchAllData(); }, [fetchAllData]); useEffect(() => { if (!autoRefresh || showMeetingModal) { return undefined; } const timer = setInterval(() => { setCountdown((prev) => { if (prev <= 1) { fetchAllData({ silent: true }); return AUTO_REFRESH_INTERVAL; } return prev - 1; }); }, 1000); return () => clearInterval(timer); }, [autoRefresh, fetchAllData, showMeetingModal]); useEffect(() => { fetchTasks(); }, [fetchTasks]); const handleKickUser = (user) => { modal.confirm({ title: '踢出用户', content: `确定要踢出用户"${user.caption}"吗?该用户将被强制下线。`, okText: '确定', okType: 'danger', cancelText: '取消', onOk: async () => { try { const response = await apiClient.post(buildApiUrl(API_ENDPOINTS.ADMIN.KICK_USER(user.user_id))); if (response.code === '200') { message.success('用户已被踢出'); fetchOnlineUsers(); } } catch { message.error('踢出用户失败'); } }, }); }; const handleViewMeeting = async (meetingId) => { setMeetingLoading(true); setShowMeetingModal(true); try { const response = await apiClient.get(buildApiUrl(API_ENDPOINTS.MEETINGS.DETAIL(meetingId))); if (response.code === '200') { setMeetingDetails(response.data); } } catch { message.error('获取会议详情失败'); } finally { setMeetingLoading(false); } }; const handleDownloadTranscript = async (meetingId) => { try { const response = await apiClient.get(buildApiUrl(`/api/meetings/${meetingId}/transcript`)); if (response.code === '200') { const dataStr = JSON.stringify(response.data, null, 2); const blob = new Blob([dataStr], { type: 'application/json' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = `transcript_${meetingId}.json`; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); } } catch { message.error('下载失败'); } }; const closeMeetingModal = () => { setShowMeetingModal(false); setMeetingDetails(null); }; const taskCompletionRate = useMemo(() => { const all = tasks.length || 1; const completed = tasks.filter((item) => item.status === 'completed').length; return Math.round((completed / all) * 100); }, [tasks]); return { stats, onlineUsers, usersList, tasks, resources, loading, taskLoading, lastUpdatedAt, taskType, setTaskType, taskStatus, setTaskStatus, autoRefresh, setAutoRefresh, countdown, showMeetingModal, meetingDetails, meetingLoading, fetchAllData, fetchOnlineUsers, handleKickUser, handleViewMeeting, handleDownloadTranscript, closeMeetingModal, taskCompletionRate, }; }