From 7e78e1bdaa5949fcdc67054745e2b5386b0e32a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Wed, 5 Aug 2026 17:49:52 +0800 Subject: [PATCH] =?UTF-8?q?=E9=9A=94=E7=A6=BB=E8=B4=A6=E5=8F=B7=E5=88=87?= =?UTF-8?q?=E6=8D=A2=E5=90=8E=E7=9A=84=E5=85=91=E6=8D=A2=E5=9B=9E=E8=B0=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 奖励码与邀请码兑换记录发起账号和账号生命周期 revision,忽略切换账号后的成功、失败与收尾回调。 账号切换时同步结束兑换提交态,并补充两类兑换回归测试。 --- ...rofileCenterController.redemption.test.tsx | 86 +++++++++++++++++++ .../usePlatformProfileCenterController.ts | 50 ++++++++++- 2 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 src/components/platform-entry/usePlatformProfileCenterController.redemption.test.tsx diff --git a/src/components/platform-entry/usePlatformProfileCenterController.redemption.test.tsx b/src/components/platform-entry/usePlatformProfileCenterController.redemption.test.tsx new file mode 100644 index 000000000..0ac05b86f --- /dev/null +++ b/src/components/platform-entry/usePlatformProfileCenterController.redemption.test.tsx @@ -0,0 +1,86 @@ +/* @vitest-environment jsdom */ + +import { act } from '@testing-library/react'; +import { beforeEach, describe, expect, test, vi } from 'vitest'; + +import { usePlatformWalletStore } from '../../stores/usePlatformWalletStore'; +import { + profileClientMocks, + renderController, + userA, + userB, +} from './usePlatformProfileCenterController.testSupport'; + +describe('usePlatformProfileCenterController redemption lifecycle', () => { + beforeEach(() => { + window.history.replaceState(null, '', '/profile'); + usePlatformWalletStore.getState().resetWalletBalance(); + vi.clearAllMocks(); + }); + + test('ignores a reward-code response after the account changes', async () => { + let resolveReward!: (value: { + walletBalance: number; + amountGranted: number; + ledgerEntry: never; + }) => void; + profileClientMocks.redeemPlatformProfileRewardCode.mockImplementation( + () => + new Promise((resolve) => { + resolveReward = resolve; + }), + ); + const onRechargeSuccess = vi.fn(); + const { result, rerender } = renderController(userA, onRechargeSuccess); + act(() => result.current.setRewardCodeInput('reward-code')); + act(() => result.current.submitRewardCode()); + + rerender({ user: userB }); + await act(async () => { + resolveReward({ + walletBalance: 100, + amountGranted: 20, + ledgerEntry: undefined as never, + }); + }); + + expect(result.current.rewardCodeSuccess).toBeNull(); + expect(result.current.isSubmittingRewardCode).toBe(false); + expect(onRechargeSuccess).not.toHaveBeenCalled(); + }); + + test('ignores an invite-code response after the account changes', async () => { + let resolveReferral!: (value: { + center: never; + inviteeRewardGranted: boolean; + inviterRewardGranted: boolean; + inviteeBalanceAfter: number; + inviterBalanceAfter: number; + }) => void; + profileClientMocks.redeemPlatformProfileReferralInviteCode.mockImplementation( + () => + new Promise((resolve) => { + resolveReferral = resolve; + }), + ); + const onRechargeSuccess = vi.fn(); + const { result, rerender } = renderController(userA, onRechargeSuccess); + act(() => result.current.setReferralRedeemCode('invite-code')); + act(() => result.current.submitReferralRedeemCode()); + + rerender({ user: userB }); + await act(async () => { + resolveReferral({ + center: undefined as never, + inviteeRewardGranted: true, + inviterRewardGranted: false, + inviteeBalanceAfter: 100, + inviterBalanceAfter: 0, + }); + }); + + expect(result.current.referralSuccess).toBeNull(); + expect(result.current.isSubmittingReferralRedeem).toBe(false); + expect(onRechargeSuccess).not.toHaveBeenCalled(); + }); +}); diff --git a/src/components/platform-entry/usePlatformProfileCenterController.ts b/src/components/platform-entry/usePlatformProfileCenterController.ts index bd117ba93..c4ef7cc31 100644 --- a/src/components/platform-entry/usePlatformProfileCenterController.ts +++ b/src/components/platform-entry/usePlatformProfileCenterController.ts @@ -397,8 +397,10 @@ export function usePlatformProfileCenterController({ setIsLoadingWalletLedger(false); setIsRewardCodeOpen(false); setRewardCodeInput(''); + setIsSubmittingRewardCode(false); setRewardCodeError(null); setRewardCodeSuccess(null); + setIsSubmittingReferralRedeem(false); }, [currentUserId]); // 中文注释:支持带邀请码 query 的直达场景,登录成功后自动打开兑换面板并复用同一套输入状态。 @@ -1136,8 +1138,16 @@ export function usePlatformProfileCenterController({ setIsSubmittingReferralRedeem(true); setReferralError(null); setReferralSuccess(null); + const snapshotOwnerUserId = currentUserId; + const accountRevision = accountLifecycleRevisionRef.current; void redeemPlatformProfileReferralInviteCode(inviteCode) .then((response) => { + if ( + accountRevision !== accountLifecycleRevisionRef.current || + currentUserIdRef.current !== snapshotOwnerUserId + ) { + return; + } setReferralCenter(response.center); setReferralRedeemCode(''); setReferralSuccess('已填写'); @@ -1145,12 +1155,26 @@ export function usePlatformProfileCenterController({ void onRechargeSuccess?.(); }) .catch((error: unknown) => { + if ( + accountRevision !== accountLifecycleRevisionRef.current || + currentUserIdRef.current !== snapshotOwnerUserId + ) { + return; + } setReferralError( error instanceof Error ? error.message : '填写邀请码失败', ); }) - .finally(() => setIsSubmittingReferralRedeem(false)); + .finally(() => { + if ( + accountRevision === accountLifecycleRevisionRef.current && + currentUserIdRef.current === snapshotOwnerUserId + ) { + setIsSubmittingReferralRedeem(false); + } + }); }, [ + currentUserId, isSubmittingReferralRedeem, onRechargeSuccess, onWalletBalanceMayHaveChanged, @@ -1165,18 +1189,40 @@ export function usePlatformProfileCenterController({ setIsSubmittingRewardCode(true); setRewardCodeError(null); setRewardCodeSuccess(null); + const snapshotOwnerUserId = currentUserId; + const accountRevision = accountLifecycleRevisionRef.current; void redeemPlatformProfileRewardCode(rewardCodeInput) .then((response: RedeemProfileRewardCodeResponse) => { + if ( + accountRevision !== accountLifecycleRevisionRef.current || + currentUserIdRef.current !== snapshotOwnerUserId + ) { + return; + } setRewardCodeInput(''); setRewardCodeSuccess(`已到账 ${response.amountGranted} 泥点`); void onWalletBalanceMayHaveChanged(); void onRechargeSuccess?.(); }) .catch((error: unknown) => { + if ( + accountRevision !== accountLifecycleRevisionRef.current || + currentUserIdRef.current !== snapshotOwnerUserId + ) { + return; + } setRewardCodeError(error instanceof Error ? error.message : '兑换失败'); }) - .finally(() => setIsSubmittingRewardCode(false)); + .finally(() => { + if ( + accountRevision === accountLifecycleRevisionRef.current && + currentUserIdRef.current === snapshotOwnerUserId + ) { + setIsSubmittingRewardCode(false); + } + }); }, [ + currentUserId, isSubmittingRewardCode, onRechargeSuccess, onWalletBalanceMayHaveChanged,