From 9ade40c29bd39b2a4ad16efa1901dab353f3aa5e Mon Sep 17 00:00:00 2001 From: JiaJun <2394389886@qq.com> Date: Wed, 15 Jul 2026 18:00:33 +0800 Subject: [PATCH] =?UTF-8?q?feat(auth):=20=E6=9B=B4=E6=96=B0=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E8=AE=A4=E8=AF=81=E7=B1=BB=E5=9E=8B=E5=AE=9A=E4=B9=89?= =?UTF-8?q?=E5=B9=B6=E4=BC=98=E5=8C=96=E6=8F=90=E6=AC=BE=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加 AuthCoinAmount 类型和 AuthWithdrawAccount 接口 - 在 AuthUser 和相关 DTO 中增加余额和提款账户字段 - 将获取用户资料的请求方法从 POST 改为 GET - 实现提款账户信息的默认值设置和状态管理 - 添加 JackpotPoolTicker 组件并在状态栏中显示 - 更新多语言文件中的游戏规则说明 - 实现提款表单的预填充和状态重置功能 --- src/api/auth-api.ts | 4 +- src/components/message-broadcast.tsx | 16 ++-- .../components/desktop/desktop-status.tsx | 23 +++-- src/hooks/use-withdraw-vm.ts | 86 ++++++++++++++++--- src/lib/auth/auth-normalizers.ts | 12 +++ src/locales/en-US.ts | 3 +- src/locales/id-ID.ts | 3 +- src/locales/ms-MY.ts | 3 +- src/locales/zh-CN.ts | 5 +- src/type/auth.type.ts | 30 ++++++- 10 files changed, 154 insertions(+), 31 deletions(-) diff --git a/src/api/auth-api.ts b/src/api/auth-api.ts index 57aaaea..edc0d42 100644 --- a/src/api/auth-api.ts +++ b/src/api/auth-api.ts @@ -67,7 +67,7 @@ function logAuthSessionExpiry(action: string, session: AuthSessionInput) { } async function getCurrentUserProfileByToken(userToken: string) { - const response = await api.post(AUTH_ENDPOINTS.profile, { + const response = await api.get(AUTH_ENDPOINTS.profile, { headers: { Authorization: `Bearer ${userToken}`, 'user-token': userToken, @@ -195,7 +195,7 @@ export async function sendSmsCode( } export async function getCurrentUserProfile() { - const response = await api.post(AUTH_ENDPOINTS.profile) + const response = await api.get(AUTH_ENDPOINTS.profile) return normalizeAuthUserProfile( unwrapEnvelope( diff --git a/src/components/message-broadcast.tsx b/src/components/message-broadcast.tsx index 0feb071..72c2f02 100644 --- a/src/components/message-broadcast.tsx +++ b/src/components/message-broadcast.tsx @@ -21,9 +21,13 @@ function formatWinAmount(value: string) { type MessageBroadcastProps = { className?: string + showJackpotPool?: boolean } -export function MessageBroadcast({ className }: MessageBroadcastProps) { +export function MessageBroadcast({ + className, + showJackpotPool = true, +}: MessageBroadcastProps) { const { t } = useTranslation() const prefersReducedMotion = useReducedMotion() const jackpotBroadcasts = useGameSessionStore( @@ -165,10 +169,12 @@ export function MessageBroadcast({ className }: MessageBroadcastProps) { - + {showJackpotPool ? ( + + ) : null} ) } diff --git a/src/features/game/components/desktop/desktop-status.tsx b/src/features/game/components/desktop/desktop-status.tsx index 5f36a7c..cd831ef 100644 --- a/src/features/game/components/desktop/desktop-status.tsx +++ b/src/features/game/components/desktop/desktop-status.tsx @@ -8,6 +8,7 @@ import fire from '@/assets/system/fire.webp' import lock from '@/assets/system/lock.webp' import statusCenter from '@/assets/system/status-center.webp' import statusLine from '@/assets/system/status-line.webp' +import { JackpotPoolTicker } from '@/components/jackpot-pool-ticker.tsx' import { LottiePlayer } from '@/components/lottie-player.tsx' import { MessageBroadcast } from '@/components/message-broadcast.tsx' import { SmartBackground } from '@/components/smart-background.tsx' @@ -43,7 +44,7 @@ export function DesktopStatusLine() { return (
- +
-
-
+
+
-
+
{t('gameDesktop.status.roundId')}: - + {roundId}
+ +
+ div:first-child]:md:!mr-design-5 [&>div:first-child]:md:!h-design-20 [&>div:first-child]:md:!w-design-20', + '[&>span:first-of-type]:md:!mr-[20px] [&>span:first-of-type]:md:!text-design-10', + '[&>span:last-child]:!shrink-0 [&>span:last-child]:!text-design-9 md:[&>span:last-child]:!text-design-20', + )} + /> +
diff --git a/src/hooks/use-withdraw-vm.ts b/src/hooks/use-withdraw-vm.ts index f39dfe7..40ef5aa 100644 --- a/src/hooks/use-withdraw-vm.ts +++ b/src/hooks/use-withdraw-vm.ts @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useMemo, useState } from 'react' +import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { DEFAULT_WITHDRAW_CONFIG, @@ -7,7 +7,7 @@ import { } from '@/constants' import { useDepositWithdrawConfig } from '@/hooks/use-deposit-withdraw-config' import { useAuthStore } from '@/store' -import type { DepositWithdrawConfig } from '@/type' +import type { AuthWithdrawAccount, DepositWithdrawConfig } from '@/type' const QUICK_WITHDRAW_OPTION_COUNT = 6 @@ -16,6 +16,24 @@ interface WithdrawDiamondBounds { minimumDiamonds: number } +interface WithdrawAccountDefaults { + bankAccount: string + holderName: string + receiverEmail: string + receiverPhone: string +} + +function getWithdrawAccountDefaults( + withdraw: AuthWithdrawAccount | null | undefined, +): WithdrawAccountDefaults { + return { + bankAccount: withdraw?.receiveAccount ?? '', + holderName: withdraw?.receiverName ?? '', + receiverEmail: withdraw?.receiverEmail ?? '', + receiverPhone: withdraw?.receiverMobile ?? '', + } +} + function formatNumber(locale: string, value: number) { return new Intl.NumberFormat(locale).format(value) } @@ -200,6 +218,11 @@ export function useWithdrawVm() { const { i18n, t } = useTranslation() const currentUser = useAuthStore((state) => state.currentUser) const withdrawConfigQuery = useDepositWithdrawConfig() + const withdrawAccountDefaults = useMemo( + () => getWithdrawAccountDefaults(currentUser?.withdraw), + [currentUser?.withdraw], + ) + const hasEditedWithdrawAccountRef = useRef(false) const config = useMemo(() => { const baseConfig = getNormalizedConfig( withdrawConfigQuery.data, @@ -228,10 +251,18 @@ export function useWithdrawVm() { const [paymentChannelCode, setPaymentChannelCode] = useState('') const [paymentType, setPaymentType] = useState('') const [bankCode, setBankCode] = useState('') - const [holderName, setHolderName] = useState('') - const [bankAccount, setBankAccount] = useState('') - const [receiverEmail, setReceiverEmail] = useState('') - const [receiverPhone, setReceiverPhone] = useState('') + const [holderName, setHolderNameState] = useState( + () => withdrawAccountDefaults.holderName, + ) + const [bankAccount, setBankAccountState] = useState( + () => withdrawAccountDefaults.bankAccount, + ) + const [receiverEmail, setReceiverEmailState] = useState( + () => withdrawAccountDefaults.receiverEmail, + ) + const [receiverPhone, setReceiverPhoneState] = useState( + () => withdrawAccountDefaults.receiverPhone, + ) const sortedPayChannels = useMemo( () => @@ -318,6 +349,28 @@ export function useWithdrawVm() { }, [maxWithdrawAmount], ) + const applyWithdrawAccountDefaults = useCallback(() => { + setHolderNameState(withdrawAccountDefaults.holderName) + setBankAccountState(withdrawAccountDefaults.bankAccount) + setReceiverEmailState(withdrawAccountDefaults.receiverEmail) + setReceiverPhoneState(withdrawAccountDefaults.receiverPhone) + }, [withdrawAccountDefaults]) + const setHolderName = useCallback((nextHolderName: string) => { + hasEditedWithdrawAccountRef.current = true + setHolderNameState(nextHolderName) + }, []) + const setBankAccount = useCallback((nextBankAccount: string) => { + hasEditedWithdrawAccountRef.current = true + setBankAccountState(nextBankAccount) + }, []) + const setReceiverEmail = useCallback((nextReceiverEmail: string) => { + hasEditedWithdrawAccountRef.current = true + setReceiverEmailState(nextReceiverEmail) + }, []) + const setReceiverPhone = useCallback((nextReceiverPhone: string) => { + hasEditedWithdrawAccountRef.current = true + setReceiverPhoneState(nextReceiverPhone) + }, []) useEffect(() => { const firstAvailablePayChannel = sortedPayChannels[0] @@ -406,6 +459,14 @@ export function useWithdrawVm() { } }, [amount, maxWithdrawAmount, setAmount]) + useEffect(() => { + if (hasEditedWithdrawAccountRef.current) { + return + } + + applyWithdrawAccountDefaults() + }, [applyWithdrawAccountDefaults]) + const quickAmounts = useMemo(() => { if (!selectedCurrency || !withdrawDiamondBounds) { return [] @@ -474,11 +535,14 @@ export function useWithdrawVm() { setPaymentChannelCode(nextPaymentChannelCode) setPaymentType(nextPaymentMethod?.code ?? '') setBankCode('') - setHolderName('') - setBankAccount('') - setReceiverEmail('') - setReceiverPhone('') - }, [config, maxWithdrawAmount, sortedPayChannels]) + hasEditedWithdrawAccountRef.current = false + applyWithdrawAccountDefaults() + }, [ + applyWithdrawAccountDefaults, + config, + maxWithdrawAmount, + sortedPayChannels, + ]) const selectedCurrencyPreview = useMemo(() => { const previewCurrencyCode = selectedCurrency?.code ?? '--' diff --git a/src/lib/auth/auth-normalizers.ts b/src/lib/auth/auth-normalizers.ts index a1e90c2..2a5ebbf 100644 --- a/src/lib/auth/auth-normalizers.ts +++ b/src/lib/auth/auth-normalizers.ts @@ -24,9 +24,11 @@ export function normalizeAuthUserProfile(dto: AuthUserProfileDto): AuthUser { return { channelId: dto.channel_id, coin: dto.coin, + coinBalance: dto.coin_balance, createTime: dto.create_time, currentStreak: dto.current_streak, email: dto.email, + frozenBalance: dto.frozen_balance, headImage: dto.head_image, id: dto.uuid, lastBetPeriodNo: dto.last_bet_period_no, @@ -34,8 +36,18 @@ export function normalizeAuthUserProfile(dto: AuthUserProfileDto): AuthUser { phone: dto.phone, registerInviteCode: dto.register_invite_code, riskFlags: dto.risk_flags, + totalDepositCoin: dto.total_deposit_coin, + totalWithdrawCoin: dto.total_withdraw_coin, username: dto.username, uuid: dto.uuid, + withdraw: dto.withdraw + ? { + receiveAccount: dto.withdraw.receive_account, + receiverEmail: dto.withdraw.receiver_email, + receiverMobile: dto.withdraw.receiver_mobile, + receiverName: dto.withdraw.receiver_name, + } + : undefined, } } diff --git a/src/locales/en-US.ts b/src/locales/en-US.ts index c31c4cc..310acae 100644 --- a/src/locales/en-US.ts +++ b/src/locales/en-US.ts @@ -251,7 +251,8 @@ export default { '', '1. Only **1 winning number** is drawn in each round.', '2. If the player’s selected numbers **include the winning number**, the bet is considered a win.', - '3. If a player selects multiple numbers and the bet amount is the same for each number, then:', + '3. Each round, a player may bet on **1–5 numbers only**, and must not select more than **5**.', + '4. If a player selects multiple numbers and the bet amount is the same for each number, then:', '', '```text', 'Winning Number Stake = Total Bet Amount ÷ Number of Selected Numbers', diff --git a/src/locales/id-ID.ts b/src/locales/id-ID.ts index 692f1e8..76a03c1 100644 --- a/src/locales/id-ID.ts +++ b/src/locales/id-ID.ts @@ -250,7 +250,8 @@ export default { '', '1. Hanya **1 nomor pemenang** yang akan dikeluarkan di setiap putaran.', '2. Jika nomor yang dipilih pemain **mencakup nomor pemenang**, maka taruhan tersebut dianggap menang.', - '3. Jika pemain memilih beberapa nomor dan jumlah taruhan untuk setiap nomor sama, maka:', + '3. Dalam setiap putaran, pemain hanya boleh bertaruh pada **1–5 nomor**, dan tidak boleh melebihi **5 nomor**.', + '4. Jika pemain memilih beberapa nomor dan jumlah taruhan untuk setiap nomor sama, maka:', '', '```text', 'Nilai Taruhan Nomor Menang = Total Taruhan ÷ Jumlah Nomor yang Dipilih', diff --git a/src/locales/ms-MY.ts b/src/locales/ms-MY.ts index 22722e0..91cb9d2 100644 --- a/src/locales/ms-MY.ts +++ b/src/locales/ms-MY.ts @@ -253,7 +253,8 @@ export default { '', '1. Hanya **1 nombor kemenangan** akan dikeluarkan dalam setiap pusingan.', '2. Jika nombor yang dipilih oleh pemain **mengandungi nombor kemenangan**, pertaruhan tersebut dianggap menang.', - '3. Jika pemain memilih beberapa nombor dan jumlah pertaruhan bagi setiap nombor adalah sama, maka:', + '3. Dalam setiap pusingan, pemain hanya boleh bertaruh pada **1–5 nombor**, dan tidak boleh melebihi **5 nombor**.', + '4. Jika pemain memilih beberapa nombor dan jumlah pertaruhan bagi setiap nombor adalah sama, maka:', '', '```text', 'Nilai Pertaruhan Nombor Menang = Jumlah Pertaruhan ÷ Bilangan Nombor yang Dipilih', diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts index a2e009d..fe3ac98 100644 --- a/src/locales/zh-CN.ts +++ b/src/locales/zh-CN.ts @@ -243,10 +243,11 @@ export default { '', '1. 每期只开出 **1 个中奖号码**。', '2. 如果玩家押注的号码中 **包含开奖号码**,则视为中奖。', - '3. 如果玩家选择了多个号码,且每个号码押注金额相同,则:', + '3. 每场玩家最多只能下注 **1~5 个号码**,不能超过 **5 个**。', + '4. 如果玩家选择了多个号码,且每个号码押注金额相同,则:', '', '```text', - '压中号码面额 = 本笔压注总额 ÷ 所选号码个数', + '压中号码面额 = 本笔投注总额 ÷ 所选号码个数', '```', '', '**示例:**', diff --git a/src/type/auth.type.ts b/src/type/auth.type.ts index 4d03edd..895d0ab 100644 --- a/src/type/auth.type.ts +++ b/src/type/auth.type.ts @@ -4,12 +4,23 @@ export type AuthStatus = 'anonymous' | 'authenticated' | 'restoring' export type AuthSubmitContext = 'login' | 'register' +export type AuthCoinAmount = number | string + +export interface AuthWithdrawAccount { + receiveAccount?: string + receiverEmail?: string + receiverMobile?: string + receiverName?: string +} + export interface AuthUser { createTime?: number channelId?: number - coin?: string + coin?: AuthCoinAmount + coinBalance?: number currentStreak?: number email?: string + frozenBalance?: number headImage?: string id: string isJackpot?: boolean @@ -21,8 +32,11 @@ export interface AuthUser { riskFlags?: number roles?: string[] streakLevel?: number + totalDepositCoin?: number + totalWithdrawCoin?: number username?: string uuid?: string + withdraw?: AuthWithdrawAccount } export interface AuthSessionInput { @@ -47,7 +61,7 @@ export interface AuthTokenDto { export interface AuthUserDto { channel_id: number - coin: string + coin: AuthCoinAmount phone?: string risk_flags: number username: string @@ -69,17 +83,27 @@ export interface RefreshTokenDto { export interface AuthUserProfileDto { channel_id: number - coin: string + coin: number + coin_balance: number create_time: number current_streak: number email: string + frozen_balance: number head_image: string last_bet_period_no: string phone: string register_invite_code: string risk_flags: number + total_deposit_coin: number + total_withdraw_coin: number username: string uuid: string + withdraw?: { + receive_account: string + receiver_email: string + receiver_mobile: string + receiver_name: string + } | null } export interface LoginRequestDto {