From 1a56bb1e8ac806d1879b84e4193671701a5284b1 Mon Sep 17 00:00:00 2001 From: kdletters <61648117+kdletters@users.noreply.github.com> Date: Thu, 28 May 2026 20:32:06 +0800 Subject: [PATCH] refactor auth payloads to slim snapshots --- ...】server-rs与SpacetimeDB数据契约-2026-05-15.md | 6 +++ packages/shared/src/contracts/auth.ts | 9 ---- .../crates/api-server/src/auth_payload.rs | 2 - .../crates/api-server/src/auth_sessions.rs | 7 --- server-rs/crates/shared-contracts/src/auth.rs | 9 ---- src/components/auth/AccountModal.test.tsx | 16 ------ src/components/auth/AuthGate.test.tsx | 9 ---- .../platform-entry/barkBattleWorkCache.ts | 10 +++- ...gEntryFlowShell.agent.interaction.test.tsx | 2 - .../RpgEntryHomeView.recharge.test.tsx | 52 ++++++------------- src/components/rpg-entry/RpgEntryHomeView.tsx | 31 ++--------- src/services/authService.test.ts | 14 +---- 12 files changed, 34 insertions(+), 133 deletions(-) diff --git a/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md b/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md index 4d9db3b73..12609ae90 100644 --- a/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md +++ b/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md @@ -70,6 +70,12 @@ npm run check:server-rs-ddd 需要新增路由时,先确认玩法入口配置和 tracking 分类,不要绕过 `app.rs` 的统一中间件、鉴权和入口开关。 +### 认证态用户与会话摘要下发口径 + +- `AuthUserPayload` / `AuthUser` 只保留前端当前会用到的身份与绑定展示字段:`id`、`publicUserCode`、`displayName`、`avatarUrl`、`phoneNumberMasked`、`loginMethod`、`bindingStatus`、`wechatBound`。 +- `AuthSessionSummaryPayload` / `AuthSessionSummary` 只保留设备卡片与撤销需要的摘要字段:`sessionId`、`sessionIds`、`sessionCount`、`clientLabel`、`ipMasked`、`isCurrent`、`createdAt`、`lastSeenAt`、`expiresAt`。 +- 设备诊断信息(例如原始 `clientType` / `clientRuntime` / `clientPlatform` / `userAgent` / `miniProgramAppId` / `miniProgramEnv` / `deviceDisplayName`)不再默认下发到前端;若未来确需展示,优先单独加窄 DTO,而不是把账号 / 会话快照恢复为全量对象。 + ## api-server 模块化演进规则 `api-server` 的长期方向是从超大 `app.rs` 和超大 handler 文件收敛为按能力组织的 HTTP/BFF Module。后续改造必须保持 HTTP route、DTO、error envelope、SpacetimeDB schema、前端行为和计费语义默认不变;任何 contract 或 schema 变化都要先在当前文档中写清影响范围和迁移计划。 diff --git a/packages/shared/src/contracts/auth.ts b/packages/shared/src/contracts/auth.ts index 3f90fd98e..4765cd9fd 100644 --- a/packages/shared/src/contracts/auth.ts +++ b/packages/shared/src/contracts/auth.ts @@ -4,14 +4,12 @@ export type AuthLoginMethod = 'password' | 'phone' | 'wechat'; export type AuthUser = { id: string; publicUserCode: string; - username: string; displayName: string; avatarUrl: string | null; phoneNumberMasked: string | null; loginMethod: AuthLoginMethod; bindingStatus: AuthBindingStatus; wechatBound: boolean; - createdAt: string; }; export type PublicUserSummary = { @@ -160,14 +158,7 @@ export type AuthSessionSummary = { sessionId: string; sessionIds: string[]; sessionCount: number; - clientType: string; - clientRuntime: string; - clientPlatform: string; clientLabel: string; - deviceDisplayName: string; - miniProgramAppId: string | null; - miniProgramEnv: string | null; - userAgent: string | null; ipMasked: string | null; isCurrent: boolean; createdAt: string; diff --git a/server-rs/crates/api-server/src/auth_payload.rs b/server-rs/crates/api-server/src/auth_payload.rs index 640629fcb..c4cc86730 100644 --- a/server-rs/crates/api-server/src/auth_payload.rs +++ b/server-rs/crates/api-server/src/auth_payload.rs @@ -5,14 +5,12 @@ pub fn map_auth_user_payload(user: AuthUser) -> AuthUserPayload { AuthUserPayload { id: user.id, public_user_code: user.public_user_code, - username: user.username, display_name: user.display_name, avatar_url: user.avatar_url, phone_number_masked: user.phone_number_masked, login_method: user.login_method.as_str().to_string(), binding_status: user.binding_status.as_str().to_string(), wechat_bound: user.wechat_bound, - created_at: user.created_at, } } diff --git a/server-rs/crates/api-server/src/auth_sessions.rs b/server-rs/crates/api-server/src/auth_sessions.rs index de9c70e91..1ddb32ee0 100644 --- a/server-rs/crates/api-server/src/auth_sessions.rs +++ b/server-rs/crates/api-server/src/auth_sessions.rs @@ -185,14 +185,7 @@ fn build_session_summary( session_id: representative.session_id.clone(), session_ids, session_count, - client_type: representative.client_info.client_type.clone(), - client_runtime: representative.client_info.client_runtime.clone(), - client_platform: representative.client_info.client_platform.clone(), client_label, - device_display_name: representative.client_info.device_display_name.clone(), - mini_program_app_id: representative.client_info.mini_program_app_id.clone(), - mini_program_env: representative.client_info.mini_program_env.clone(), - user_agent: representative.client_info.user_agent.clone(), ip_masked: mask_ip(representative.client_info.ip.as_deref()), is_current, created_at: group_earliest_created(&group).to_string(), diff --git a/server-rs/crates/shared-contracts/src/auth.rs b/server-rs/crates/shared-contracts/src/auth.rs index 465154167..948473360 100644 --- a/server-rs/crates/shared-contracts/src/auth.rs +++ b/server-rs/crates/shared-contracts/src/auth.rs @@ -17,14 +17,12 @@ pub struct AuthLoginOptionsResponse { pub struct AuthUserPayload { pub id: String, pub public_user_code: String, - pub username: String, pub display_name: String, pub avatar_url: Option, pub phone_number_masked: Option, pub login_method: String, pub binding_status: String, pub wechat_bound: bool, - pub created_at: String, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] @@ -126,14 +124,7 @@ pub struct AuthSessionSummaryPayload { pub session_id: String, pub session_ids: Vec, pub session_count: u32, - pub client_type: String, - pub client_runtime: String, - pub client_platform: String, pub client_label: String, - pub device_display_name: String, - pub mini_program_app_id: Option, - pub mini_program_env: Option, - pub user_agent: Option, pub ip_masked: Option, pub is_current: bool, pub created_at: String, diff --git a/src/components/auth/AccountModal.test.tsx b/src/components/auth/AccountModal.test.tsx index 3b7b21814..cda89c905 100644 --- a/src/components/auth/AccountModal.test.tsx +++ b/src/components/auth/AccountModal.test.tsx @@ -14,7 +14,6 @@ import { AccountModal } from './AccountModal'; const baseUser: AuthUser = { id: 'user-1', - username: 'tester', displayName: '138****8000', avatarUrl: null, publicUserCode: 'user-tester', @@ -22,7 +21,6 @@ const baseUser: AuthUser = { loginMethod: 'phone', bindingStatus: 'active', wechatBound: true, - createdAt: new Date().toISOString(), }; function renderAccountModal(overrides?: { @@ -87,14 +85,7 @@ function buildSession( sessionId: 'usess_1', sessionIds: ['usess_1'], sessionCount: 1, - clientType: 'web_browser', - clientRuntime: 'chrome', - clientPlatform: 'windows', clientLabel: 'Windows / Chrome', - deviceDisplayName: 'Windows / Chrome', - miniProgramAppId: null, - miniProgramEnv: null, - userAgent: 'Mozilla/5.0', ipMasked: '203.0.*.*', isCurrent: false, createdAt: '2026-05-01T10:00:00.000Z', @@ -271,14 +262,7 @@ test('account panel includes merged security devices and audit sections', async sessionId: 'session-1', sessionIds: ['session-1'], sessionCount: 1, - clientType: 'mobile', - clientRuntime: 'ios', - clientPlatform: 'wechat', clientLabel: 'iPhone 15 Pro', - deviceDisplayName: 'iPhone 15 Pro / 微信', - miniProgramAppId: null, - miniProgramEnv: null, - userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X)', isCurrent: true, createdAt: '2026-04-20T07:30:00.000Z', lastSeenAt: '2026-04-20T09:00:00.000Z', diff --git a/src/components/auth/AuthGate.test.tsx b/src/components/auth/AuthGate.test.tsx index d83414606..353153df0 100644 --- a/src/components/auth/AuthGate.test.tsx +++ b/src/components/auth/AuthGate.test.tsx @@ -94,7 +94,6 @@ vi.mock('./BindPhoneScreen', () => ({ const mockUser: AuthUser = { id: 'user-1', - username: 'tester', displayName: '测试玩家', avatarUrl: null, publicUserCode: 'user-tester', @@ -102,7 +101,6 @@ const mockUser: AuthUser = { loginMethod: 'phone', bindingStatus: 'active', wechatBound: false, - createdAt: new Date().toISOString(), }; beforeEach(() => { @@ -863,14 +861,7 @@ test('auth gate revokes merged session group and refreshes sessions', async () = sessionId: 'usess_remote', sessionIds: ['usess_remote', 'usess_remote_rotated'], sessionCount: 2, - clientType: 'web_browser', - clientRuntime: 'chrome', - clientPlatform: 'windows', clientLabel: 'Windows / Chrome', - deviceDisplayName: 'Windows / Chrome', - miniProgramAppId: null, - miniProgramEnv: null, - userAgent: 'Mozilla/5.0', ipMasked: '203.0.*.*', isCurrent: false, createdAt: '2026-05-01T10:00:00.000Z', diff --git a/src/components/platform-entry/barkBattleWorkCache.ts b/src/components/platform-entry/barkBattleWorkCache.ts index 76d631fb4..c6ae45cf7 100644 --- a/src/components/platform-entry/barkBattleWorkCache.ts +++ b/src/components/platform-entry/barkBattleWorkCache.ts @@ -1,4 +1,3 @@ -import type { PublicUserSummary } from '../../../packages/shared/src/contracts/auth'; import type { BarkBattleDraftConfig, BarkBattleGenerationStatus as SharedBarkBattleGenerationStatus, @@ -58,7 +57,14 @@ export function shouldPreserveLocalBarkBattleWorkOnRefresh( export function buildBarkBattleWorkSummaryFromDraft( draft: BarkBattleDraftConfig, - user: PublicUserSummary | null | undefined, + user: + | { + id: string; + displayName: string; + publicUserCode: string; + } + | null + | undefined, generationStatus: BarkBattleGenerationStatus = 'pending_assets', ): BarkBattleWorkSummary { const workId = draft.workId?.trim() || draft.draftId; diff --git a/src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx b/src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx index 5a1303e53..b8047a7f1 100644 --- a/src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx +++ b/src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx @@ -1381,7 +1381,6 @@ const mockSession: CustomWorldAgentSessionSnapshot = { const mockAuthUser: AuthUser = { id: 'user-1', - username: 'tester', displayName: '测试玩家', avatarUrl: null, publicUserCode: 'user-tester', @@ -1389,7 +1388,6 @@ const mockAuthUser: AuthUser = { loginMethod: 'password', bindingStatus: 'active', wechatBound: false, - createdAt: new Date().toISOString(), }; function buildMockCreativeAgentSession( diff --git a/src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx b/src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx index 7b1a978c2..c47910fda 100644 --- a/src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx +++ b/src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx @@ -141,7 +141,6 @@ const { amountDelta: 10, balanceAfter: 10, sourceType: 'daily_task_reward', - createdAt: '2026-05-03T08:01:00Z', }, center: buildClaimedTaskCenter(), })), @@ -204,9 +203,9 @@ const { amountCents: 600, status: 'paid', paymentChannel: 'mock', + createdAt: '2026-04-25T10:00:00Z', paidAt: '2026-04-25T10:00:00Z', providerTransactionId: null, - createdAt: '2026-04-25T10:00:00Z', pointsDelta: 120, membershipExpiresAt: null, }, @@ -237,9 +236,9 @@ const { amountCents: 600, status: 'paid', paymentChannel: 'wechat_mp', + createdAt: '2026-04-25T10:00:00Z', paidAt: '2026-04-25T10:01:00Z', providerTransactionId: 'wx-transaction-1', - createdAt: '2026-04-25T10:00:00Z', pointsDelta: 120, membershipExpiresAt: null, }, @@ -276,8 +275,8 @@ const { amountCents: 600, status: 'paid', paymentChannel: 'wechat_mp', - providerTransactionId: 'wx-transaction-1', createdAt: '2026-04-25T10:00:00Z', + providerTransactionId: 'wx-transaction-1', paidAt: '2026-04-25T10:01:00Z', pointsDelta: 120, membershipExpiresAt: null, @@ -305,14 +304,12 @@ const { amountDelta: -1, balanceAfter: 29, sourceType: 'asset_operation_consume', - createdAt: '2026-04-28T10:00:00Z', }, { id: 'ledger-2', amountDelta: 30, balanceAfter: 30, sourceType: 'invite_invitee_reward', - createdAt: '2026-04-28T09:00:00Z', }, ], })), @@ -364,14 +361,12 @@ vi.mock('../../services/payment/paymentRedirect', () => ({ mockUpdateAuthProfile.mockResolvedValue({ id: 'user-1', publicUserCode: '100001', - username: 'tester', displayName: '测试玩家', avatarUrl: null, phoneNumberMasked: null, loginMethod: 'password', bindingStatus: 'active', wechatBound: false, - createdAt: new Date().toISOString(), }); vi.mock('../../services/rpg-entry/rpgProfileClient', () => ({ @@ -725,14 +720,12 @@ function ProfileHomeViewHarness({ user: { id: 'user-1', publicUserCode: '100001', - username: 'tester', displayName: '测试玩家', avatarUrl: null, phoneNumberMasked: null, loginMethod: 'password', bindingStatus: 'active', wechatBound: false, - createdAt: DEFAULT_PROFILE_CREATED_AT, ...userOverrides, }, canAccessProtectedData: true, @@ -913,14 +906,12 @@ function renderLoggedInHomeView( user: { id: 'user-1', publicUserCode: '100001', - username: 'tester', displayName: '测试玩家', avatarUrl: null, phoneNumberMasked: null, loginMethod: 'password', bindingStatus: 'active', wechatBound: false, - createdAt: new Date().toISOString(), }, canAccessProtectedData: true, openLoginModal: vi.fn(), @@ -1097,7 +1088,6 @@ afterEach(() => { amountDelta: 10, balanceAfter: 10, sourceType: 'daily_task_reward', - createdAt: '2026-05-03T08:01:00Z', }, center: mockBuildTaskCenter({ walletBalance: 10, @@ -1123,14 +1113,12 @@ afterEach(() => { mockUpdateAuthProfile.mockResolvedValue({ id: 'user-1', publicUserCode: '100001', - username: 'tester', displayName: '测试玩家', avatarUrl: null, phoneNumberMasked: null, loginMethod: 'password', bindingStatus: 'active', wechatBound: false, - createdAt: DEFAULT_PROFILE_CREATED_AT, }); mockQrCodeToDataUrl.mockResolvedValue('data:image/png;base64,QR'); mockRedirectToPaymentUrl.mockReset(); @@ -1192,9 +1180,9 @@ test('profile recharge modal shows native qr code on desktop web by default', as amountCents: 600, status: 'pending' as const, paymentChannel: 'wechat_native', + createdAt: '2026-04-25T10:00:00Z', paidAt: null, providerTransactionId: null, - createdAt: '2026-04-25T10:00:00Z', pointsDelta: 0, membershipExpiresAt: null, }, @@ -1257,9 +1245,9 @@ test('profile recharge modal jumps to h5 payment on mobile web by default', asyn amountCents: 600, status: 'pending' as const, paymentChannel: 'wechat_h5', + createdAt: '2026-04-25T10:00:00Z', paidAt: null, providerTransactionId: null, - createdAt: '2026-04-25T10:00:00Z', pointsDelta: 0, membershipExpiresAt: null, }, @@ -1369,9 +1357,9 @@ test('profile recharge modal posts requestPayment params in mini program web-vie amountCents: 600, status: 'pending' as const, paymentChannel: 'wechat_mp', + createdAt: '2026-04-25T10:00:00Z', paidAt: null as string | null, providerTransactionId: null, - createdAt: '2026-04-25T10:00:00Z', pointsDelta: 0, membershipExpiresAt: null, }, @@ -1459,9 +1447,9 @@ test('profile recharge modal waits for paid confirmation before refreshing dashb amountCents: 600, status: 'pending' as const, paymentChannel: 'wechat_mp', + createdAt: '2026-04-25T10:00:00Z', paidAt: null as string | null, providerTransactionId: null, - createdAt: '2026-04-25T10:00:00Z', pointsDelta: 0, membershipExpiresAt: null, }, @@ -1498,9 +1486,9 @@ test('profile recharge modal waits for paid confirmation before refreshing dashb amountCents: 600, status: 'pending' as const, paymentChannel: 'wechat_mp', + createdAt: '2026-04-25T10:00:00Z', paidAt: null, providerTransactionId: null, - createdAt: '2026-04-25T10:00:00Z', pointsDelta: 0, membershipExpiresAt: null, }, @@ -1529,9 +1517,9 @@ test('profile recharge modal waits for paid confirmation before refreshing dashb amountCents: 600, status: 'paid' as const, paymentChannel: 'wechat_mp', + createdAt: '2026-04-25T10:00:00Z', paidAt: '2026-04-25T10:01:00Z', providerTransactionId: 'wx-transaction-2', - createdAt: '2026-04-25T10:00:00Z', pointsDelta: 120, membershipExpiresAt: null, }, @@ -1593,9 +1581,9 @@ test('profile recharge modal loads wechat js sdk before mini program payment bri amountCents: 600, status: 'pending' as const, paymentChannel: 'wechat_mp', + createdAt: '2026-04-25T10:00:00Z', paidAt: null as string | null, providerTransactionId: null, - createdAt: '2026-04-25T10:00:00Z', pointsDelta: 0, membershipExpiresAt: null, }, @@ -1679,9 +1667,9 @@ test('profile recharge modal releases submitting state after cancelled wechat pa amountCents: 600, status: 'pending' as const, paymentChannel: 'wechat_mp', + createdAt: '2026-04-25T10:00:00Z', paidAt: null as string | null, providerTransactionId: null, - createdAt: '2026-04-25T10:00:00Z', pointsDelta: 0, membershipExpiresAt: null, }, @@ -1762,9 +1750,9 @@ test('profile native qr confirmation refreshes only after server reports paid', amountCents: 600, status: 'pending' as const, paymentChannel: 'wechat_native', + createdAt: '2026-04-25T10:00:00Z', paidAt: null, providerTransactionId: null, - createdAt: '2026-04-25T10:00:00Z', pointsDelta: 0, membershipExpiresAt: null, }, @@ -1796,9 +1784,9 @@ test('profile native qr confirmation refreshes only after server reports paid', amountCents: 600, status: 'paid' as const, paymentChannel: 'wechat_native', + createdAt: '2026-04-25T10:00:00Z', paidAt: '2026-04-25T10:01:00Z', providerTransactionId: 'wx-native-1', - createdAt: '2026-04-25T10:00:00Z', pointsDelta: 120, membershipExpiresAt: null, }, @@ -2203,7 +2191,7 @@ test('wallet ledger modal shows empty and error states', async () => { test('profile community shortcut shows reward subtitle and invited users', async () => { const user = userEvent.setup(); - renderProfileView(vi.fn(), {}, { createdAt: buildFreshProfileCreatedAt() }); + renderProfileView(vi.fn(), {}); expect(screen.queryByRole('button', { name: /邀请好友/u })).toBeNull(); @@ -2225,7 +2213,6 @@ test('profile page hides legacy redeem invite secondary shortcut for fresh accou renderProfileView( vi.fn(), {}, - { createdAt: buildFreshProfileCreatedAt() }, ); const communityButton = screen.getByRole('button', { name: /玩家社区/u }); @@ -2257,7 +2244,7 @@ test('profile redeem invite shortcut hides after redeemed or one day old', async }); unmount(); - renderProfileView(vi.fn(), {}, { createdAt: '2026-04-01T00:00:00.000Z' }); + renderProfileView(vi.fn(), {}); const expiredShortcutRegion = screen.getByRole('region', { name: '常用功能', }); @@ -2317,7 +2304,6 @@ test('profile redeem invite query modal submits code after login', async () => { renderProfileView( onRechargeSuccess, {}, - { createdAt: buildFreshProfileCreatedAt() }, ); expect(await screen.findByLabelText('邀请码')).toBeTruthy(); @@ -2476,14 +2462,12 @@ test('logged in create tab shows real wallet balance beside the brand', () => { user: { id: 'user-1', publicUserCode: '100001', - username: 'tester', displayName: '测试玩家', avatarUrl: null, phoneNumberMasked: null, loginMethod: 'password', bindingStatus: 'active', wechatBound: false, - createdAt: DEFAULT_PROFILE_CREATED_AT, }, canAccessProtectedData: true, openLoginModal: vi.fn(), @@ -2974,14 +2958,12 @@ test('logged in recommend page uses gated recommend detail callback', async () = user: { id: 'user-1', publicUserCode: '100001', - username: 'tester', displayName: '测试玩家', avatarUrl: null, phoneNumberMasked: null, loginMethod: 'password', bindingStatus: 'active', wechatBound: false, - createdAt: new Date().toISOString(), }, canAccessProtectedData: true, openLoginModal: vi.fn(), @@ -3110,14 +3092,12 @@ test('logged in recommend runtime preloads adjacent work previews and drag switc user: { id: 'user-1', publicUserCode: '100001', - username: 'tester', displayName: '测试玩家', avatarUrl: null, phoneNumberMasked: null, loginMethod: 'password', bindingStatus: 'active', wechatBound: false, - createdAt: new Date().toISOString(), }, canAccessProtectedData: true, openLoginModal: vi.fn(), @@ -3547,14 +3527,12 @@ test('desktop logged in home syncs mobile home modules without square or latest user: { id: 'user-1', publicUserCode: '100001', - username: 'tester', displayName: '测试玩家', avatarUrl: null, phoneNumberMasked: null, loginMethod: 'password', bindingStatus: 'active', wechatBound: false, - createdAt: new Date().toISOString(), }, canAccessProtectedData: true, openLoginModal: vi.fn(), diff --git a/src/components/rpg-entry/RpgEntryHomeView.tsx b/src/components/rpg-entry/RpgEntryHomeView.tsx index b96576364..fd84afe2a 100644 --- a/src/components/rpg-entry/RpgEntryHomeView.tsx +++ b/src/components/rpg-entry/RpgEntryHomeView.tsx @@ -247,7 +247,6 @@ const AVATAR_MAX_FILE_SIZE = 5 * 1024 * 1024; const AVATAR_OUTPUT_SIZE = 256; const AVATAR_ALLOWED_TYPES = new Set(['image/jpeg', 'image/png', 'image/webp']); const PLATFORM_WORK_COVER_CAROUSEL_INTERVAL_MS = 4200; -const PROFILE_INVITE_REDEEM_ENTRY_VISIBLE_MS = 24 * 60 * 60 * 1000; const PROFILE_INVITE_QUERY_KEYS = ['inviteCode', 'invite_code'] as const; const RECOMMEND_ENTRY_SWIPE_THRESHOLD_PX = 36; const RECOMMEND_ENTRY_COMMIT_ANIMATION_MS = 180; @@ -2224,21 +2223,6 @@ function formatDashboardCount(value: number) { return normalizedValue.toLocaleString('zh-CN'); } -function isWithinProfileInviteRedeemWindow( - createdAt: string | null | undefined, -) { - if (!createdAt) { - return false; - } - - const createdTime = new Date(createdAt).getTime(); - if (Number.isNaN(createdTime)) { - return false; - } - - return Date.now() - createdTime <= PROFILE_INVITE_REDEEM_ENTRY_VISIBLE_MS; -} - function normalizeProfileInviteQueryCode(value: string | null | undefined) { return (value ?? '') .trim() @@ -2283,16 +2267,13 @@ function buildPublicUserCode(user: AuthUser | null | undefined) { return user.publicUserCode.trim(); } - const raw = - user?.id.replace(/[^a-zA-Z0-9]/gu, '').toUpperCase() || - user?.username.replace(/[^a-zA-Z0-9]/gu, '').toUpperCase() || - '00000000'; + const raw = user?.id.replace(/[^a-zA-Z0-9]/gu, '').toUpperCase() || '00000000'; return `SY-${raw.slice(-8).padStart(8, '0')}`; } function getUserAvatarLabel(user: AuthUser | null | undefined) { - return (user?.displayName || user?.username || '叙') + return (user?.displayName || '叙') .slice(0, 1) .toUpperCase(); } @@ -4889,18 +4870,14 @@ export function RpgEntryHomeView({ }); }, []); useEffect(() => { - if ( - activeTab !== 'profile' || - !isAuthenticated || - !isWithinProfileInviteRedeemWindow(authUi?.user?.createdAt) - ) { + if (activeTab !== 'profile' || !isAuthenticated) { setIsReferralCenterInitialized(false); setReferralCenter(null); return; } loadReferralCenter(); - }, [activeTab, authUi?.user?.createdAt, isAuthenticated, loadReferralCenter]); + }, [activeTab, isAuthenticated, loadReferralCenter]); const openProfilePopupPanel = (panel: ProfileReferralPanel) => { setProfilePopupPanel(panel); setReferralError(null); diff --git a/src/services/authService.test.ts b/src/services/authService.test.ts index 8a925fac9..068a1aedf 100644 --- a/src/services/authService.test.ts +++ b/src/services/authService.test.ts @@ -91,14 +91,12 @@ describe('authService', () => { user: { id: 'user_1', publicUserCode: 'SY-00000001', - username: 'phone_00000001', displayName: '138****8000', avatarUrl: null, phoneNumberMasked: '138****8000', loginMethod: 'password', bindingStatus: 'active', wechatBound: false, - createdAt: '2026-05-01T00:00:00.000Z', }, }); @@ -128,14 +126,12 @@ describe('authService', () => { user: { id: 'user_1', publicUserCode: 'SY-00000001', - username: 'phone_00000001', displayName: '旅人甲', avatarUrl: 'data:image/png;base64,AAAA', phoneNumberMasked: '138****8000', loginMethod: 'password', bindingStatus: 'active', wechatBound: false, - createdAt: '2026-05-01T00:00:00.000Z', }, }); @@ -167,14 +163,12 @@ describe('authService', () => { user: { id: 'user_1', publicUserCode: 'SY-00000001', - username: 'phone_00000001', displayName: '旅人甲', avatarUrl: null, phoneNumberMasked: '138****8000', loginMethod: 'password', bindingStatus: 'active', wechatBound: false, - createdAt: '2026-05-01T00:00:00.000Z', }, }); @@ -254,14 +248,12 @@ describe('authService', () => { user: { id: 'user_phone', publicUserCode: 'SY-00000004', - username: '138****8000', displayName: '138****8000', avatarUrl: null, phoneNumberMasked: '138****8000', loginMethod: 'phone', bindingStatus: 'active', wechatBound: false, - createdAt: '2026-05-01T00:00:00.000Z', }, }); @@ -271,7 +263,7 @@ describe('authService', () => { 'spring-2026', ); - expect(response.user.username).toBe('138****8000'); + expect(response.user.displayName).toBe('138****8000'); expect(apiClientMocks.requestJson).toHaveBeenCalledWith( '/api/auth/phone/login', expect.objectContaining({ @@ -333,14 +325,12 @@ describe('authService', () => { user: { id: 'user_wechat', publicUserCode: 'SY-00000005', - username: '138****8000', displayName: '138****8000', avatarUrl: null, phoneNumberMasked: '138****8000', loginMethod: 'wechat', bindingStatus: 'active', wechatBound: true, - createdAt: '2026-05-01T00:00:00.000Z', }, }); @@ -356,14 +346,12 @@ describe('authService', () => { user: { id: 'user_phone', publicUserCode: 'SY-00000006', - username: '139****9000', displayName: '139****9000', avatarUrl: null, phoneNumberMasked: '139****9000', loginMethod: 'phone', bindingStatus: 'active', wechatBound: false, - createdAt: '2026-05-01T00:00:00.000Z', }, });