diff --git a/public/index.html b/public/index.html index 4e4c44e..02a0702 100644 --- a/public/index.html +++ b/public/index.html @@ -2357,13 +2357,13 @@ class="w-full" @keydown.enter.prevent="confirmGoogleAccountAgTask()">
- +
- 供应数据中的辅助邮箱及后续字段会被忽略;数据库已有邮箱会跳过且不覆盖。临时比特浏览器固定使用直连(无代理),并严格按邮箱串行处理:完成一个、删除一个,再开始下一个。 + 供应数据中的辅助邮箱及后续字段会被忽略;数据库已有邮箱会跳过且不覆盖。临时比特浏览器固定使用直连(无代理);每个并发工位完成一个账号并删除对应浏览器后,再领取下一个账号。
diff --git a/src/workers/task_runner.js b/src/workers/task_runner.js index 8d705b8..fec4a59 100644 --- a/src/workers/task_runner.js +++ b/src/workers/task_runner.js @@ -271,18 +271,6 @@ function stopMonitorForTask(monitor, accountLog) { // ==================== Pipeline 任务类型集合 ==================== const PIPELINE_TASKS = new Set([...Object.keys(RECIPES), 'custom_pipeline']); -const TEMPORARY_BROWSER_TASKS = new Set([ - ...[...PIPELINE_TASKS].filter(type => type !== 'openai_query_plan'), - 'google_account_ag_credential', - 'ag_revoke_authorization', - 'ag_get_credential', - 'gemini_cli_get_credential', - 'open', - 'open_manual_aistudio', - 'open_url', - 'change_proxy', -]); - // ==================== 任务入口 ==================== const { taskType, accounts, params, taskId } = workerData; @@ -452,10 +440,9 @@ async function runTask() { const threadCount = taskType === 'change_proxy' && accounts.length > 300 ? Math.min(requestedThreadCount, 12) : requestedThreadCount; - const browserTaskConcurrency = TEMPORARY_BROWSER_TASKS.has(taskType) ? 1 : threadCount; - if (TEMPORARY_BROWSER_TASKS.has(taskType) && threadCount > 1) { - log(`[任务] 临时比特浏览器模式固定串行执行;已将浏览器任务并发从 ${threadCount} 调整为 1`); - } + // 浏览器任务也遵循页面设置的并发数。每个并发槽独立完成 + // “创建/复用当前账号窗口 → 执行业务 → 删除窗口”后再领取下一个账号。 + const browserTaskConcurrency = threadCount; const openRequestConcurrency = setOpenBrowserConcurrency(Math.min(browserTaskConcurrency, 12)); // 读取打开限速配置(每秒最多打开几个窗口),默认 9 @@ -997,7 +984,7 @@ async function runTask() { case 'google_account_ag_credential': { const requestedQuantity = Number.parseInt(params && params.quantity, 10); - const taskConcurrency = 1; + const taskConcurrency = browserTaskConcurrency; const defaultCustomerName = '企业字-Free'; log(`[链接取号] 开始处理,计划取号 ${requestedQuantity} 个,并发 ${taskConcurrency}`); log('[链接取号] 执行方式:每个并发工位每次只取 1 个账号,完成浏览器创建和反重力凭证后再领取下一个'); diff --git a/tests/google_account_ag_feature.test.js b/tests/google_account_ag_feature.test.js index 6dfe0b2..2501219 100644 --- a/tests/google_account_ag_feature.test.js +++ b/tests/google_account_ag_feature.test.js @@ -28,10 +28,10 @@ function responseStub() { '获取Cli凭证按钮必须位于获取凭证和链接提取按钮之间'); assert.match(html, /Google 账号取号 API[\s\S]{0,2500}googleAccountCredentialForm\.value/, '设置页应提供加密取号 API 配置'); - assert.match(html, /处理账号数量(1~5000)[\s\S]{0,800}并发参数(兼容保留)/, - '任务弹窗应说明并发参数只为兼容保留'); - assert.match(html, /临时比特浏览器[\s\S]{0,120}严格按邮箱串行处理:完成一个、删除一个,再开始下一个/, - '任务弹窗应告知用户临时比特浏览器严格串行'); + assert.match(html, /处理账号数量(1~5000)[\s\S]{0,800}并发数量(1~30)/, + '任务弹窗应允许用户设置实际并发数量'); + assert.match(html, /临时比特浏览器[\s\S]{0,180}每个并发工位完成一个账号并删除对应浏览器后,再领取下一个账号/, + '任务弹窗应说明每个并发工位独立执行完整浏览器生命周期'); assert(server.includes("/api/google-account-credentials"), '服务端应挂载取号凭据路由'); assert(operations.includes("'google_account_ag_credential'"), '取号任务必须受本机凭据任务保护'); assert(operations.includes('importAccountsFromText'), 'Worker 应通过主线程安全导入新账号'); @@ -46,8 +46,12 @@ function responseStub() { '链接取号导入账号必须使用默认客户名“企业字-Free”'); assert.match(worker, /fetchGoogleAccounts\(\{[\s\S]{0,220}quantity:\s*1/, '每次供应接口调用只能提取 1 个账号'); - assert(worker.includes('const taskConcurrency = 1;'), - '浏览器账号必须固定串行,删除当前临时窗口后再处理下一个'); + assert(worker.includes('const browserTaskConcurrency = threadCount;'), + '浏览器任务必须采用页面传入的并发数'); + assert(worker.includes('const taskConcurrency = browserTaskConcurrency;'), + '链接取号任务必须采用页面传入的并发数'); + assert(!worker.includes('已将浏览器任务并发'), + '浏览器任务不得静默把用户设置的并发数改成 1'); assert(worker.includes('const taskResults = await runConcurrent(taskSlots'), '账号应通过滑动队列按顺序处理'); const fetchOneAt = worker.indexOf('quantity: 1,');