优化任务列表性能和本地登录代理
This commit is contained in:
@@ -31,6 +31,7 @@ import type {
|
||||
MessageResponse,
|
||||
PageParams,
|
||||
PaginatedResponse,
|
||||
TaskSummary,
|
||||
} from './types';
|
||||
|
||||
export const huyaApi = {
|
||||
@@ -108,6 +109,7 @@ export const huyaApi = {
|
||||
// 列表轮询默认不带 base64 小程序码,避免每次 1MB+ 流量。
|
||||
params: batchId ? { batch_id: batchId, include_images: false } : { include_images: false },
|
||||
}),
|
||||
tasksSummary: () => api.get<TaskSummary, TaskSummary>('/huya/tasks/summary'),
|
||||
getTask: (taskId: number) =>
|
||||
api.get<HuyaTaskItem, HuyaTaskItem>(`/huya/tasks/${taskId}`),
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import api from './client';
|
||||
import type { BatchLoginResult, LoginTaskItem, MessageDeletedResponse, MessageResponse } from './types';
|
||||
import type { BatchLoginResult, LoginTaskItem, MessageDeletedResponse, MessageResponse, TaskSummary } from './types';
|
||||
|
||||
interface CreateBatchParams {
|
||||
account_ids: number[];
|
||||
@@ -15,6 +15,7 @@ export const loginApi = {
|
||||
api.post<BatchLoginResult, BatchLoginResult>('/login/batch', params),
|
||||
listTasks: (batch_id?: string) =>
|
||||
api.get<LoginTaskItem[], LoginTaskItem[]>('/login/tasks', { params: batch_id ? { batch_id } : {} }),
|
||||
tasksSummary: () => api.get<TaskSummary, TaskSummary>('/login/tasks/summary'),
|
||||
stop: (batch_id: string) => api.post<MessageResponse, MessageResponse>(`/login/stop/${batch_id}`),
|
||||
deleteTask: (id: number) => api.delete<MessageResponse, MessageResponse>(`/login/tasks/${id}`),
|
||||
deleteTasks: (ids: number[]) => api.delete<MessageDeletedResponse, MessageDeletedResponse>(`/login/tasks`, { params: { task_ids: ids.join(',') } }),
|
||||
|
||||
@@ -50,6 +50,13 @@ export interface BasicSummary {
|
||||
tag_count?: number;
|
||||
}
|
||||
|
||||
export interface TaskSummary {
|
||||
total: number;
|
||||
success: number;
|
||||
failed: number;
|
||||
status_counts: Record<string, number>;
|
||||
}
|
||||
|
||||
// ==================== Auth ====================
|
||||
|
||||
export interface LoginResult {
|
||||
|
||||
@@ -5,8 +5,6 @@ import {
|
||||
cookieApi,
|
||||
huyaApi,
|
||||
loginApi,
|
||||
type HuyaTaskItem,
|
||||
type LoginTaskItem,
|
||||
} from '../api/modules';
|
||||
import { usePermissions } from '../hooks/usePermissions';
|
||||
|
||||
@@ -47,10 +45,6 @@ const EMPTY_STATS: DashboardStats = {
|
||||
},
|
||||
};
|
||||
|
||||
function countFailed<T extends { status: string }>(items: T[]) {
|
||||
return items.filter((item) => ['failed', 'error'].includes(item.status)).length;
|
||||
}
|
||||
|
||||
function StatCard({
|
||||
title,
|
||||
value,
|
||||
@@ -98,10 +92,10 @@ export default function DashboardPage() {
|
||||
huyaRechargeGoodsResult,
|
||||
] = await Promise.allSettled([
|
||||
canViewDouyuAccounts ? accountApi.summary() : Promise.resolve(null),
|
||||
canViewDouyuTasks ? loginApi.listTasks() : Promise.resolve([]),
|
||||
canViewDouyuTasks ? loginApi.tasksSummary() : Promise.resolve(null),
|
||||
canViewCookies ? cookieApi.summary() : Promise.resolve(null),
|
||||
canViewHuyaAccounts ? huyaApi.accountsSummary() : Promise.resolve(null),
|
||||
canViewHuyaTasks ? huyaApi.listTasks() : Promise.resolve([]),
|
||||
canViewHuyaTasks ? huyaApi.tasksSummary() : Promise.resolve(null),
|
||||
canViewHuyaTasks ? huyaApi.listGoods() : Promise.resolve([]),
|
||||
canViewHuyaTasks ? huyaApi.listRechargeGoods() : Promise.resolve([]),
|
||||
]);
|
||||
@@ -109,26 +103,26 @@ export default function DashboardPage() {
|
||||
if (ignore) return;
|
||||
|
||||
const douyuAccounts = douyuAccountsResult.status === 'fulfilled' ? douyuAccountsResult.value : null;
|
||||
const douyuTasks = douyuTasksResult.status === 'fulfilled' ? douyuTasksResult.value : [];
|
||||
const douyuTasks = douyuTasksResult.status === 'fulfilled' ? douyuTasksResult.value : null;
|
||||
const cookies = cookiesResult.status === 'fulfilled' ? cookiesResult.value : null;
|
||||
const huyaAccounts = huyaAccountsResult.status === 'fulfilled' ? huyaAccountsResult.value : null;
|
||||
const huyaTasks = huyaTasksResult.status === 'fulfilled' ? huyaTasksResult.value : [];
|
||||
const huyaTasks = huyaTasksResult.status === 'fulfilled' ? huyaTasksResult.value : null;
|
||||
const huyaGoods = huyaGoodsResult.status === 'fulfilled' ? huyaGoodsResult.value : [];
|
||||
const huyaRechargeGoods = huyaRechargeGoodsResult.status === 'fulfilled' ? huyaRechargeGoodsResult.value : [];
|
||||
|
||||
setStats({
|
||||
douyu: {
|
||||
accounts: douyuAccounts?.total || 0,
|
||||
tasks: douyuTasks.length,
|
||||
success: douyuTasks.filter((task: LoginTaskItem) => task.status === 'success').length,
|
||||
failed: countFailed(douyuTasks),
|
||||
tasks: douyuTasks?.total || 0,
|
||||
success: douyuTasks?.success || 0,
|
||||
failed: douyuTasks?.failed || 0,
|
||||
cookies: cookies?.total || 0,
|
||||
},
|
||||
huya: {
|
||||
accounts: huyaAccounts?.total || 0,
|
||||
tasks: huyaTasks.length,
|
||||
success: huyaTasks.filter((task: HuyaTaskItem) => task.status === 'success').length,
|
||||
failed: countFailed(huyaTasks),
|
||||
tasks: huyaTasks?.total || 0,
|
||||
success: huyaTasks?.success || 0,
|
||||
failed: huyaTasks?.failed || 0,
|
||||
goods: huyaGoods.length,
|
||||
rechargeGoods: huyaRechargeGoods.length,
|
||||
},
|
||||
|
||||
@@ -302,6 +302,7 @@ export default function DouyuTasksPage({ handbook }: { handbook: HandbookKind })
|
||||
const v = Number(localStorage.getItem('douyu_task_account_page_size'));
|
||||
return [10, 20, 50, 100].includes(v) ? v : 20;
|
||||
});
|
||||
const tasksLoadingRef = useRef(false);
|
||||
|
||||
const [importOpen, setImportOpen] = useState(false);
|
||||
const [importPool, setImportPool] = useState<DouyuTaskAccountItem[]>([]);
|
||||
@@ -507,6 +508,8 @@ export default function DouyuTasksPage({ handbook }: { handbook: HandbookKind })
|
||||
}, [canConfig, isEsportsHandbook, workbenchIds]);
|
||||
|
||||
const loadTasks = useCallback(async () => {
|
||||
if (tasksLoadingRef.current) return;
|
||||
tasksLoadingRef.current = true;
|
||||
try {
|
||||
const data = await douyuApi.listTasks();
|
||||
setTasks(data);
|
||||
@@ -516,6 +519,8 @@ export default function DouyuTasksPage({ handbook }: { handbook: HandbookKind })
|
||||
}
|
||||
} catch {
|
||||
// 轮询失败不打扰操作
|
||||
} finally {
|
||||
tasksLoadingRef.current = false;
|
||||
}
|
||||
}, []);
|
||||
|
||||
|
||||
@@ -316,6 +316,7 @@ export default function HuyaTasksPage() {
|
||||
const notifiedPaidTaskIds = useRef<Set<number>>(new Set());
|
||||
const accountTableAreaRef = useRef<HTMLDivElement | null>(null);
|
||||
const [accountTableAreaHeight, setAccountTableAreaHeight] = useState(460);
|
||||
const tasksLoadingRef = useRef(false);
|
||||
const { logs, connected: wsConnected, connect: connectLogs } = useWebSocketLogs();
|
||||
const { can } = usePermissions();
|
||||
|
||||
@@ -455,6 +456,8 @@ export default function HuyaTasksPage() {
|
||||
}, [canConfig, form, rememberExistingPaymentQrcodes, rememberExistingQrcodes]);
|
||||
|
||||
const loadTasks = useCallback(async () => {
|
||||
if (tasksLoadingRef.current) return;
|
||||
tasksLoadingRef.current = true;
|
||||
try {
|
||||
const data = await huyaApi.listTasks();
|
||||
const nextTasks = mergeTaskImageCache(data, qrImageCacheRef.current);
|
||||
@@ -466,6 +469,8 @@ export default function HuyaTasksPage() {
|
||||
}
|
||||
} catch {
|
||||
// 轮询失败不打扰操作,下一轮继续刷新。
|
||||
} finally {
|
||||
tasksLoadingRef.current = false;
|
||||
}
|
||||
}, [rememberExistingPaymentQrcodes, rememberExistingQrcodes]);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useState, useMemo, useCallback } from 'react';
|
||||
import { useEffect, useState, useMemo, useCallback, useRef } from 'react';
|
||||
import {
|
||||
Table, Button, Select, Tag, Space, InputNumber, Tooltip, Popconfirm, theme, Modal, Form,
|
||||
} from 'antd';
|
||||
@@ -71,6 +71,7 @@ export default function LoginTasksPage() {
|
||||
return localStorage.getItem('login_api_strategy') || 'wgapi';
|
||||
});
|
||||
const [settingsOpen, setSettingsOpen] = useState(false);
|
||||
const tasksLoadingRef = useRef(false);
|
||||
|
||||
// 值变化时自动持久化
|
||||
useEffect(() => { localStorage.setItem('login_concurrency', String(concurrency)); }, [concurrency]);
|
||||
@@ -145,22 +146,32 @@ export default function LoginTasksPage() {
|
||||
}, [selectedTags, tagAccountMap, accounts]);
|
||||
|
||||
const loadTasks = useCallback(async () => {
|
||||
if (tasksLoadingRef.current) return;
|
||||
tasksLoadingRef.current = true;
|
||||
try {
|
||||
const data = await loginApi.listTasks(batchId || undefined);
|
||||
setTasks(data);
|
||||
} catch {
|
||||
// 忽略轮询失败,下一次定时刷新会继续尝试。
|
||||
} finally {
|
||||
tasksLoadingRef.current = false;
|
||||
}
|
||||
}, [batchId]);
|
||||
|
||||
const hasActiveTasks = useMemo(
|
||||
() => tasks.some((task) => ['pending', 'running'].includes(task.status)),
|
||||
[tasks],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
Promise.all([loadAccounts(), loadTasks()]);
|
||||
}, [loadAccounts, loadTasks]);
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setInterval(loadTasks, 3000);
|
||||
const intervalMs = hasActiveTasks || wsConnected || starting || batchId ? 3000 : 15000;
|
||||
const timer = setInterval(loadTasks, intervalMs);
|
||||
return () => clearInterval(timer);
|
||||
}, [loadTasks]);
|
||||
}, [batchId, hasActiveTasks, loadTasks, starting, wsConnected]);
|
||||
|
||||
// 共享的批量任务启动逻辑
|
||||
const startBatch = async (accountIds: number[], mode: BatchMode = 'login') => {
|
||||
|
||||
Reference in New Issue
Block a user