重构(平台入口): 平台侧与 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: [] });