// ==================== 自动管理器工厂 ==================== /** * 创建一个自动获取管理器实例 * @param {string} source 'mexc' 或 'binance' * @param {number} intervalSec 自动获取间隔(秒) * @param {string} endpoint API 端点 * @param {Object} state 全局状态对象(AppState) * @param {Object} dom DOM 元素引用 * @returns {Object} 管理器对象 { start, stop, isRunning, getStatusText } */ function createAutoManager(source, intervalSec, endpoint, state, dom) { // 私有变量(闭包内,不暴露) let running = false; let intervalId = null; // 周期定时器 let countdownTimerId = null; // 倒计时定时器 let currentRound = 1; let currentCountdown = intervalSec; let tempMsg = ''; let tempExpire = 0; // 辅助:获取状态栏 DOM 元素 const statusEl = (source === 'mexc') ? dom.autoStatusMEXC : dom.autoStatusBinance; // 对应的自动按钮(用于禁用/启用) const autoBtn = (source === 'mexc') ? dom.mexcAutoBtn : dom.binanceAutoBtn; // 更新状态栏显示 function updateDisplay() { if (!statusEl) return; if (!running) { statusEl.textContent = '⚫ 已停止'; return; } let displayText = `⏳ 本次第${currentRound}轮,下轮获取: ${currentCountdown}s`; const now = Date.now(); if (tempMsg && now < tempExpire) { displayText += ` ${tempMsg}`; } else { tempMsg = ''; } statusEl.textContent = displayText; } // 设置临时消息(自动保存成功时调用) function setTempMessage(msg, durationMs = 5000) { tempMsg = msg; tempExpire = Date.now() + durationMs; updateDisplay(); setTimeout(() => { if (tempMsg === msg) { tempMsg = ''; updateDisplay(); } }, durationMs); } // 停止倒计时定时器 function stopCountdownTimer() { if (countdownTimerId) { clearInterval(countdownTimerId); countdownTimerId = null; } } // 启动倒计时(每秒更新) function startCountdown() { stopCountdownTimer(); if (!running) return; // 确保倒计时从当前值开始(如果已经是0则重置为间隔) if (currentCountdown <= 0) { currentCountdown = intervalSec; } updateDisplay(); countdownTimerId = setInterval(() => { if (running && currentCountdown > 0) { currentCountdown--; updateDisplay(); } else if (!running) { stopCountdownTimer(); } }, 1000); } // 执行一轮获取(内部调用,不暴露) async function performFetch() { // 检查并发锁(与手动共享) const fetchingKey = (source === 'mexc') ? 'mexc' : 'binance'; if (state.auto[fetchingKey].fetching) { console.log(`[自动获取] ${source.toUpperCase()} 已有请求进行中,跳过本轮`); return false; } state.auto[fetchingKey].fetching = true; if (dom.infoCard) { dom.infoCard.classList.add('hide'); dom.infoCard.style.display = 'none'; } const startTime = Date.now(); try { const json = await requestKlines(endpoint); const elapsed = ((Date.now() - startTime) / 1000).toFixed(1); state.cachedData = json; state.activeSource = source; state.cachedRealtimeIndicators = null; state.cachedReportData = null; state.cachedResultReportText = null; // 可选 try { await preloadDynamicIndicators(state, dom); } catch (err) { console.warn(`[自动获取] ${source.toUpperCase()} 动态指标预加载失败:`, err); } // ✅ 修改这里:传入 type='auto' 和 exchange=source const saveResult = await saveAllDataToServer(state, { type: 'auto', exchange: source }); if (saveResult && saveResult.success) { setTempMessage(`✅ 第${currentRound}轮 保存成功 (耗时${elapsed}s)`); } else { setTempMessage(`⚠️ 第${currentRound}轮 保存失败`, 3000); } if (state.currentMode !== null && state.cachedData) { if (typeof window.updateViewFromPart2 === 'function') { window.updateViewFromPart2(state, dom); } else { console.warn('[自动获取] updateView 函数尚未加载,稍后重试'); } } console.log(`[自动获取] ${source.toUpperCase()} 第${currentRound}轮完成,耗时 ${elapsed}s`); return true; } catch (err) { console.error(`[自动获取] ${source.toUpperCase()} 第${currentRound}轮失败:`, err.message); setTempMessage(`❌ 第${currentRound}轮 请求失败: ${err.message}`, 4000); return false; } finally { state.auto[fetchingKey].fetching = false; } } // 启动自动获取 async function start() { if (running) { console.log(`[自动获取] ${source.toUpperCase()} 已在运行中`); return; } // 停止任何残留定时器 stop(); running = true; currentRound = 1; currentCountdown = intervalSec; tempMsg = ''; if (autoBtn) autoBtn.disabled = true; // 立即启动倒计时显示(不等待第一轮获取完成) startCountdown(); updateDisplay(); // 异步执行第一轮获取(不阻塞倒计时) performFetch().catch(err => console.error(`[自动获取] ${source.toUpperCase()} 首轮执行出错:`, err)); // 启动周期定时器(间隔 intervalSec 毫秒) intervalId = setInterval(async () => { if (running) { // 检查是否已有请求在进行中(避免重叠) const fetchingKey = (source === 'mexc') ? 'mexc' : 'binance'; if (state.auto[fetchingKey].fetching) { console.log(`[自动获取] ${source.toUpperCase()} 上一轮尚未完成,跳过本轮`); return; } currentRound++; // 重置倒计时(重新从 intervalSec 开始倒数) stopCountdownTimer(); currentCountdown = intervalSec; startCountdown(); await performFetch(); } }, intervalSec * 1000); console.log(`[自动获取] ${source.toUpperCase()} 已启动,间隔 ${intervalSec} 秒`); } // 停止自动获取 function stop() { if (!running) return; running = false; if (intervalId) { clearInterval(intervalId); intervalId = null; } stopCountdownTimer(); if (autoBtn) autoBtn.disabled = false; tempMsg = ''; updateDisplay(); console.log(`[自动获取] ${source.toUpperCase()} 已停止`); } // 获取运行状态 function isRunning() { return running; } // 获取当前状态文本(供外部直接显示,但通常由内部 updateDisplay 处理) function getStatusText() { if (!running) return '⚫ 已停止'; let text = `⏳ 本次第${currentRound}轮,下轮获取: ${currentCountdown}s`; if (tempMsg && Date.now() < tempExpire) text += ` ${tempMsg}`; return text; } return { start, stop, isRunning, getStatusText }; }