修复兼容钱包总额展示
向个人中心和图片画板传递 owner 匹配的 legacy 总额 保持钱包分桶与账单明细为空避免伪造数据 补充个人中心和图片画板回归测试及契约文档
This commit is contained in:
@@ -670,6 +670,45 @@ describe('ImageCanvasEditorView', () => {
|
||||
expect(openAccountModal).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('shows the owner-matched legacy wallet total without inventing breakdown rows', async () => {
|
||||
const user = userEvent.setup();
|
||||
usePlatformWalletStore.getState().setWalletOwner('user-1');
|
||||
getPlatformProfileRechargeCenterMock.mockResolvedValue({
|
||||
walletBalance: 37,
|
||||
});
|
||||
render(
|
||||
<AuthUiContext.Provider
|
||||
value={createAuthValue({
|
||||
user: {
|
||||
id: 'user-1',
|
||||
publicUserCode: 'U001',
|
||||
displayName: '测试用户',
|
||||
avatarUrl: null,
|
||||
phoneNumberMasked: '138****0000',
|
||||
loginMethod: 'password',
|
||||
bindingStatus: 'active',
|
||||
wechatBound: false,
|
||||
},
|
||||
canAccessProtectedData: true,
|
||||
})}
|
||||
>
|
||||
<ImageCanvasEditorView legacyWalletBalance={37} />
|
||||
</AuthUiContext.Provider>,
|
||||
);
|
||||
|
||||
const walletButton = await screen.findByRole('button', {
|
||||
name: '泥点 37',
|
||||
});
|
||||
await user.hover(walletButton);
|
||||
|
||||
const details = await screen.findByRole('dialog', {
|
||||
name: '泥点账户详情',
|
||||
});
|
||||
expect(within(details).getByText('充值中心响应缺少泥点余额')).toBeTruthy();
|
||||
expect(within(details).queryByText('不限时泥点')).toBeNull();
|
||||
expect(within(details).queryByText('每日免费泥点')).toBeNull();
|
||||
});
|
||||
|
||||
it('opens the shared wallet breakdown and ledger from the canvas topbar', async () => {
|
||||
const user = userEvent.setup();
|
||||
usePlatformWalletStore.getState().setWalletOwner('user-1');
|
||||
|
||||
@@ -137,6 +137,7 @@ const CANVAS_STARTUP_TOOLS: CanvasStartupTool[] = [
|
||||
];
|
||||
|
||||
type ImageCanvasEditorViewProps = {
|
||||
legacyWalletBalance?: number | null;
|
||||
onProjectAccessLost?: () => void;
|
||||
};
|
||||
|
||||
@@ -319,6 +320,7 @@ const DEAD_INLINE_PLACEHOLDER_NOTICE =
|
||||
'上次的完美像素处理未完成,画布占位已清理。请确认素材库是否已生成派生图。';
|
||||
|
||||
export function ImageCanvasEditorView({
|
||||
legacyWalletBalance = null,
|
||||
onProjectAccessLost,
|
||||
}: ImageCanvasEditorViewProps = {}) {
|
||||
const authUi = useAuthUi();
|
||||
@@ -352,7 +354,9 @@ export function ImageCanvasEditorView({
|
||||
const mudPointBalanceError = walletOwnerMatchesCurrentUser
|
||||
? storedMudPointBalanceError
|
||||
: '';
|
||||
const walletBalance = mudPointBalance?.totalPoints ?? null;
|
||||
const walletBalance =
|
||||
mudPointBalance?.totalPoints ??
|
||||
(walletOwnerMatchesCurrentUser ? legacyWalletBalance : null);
|
||||
const editorRootRef = useRef<HTMLElement | null>(null);
|
||||
const canvasViewportRef = useRef<HTMLDivElement | null>(null);
|
||||
const assetListRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
@@ -49,6 +49,7 @@ describe('PlatformActiveProfileView', () => {
|
||||
dashboard={null}
|
||||
isLoadingDashboard={false}
|
||||
isLoadingWalletBalance={false}
|
||||
legacyWalletBalance={null}
|
||||
mudPointBalance={null}
|
||||
user={null}
|
||||
/>,
|
||||
@@ -71,6 +72,7 @@ describe('PlatformActiveProfileView', () => {
|
||||
}}
|
||||
isLoadingDashboard={false}
|
||||
isLoadingWalletBalance={false}
|
||||
legacyWalletBalance={null}
|
||||
mudPointBalance={{
|
||||
totalPoints: 108,
|
||||
permanentPoints: 88,
|
||||
@@ -114,6 +116,7 @@ describe('PlatformActiveProfileView', () => {
|
||||
dashboard={null}
|
||||
isLoadingDashboard={false}
|
||||
isLoadingWalletBalance={false}
|
||||
legacyWalletBalance={null}
|
||||
mudPointBalance={null}
|
||||
user={authenticatedUser}
|
||||
/>,
|
||||
@@ -148,6 +151,7 @@ describe('PlatformActiveProfileView', () => {
|
||||
dashboard={null}
|
||||
isLoadingDashboard={false}
|
||||
isLoadingWalletBalance={false}
|
||||
legacyWalletBalance={null}
|
||||
mudPointBalance={null}
|
||||
user={authenticatedUser}
|
||||
/>,
|
||||
@@ -212,6 +216,7 @@ describe('PlatformActiveProfileView', () => {
|
||||
dashboard={null}
|
||||
isLoadingDashboard={false}
|
||||
isLoadingWalletBalance={false}
|
||||
legacyWalletBalance={null}
|
||||
mudPointBalance={null}
|
||||
user={authenticatedUser}
|
||||
/>,
|
||||
|
||||
@@ -63,6 +63,7 @@ type PlatformActiveProfileViewProps = {
|
||||
dashboard: ProfileDashboardSummary | null;
|
||||
isLoadingDashboard: boolean;
|
||||
isLoadingWalletBalance: boolean;
|
||||
legacyWalletBalance: number | null;
|
||||
mudPointBalance: ProfileMudPointBalance | null;
|
||||
onLogin: () => void;
|
||||
onOpenApiKeys: () => void;
|
||||
@@ -245,11 +246,15 @@ function formatDashboardCount(value: number) {
|
||||
|
||||
function formatWalletBalance(
|
||||
balance: ProfileMudPointBalance | null,
|
||||
legacyBalance: number | null,
|
||||
isLoading: boolean,
|
||||
) {
|
||||
if (balance) {
|
||||
return formatDashboardCount(balance.totalPoints);
|
||||
}
|
||||
if (legacyBalance !== null) {
|
||||
return formatDashboardCount(legacyBalance);
|
||||
}
|
||||
if (isLoading) {
|
||||
return '读取中';
|
||||
}
|
||||
@@ -267,6 +272,7 @@ export function PlatformActiveProfileView({
|
||||
dashboard,
|
||||
isLoadingDashboard,
|
||||
isLoadingWalletBalance,
|
||||
legacyWalletBalance,
|
||||
mudPointBalance,
|
||||
onLogin,
|
||||
onOpenApiKeys,
|
||||
@@ -562,6 +568,7 @@ export function PlatformActiveProfileView({
|
||||
label="泥点余额"
|
||||
value={formatWalletBalance(
|
||||
mudPointBalance,
|
||||
legacyWalletBalance,
|
||||
isLoadingWalletBalance,
|
||||
)}
|
||||
icon={Coins}
|
||||
|
||||
@@ -99,7 +99,16 @@ vi.mock('../project/ProjectGalleryView', () => ({
|
||||
}));
|
||||
|
||||
vi.mock('../image-editor/ImageCanvasEditorView', () => ({
|
||||
ImageCanvasEditorView: () => <main aria-label="图片画布编辑器" />,
|
||||
ImageCanvasEditorView: ({
|
||||
legacyWalletBalance,
|
||||
}: {
|
||||
legacyWalletBalance?: number | null;
|
||||
}) => (
|
||||
<main
|
||||
aria-label="图片画布编辑器"
|
||||
data-legacy-wallet-balance={legacyWalletBalance ?? ''}
|
||||
/>
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock(
|
||||
@@ -214,7 +223,7 @@ describe('PlatformEntryActiveFlowShell', () => {
|
||||
expect(screen.queryByText('999')).toBeNull();
|
||||
});
|
||||
|
||||
it('uses the owner-matched legacy recharge balance for an empty ledger', async () => {
|
||||
it('passes the owner-matched legacy total to profile and editor while keeping the ledger empty', async () => {
|
||||
authUiMock.value.user = {
|
||||
id: 'user-1',
|
||||
publicUserCode: '100001',
|
||||
@@ -233,15 +242,29 @@ describe('PlatformEntryActiveFlowShell', () => {
|
||||
profileCenterMock.isWalletLedgerOpen = true;
|
||||
profileCenterMock.rechargeCenter = { walletBalance: 37 };
|
||||
|
||||
render(
|
||||
const { rerender } = render(
|
||||
<PlatformEntryFlowShellImpl
|
||||
selectionStage="profile"
|
||||
setSelectionStage={vi.fn()}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(
|
||||
await screen.findByRole('button', { name: '泥点余额 37' }),
|
||||
).toBeTruthy();
|
||||
expect(await screen.findByText('37泥点')).toBeTruthy();
|
||||
expect(screen.getByText('暂无账单记录')).toBeTruthy();
|
||||
|
||||
rerender(
|
||||
<PlatformEntryFlowShellImpl
|
||||
selectionStage="image-editor"
|
||||
setSelectionStage={vi.fn()}
|
||||
/>,
|
||||
);
|
||||
const imageEditor = await screen.findByRole('main', {
|
||||
name: '图片画布编辑器',
|
||||
});
|
||||
expect(imageEditor.getAttribute('data-legacy-wallet-balance')).toBe('37');
|
||||
});
|
||||
|
||||
it('clears the previous wallet synchronously when the authenticated account changes', async () => {
|
||||
|
||||
@@ -287,6 +287,9 @@ export function PlatformEntryFlowShellImpl({
|
||||
requestLogin: () => authUi?.openLoginModal(),
|
||||
currentUser: authUi?.user,
|
||||
});
|
||||
const legacyWalletBalance = walletOwnerMatchesCurrentUser
|
||||
? (profileCenter.rechargeCenter?.walletBalance ?? null)
|
||||
: null;
|
||||
|
||||
const openCreation = useCallback(() => {
|
||||
if (!isDesktopLayout) {
|
||||
@@ -362,6 +365,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
<div className="image-editor-stage-shell flex h-full min-h-0 min-w-0 flex-col overflow-hidden">
|
||||
<Suspense fallback={<LoadingPanel label="正在加载编辑器..." />}>
|
||||
<ImageCanvasEditorView
|
||||
legacyWalletBalance={legacyWalletBalance}
|
||||
onProjectAccessLost={replaceWithProjectGallery}
|
||||
/>
|
||||
</Suspense>
|
||||
@@ -370,9 +374,6 @@ export function PlatformEntryFlowShellImpl({
|
||||
}
|
||||
|
||||
const isAuthenticated = Boolean(authUi?.user);
|
||||
const legacyWalletBalance = walletOwnerMatchesCurrentUser
|
||||
? (profileCenter.rechargeCenter?.walletBalance ?? null)
|
||||
: null;
|
||||
const balance = mudPointBalance?.totalPoints ?? legacyWalletBalance;
|
||||
const isCreationStage =
|
||||
!isProfileStage &&
|
||||
@@ -543,6 +544,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
dashboard={dashboard}
|
||||
isLoadingDashboard={isLoadingDashboard}
|
||||
isLoadingWalletBalance={mudPointBalanceStatus === 'loading'}
|
||||
legacyWalletBalance={legacyWalletBalance}
|
||||
mudPointBalance={mudPointBalance}
|
||||
user={authUi?.user}
|
||||
onLogin={() => authUi?.openLoginModal()}
|
||||
|
||||
Reference in New Issue
Block a user