From 71a6ed5945b4223bcb49b088bec7365d6357eb0c Mon Sep 17 00:00:00 2001 From: kdletters Date: Wed, 1 Jul 2026 17:48:01 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B3=A8=E5=86=8C=E6=97=B6=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E9=93=BE=E6=8E=A5=E9=82=80=E8=AF=B7=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 手机号注册提交链接中的邀请码。 系统邀请码 metadata.tags 会合并到账户标签。 更新自动兑换邀请码的前端与模块测试。 --- .../spacetime-module/src/runtime/profile.rs | 11 ++++++ src/components/auth/AuthGate.test.tsx | 35 +++++++------------ src/components/auth/AuthGate.tsx | 21 +++++++++-- 3 files changed, 41 insertions(+), 26 deletions(-) diff --git a/server-rs/crates/spacetime-module/src/runtime/profile.rs b/server-rs/crates/spacetime-module/src/runtime/profile.rs index d8adf26c8..462cf0d78 100644 --- a/server-rs/crates/spacetime-module/src/runtime/profile.rs +++ b/server-rs/crates/spacetime-module/src/runtime/profile.rs @@ -1631,6 +1631,16 @@ mod tests { assert!(result.error_message.is_none()); } + #[test] + fn invite_code_metadata_tags_are_user_tags() { + let tags = profile_invite_code_metadata_user_tags( + r#"{"tags":["系统"," beta ","系统"],"channel":"spring"}"#, + ) + .expect("metadata tags should parse"); + + assert_eq!(tags, vec!["系统".to_string(), "beta".to_string()]); + } + #[test] fn recent_public_work_play_counts_group_requested_profiles_in_window() { let now_micros = PUBLIC_WORK_PLAY_DAY_MICROS * 10; @@ -2801,6 +2811,7 @@ fn profile_invite_code_metadata_user_tags(metadata_json: &str) -> Result { +test('auth gate auto redeems link invite code for new sms account', async () => { const user = userEvent.setup(); window.history.replaceState(null, '', '/?inviteCode=spring-2026'); authMocks.getAuthLoginOptions.mockResolvedValue({ @@ -646,7 +646,14 @@ test('auth gate hides register entry and opens invite modal for new sms account' token: 'jwt-phone-new', user: mockUser, created: true, - referral: null, + referral: { + ok: true, + message: '邀请码已绑定', + inviteeRewardGranted: true, + inviterRewardGranted: false, + inviteeBalanceAfter: 30, + inviterBalanceAfter: 0, + }, }); render( @@ -674,30 +681,12 @@ test('auth gate hides register entry and opens invite modal for new sms account' expect(authMocks.loginWithPhoneCode).toHaveBeenCalledWith( '13800000000', '123456', - ); - }); - - const inviteDialog = await screen.findByRole('dialog', { - name: '请填写邀请码', - }); - expect(inviteDialog.className).toContain('platform-auth-card'); - expect(inviteDialog.className).toContain('platform-modal-shell'); - const inviteCodeInput = within(inviteDialog).getByLabelText( - '邀请码', - ) as HTMLInputElement; - expect(inviteCodeInput.value).toBe('SPRING2026'); - expect(inviteCodeInput.className).toContain('platform-text-field'); - expect( - within(inviteDialog).getByRole('button', { name: '提交' }), - ).toBeTruthy(); - - await user.click(within(inviteDialog).getByRole('button', { name: '提交' })); - - await waitFor(() => { - expect(authMocks.redeemRegistrationInviteCode).toHaveBeenCalledWith( 'SPRING2026', ); }); + + expect(screen.queryByRole('dialog', { name: '请填写邀请码' })).toBeNull(); + expect(authMocks.redeemRegistrationInviteCode).not.toHaveBeenCalled(); }); test('registration invite modal can skip when invite code is empty', async () => { diff --git a/src/components/auth/AuthGate.tsx b/src/components/auth/AuthGate.tsx index 715c72586..80331e5a5 100644 --- a/src/components/auth/AuthGate.tsx +++ b/src/components/auth/AuthGate.tsx @@ -966,11 +966,26 @@ export function AuthGate({ children }: AuthGateProps) { setLoggingIn(true); setError(''); try { - const response = await loginWithPhoneCode(phone, code); + const registrationInviteCode = + pendingInviteCode || readInviteCodeFromLocation(); + const response = registrationInviteCode + ? await loginWithPhoneCode(phone, code, registrationInviteCode) + : await loginWithPhoneCode(phone, code); + const autoRedeemedInvite = response.referral?.ok === true; setStoredLastLoginPhone(phone); setLoginCaptchaChallenge(null); - setShowRegistrationInviteModal(response.created); - setRegistrationInviteError(''); + setShowRegistrationInviteModal( + response.created && !autoRedeemedInvite, + ); + setRegistrationInviteError( + response.referral?.ok === false + ? (response.referral.message ?? + '邀请码绑定失败,请确认后重试。') + : '', + ); + if (autoRedeemedInvite) { + setPendingInviteCode(''); + } activateReadyUser(response.user); } catch (loginError) { setError(