fix(app): 修复WebSocket连接关闭时的重复轮询和toast显示问题

This commit is contained in:
cnlimiter
2026-03-15 16:25:06 +08:00
parent 23a6c9be96
commit 75f5bb439b

View File

@@ -12,6 +12,12 @@ let accountsPollingInterval = null;
let isBatchMode = false; let isBatchMode = false;
let isOutlookBatchMode = false; let isOutlookBatchMode = false;
let outlookAccounts = []; let outlookAccounts = [];
let taskCompleted = false; // 标记任务是否已完成
let batchCompleted = false; // 标记批量任务是否已完成
let taskFinalStatus = null; // 保存任务的最终状态
let batchFinalStatus = null; // 保存批量任务的最终状态
let displayedLogs = new Set(); // 用于日志去重
let toastShown = false; // 标记是否已显示过 toast
let availableServices = { let availableServices = {
tempmail: { available: true, services: [] }, tempmail: { available: true, services: [] },
outlook: { available: false, services: [] }, outlook: { available: false, services: [] },
@@ -91,6 +97,7 @@ function initEventListeners() {
// 清空日志 // 清空日志
elements.clearLogBtn.addEventListener('click', () => { elements.clearLogBtn.addEventListener('click', () => {
elements.consoleLog.innerHTML = '<div class="log-line info">[系统] 日志已清空</div>'; elements.consoleLog.innerHTML = '<div class="log-line info">[系统] 日志已清空</div>';
displayedLogs.clear(); // 清空日志去重集合
}); });
// 刷新账号列表 // 刷新账号列表
@@ -294,6 +301,12 @@ async function handleStartRegistration(e) {
// 单次注册 // 单次注册
async function handleSingleRegistration(requestData) { async function handleSingleRegistration(requestData) {
// 重置任务状态
taskCompleted = false;
taskFinalStatus = null;
displayedLogs.clear(); // 清空日志去重集合
toastShown = false; // 重置 toast 标志
addLog('info', '[系统] 正在启动注册任务...'); addLog('info', '[系统] 正在启动注册任务...');
try { try {
@@ -345,19 +358,30 @@ function connectWebSocket(taskUuid) {
// 检查是否完成 // 检查是否完成
if (['completed', 'failed', 'cancelled', 'cancelling'].includes(data.status)) { if (['completed', 'failed', 'cancelled', 'cancelling'].includes(data.status)) {
// 保存最终状态,用于 onclose 判断
taskFinalStatus = data.status;
taskCompleted = true;
// 断开 WebSocket异步操作
disconnectWebSocket(); disconnectWebSocket();
// 任务完成后再重置按钮
resetButtons(); resetButtons();
if (data.status === 'completed') { // 只显示一次 toast
addLog('success', '[成功] 注册成功!'); if (!toastShown) {
toast.success('注册成功!'); toastShown = true;
// 刷新账号列表 if (data.status === 'completed') {
loadRecentAccounts(); addLog('success', '[成功] 注册成功!');
} else if (data.status === 'failed') { toast.success('注册成功!');
addLog('error', '[错误] 注册失败'); // 刷新账号列表
toast.error('注册失败'); loadRecentAccounts();
} else if (data.status === 'cancelled' || data.status === 'cancelling') { } else if (data.status === 'failed') {
addLog('warning', '[警告] 任务已取消'); addLog('error', '[错误] 注册失败');
toast.error('注册失败');
} else if (data.status === 'cancelled' || data.status === 'cancelling') {
addLog('warning', '[警告] 任务已取消');
}
} }
} }
} else if (data.type === 'pong') { } else if (data.type === 'pong') {
@@ -369,8 +393,12 @@ function connectWebSocket(taskUuid) {
console.log('WebSocket 连接关闭:', event.code); console.log('WebSocket 连接关闭:', event.code);
stopWebSocketHeartbeat(); stopWebSocketHeartbeat();
// 如果任务仍在运行,切换到轮询 // 只有在任务未完成且最终状态不是完成状态时才切换到轮询
if (currentTask && ['pending', 'running'].includes(currentTask.status)) { // 使用 taskFinalStatus 而不是 currentTask.status,因为 currentTask 可能已被重置
const shouldPoll = !taskCompleted &&
taskFinalStatus === null; // 如果 taskFinalStatus 有值,说明任务已完成
if (shouldPoll && currentTask) {
console.log('切换到轮询模式'); console.log('切换到轮询模式');
useWebSocket = false; useWebSocket = false;
startLogPolling(currentTask.task_uuid); startLogPolling(currentTask.task_uuid);
@@ -428,6 +456,12 @@ function cancelViaWebSocket() {
// 批量注册 // 批量注册
async function handleBatchRegistration(requestData) { async function handleBatchRegistration(requestData) {
// 重置批量任务状态
batchCompleted = false;
batchFinalStatus = null;
displayedLogs.clear(); // 清空日志去重集合
toastShown = false; // 重置 toast 标志
const count = parseInt(elements.batchCount.value) || 5; const count = parseInt(elements.batchCount.value) || 5;
const intervalMin = parseInt(elements.intervalMin.value) || 5; const intervalMin = parseInt(elements.intervalMin.value) || 5;
const intervalMax = parseInt(elements.intervalMax.value) || 30; const intervalMax = parseInt(elements.intervalMax.value) || 30;
@@ -546,16 +580,20 @@ function startLogPolling(taskUuid) {
stopLogPolling(); stopLogPolling();
resetButtons(); resetButtons();
if (data.status === 'completed') { // 只显示一次 toast
addLog('success', '[成功] 注册成功!'); if (!toastShown) {
toast.success('注册成功!'); toastShown = true;
// 刷新账号列表 if (data.status === 'completed') {
loadRecentAccounts(); addLog('success', '[成功] 注册成功!');
} else if (data.status === 'failed') { toast.success('注册成功!');
addLog('error', '[错误] 注册失败'); // 刷新账号列表
toast.error('注册失败'); loadRecentAccounts();
} else if (data.status === 'cancelled') { } else if (data.status === 'failed') {
addLog('warning', '[警告] 任务已取消'); addLog('error', '[错误] 注册失败');
toast.error('注册失败');
} else if (data.status === 'cancelled') {
addLog('warning', '[警告] 任务已取消');
}
} }
} }
} catch (error) { } catch (error) {
@@ -584,13 +622,17 @@ function startBatchPolling(batchId) {
stopBatchPolling(); stopBatchPolling();
resetButtons(); resetButtons();
addLog('info', `[完成] 批量任务完成!成功: ${data.success}, 失败: ${data.failed}`); // 只显示一次 toast
if (data.success > 0) { if (!toastShown) {
toast.success(`批量注册完成,成功 ${data.success}`); toastShown = true;
// 刷新账号列表 addLog('info', `[完成] 批量任务完成!成功: ${data.success}, 失败: ${data.failed}`);
loadRecentAccounts(); if (data.success > 0) {
} else { toast.success(`批量注册完成,成功 ${data.success}`);
toast.warning('批量注册完成,但没有成功注册任何账号'); // 刷新账号列表
loadRecentAccounts();
} else {
toast.warning('批量注册完成,但没有成功注册任何账号');
}
} }
} }
} catch (error) { } catch (error) {
@@ -736,6 +778,20 @@ function startAccountsPolling() {
// 添加日志 // 添加日志
function addLog(type, message) { function addLog(type, message) {
// 日志去重:使用消息内容的 hash 作为键
const logKey = `${type}:${message}`;
if (displayedLogs.has(logKey)) {
return; // 已经显示过,跳过
}
displayedLogs.add(logKey);
// 限制去重集合大小,避免内存泄漏
if (displayedLogs.size > 1000) {
// 清空一半的记录
const keys = Array.from(displayedLogs);
keys.slice(0, 500).forEach(k => displayedLogs.delete(k));
}
const line = document.createElement('div'); const line = document.createElement('div');
line.className = `log-line ${type}`; line.className = `log-line ${type}`;
@@ -783,6 +839,12 @@ function resetButtons() {
currentTask = null; currentTask = null;
currentBatch = null; currentBatch = null;
isBatchMode = false; isBatchMode = false;
// 重置完成标志
taskCompleted = false;
batchCompleted = false;
// 重置最终状态标志
taskFinalStatus = null;
batchFinalStatus = null;
// 断开 WebSocket // 断开 WebSocket
disconnectWebSocket(); disconnectWebSocket();
disconnectBatchWebSocket(); disconnectBatchWebSocket();
@@ -869,6 +931,12 @@ function deselectAllOutlookAccounts() {
// 处理 Outlook 批量注册 // 处理 Outlook 批量注册
async function handleOutlookBatchRegistration() { async function handleOutlookBatchRegistration() {
// 重置批量任务状态
batchCompleted = false;
batchFinalStatus = null;
displayedLogs.clear(); // 清空日志去重集合
toastShown = false; // 重置 toast 标志
// 获取选中的账户 // 获取选中的账户
const selectedIds = []; const selectedIds = [];
document.querySelectorAll('.outlook-account-checkbox:checked').forEach(cb => { document.querySelectorAll('.outlook-account-checkbox:checked').forEach(cb => {
@@ -964,22 +1032,33 @@ function connectBatchWebSocket(batchId) {
// 检查是否完成 // 检查是否完成
if (['completed', 'failed', 'cancelled', 'cancelling'].includes(data.status)) { if (['completed', 'failed', 'cancelled', 'cancelling'].includes(data.status)) {
// 保存最终状态,用于 onclose 判断
batchFinalStatus = data.status;
batchCompleted = true;
// 断开 WebSocket异步操作
disconnectBatchWebSocket(); disconnectBatchWebSocket();
// 任务完成后再重置按钮
resetButtons(); resetButtons();
if (data.status === 'completed') { // 只显示一次 toast
addLog('success', `[完成] Outlook 批量任务完成!成功: ${data.success}, 失败: ${data.failed}, 跳过: ${data.skipped || 0}`); if (!toastShown) {
if (data.success > 0) { toastShown = true;
toast.success(`Outlook 批量注册完成,成功 ${data.success}`); if (data.status === 'completed') {
loadRecentAccounts(); addLog('success', `[完成] Outlook 批量任务完成!成功: ${data.success}, 失败: ${data.failed}, 跳过: ${data.skipped || 0}`);
} else { if (data.success > 0) {
toast.warning('Outlook 批量注册完成,但没有成功注册任何账号'); toast.success(`Outlook 批量注册完成,成功 ${data.success}`);
loadRecentAccounts();
} else {
toast.warning('Outlook 批量注册完成,但没有成功注册任何账号');
}
} else if (data.status === 'failed') {
addLog('error', '[错误] 批量任务执行失败');
toast.error('批量任务执行失败');
} else if (data.status === 'cancelled' || data.status === 'cancelling') {
addLog('warning', '[警告] 批量任务已取消');
} }
} else if (data.status === 'failed') {
addLog('error', '[错误] 批量任务执行失败');
toast.error('批量任务执行失败');
} else if (data.status === 'cancelled' || data.status === 'cancelling') {
addLog('warning', '[警告] 批量任务已取消');
} }
} }
} else if (data.type === 'pong') { } else if (data.type === 'pong') {
@@ -991,8 +1070,12 @@ function connectBatchWebSocket(batchId) {
console.log('批量任务 WebSocket 连接关闭:', event.code); console.log('批量任务 WebSocket 连接关闭:', event.code);
stopBatchWebSocketHeartbeat(); stopBatchWebSocketHeartbeat();
// 如果任务仍在运行,切换到轮询 // 只有在任务未完成且最终状态不是完成状态时才切换到轮询
if (currentBatch && !['completed', 'failed', 'cancelled'].includes(currentBatch.status)) { // 使用 batchFinalStatus 而不是 currentBatch.status因为 currentBatch 可能已被重置
const shouldPoll = !batchCompleted &&
batchFinalStatus === null; // 如果 batchFinalStatus 有值,说明任务已完成
if (shouldPoll && currentBatch) {
console.log('切换到轮询模式'); console.log('切换到轮询模式');
startOutlookBatchPolling(currentBatch.batch_id); startOutlookBatchPolling(currentBatch.batch_id);
} }
@@ -1075,12 +1158,16 @@ function startOutlookBatchPolling(batchId) {
stopBatchPolling(); stopBatchPolling();
resetButtons(); resetButtons();
addLog('info', `[完成] Outlook 批量任务完成!成功: ${data.success}, 失败: ${data.failed}, 跳过: ${data.skipped || 0}`); // 只显示一次 toast
if (data.success > 0) { if (!toastShown) {
toast.success(`Outlook 批量注册完成,成功 ${data.success}`); toastShown = true;
loadRecentAccounts(); addLog('info', `[完成] Outlook 批量任务完成!成功: ${data.success}, 失败: ${data.failed}, 跳过: ${data.skipped || 0}`);
} else { if (data.success > 0) {
toast.warning('Outlook 批量注册完成,但没有成功注册任何账号'); toast.success(`Outlook 批量注册完成,成功 ${data.success}`);
loadRecentAccounts();
} else {
toast.warning('Outlook 批量注册完成,但没有成功注册任何账号');
}
} }
} }
} catch (error) { } catch (error) {