From 55542dd212e2fffdc57f7b4283a9339ad34c5ad3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B2=A1=E5=A4=8D=E4=B9=A0?= <2353956224@qq.com> Date: Mon, 1 Dec 2025 20:04:52 +0800 Subject: [PATCH] =?UTF-8?q?3.1.0=E7=A8=B3=E5=AE=9A=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- js/rabbitmq-service.js | 25 ++++----- main.js | 120 +++++++++++++++++++++++++++++++---------- package.json | 2 +- 3 files changed, 107 insertions(+), 40 deletions(-) diff --git a/js/rabbitmq-service.js b/js/rabbitmq-service.js index f444ded..31ed719 100644 --- a/js/rabbitmq-service.js +++ b/js/rabbitmq-service.js @@ -5,7 +5,8 @@ const { EventEmitter } = require('events'); const CFG = { protocol: process.env.RABBIT_PROTOCOL || 'amqp', // 'amqp' | 'amqps' // host: process.env.RABBIT_HOST || '192.168.1.144', - host: process.env.RABBIT_HOST || 'crawlclient.api.yolozs.com', + // host: process.env.RABBIT_HOST || 'crawlclient.api.yolozs.com', + host: process.env.RABBIT_HOST || '47.79.98.113', port: Number(process.env.RABBIT_PORT || 5672), user: process.env.RABBIT_USER || 'tkdata', pass: process.env.RABBIT_PASS || '6rARaRj8Z7UG3ahLzh', @@ -58,31 +59,31 @@ async function createConnection() { // 心跳超时常见,避免重复噪音 const msg = e?.message || String(e); if (msg && /heartbeat/i.test(msg)) { - console.error('[AMQP] connection error (heartbeat):', msg); + console.error('[AMQP] 连接错误 (心跳):', msg); } else { - console.error('[AMQP] connection error:', msg); + console.error('[AMQP] 连接错误:', msg); } emitter.emit('error', e); }); connection.on('close', () => { if (closing) return; // 正在关闭时不重连 - console.error('[AMQP] connection closed'); + console.error('[AMQP] 连接已关闭'); conn = null; pubCh = null; conCh = null; scheduleReconnect(); }); // Broker 侧内存/磁盘压力会 block 连接 connection.on('blocked', (reason) => { - console.warn('[AMQP] connection blocked by broker:', reason); + console.warn('[AMQP] 连接被代理阻塞::', reason); emitter.emit('blocked', reason); }); connection.on('unblocked', () => { - console.log('[AMQP] connection unblocked'); + console.log('[AMQP] 链接解锁'); emitter.emit('unblocked'); }); - console.log(`[AMQP] connected to ${CFG.host} (hb=${CFG.heartbeat}s)`); + console.log(`[AMQP] 已连接到 ${CFG.host} (hb=${CFG.heartbeat}s)`); return connection; } @@ -92,13 +93,13 @@ async function ensureChannels() { if (!pubCh) { pubCh = await conn.createConfirmChannel(); - pubCh.on('error', e => console.error('[AMQP] pub channel error:', e?.message || e)); + pubCh.on('error', e => console.error('[AMQP] 通道错误:', e?.message || e)); pubCh.on('close', () => { pubCh = null; if (!closing) scheduleReconnect(); }); } if (!conCh) { conCh = await conn.createChannel(); - conCh.on('error', e => console.error('[AMQP] con channel error:', e?.message || e)); + conCh.on('error', e => console.error('[AMQP] 通道错误:', e?.message || e)); conCh.on('close', () => { conCh = null; if (!closing) scheduleReconnect(); }); } } @@ -117,12 +118,12 @@ function scheduleReconnect() { reconnecting = false; backoff = 1000; emitter.emit('reconnected'); - console.log('[AMQP] reconnected and consumers resumed'); + console.log('[AMQP] 重新连接并恢复了消费者'); } catch (e) { const base = Math.min(backoff, MAX_BACKOFF); // 加抖动,避免雪崩:在 75%~125% 之间浮动 const jitter = base * (0.75 + Math.random() * 0.5); - console.warn(`[AMQP] reconnect failed: ${e?.message || e}; retry in ${Math.round(jitter)}ms`); + console.warn(`[AMQP] 重连失败: ${e?.message || e}; retry in ${Math.round(jitter)}ms`); backoff = Math.min(backoff * 1.6, MAX_BACKOFF); reconnectTimer = setTimeout(attempt, jitter); } @@ -193,7 +194,7 @@ async function startOneConsumer(queueName, onMessage, options = {}, isResume = f consumers.set(queueName, { onMessage, options, consumerTag: consumeResult.consumerTag }); } - console.log(`[*] consuming "${queueName}" (prefetch=${prefetch})`); + console.log(`[*] 消费 "${queueName}" (预取=${prefetch})`); return consumeResult; } diff --git a/main.js b/main.js index bf699e6..ead011c 100644 --- a/main.js +++ b/main.js @@ -17,8 +17,20 @@ Object.assign(console, log.functions) // 存储 SSE 服务器的引用 let sseServer = null; + +// ✅ 本地 HTTPS 白名单(按你的实际地址改端口) +const LOCAL_HTTPS_WHITELIST = [ + 'https://192.168.1.218:34567', + 'https://192.168.1.231:34567', + 'https://127.0.0.1:34567', + 'https://localhost:34567' +] + let userData = { tenantId: null, userId: null } const { exec, spawn, execFile } = require('child_process'); // 如果你用 exec 启动 scrcpy,保留此行 + +// app.commandLine.appendSwitch('remote-debugging-port', '9222'); //远程控制台端口F12 + app.commandLine.appendSwitch('enable-precise-memory-info') app.commandLine.appendSwitch('js-flags', '--expose-gc --max-old-space-size=4096') // 以 MB 为单位,建议设置 2048-4096 @@ -45,7 +57,7 @@ const SAFE_MODE = // (可选)允许命令行清除持久化标记:--clear-safe-mode if (process.argv.includes('--clear-safe-mode')) { const c = readCompat(); delete c.safeMode; writeCompat(c); - console.log('[Compat] cleared safeMode flag'); + console.log('[兼容性] 已清除安全模式标志'); } // === 兼容模式等价于 bat 里的那些开关(不含日志) === @@ -56,7 +68,7 @@ if (SAFE_MODE) { app.commandLine.appendSwitch('disable-direct-composition'); // --disable-direct-composition // 如需更稳再关沙箱:默认开着更安全;若你确实要关,则解除注释 // app.commandLine.appendSwitch('no-sandbox'); // --no-sandbox - console.log('[SAFE_MODE] enabled'); + console.log('[安全模式] 已启用'); } @@ -66,13 +78,13 @@ if (SAFE_MODE) { function flushPendingNavs() { const fns = pendingNavs.slice(); pendingNavs = []; - for (const fn of fns) { try { fn(); } catch (e) { console.warn('[Nav] deferred run err:', e); } } + for (const fn of fns) { try { fn(); } catch (e) { console.warn('[导航] 延迟运行错误:', e); } } } function safeLoadURL(win, url, onFail) { if (!win || win.isDestroyed()) return; win.loadURL(url).catch(err => { - console.warn('[loadURL 失败]', err); + console.warn('[加载url 失败]', err); onFail?.(err); }); } @@ -90,7 +102,8 @@ app.on('web-contents-created', (_, contents) => { console.error('⚠️ 渲染崩溃:', details); if (['launch-failed', 'abnorm al-exit'].includes(details.reason)) { const c = readCompat(); c.safeMode = true; writeCompat(c); - console.warn('[Compat] set safeMode=true (persisted), will auto-use SAFE_MODE next launch'); + console.warn('[兼容性] 已设置安全模式为true(已持久化),下次启动时将自动使用安全模式'); + } }); }); @@ -99,10 +112,23 @@ app.on('child-process-gone', (_e, details) => { if ((details.type === 'GPU' || details.type === 'Renderer') && ['launch-failed', 'abnormal-exit'].includes(details.reason)) { const c = readCompat(); c.safeMode = true; writeCompat(c); - console.warn('[Compat] GPU/Renderer gone, safeMode persisted'); + console.warn('[兼容性] 已设置安全模式为true(已持久化),下次启动时将自动使用安全模式'); + } }); +//本地https 证书跳过校验 +app.on('certificate-error', (event, webContents, url, error, certificate, callback) => { + // console.warn('[certificate-error]', url, error) + // 只放行你自己的本地 HTTPS + if (LOCAL_HTTPS_WHITELIST.some(prefix => url.startsWith(prefix))) { + event.preventDefault() // 告诉 Electron:我来处理这个错误 + callback(true) // ✅ 忽略证书错误,照样加载 + } else { + // 其他地址还是正常校验证书,防止误放行 + callback(false) + } +}) app.on('ready', () => { @@ -179,7 +205,8 @@ app.on('ready', () => { // 安装前做一次业务清理:但避免误杀自身或阻塞 await killIOSAI(); } catch (e) { - console.warn('[update] pre-install cleanup warning:', e); + console.warn('[更新] 预安装清理警告:', e); + } // electron-updater 实际上传给 NSIS /S 静默参数,第二个 true 表示强制重启后运行新版本 autoUpdater.quitAndInstall(false, true); @@ -207,7 +234,8 @@ app.whenReady().then(() => { // globalShortcut.register('F12', () => toggleDevtools(BrowserWindow.getFocusedWindow())); - globalShortcut.register('Control+Shift+I', () => toggleDevtools(BrowserWindow.getFocusedWindow())); + globalShortcut.register('Control+Shift+i', () => toggleDevtools(BrowserWindow.getFocusedWindow())); + // globalShortcut.register('Control+Alt+m', () => toggleDevtools(BrowserWindow.getFocusedWindow())); }); app.on('will-quit', () => globalShortcut.unregisterAll()); @@ -217,7 +245,8 @@ app.on('before-quit', async () => { if (!installingNow) { await killIOSAI(); } else { - console.log('[before-quit] skip killIOSAI because installingNow = true'); + console.log('[退出前] 跳过 killIOSAI 因为 installingNow = true'); + } }); function toggleDevtools(win) { @@ -273,33 +302,56 @@ function dumpAllMem() { // console.log('> [MEM] SUM Renderer workingSetMB=', sum(renderers, 'workingSetMB')); // if (gpu.length) console.log('> [MEM] GPU=', gpu); } catch (e) { - console.warn('getAppMetrics error:', e); + console.warn('获取应用指标错误:', e); + } } // 在函数外定义计数器(或者放在函数内部,用闭包封装) let consumeCount = 0; async function setupMQConsumerAndPublisher(emitMessage, tenantId) { - // 启动持续消费:来一条 -> 立刻 SSE 广播 - await mq.startConsumer( + const queueNames = [ `q.tenant.${tenantId}`, - async (msg) => { - const payload = msg.json ?? msg.text; // 数据 - consumeCount++; // 每消费一条消息就加 1 + `b.tenant.${tenantId}`, // 新增队列 + ]; - console.log('消费到:', payload.hostsId, payload.country, '共' + consumeCount + '条数据'); + for (const qName of queueNames) { + await mq.startConsumer( + qName, + async (msg) => { + const payload = msg.json ?? msg.text; // 原始业务数据 + consumeCount++; // 所有队列共用计数器 - // 广播到前端(事件名自定义为 'mq') - emitMessage(payload); - // 成功返回会在 mq 客户端内部自动 ack - }, - { prefetch: 1, requeueOnError: false, durable: true, assertQueue: true } - ); + // 标记来源类型:普通队列 / burst 队列 + const isBurstQueue = qName.startsWith('b.'); + const meta = isBurstQueue ? 2 : 1; - // 供渲染进程发送消息到队列 + console.log( + `[MQ消费] [${qName}]`, + payload?.hostsId, + payload?.country, + '共' + consumeCount + '条数据' + ); + + // ⚠️ 关键:在原有 payload 的基础上,增加 _mqMeta 字段 + // 这样你之前前端用 payload.hostsId / payload.country 的地方完全不用改 + const wrapped = { + ...payload, + _mqMeta: meta + }; + + // 广播到前端 + emitMessage(wrapped); + // 成功返回会在 mq 客户端内部自动 ack + }, + { prefetch: 1, requeueOnError: false, durable: true, assertQueue: true } + ); + } + + // 供渲染进程发送消息到队列(保持原来的 q.tenant.* 不变) ipcMain.removeHandler('mq-send'); ipcMain.handle('mq-send', async (_event, user) => { - console.log('消息已发送', user) + console.log('消息已发送', user); await mq.publishToQueue(`q.tenant.${tenantId}`, user); return { ok: true }; }); @@ -342,10 +394,23 @@ function createWindow() { win.setMenu(null); // 禁用菜单栏F12 win.maximize(); + + // ✅ 把渲染进程的 console.* 打进 main 的日志 + win.webContents.on('console-message', (event, level, message, line, sourceId) => { + // level: 0=log,1=warn,2=error,3=info,4=debug + if (level == 1) { + console.log( + `[页面日志] ${message}` + ); + } + + }); + + // 自动判断环境使用不同的页面地址 const isProd = app.isPackaged; - const targetURL = isProd ? 'https://iosaitest.yolozs.com' : 'http://192.168.1.128:8080'; - // const targetURL = isProd ? 'https://iosai.yolozs.com' : 'http://192.168.1.128:8080'; + // const targetURL = isProd ? 'https://iosaitest.yolozs.com' : 'http://192.168.1.128:8080'; + const targetURL = isProd ? 'https://iosai.yolozs.com' : 'http://192.168.1.128:8080'; console.log('[页面加载] 使用地址:', targetURL); let retryIntervalId = null; @@ -724,7 +789,7 @@ async function killIOSAI() { }).then(response => { console.log("发送登出请求成功") }).catch(error => { - console.log("发送登出请求错误", error.message) + console.log("发送登出请求错误", error) }).finally(error => { // console.log("发送") }) @@ -744,6 +809,7 @@ async function killIOSAI() { exec('taskkill /IM IOSAI.exe /F'); exec('taskkill /IM iproxy.exe /F'); exec('taskkill /IM pythonw.exe /F'); + exec('taskkill /IM ios.exe /F'); } } catch (_) { } iosAIProcess = null; diff --git a/package.json b/package.json index 33d7e1a..21943e6 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "YOLO-ios-ai", "productName": "YOLO(AI助手ios)", - "version": "2.8.0", + "version": "3.2.0", "description": "Vue3 + WS 控制台", "author": "yourname", "main": "main.js",