From a7f7e68c76385c129c6aa437310fbde9be01a99e Mon Sep 17 00:00:00 2001 From: QLHazyCoder <2825305047@qq.com> Date: Thu, 28 May 2026 16:28:52 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=9C=80=E7=BB=88=E8=8A=82=E7=82=B9?= =?UTF-8?q?=E5=89=8D=E6=B8=85=E7=90=86=E6=89=8B=E6=9C=BA=E5=8F=B7=E6=B3=A8?= =?UTF-8?q?=E5=86=8C=E8=BA=AB=E4=BB=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- background.js | 94 +++++++++++++++++++++++ tests/background-step-completion.test.js | 97 ++++++++++++++++++++++-- 2 files changed, 186 insertions(+), 5 deletions(-) diff --git a/background.js b/background.js index 5178673..c8694e3 100644 --- a/background.js +++ b/background.js @@ -4480,6 +4480,91 @@ function isPhoneActivationForNumber(activation, phoneNumber) { return Boolean(activationDigits && targetDigits && activationDigits === targetDigits); } +function getSignupPhoneIdentityValue(state = {}) { + const accountIdentifierType = String(state?.accountIdentifierType || '').trim().toLowerCase(); + return String( + state?.signupPhoneNumber + || getPhoneActivationPhoneNumber(state?.signupPhoneCompletedActivation) + || getPhoneActivationPhoneNumber(state?.signupPhoneActivation) + || (accountIdentifierType === 'phone' ? state?.accountIdentifier : '') + || '' + ).trim(); +} + +function isPhoneSignupCompletionState(state = {}) { + const signupMethod = String(state?.resolvedSignupMethod || state?.signupMethod || '').trim().toLowerCase(); + return signupMethod === 'phone'; +} + +function signupPhoneIdentityValuesMatch(left = '', right = '') { + const leftRaw = String(left || '').trim(); + const rightRaw = String(right || '').trim(); + if (!leftRaw || !rightRaw) { + return false; + } + if (leftRaw === rightRaw) { + return true; + } + const leftDigits = normalizePhoneIdentityDigits(leftRaw); + const rightDigits = normalizePhoneIdentityDigits(rightRaw); + return Boolean(leftDigits && rightDigits && leftDigits === rightDigits); +} + +async function clearSignupPhoneIdentityBeforeFinalNodeNotify(completionState = {}, options = {}) { + if (!isPhoneSignupCompletionState(completionState)) { + return { cleared: false, reason: 'not_phone_signup' }; + } + + const completedPhone = getSignupPhoneIdentityValue(completionState); + if (!completedPhone) { + return { cleared: false, reason: 'missing_completed_phone' }; + } + + const latestState = await getState(); + const currentPhone = getSignupPhoneIdentityValue(latestState); + if (!currentPhone) { + return { cleared: false, reason: 'missing_current_phone' }; + } + + const currentPhoneCandidates = [ + latestState?.signupPhoneNumber, + getPhoneActivationPhoneNumber(latestState?.signupPhoneCompletedActivation), + getPhoneActivationPhoneNumber(latestState?.signupPhoneActivation), + String(latestState?.accountIdentifierType || '').trim().toLowerCase() === 'phone' + ? latestState?.accountIdentifier + : '', + ] + .map((value) => String(value || '').trim()) + .filter(Boolean); + + if ( + !signupPhoneIdentityValuesMatch(completedPhone, currentPhone) + || currentPhoneCandidates.some((candidate) => !signupPhoneIdentityValuesMatch(completedPhone, candidate)) + ) { + return { cleared: false, reason: 'changed' }; + } + + const updates = { + phoneNumber: '', + signupPhoneNumber: '', + signupPhoneActivation: null, + signupPhoneCompletedActivation: null, + signupPhoneVerificationRequestedAt: null, + signupPhoneVerificationPurpose: '', + }; + if (String(latestState?.accountIdentifierType || '').trim().toLowerCase() === 'phone') { + updates.accountIdentifierType = null; + updates.accountIdentifier = ''; + } + + await setState(updates); + broadcastDataUpdate(updates); + await addLog('手机号注册:最终节点完成前已清空本轮注册手机号,避免下一轮复用。', 'ok', { + nodeId: options?.nodeId || '', + }); + return { cleared: true, phoneNumber: currentPhone }; +} + async function setEmailStateSilently(email, options = {}) { const currentState = await getState(); const preserveAccountIdentity = Boolean(options?.preserveAccountIdentity); @@ -11157,6 +11242,15 @@ async function completeNodeFromBackground(nodeId, payload = {}) { await addLog('已完成', 'ok', { nodeId: normalizedNodeId }); if (normalizedNodeId === lastNodeId) { + try { + await clearSignupPhoneIdentityBeforeFinalNodeNotify(completionState, { + nodeId: normalizedNodeId, + }); + } catch (error) { + await addLog(`手机号注册:最终节点完成前清理手机号身份失败:${getErrorMessage(error)}`, 'warn', { + nodeId: normalizedNodeId, + }); + } notifyNodeComplete(normalizedNodeId, payload); void runCompletedNodeSideEffects(normalizedNodeId, payload, completionState, lastNodeId) .catch((error) => reportCompletedNodeSideEffectError(normalizedNodeId, error)); diff --git a/tests/background-step-completion.test.js b/tests/background-step-completion.test.js index 7c7d38a..6793185 100644 --- a/tests/background-step-completion.test.js +++ b/tests/background-step-completion.test.js @@ -51,23 +51,44 @@ function extractFunction(name) { return source.slice(start, end); } -function createApi(events, lastNodeId = 'platform-verify') { - return new Function('events', 'lastNodeId', ` +function createApi(events, lastNodeId = 'platform-verify', initialState = {}, options = {}) { + return new Function('events', 'lastNodeId', 'initialState', 'options', ` let stopRequested = false; const LOG_PREFIX = '[test]'; +let currentState = { nodeStatuses: {}, accountContributionEnabled: true, ...initialState }; const STOP_ERROR_MESSAGE = '流程已被用户停止。'; function getErrorMessage(error) { return error?.message || String(error || ''); } async function getState() { events.push({ type: 'getState' }); - return { nodeStatuses: {}, accountContributionEnabled: true }; + if (typeof options.beforeGetState === 'function') { + const patch = options.beforeGetState(currentState); + if (patch && typeof patch === 'object') { + currentState = { ...currentState, ...patch }; + } + } + return currentState; } function getLastNodeIdForState() { return lastNodeId; } async function setNodeStatus(nodeId, status) { events.push({ type: 'status', nodeId, status }); + currentState = { + ...currentState, + nodeStatuses: { + ...(currentState.nodeStatuses || {}), + [nodeId]: status, + }, + }; +} +async function setState(updates) { + currentState = { ...currentState, ...updates }; + events.push({ type: 'setState', updates }); +} +function broadcastDataUpdate(updates) { + events.push({ type: 'broadcast', updates }); } async function addLog(message, level, options = {}) { events.push({ type: 'log', message, level, options }); @@ -89,11 +110,17 @@ async function handleNodeData(nodeId, payload) { async function appendAndBroadcastAccountRunRecord(status, state) { events.push({ type: 'record', status, state }); } +${extractFunction('normalizePhoneIdentityDigits')} +${extractFunction('getPhoneActivationPhoneNumber')} +${extractFunction('getSignupPhoneIdentityValue')} +${extractFunction('isPhoneSignupCompletionState')} +${extractFunction('signupPhoneIdentityValuesMatch')} +${extractFunction('clearSignupPhoneIdentityBeforeFinalNodeNotify')} ${extractFunction('runCompletedNodeSideEffects')} ${extractFunction('reportCompletedNodeSideEffectError')} ${extractFunction('completeNodeFromBackground')} -return { completeNodeFromBackground }; -`)(events, lastNodeId); +return { completeNodeFromBackground, getCurrentState: () => currentState }; +`)(events, lastNodeId, initialState, options); } test('completeNodeFromBackground releases final node before slow post-completion side effects', async () => { @@ -124,3 +151,63 @@ test('completeNodeFromBackground keeps non-final node data handling before compl assert.equal(types.indexOf('handle-done') < types.indexOf('notify'), true); assert.equal(types.includes('record'), false); }); + +test('completeNodeFromBackground clears matching signup phone identity before final completion signal', async () => { + const events = []; + const api = createApi(events, 'platform-verify', { + signupMethod: 'phone', + accountIdentifierType: 'phone', + accountIdentifier: '+56 979206303', + phoneNumber: '+56 979206303', + signupPhoneNumber: '+56979206303', + signupPhoneActivation: { activationId: 'active', phoneNumber: '+56979206303' }, + signupPhoneCompletedActivation: { activationId: 'done', phoneNumber: '+56 979206303' }, + signupPhoneVerificationRequestedAt: 123, + signupPhoneVerificationPurpose: 'login', + }); + + await api.completeNodeFromBackground('platform-verify', { localhostUrl: 'http://localhost:1455/auth/callback?code=ok' }); + + const types = events.map((event) => event.type); + assert.equal(types.indexOf('setState') < types.indexOf('notify'), true); + assert.equal(types.indexOf('broadcast') < types.indexOf('notify'), true); + assert.equal(api.getCurrentState().phoneNumber, ''); + assert.equal(api.getCurrentState().signupPhoneNumber, ''); + assert.equal(api.getCurrentState().signupPhoneActivation, null); + assert.equal(api.getCurrentState().signupPhoneCompletedActivation, null); + assert.equal(api.getCurrentState().signupPhoneVerificationRequestedAt, null); + assert.equal(api.getCurrentState().signupPhoneVerificationPurpose, ''); + assert.equal(api.getCurrentState().accountIdentifierType, null); + assert.equal(api.getCurrentState().accountIdentifier, ''); +}); + +test('completeNodeFromBackground keeps latest signup phone identity when it changed before final signal', async () => { + const events = []; + let mutated = false; + const api = createApi(events, 'platform-verify', { + signupMethod: 'phone', + accountIdentifierType: 'phone', + accountIdentifier: '+56979206303', + signupPhoneNumber: '+56979206303', + signupPhoneActivation: { activationId: 'old', phoneNumber: '+56979206303' }, + }, { + beforeGetState: (state) => { + if (!mutated && state.nodeStatuses?.['platform-verify'] === 'completed') { + mutated = true; + return { + accountIdentifier: '+84901122334', + signupPhoneNumber: '+84901122334', + signupPhoneActivation: { activationId: 'new', phoneNumber: '+84901122334' }, + }; + } + return null; + }, + }); + + await api.completeNodeFromBackground('platform-verify', { localhostUrl: 'http://localhost:1455/auth/callback?code=ok' }); + + assert.equal(events.some((event) => event.type === 'setState'), false); + assert.equal(api.getCurrentState().accountIdentifier, '+84901122334'); + assert.equal(api.getCurrentState().signupPhoneNumber, '+84901122334'); + assert.deepEqual(api.getCurrentState().signupPhoneActivation, { activationId: 'new', phoneNumber: '+84901122334' }); +});