From 144068187cee2f79e9aedf1f4ed8edc4cbe9c2e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Fri, 2 Oct 2026 19:03:30 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84(=E5=B9=B3=E5=8F=B0=E5=85=A5?= =?UTF-8?q?=E5=8F=A3):=20=E5=B9=B3=E5=8F=B0=E4=BE=A7=E4=B8=8E=20AGC=20?= =?UTF-8?q?=E5=A3=B3=E6=94=B9=E7=94=A8=E4=B8=89=E6=B1=A0=E4=BD=99=E9=A2=9D?= =?UTF-8?q?=EF=BC=8C=E9=80=80=E5=BD=B9=20legacy=20=E6=80=BB=E9=A2=9D?= =?UTF-8?q?=E5=86=99=E5=85=A5=E9=80=9A=E9=81=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 平台入口与钱包 store 不再从充值中心响应读 walletBalance,合计改为按三池派生 - AGC 账户钱包同步:只透传 mudPointBalance,删掉 RechargeContent 的 walletBalance 排除项 - 删除已无生产调用方的 applyLegacyWalletBalanceSnapshot 与对应测试;明细读取失败不再伪造「只有总额」的中间态 - 图片编辑器与平台入口测试 fixture 改为三池形状,钱包面板断言改用「永久泥点 / 月度泥点」 Co-authored-by: Junie --- apps/ai-game-creator-shell/src/app/types.ts | 2 +- .../features/app-shell/useAccountWallet.ts | 19 ++------ .../tests/appSurface/home.suite.ts | 38 ++++++--------- .../tests/walletStore.test.ts | 43 +++++++---------- .../stores/createProfileWalletStore.test.ts | 23 ++-------- .../src/stores/createProfileWalletStore.ts | 32 +------------ .../ImageCanvasEditorView.test.tsx | 46 ++++++++----------- .../ImageCanvasTopbarView.test.tsx | 12 ++--- .../PlatformActiveProfileView.test.tsx | 4 +- .../PlatformActiveProfileView.tsx | 6 +-- .../PlatformEntryActiveFlowShell.test.tsx | 38 +++++++++------ .../PlatformEntryActiveFlowShell.tsx | 5 +- ...mProfileCenterController.recharge.test.tsx | 21 +++++---- ...sePlatformProfileCenterController.test.tsx | 15 +++--- .../usePlatformProfileCenterController.ts | 2 - src/stores/usePlatformWalletStore.test.tsx | 6 +-- 16 files changed, 116 insertions(+), 196 deletions(-) diff --git a/apps/ai-game-creator-shell/src/app/types.ts b/apps/ai-game-creator-shell/src/app/types.ts index 5d0c1e99a..0cc1c072a 100644 --- a/apps/ai-game-creator-shell/src/app/types.ts +++ b/apps/ai-game-creator-shell/src/app/types.ts @@ -12,7 +12,7 @@ import type { HomeCreationType, HomeDraft } from '../view/home'; export type RechargeContent = Omit< ProfileRechargeCenterResponse, - 'walletBalance' | 'mudPointBalance' + 'mudPointBalance' >; export type AgentChatInteractionMode = 'run' | 'chat' | 'goal'; export type AgentBackgroundSubmitMode = 'steer' | 'queue'; diff --git a/apps/ai-game-creator-shell/src/features/app-shell/useAccountWallet.ts b/apps/ai-game-creator-shell/src/features/app-shell/useAccountWallet.ts index 1d6f0a339..c88492ff6 100644 --- a/apps/ai-game-creator-shell/src/features/app-shell/useAccountWallet.ts +++ b/apps/ai-game-creator-shell/src/features/app-shell/useAccountWallet.ts @@ -26,7 +26,6 @@ export function useAccountWallet(currentUserId: string) { setWalletOwner, captureWalletBalanceSnapshot, applyWalletBalanceSnapshot, - applyLegacyWalletBalanceSnapshot, onWalletBalanceMayHaveChanged, resetWalletBalance, } = useWalletStore(); @@ -76,8 +75,6 @@ export function useAccountWallet(currentUserId: string) { const visibleMudPointBalanceError = walletOwnerMatchesCurrentUser ? mudPointBalanceError : ''; - const visibleWalletBalance = - visibleMudPointBalance?.totalPoints ?? visibleLegacyWalletBalance; useLayoutEffect(() => { currentUserIdRef.current = currentUserId; @@ -165,16 +162,9 @@ export function useAccountWallet(currentUserId: string) { walletSnapshot: ReturnType, center: ProfileRechargeCenterResponse, ) { - const { - walletBalance, - mudPointBalance: nextMudPointBalance, - ...content - } = center; + const { mudPointBalance: nextMudPointBalance, ...content } = center; if (nextMudPointBalance && walletSnapshot) { applyWalletBalanceSnapshot(walletSnapshot, nextMudPointBalance); - } else if (walletSnapshot && Number.isFinite(walletBalance)) { - applyLegacyWalletBalanceSnapshot(walletSnapshot, walletBalance); - void onWalletBalanceMayHaveChanged(); } else { void onWalletBalanceMayHaveChanged(); } @@ -400,13 +390,10 @@ export function useAccountWallet(currentUserId: string) { walletLedgerError: walletUiIsVisible ? walletLedgerError : null, rechargeOpen: walletUiIsVisible && rechargeOpen, rechargeModalCenter: - rechargeContent && visibleWalletBalance !== null + rechargeContent && visibleMudPointBalance !== null ? { ...rechargeContent, - walletBalance: visibleWalletBalance, - ...(visibleMudPointBalance - ? { mudPointBalance: visibleMudPointBalance } - : {}), + mudPointBalance: visibleMudPointBalance, } : null, rechargeLoading: walletUiIsVisible && rechargeLoading, diff --git a/apps/ai-game-creator-shell/tests/appSurface/home.suite.ts b/apps/ai-game-creator-shell/tests/appSurface/home.suite.ts index 912be89a7..b8f71f910 100644 --- a/apps/ai-game-creator-shell/tests/appSurface/home.suite.ts +++ b/apps/ai-game-creator-shell/tests/appSurface/home.suite.ts @@ -767,15 +767,13 @@ export function registerClientHomeTests() { it('starts from the client home and opens a project in the same window', async () => { // 账户与钱包的远端真相在 Rust typed command:渲染层只消费投影,不再自己发请求。 const rechargeCenter = { - walletBalance: 40, mudPointBalance: { - totalPoints: 40, - permanentPoints: 20, - limitedPoints: 0, - limitedExpiresAt: null, dailyFreePoints: 20, dailyFreeResetPoints: 20, dailyFreeResetsAt: '2026-07-18T00:00:00.000Z', + monthlyPoints: 0, + monthlyResetsAt: null, + permanentPoints: 20, }, products: [], membership: null, @@ -887,19 +885,17 @@ export function registerClientHomeTests() { it('opens the shared recharge modal from the wallet and sidebar entries', async () => { const rechargeCenter = { - walletBalance: 120, mudPointBalance: { - totalPoints: 120, - permanentPoints: 100, - limitedPoints: 20, - limitedExpiresAt: '2026-08-01T00:00:00Z', dailyFreePoints: 0, dailyFreeResetPoints: 20, dailyFreeResetsAt: '2026-07-18T00:00:00Z', + monthlyPoints: 20, + monthlyResetsAt: '2026-08-01T00:00:00Z', + permanentPoints: 100, }, membership: { - status: 'inactive', - tier: 'normal', + status: 'normal', + plan: 'normal', startedAt: null, expiresAt: null, updatedAt: null, @@ -907,7 +903,9 @@ export function registerClientHomeTests() { cycleResetsAt: null, cycleGrantedPoints: 0, cycleRemainingPoints: 0, - cyclePeriodDays: 0, + cycleIndex: 1, + cycleCount: 1, + cycleKind: 'monthly', }, pointProducts: [ { @@ -920,15 +918,9 @@ export function registerClientHomeTests() { durationDays: 0, badgeLabel: '', description: '', - tier: 'normal', - membershipPeriodPoints: 0, - membershipPeriodDays: 0, - membershipQueueLimit: 0, - membershipDiscountBps: 0, }, ], - membershipProducts: [], - benefits: [], + membershipPlans: [], latestOrder: null, hasPointsRecharged: true, }; @@ -939,10 +931,9 @@ export function registerClientHomeTests() { rechargePaid ? { ...rechargeCenter, - walletBalance: 180, mudPointBalance: { ...rechargeCenter.mudPointBalance, - totalPoints: 180, + monthlyPoints: 20, permanentPoints: 160, }, } @@ -1003,10 +994,9 @@ export function registerClientHomeTests() { }, center: { ...rechargeCenter, - walletBalance: 180, mudPointBalance: { ...rechargeCenter.mudPointBalance, - totalPoints: 180, + monthlyPoints: 20, permanentPoints: 160, }, }, diff --git a/apps/ai-game-creator-shell/tests/walletStore.test.ts b/apps/ai-game-creator-shell/tests/walletStore.test.ts index 984fb5cff..8062796dc 100644 --- a/apps/ai-game-creator-shell/tests/walletStore.test.ts +++ b/apps/ai-game-creator-shell/tests/walletStore.test.ts @@ -19,10 +19,9 @@ import { useWalletStore } from '../src/stores/useWalletStore'; function detailedBalance(totalPoints: number) { return { - totalPoints, permanentPoints: Math.max(0, totalPoints - 20), - limitedPoints: 20, - limitedExpiresAt: '2026-08-01T00:00:00Z', + monthlyPoints: Math.min(20, totalPoints), + monthlyResetsAt: '2026-08-01T00:00:00Z', dailyFreePoints: 0, dailyFreeResetPoints: 20, dailyFreeResetsAt: '2026-07-18T00:00:00Z', @@ -39,7 +38,6 @@ describe('useWalletStore', () => { test('keeps mudPointBalance as the authoritative detailed balance state', async () => { const mudPointBalance = detailedBalance(120); clientApi.getClientProfileRechargeCenter.mockResolvedValue({ - walletBalance: 120, mudPointBalance, }); const { result } = renderHook(() => useWalletStore()); @@ -48,8 +46,7 @@ describe('useWalletStore', () => { await result.current.onWalletBalanceMayHaveChanged(); }); - expect(result.current.mudPointBalance).toEqual(mudPointBalance); - expect(result.current.legacyWalletBalance).toBe(120); + expect(result.current.mudPointBalance).toMatchObject(mudPointBalance); expect(result.current.mudPointBalanceStatus).toBe('ready'); expect(result.current.mudPointBalanceError).toBe(''); expect(Object.keys(result.current).sort()).toEqual([ @@ -81,7 +78,9 @@ describe('useWalletStore', () => { .getState() .applyWalletBalanceSnapshot(snapshot!, mudPointBalance); - expect(useWalletStore.getState().mudPointBalance).toEqual(mudPointBalance); + expect(useWalletStore.getState().mudPointBalance).toMatchObject( + mudPointBalance, + ); expect(useWalletStore.getState().mudPointBalanceStatus).toBe('ready'); expect(useWalletStore.getState().mudPointBalanceError).toBe(''); }); @@ -106,20 +105,20 @@ describe('useWalletStore', () => { rejectRequest?.(new Error('过期请求失败')); await refresh; - expect(useWalletStore.getState().mudPointBalance).toEqual(mudPointBalance); + expect(useWalletStore.getState().mudPointBalance).toMatchObject( + mudPointBalance, + ); expect(useWalletStore.getState().mudPointBalanceStatus).toBe('ready'); expect(useWalletStore.getState().mudPointBalanceError).toBe(''); }); test('rejects a response that omits mudPointBalance instead of inventing a breakdown', async () => { - clientApi.getClientProfileRechargeCenter.mockResolvedValue({ - walletBalance: 120, - }); + clientApi.getClientProfileRechargeCenter.mockResolvedValue({}); await useWalletStore.getState().onWalletBalanceMayHaveChanged(); expect(useWalletStore.getState().mudPointBalance).toBeNull(); - expect(useWalletStore.getState().legacyWalletBalance).toBe(120); + expect(useWalletStore.getState().legacyWalletBalance).toBeNull(); expect(useWalletStore.getState().mudPointBalanceStatus).toBe('error'); expect(useWalletStore.getState().mudPointBalanceError).toBe( '泥点明细读取失败', @@ -129,13 +128,14 @@ describe('useWalletStore', () => { test('uses mudPointBalance from recharge-center as the authoritative snapshot', async () => { const mudPointBalance = detailedBalance(180); clientApi.getClientProfileRechargeCenter.mockResolvedValue({ - walletBalance: 999, mudPointBalance, }); await useWalletStore.getState().onWalletBalanceMayHaveChanged(); - expect(useWalletStore.getState().mudPointBalance).toEqual(mudPointBalance); + expect(useWalletStore.getState().mudPointBalance).toMatchObject( + mudPointBalance, + ); }); test('discards an outdated response and performs one trailing refresh', async () => { @@ -148,7 +148,6 @@ describe('useWalletStore', () => { }), ) .mockResolvedValueOnce({ - walletBalance: 80, mudPointBalance: detailedBalance(80), }); @@ -159,7 +158,6 @@ describe('useWalletStore', () => { .getState() .onWalletBalanceMayHaveChanged(); resolveFirst?.({ - walletBalance: 120, mudPointBalance: detailedBalance(120), }); await Promise.all([firstRefresh, trailingRefresh]); @@ -170,7 +168,6 @@ describe('useWalletStore', () => { test('retains the last balance when a refresh fails', async () => { clientApi.getClientProfileRechargeCenter.mockResolvedValueOnce({ - walletBalance: 90, mudPointBalance: detailedBalance(90), }); await useWalletStore.getState().onWalletBalanceMayHaveChanged(); @@ -199,7 +196,6 @@ describe('useWalletStore', () => { const refresh = useWalletStore.getState().onWalletBalanceMayHaveChanged(); useWalletStore.getState().resetWalletBalance(); resolveRequest?.({ - walletBalance: 75, mudPointBalance: detailedBalance(75), }); await refresh; @@ -248,7 +244,6 @@ describe('useWalletStore', () => { test('binds the account owner and refreshes again when the window regains focus', async () => { clientApi.getClientProfileRechargeCenter.mockResolvedValue({ - walletBalance: 18, mudPointBalance: detailedBalance(18), }); @@ -267,16 +262,14 @@ describe('useWalletStore', () => { }); }); - test('keeps a successful legacy recharge total when the breakdown refresh fails', async () => { + test('keeps the last known good breakdown when the recharge center omits it', async () => { clientApi.getClientProfileRechargeCenter.mockResolvedValueOnce({ - walletBalance: 90, mudPointBalance: detailedBalance(90), }); const { result } = renderHook(() => useAccountWallet('user-a')); await act(async () => undefined); clientApi.getClientProfileRechargeCenter .mockResolvedValueOnce({ - walletBalance: 37, membership: null, pointProducts: [], membershipProducts: [], @@ -291,14 +284,13 @@ describe('useWalletStore', () => { await Promise.resolve(); }); - expect(result.current.legacyWalletBalance).toBe(37); - expect(result.current.mudPointBalance).toBeNull(); + expect(result.current.mudPointBalance?.totalPoints).toBe(90); + expect(result.current.mudPointBalanceStatus).toBe('error'); }); test('keeps ledger loading independent from the recharge lifecycle', async () => { let resolveLedger!: (value: { entries: [] }) => void; clientApi.getClientProfileRechargeCenter.mockResolvedValue({ - walletBalance: 18, mudPointBalance: detailedBalance(18), }); clientApi.getClientProfileWalletLedger.mockImplementation( @@ -326,7 +318,6 @@ describe('useWalletStore', () => { test('hides account-owned dialogs and data when the account changes', async () => { clientApi.getClientProfileRechargeCenter.mockResolvedValue({ - walletBalance: 18, mudPointBalance: detailedBalance(18), }); clientApi.getClientProfileWalletLedger.mockResolvedValue({ entries: [] }); diff --git a/packages/shared/src/stores/createProfileWalletStore.test.ts b/packages/shared/src/stores/createProfileWalletStore.test.ts index 609132cdc..88c5da148 100644 --- a/packages/shared/src/stores/createProfileWalletStore.test.ts +++ b/packages/shared/src/stores/createProfileWalletStore.test.ts @@ -290,28 +290,15 @@ describe('createProfileWalletStore', () => { expect(store.getState().mudPointBalance?.permanentPoints).toBe(90); }); - test('applies an owner-scoped legacy mutation snapshot atomically', () => { + test('rejects a snapshot from another owner', () => { const store = createProfileWalletStore({ getRechargeCenter: vi.fn() }); store.getState().setWalletOwner('user-a'); - const structuredSnapshot = store - .getState() - .captureWalletBalanceSnapshot('user-a'); - store - .getState() - .applyWalletBalanceSnapshot(structuredSnapshot!, balance(90)); - const legacySnapshot = store - .getState() - .captureWalletBalanceSnapshot('user-a'); + const snapshot = store.getState().captureWalletBalanceSnapshot('user-b'); expect( - store.getState().applyLegacyWalletBalanceSnapshot(legacySnapshot!, 37), - ).toBe(true); - expect(store.getState()).toMatchObject({ - legacyWalletBalance: 37, - mudPointBalance: null, - mudPointBalanceStatus: 'ready', - mudPointBalanceError: '', - }); + store.getState().applyWalletBalanceSnapshot(snapshot!, balance(37)), + ).toBe(false); + expect(store.getState().mudPointBalance).toBeNull(); }); test('rejects a snapshot after switching away from and back to the same owner', () => { diff --git a/packages/shared/src/stores/createProfileWalletStore.ts b/packages/shared/src/stores/createProfileWalletStore.ts index 2dd46c251..a2ec1eeeb 100644 --- a/packages/shared/src/stores/createProfileWalletStore.ts +++ b/packages/shared/src/stores/createProfileWalletStore.ts @@ -36,11 +36,7 @@ export type ProfileWalletStore = { ) => ProfileWalletBalanceSnapshot | null; applyWalletBalanceSnapshot: ( snapshot: ProfileWalletBalanceSnapshot, - balance: ProfileMudPointBalance | ProfileMudPointBalanceView, - ) => boolean; - applyLegacyWalletBalanceSnapshot: ( - snapshot: ProfileWalletBalanceSnapshot, - balance: number, + balance: ProfileMudPointBalance, ) => boolean; onWalletBalanceMayHaveChanged: () => Promise; resetWalletBalance: () => void; @@ -132,32 +128,6 @@ export function createProfileWalletStore( }); return true; }, - applyLegacyWalletBalanceSnapshot: (snapshot, balance) => { - if ( - !Number.isFinite(balance) || - get().ownerUserId !== snapshot.ownerUserId || - ownerVersion !== snapshot.ownerVersion || - snapshot.invalidationVersion < refreshVersion || - snapshot.invalidationVersion < settledRefreshVersion || - snapshot.operationSequence < latestAppliedOperationSequence - ) { - return false; - } - - invalidateActiveRefresh(); - latestAppliedOperationSequence = Math.max( - latestAppliedOperationSequence, - snapshot.operationSequence, - ); - settledRefreshVersion = snapshot.invalidationVersion; - set({ - mudPointBalance: null, - legacyWalletBalance: balance, - mudPointBalanceStatus: 'ready', - mudPointBalanceError: '', - }); - return true; - }, onWalletBalanceMayHaveChanged: () => { refreshVersion += 1; if (activeRefresh) { diff --git a/src/components/image-editor/ImageCanvasEditorView.test.tsx b/src/components/image-editor/ImageCanvasEditorView.test.tsx index 92b947c88..0d10fcf7f 100644 --- a/src/components/image-editor/ImageCanvasEditorView.test.tsx +++ b/src/components/image-editor/ImageCanvasEditorView.test.tsx @@ -339,19 +339,18 @@ describe('ImageCanvasEditorView', () => { updatedAt: null, }); getPlatformProfileRechargeCenterMock.mockResolvedValue({ - walletBalance: 1234, mudPointBalance: { totalPoints: 1234, permanentPoints: 1000, - limitedPoints: 214, - limitedExpiresAt: '2026-07-31T16:00:00Z', + monthlyPoints: 214, + monthlyResetsAt: '2026-07-31T16:00:00Z', dailyFreePoints: 20, dailyFreeResetPoints: 20, dailyFreeResetsAt: '2026-07-12T16:00:00Z', }, membership: { status: 'normal', - tier: 'normal', + plan: 'normal', startedAt: null, expiresAt: null, updatedAt: null, @@ -359,11 +358,12 @@ describe('ImageCanvasEditorView', () => { cycleResetsAt: null, cycleGrantedPoints: 0, cycleRemainingPoints: 0, - cyclePeriodDays: 30, + cycleIndex: 1, + cycleCount: 1, + cycleKind: 'monthly', }, pointProducts: [], - membershipProducts: [], - benefits: [], + membershipPlans: [], latestOrder: null, hasPointsRecharged: false, }); @@ -635,10 +635,9 @@ describe('ImageCanvasEditorView', () => { usePlatformWalletStore .getState() .applyWalletBalanceSnapshot(walletSnapshot!, { - totalPoints: 1234, permanentPoints: 1000, - limitedPoints: 214, - limitedExpiresAt: '2026-07-31T16:00:00Z', + monthlyPoints: 214, + monthlyResetsAt: '2026-07-31T16:00:00Z', dailyFreePoints: 20, dailyFreeResetPoints: 20, dailyFreeResetsAt: '2026-07-12T16:00:00Z', @@ -703,18 +702,12 @@ describe('ImageCanvasEditorView', () => { expect(openAccountModal).toHaveBeenCalledTimes(1); }); - it('shows the owner-matched legacy wallet total without inventing breakdown rows', async () => { + it('shows the breakdown read failure without inventing rows', async () => { const user = userEvent.setup(); usePlatformWalletStore.getState().setWalletOwner('user-1'); - const walletSnapshot = usePlatformWalletStore - .getState() - .captureWalletBalanceSnapshot('user-1'); - usePlatformWalletStore - .getState() - .applyLegacyWalletBalanceSnapshot(walletSnapshot!, 37); - getPlatformProfileRechargeCenterMock.mockResolvedValue({ - walletBalance: 37, - }); + getPlatformProfileRechargeCenterMock.mockRejectedValue( + new Error('network down'), + ); render( { ); const walletButton = await screen.findByRole('button', { - name: '泥点 37', + name: '泥点 --', }); await user.hover(walletButton); @@ -745,7 +738,7 @@ describe('ImageCanvasEditorView', () => { }); expect(within(details).getByText('泥点明细读取失败')).toBeTruthy(); expect(within(details).queryByText('充值中心响应缺少泥点余额')).toBeNull(); - expect(within(details).queryByText('不限时泥点')).toBeNull(); + expect(within(details).queryByText('永久泥点')).toBeNull(); expect(within(details).queryByText('每日免费泥点')).toBeNull(); }); @@ -786,10 +779,9 @@ describe('ImageCanvasEditorView', () => { usePlatformWalletStore .getState() .applyWalletBalanceSnapshot(walletSnapshot!, { - totalPoints: 1234, permanentPoints: 1000, - limitedPoints: 0, - limitedExpiresAt: null, + monthlyPoints: 0, + monthlyResetsAt: null, dailyFreePoints: 234, dailyFreeResetPoints: 20, dailyFreeResetsAt: '2026-07-12T16:00:00Z', @@ -823,8 +815,8 @@ describe('ImageCanvasEditorView', () => { const details = await screen.findByRole('dialog', { name: '泥点账户详情', }); - expect(within(details).getByText('不限时泥点')).toBeTruthy(); - expect(within(details).queryByText('限时泥点')).toBeNull(); + expect(within(details).getByText('永久泥点')).toBeTruthy(); + expect(within(details).getByText('月度泥点')).toBeTruthy(); expect(within(details).getByText('每日免费泥点')).toBeTruthy(); await user.click(within(details).getByRole('button', { name: '使用详情' })); diff --git a/src/components/image-editor/ImageCanvasTopbarView.test.tsx b/src/components/image-editor/ImageCanvasTopbarView.test.tsx index d8b2a1ff1..4a4c511df 100644 --- a/src/components/image-editor/ImageCanvasTopbarView.test.tsx +++ b/src/components/image-editor/ImageCanvasTopbarView.test.tsx @@ -17,8 +17,8 @@ import { useImageCanvasContextStore } from './useImageCanvasContextStore.ts'; const walletBreakdown = { totalPoints: 1_234, permanentPoints: 1_000, - limitedPoints: 214, - limitedExpiresAt: '2026-07-31T16:00:00Z', + monthlyPoints: 214, + monthlyResetsAt: '2026-07-31T16:00:00Z', dailyFreePoints: 20, dailyFreeResetPoints: 20, dailyFreeResetsAt: '2026-07-12T16:00:00Z', @@ -105,10 +105,6 @@ describe('ImageCanvasTopbarView', () => { const user = userEvent.setup(); const props = renderTopbar({ walletBalance: 12_345, - walletBreakdown: { - ...walletBreakdown, - totalPoints: 12_345, - }, }); const walletChip = screen.getByRole('button', { name: '泥点 12,345', @@ -118,8 +114,8 @@ describe('ImageCanvasTopbarView', () => { await user.hover(walletChip); const details = screen.getByRole('dialog', { name: '泥点账户详情' }); - expect(within(details).getByText('不限时泥点')).toBeTruthy(); - expect(within(details).queryByText('限时泥点')).toBeNull(); + expect(within(details).getByText('永久泥点')).toBeTruthy(); + expect(within(details).getByText('月度泥点')).toBeTruthy(); expect(within(details).getByText('每日免费泥点')).toBeTruthy(); await user.click(within(details).getByRole('button', { name: '使用详情' })); diff --git a/src/components/platform-entry/PlatformActiveProfileView.test.tsx b/src/components/platform-entry/PlatformActiveProfileView.test.tsx index acf9d4934..9b0efa1d4 100644 --- a/src/components/platform-entry/PlatformActiveProfileView.test.tsx +++ b/src/components/platform-entry/PlatformActiveProfileView.test.tsx @@ -77,8 +77,8 @@ describe('PlatformActiveProfileView', () => { mudPointBalance={{ totalPoints: 108, permanentPoints: 88, - limitedPoints: 0, - limitedExpiresAt: null, + monthlyPoints: 0, + monthlyResetsAt: null, dailyFreePoints: 20, dailyFreeResetPoints: 20, dailyFreeResetsAt: '2026-08-04T00:00:00+08:00', diff --git a/src/components/platform-entry/PlatformActiveProfileView.tsx b/src/components/platform-entry/PlatformActiveProfileView.tsx index 8b431a2aa..03ca20518 100644 --- a/src/components/platform-entry/PlatformActiveProfileView.tsx +++ b/src/components/platform-entry/PlatformActiveProfileView.tsx @@ -15,7 +15,7 @@ import { useCallback, useRef, useState } from 'react'; import type { AuthUser, ProfileDashboardSummary, - ProfileMudPointBalance, + ProfileMudPointBalanceView, } from '@/packages/shared/src'; import profileClockImage from '../../../media/profile/_Image (1).png'; @@ -66,7 +66,7 @@ type PlatformActiveProfileViewProps = { isLoadingDashboard: boolean; isLoadingWalletBalance: boolean; legacyWalletBalance: number | null; - mudPointBalance: ProfileMudPointBalance | null; + mudPointBalance: ProfileMudPointBalanceView | null; onLogin: () => void; onOpenApiKeys: () => void; onOpenCommunity: () => void; @@ -248,7 +248,7 @@ function formatDashboardCount(value: number) { } function formatWalletBalance( - balance: ProfileMudPointBalance | null, + balance: ProfileMudPointBalanceView | null, legacyBalance: number | null, isLoading: boolean, ) { diff --git a/src/components/platform-entry/PlatformEntryActiveFlowShell.test.tsx b/src/components/platform-entry/PlatformEntryActiveFlowShell.test.tsx index aff31a85a..e71e02398 100644 --- a/src/components/platform-entry/PlatformEntryActiveFlowShell.test.tsx +++ b/src/components/platform-entry/PlatformEntryActiveFlowShell.test.tsx @@ -41,7 +41,13 @@ const profileClientMock = vi.hoisted(() => ({ const profileCenterMock = vi.hoisted(() => ({ isWalletLedgerOpen: false, - rechargeCenter: null as { walletBalance: number } | null, + rechargeCenter: null as { + mudPointBalance?: { + dailyFreePoints: number; + monthlyPoints: number; + permanentPoints: number; + }; + } | null, })); const gameDistributionMock = vi.hoisted(() => ({ @@ -245,12 +251,10 @@ describe('PlatformEntryActiveFlowShell', () => { updatedAt: null, }); profileClientMock.getPlatformProfileRechargeCenter.mockResolvedValue({ - walletBalance: 888, mudPointBalance: { - totalPoints: 207, permanentPoints: 180, - limitedPoints: 7, - limitedExpiresAt: '2026-08-31T16:00:00Z', + monthlyPoints: 7, + monthlyResetsAt: '2026-08-31T16:00:00Z', dailyFreePoints: 20, dailyFreeResetPoints: 20, dailyFreeResetsAt: '2026-08-04T00:00:00+08:00', @@ -294,7 +298,14 @@ describe('PlatformEntryActiveFlowShell', () => { authUiMock.value.canAccessProtectedData = true; profileClientMock.getPlatformProfileDashboard.mockResolvedValue(null); profileClientMock.getPlatformProfileRechargeCenter.mockResolvedValue({ - walletBalance: 37, + mudPointBalance: { + permanentPoints: 37, + monthlyPoints: 0, + monthlyResetsAt: null, + dailyFreePoints: 0, + dailyFreeResetPoints: 20, + dailyFreeResetsAt: '2026-08-04T00:00:00+08:00', + }, }); profileCenterMock.isWalletLedgerOpen = true; @@ -338,10 +349,9 @@ describe('PlatformEntryActiveFlowShell', () => { profileClientMock.getPlatformProfileDashboard.mockResolvedValue(null); profileClientMock.getPlatformProfileRechargeCenter.mockResolvedValue({ mudPointBalance: { - totalPoints: 66, permanentPoints: 66, - limitedPoints: 0, - limitedExpiresAt: null, + monthlyPoints: 0, + monthlyResetsAt: null, dailyFreePoints: 0, dailyFreeResetPoints: 20, dailyFreeResetsAt: '2026-08-04T00:00:00+08:00', @@ -379,10 +389,9 @@ describe('PlatformEntryActiveFlowShell', () => { }); resolveNextOwner({ mudPointBalance: { - totalPoints: 67, permanentPoints: 67, - limitedPoints: 0, - limitedExpiresAt: null, + monthlyPoints: 0, + monthlyResetsAt: null, dailyFreePoints: 0, dailyFreeResetPoints: 20, dailyFreeResetsAt: '2026-08-04T00:00:00+08:00', @@ -406,10 +415,9 @@ describe('PlatformEntryActiveFlowShell', () => { profileClientMock.getPlatformProfileDashboard.mockResolvedValue(null); profileClientMock.getPlatformProfileRechargeCenter.mockResolvedValue({ mudPointBalance: { - totalPoints: 10, permanentPoints: 10, - limitedPoints: 0, - limitedExpiresAt: null, + monthlyPoints: 0, + monthlyResetsAt: null, dailyFreePoints: 0, dailyFreeResetPoints: 20, dailyFreeResetsAt: '2026-08-04T00:00:00+08:00', diff --git a/src/components/platform-entry/PlatformEntryActiveFlowShell.tsx b/src/components/platform-entry/PlatformEntryActiveFlowShell.tsx index 26007e677..a2af89877 100644 --- a/src/components/platform-entry/PlatformEntryActiveFlowShell.tsx +++ b/src/components/platform-entry/PlatformEntryActiveFlowShell.tsx @@ -22,6 +22,7 @@ import { PlatformProfileRechargeModal } from '../../../packages/shared/src/compo import { PlatformProfileReferralModal } from '../../../packages/shared/src/components/PlatformProfileReferralModal'; import { PlatformProfileWalletLedgerModal } from '../../../packages/shared/src/components/PlatformProfileWalletLedgerModal'; import type { ProfileDashboardSummary } from '../../../packages/shared/src/contracts/runtime'; +import { resolveProfileMudPointDisplayTotal } from '../../../packages/shared/src/utils/mudPoints'; import { hasAppHistoryBackEntry, pushAppHistoryPath, @@ -376,7 +377,9 @@ export function PlatformEntryFlowShellImpl({ }); const legacyWalletBalance = walletOwnerMatchesCurrentUser ? (storedLegacyWalletBalance ?? - profileCenter.rechargeCenter?.walletBalance ?? + resolveProfileMudPointDisplayTotal( + profileCenter.rechargeCenter?.mudPointBalance, + ) ?? null) : null; diff --git a/src/components/platform-entry/usePlatformProfileCenterController.recharge.test.tsx b/src/components/platform-entry/usePlatformProfileCenterController.recharge.test.tsx index 5b222cc95..7cb0803e5 100644 --- a/src/components/platform-entry/usePlatformProfileCenterController.recharge.test.tsx +++ b/src/components/platform-entry/usePlatformProfileCenterController.recharge.test.tsx @@ -32,10 +32,9 @@ function deferred() { function mudPointBalance(totalPoints: number) { return { - totalPoints, permanentPoints: totalPoints, - limitedPoints: 0, - limitedExpiresAt: null, + monthlyPoints: 0, + monthlyResetsAt: null, dailyFreePoints: 0, dailyFreeResetPoints: 20, dailyFreeResetsAt: '2026-08-07T00:00:00+08:00', @@ -52,7 +51,6 @@ function nativeRechargeResponse(totalPoints = 80, orderId = 'order-a') { expirationCheckedAt: null, }, center: { - walletBalance: totalPoints, mudPointBalance: mudPointBalance(totalPoints), }, wechatNativePayment: { @@ -76,14 +74,13 @@ describe('usePlatformProfileCenterController recharge fallback', () => { ); }); - test('keeps the response legacy balance when no shared snapshot exists', async () => { + test('keeps the response three-pool balance when no shared snapshot exists', async () => { usePlatformWalletStore.getState().setWalletOwner('user-a'); profileClientMocks.getPlatformProfileRechargeCenter.mockResolvedValue({ - walletBalance: 37, + mudPointBalance: mudPointBalance(37), membership: null, pointProducts: [], - membershipProducts: [], - benefits: [], + membershipPlans: [], latestOrder: null, hasPointsRecharged: false, }); @@ -92,7 +89,9 @@ describe('usePlatformProfileCenterController recharge fallback', () => { act(() => result.current.loadRechargeCenter()); await waitFor(() => { - expect(result.current.rechargeCenter?.walletBalance).toBe(37); + expect( + result.current.rechargeCenter?.mudPointBalance?.permanentPoints, + ).toBe(37); }); }); @@ -198,7 +197,9 @@ describe('usePlatformProfileCenterController recharge fallback', () => { expect(result.current.submittingRechargeProductId).toBeNull(); expect(result.current.nativeWechatPayment?.orderId).toBe('order-b'); expect(result.current.rechargePaymentResult).toBeNull(); - expect(result.current.rechargeCenter?.walletBalance).toBe(90); + expect( + result.current.rechargeCenter?.mudPointBalance?.permanentPoints, + ).toBe(90); expect(usePlatformWalletStore.getState()).toMatchObject({ ownerUserId: 'user-b', mudPointBalance: mudPointBalance(90), diff --git a/src/components/platform-entry/usePlatformProfileCenterController.test.tsx b/src/components/platform-entry/usePlatformProfileCenterController.test.tsx index 4c5506bc4..40b0e4d75 100644 --- a/src/components/platform-entry/usePlatformProfileCenterController.test.tsx +++ b/src/components/platform-entry/usePlatformProfileCenterController.test.tsx @@ -19,19 +19,17 @@ vi.mock('../../services/platform-entry/platformProfileClient', async () => { }); const rechargeCenter: ProfileRechargeCenterResponse = { - walletBalance: 100, mudPointBalance: { - totalPoints: 100, permanentPoints: 100, - limitedPoints: 0, - limitedExpiresAt: null, + monthlyPoints: 0, + monthlyResetsAt: null, dailyFreePoints: 0, dailyFreeResetPoints: 0, dailyFreeResetsAt: null, }, membership: { status: 'normal', - tier: 'normal', + plan: 'normal', startedAt: null, expiresAt: null, updatedAt: null, @@ -39,11 +37,12 @@ const rechargeCenter: ProfileRechargeCenterResponse = { cycleResetsAt: null, cycleGrantedPoints: 0, cycleRemainingPoints: 0, - cyclePeriodDays: 30, + cycleIndex: 1, + cycleCount: 1, + cycleKind: 'monthly', }, pointProducts: [], - membershipProducts: [], - benefits: [], + membershipPlans: [], latestOrder: null, hasPointsRecharged: false, }; diff --git a/src/components/platform-entry/usePlatformProfileCenterController.ts b/src/components/platform-entry/usePlatformProfileCenterController.ts index 208eaccd7..eb27c29bb 100644 --- a/src/components/platform-entry/usePlatformProfileCenterController.ts +++ b/src/components/platform-entry/usePlatformProfileCenterController.ts @@ -1864,8 +1864,6 @@ export function usePlatformProfileCenterController({ } return { ...rechargeCenter, - walletBalance: - mudPointBalance?.totalPoints ?? rechargeCenter.walletBalance, mudPointBalance: mudPointBalance ?? rechargeCenter.mudPointBalance, }; }, [mudPointBalance, rechargeCenter, walletOwnerMatchesCurrentUser]); diff --git a/src/stores/usePlatformWalletStore.test.tsx b/src/stores/usePlatformWalletStore.test.tsx index 2b133a265..d7a95d234 100644 --- a/src/stores/usePlatformWalletStore.test.tsx +++ b/src/stores/usePlatformWalletStore.test.tsx @@ -22,12 +22,10 @@ beforeEach(() => { vi.clearAllMocks(); usePlatformWalletStore.getState().resetWalletBalance(); profileClientMocks.getPlatformProfileRechargeCenter.mockResolvedValue({ - walletBalance: 42, mudPointBalance: { - totalPoints: 42, permanentPoints: 42, - limitedPoints: 0, - limitedExpiresAt: null, + monthlyPoints: 0, + monthlyResetsAt: null, dailyFreePoints: 0, dailyFreeResetPoints: 20, dailyFreeResetsAt: '2026-08-08T00:00:00+08:00',