From e8ca56d650d2e62be90f48e0e4ec424ccaa41b37 Mon Sep 17 00:00:00 2001 From: QLHazyCoder <2825305047@qq.com> Date: Sun, 10 May 2026 02:30:03 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E8=BF=87=E6=9C=9F?= =?UTF-8?q?=E6=AD=A5=E9=AA=A4=E6=B6=88=E6=81=AF=E5=A4=84=E7=90=86=EF=BC=8C?= =?UTF-8?q?=E5=BF=BD=E7=95=A5=E8=87=AA=E5=8A=A8=E8=BF=90=E8=A1=8C=E4=B8=AD?= =?UTF-8?q?=E5=B7=B2=E5=AE=8C=E6=88=90=E7=9A=84=E6=AD=A5=E9=AA=A4=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E5=92=8C=E5=AE=8C=E6=88=90=E6=B6=88=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- background/message-router.js | 29 +++++++++ ...ckground-message-router-step2-skip.test.js | 64 ++++++++++++++++++- 2 files changed, 91 insertions(+), 2 deletions(-) diff --git a/background/message-router.js b/background/message-router.js index ca8b3fb..01c305c 100644 --- a/background/message-router.js +++ b/background/message-router.js @@ -167,6 +167,25 @@ return ''; } + function isStaleAutoRunStepMessage(step, state = {}) { + if (typeof isAutoRunLockedState !== 'function' || !isAutoRunLockedState(state)) { + return false; + } + const normalizedStep = Number(step); + if (!Number.isInteger(normalizedStep) || normalizedStep <= 0) { + return false; + } + const currentStatus = String(state?.stepStatuses?.[normalizedStep] || '').trim(); + if (currentStatus === 'running') { + return false; + } + const currentStep = Number(state?.currentStep) || 0; + if (currentStep > 0 && normalizedStep !== currentStep) { + return true; + } + return ['completed', 'manual_completed', 'skipped', 'failed', 'stopped'].includes(currentStatus); + } + function resolveSignupPhonePayload(payload = {}) { const directPhone = String( payload?.signupPhoneNumber @@ -540,6 +559,11 @@ } case 'STEP_COMPLETE': { + const currentState = await getState(); + if (isStaleAutoRunStepMessage(message.step, currentState)) { + await addLog(`自动运行:忽略过期的步骤 ${message.step} 完成消息,当前流程已在步骤 ${currentState.currentStep || '未知'}。`, 'warn', { step: message.step }); + return { ok: true, ignored: true }; + } if (getStopRequested()) { await setStepStatus(message.step, 'stopped'); await appendManualAccountRunRecordIfNeeded(`step${message.step}_stopped`, null, '流程已被用户停止。'); @@ -582,6 +606,11 @@ } case 'STEP_ERROR': { + const staleCheckState = await getState(); + if (isStaleAutoRunStepMessage(message.step, staleCheckState)) { + await addLog(`自动运行:忽略过期的步骤 ${message.step} 失败消息,当前流程已在步骤 ${staleCheckState.currentStep || '未知'}。原始错误:${message.error || '未知错误'}`, 'warn', { step: message.step }); + return { ok: true, ignored: true }; + } if (typeof isCloudflareSecurityBlockedError === 'function' && isCloudflareSecurityBlockedError(message.error)) { const userMessage = typeof handleCloudflareSecurityBlocked === 'function' ? await handleCloudflareSecurityBlocked(message.error) diff --git a/tests/background-message-router-step2-skip.test.js b/tests/background-message-router-step2-skip.test.js index 0157e63..acf3638 100644 --- a/tests/background-message-router-step2-skip.test.js +++ b/tests/background-message-router-step2-skip.test.js @@ -23,13 +23,17 @@ function createRouter(overrides = {}) { securityBlocks: [], invalidations: [], executedSteps: [], + accountRecords: [], }; const router = api.createMessageRouter({ addLog: async (message, level, options = {}) => { events.logs.push({ message, level, step: options.step, stepKey: options.stepKey }); }, - appendAccountRunRecord: async () => null, + appendAccountRunRecord: overrides.appendAccountRunRecord || (async (status, state, reason) => { + events.accountRecords.push({ status, state, reason }); + return null; + }), batchUpdateLuckmailPurchases: async () => {}, buildLocalhostCleanupPrefix: () => '', buildLuckmailSessionSettingsPayload: () => ({}), @@ -89,7 +93,7 @@ function createRouter(overrides = {}) { events.invalidations.push({ step, options }); }, isCloudflareSecurityBlockedError: overrides.isCloudflareSecurityBlockedError || ((error) => /^CF_SECURITY_BLOCKED::/.test(typeof error === 'string' ? error : error?.message || '')), - isAutoRunLockedState: () => false, + isAutoRunLockedState: overrides.isAutoRunLockedState || (() => false), isHotmailProvider: () => false, isLocalhostOAuthCallbackUrl: () => true, isLuckmailProvider: () => false, @@ -562,3 +566,59 @@ test('message router refreshes GPC balance through explicit sidepanel message', assert.equal(events.balanceRefreshes[0].state.gopayHelperApiKey, 'payload_api_key'); assert.deepStrictEqual(events.balanceRefreshes[0].options, { reason: 'manual' }); }); + +test('message router ignores stale step 2 errors while auto-run is already on a later step', async () => { + const { router, events } = createRouter({ + state: { + autoRunning: true, + autoRunPhase: 'running', + currentStep: 6, + stepStatuses: { + 2: 'completed', + 6: 'running', + }, + }, + isAutoRunLockedState: (state) => Boolean(state?.autoRunning) && state?.autoRunPhase === 'running', + }); + + const response = await router.handleMessage({ + type: 'STEP_ERROR', + step: 2, + error: '步骤 2:旧页面异步失败,不应覆盖当前第 6 步记录。', + }, {}); + + assert.deepStrictEqual(response, { ok: true, ignored: true }); + assert.deepStrictEqual(events.stepStatuses, []); + assert.deepStrictEqual(events.notifyErrors, []); + assert.deepStrictEqual(events.accountRecords, []); + assert.equal(events.logs.some(({ message }) => /忽略过期的步骤 2 失败消息/.test(message)), true); +}); + +test('message router ignores stale step 2 completion while auto-run is already on a later step', async () => { + const { router, events } = createRouter({ + state: { + autoRunning: true, + autoRunPhase: 'running', + currentStep: 6, + stepStatuses: { + 2: 'completed', + 6: 'running', + }, + }, + isAutoRunLockedState: (state) => Boolean(state?.autoRunning) && state?.autoRunPhase === 'running', + }); + + const response = await router.handleMessage({ + type: 'STEP_COMPLETE', + step: 2, + payload: { + email: 'late@example.com', + }, + }, {}); + + assert.deepStrictEqual(response, { ok: true, ignored: true }); + assert.deepStrictEqual(events.stepStatuses, []); + assert.deepStrictEqual(events.notifyCompletions, []); + assert.deepStrictEqual(events.emailStates, []); + assert.equal(events.logs.some(({ message }) => /忽略过期的步骤 2 完成消息/.test(message)), true); +});