重构(平台入口): 平台侧与 AGC 壳改用三池余额,退役 legacy 总额写入通道

- 平台入口与钱包 store 不再从充值中心响应读 walletBalance,合计改为按三池派生
- AGC 账户钱包同步:只透传 mudPointBalance,删掉 RechargeContent 的 walletBalance 排除项
- 删除已无生产调用方的 applyLegacyWalletBalanceSnapshot 与对应测试;明细读取失败不再伪造「只有总额」的中间态
- 图片编辑器与平台入口测试 fixture 改为三池形状,钱包面板断言改用「永久泥点 / 月度泥点」

Co-authored-by: Junie <junie@jetbrains.com>
This commit is contained in:
2026-10-02 19:03:30 +08:00
parent 3b9d645042
commit 144068187c
16 changed files with 116 additions and 196 deletions
+1 -1
View File
@@ -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';
@@ -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<typeof captureWalletBalanceSnapshot>,
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,
@@ -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,
},
},
@@ -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: [] });
@@ -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', () => {
@@ -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<void>;
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) {
@@ -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(
<AuthUiContext.Provider
value={createAuthValue({
@@ -736,7 +729,7 @@ describe('ImageCanvasEditorView', () => {
);
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: '使用详情' }));
@@ -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: '使用详情' }));
@@ -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',
@@ -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,
) {
@@ -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',
@@ -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;
@@ -32,10 +32,9 @@ function deferred<T>() {
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),
@@ -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,
};
@@ -1864,8 +1864,6 @@ export function usePlatformProfileCenterController({
}
return {
...rechargeCenter,
walletBalance:
mudPointBalance?.totalPoints ?? rechargeCenter.walletBalance,
mudPointBalance: mudPointBalance ?? rechargeCenter.mudPointBalance,
};
}, [mudPointBalance, rechargeCenter, walletOwnerMatchesCurrentUser]);
+2 -4
View File
@@ -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',