fix 余额异步倒退

This commit is contained in:
2026-07-17 14:11:41 +08:00
parent 9b3e2f279f
commit cdf39e85af
2 changed files with 36 additions and 27 deletions
+19 -15
View File
@@ -75,7 +75,6 @@ import {
selectGameCreationAppReadyTasks,
} from '../../../packages/shared/src/contracts/gameCreationApp';
import type {
ProfileDashboardSummary,
ProfileMudPointBalance,
ProfileRechargeCenterResponse,
ProfileRechargeProduct,
@@ -5360,8 +5359,8 @@ export function WorkspaceLauncher({
const [runtimeConfigOpen, setRuntimeConfigOpen] = useState(false);
const [pendingNonEmptyProject, setPendingNonEmptyProject] =
useState<PendingNonEmptyProject | null>(null);
const [profileDashboard, setProfileDashboard] =
useState<ProfileDashboardSummary | null>(null);
const [walletBalance, setWalletBalance] = useState<number | null>(null);
const walletBalanceRevisionRef = useRef(0);
const [mudPointBalance, setMudPointBalance] =
useState<ProfileMudPointBalance | null>(null);
const [mudPointBalanceStatus, setMudPointBalanceStatus] = useState<
@@ -5830,18 +5829,25 @@ export function WorkspaceLauncher({
useEffect(() => {
let disposed = false;
const walletBalanceRevision = walletBalanceRevisionRef.current;
void getClientProfileDashboard()
.then((dashboard) => {
if (disposed) {
if (
disposed ||
walletBalanceRevisionRef.current !== walletBalanceRevision
) {
return;
}
setProfileDashboard(dashboard);
setWalletBalance(dashboard.walletBalance);
})
.catch((error) => {
if (disposed) {
if (
disposed ||
walletBalanceRevisionRef.current !== walletBalanceRevision
) {
return;
}
setProfileDashboard(null);
setWalletBalance(null);
setMudPointBalanceError(
error instanceof Error ? error.message : '泥点读取失败',
);
@@ -6198,9 +6204,8 @@ export function WorkspaceLauncher({
try {
const center = await getClientProfileRechargeCenter();
setMudPointBalance(center.mudPointBalance ?? null);
setProfileDashboard((current) =>
current ? { ...current, walletBalance: center.walletBalance } : current,
);
walletBalanceRevisionRef.current += 1;
setWalletBalance(center.walletBalance);
setMudPointBalanceStatus('ready');
} catch (error) {
setMudPointBalance(null);
@@ -6236,9 +6241,8 @@ export function WorkspaceLauncher({
setMudPointBalance(center.mudPointBalance ?? null);
setMudPointBalanceStatus('ready');
setMudPointBalanceError('');
setProfileDashboard((current) =>
current ? { ...current, walletBalance: center.walletBalance } : current,
);
walletBalanceRevisionRef.current += 1;
setWalletBalance(center.walletBalance);
}
async function loadRechargeCenter() {
@@ -8682,7 +8686,7 @@ export function WorkspaceLauncher({
<div className="launcher-account-bar" aria-label="账户资产">
<PlatformMudPointWalletEntry
balance={profileDashboard?.walletBalance ?? null}
balance={walletBalance}
breakdown={mudPointBalance}
isLoading={mudPointBalanceStatus === 'loading'}
error={mudPointBalanceError || null}
@@ -9468,7 +9472,7 @@ export function WorkspaceLauncher({
<PlatformProfileWalletLedgerModal
ledger={walletLedger}
fallbackBalance={
profileDashboard?.walletBalance ?? mudPointBalance?.totalPoints ?? 0
walletBalance ?? mudPointBalance?.totalPoints ?? 0
}
isLoading={walletLedgerLoading}
error={walletLedgerError}
@@ -1585,15 +1585,6 @@ describe('AI 游戏创作 App 界面边界', () => {
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',
},
membership: {
status: 'inactive',
tier: 'normal',
@@ -1631,13 +1622,14 @@ describe('AI 游戏创作 App 界面边界', () => {
};
let rechargeOrderRequestCount = 0;
let releaseLateRechargeOrder: (() => void) | null = null;
let resolveInitialDashboard: ((response: Response) => void) | null = null;
const fetchSpy = vi
.spyOn(globalThis, 'fetch')
.mockImplementation(async (input: RequestInfo | URL) => {
const url = String(input);
if (url === '/api/profile/dashboard') {
return new Response(JSON.stringify({ walletBalance: 120 }), {
status: 200,
return new Promise<Response>((resolve) => {
resolveInitialDashboard = resolve;
});
}
if (url === '/api/profile/recharge-center') {
@@ -1698,9 +1690,13 @@ describe('AI 游戏创作 App 界面边界', () => {
...rechargeCenter,
walletBalance: 180,
mudPointBalance: {
...rechargeCenter.mudPointBalance,
totalPoints: 180,
permanentPoints: 160,
limitedPoints: 20,
limitedExpiresAt: '2026-08-01T00:00:00Z',
dailyFreePoints: 0,
dailyFreeResetPoints: 20,
dailyFreeResetsAt: '2026-07-18T00:00:00Z',
},
},
}),
@@ -1717,6 +1713,7 @@ describe('AI 游戏创作 App 界面边界', () => {
await screen.findByRole('dialog', { name: '购买更多泥点' }),
).not.toBeNull();
expect(screen.getByText('当前余额 120 泥点')).not.toBeNull();
expect(screen.getByRole('button', { name: '泥点 120' })).not.toBeNull();
fireEvent.click(screen.getByRole('button', { name: '关闭购买更多泥点' }));
fireEvent.click(screen.getByRole('button', { name: '我的' }));
@@ -1748,6 +1745,14 @@ describe('AI 游戏创作 App 界面边界', () => {
expect(screen.queryByRole('dialog', { name: '微信扫码支付' })).toBeNull();
});
expect(screen.getByText('当前余额 180 泥点')).not.toBeNull();
expect(screen.getByRole('button', { name: '泥点 180' })).not.toBeNull();
await act(async () => {
resolveInitialDashboard?.(
new Response(JSON.stringify({ walletBalance: 40 }), { status: 200 }),
);
});
expect(screen.getByRole('button', { name: '泥点 180' })).not.toBeNull();
fireEvent.click(screen.getByRole('button', { name: /60泥点.*购买/ }));
await waitFor(() => {