From f6f50ee6c7ec33f2e16bcaf59e03b0356adebb86 Mon Sep 17 00:00:00 2001 From: JiaJun <2394389886@qq.com> Date: Thu, 23 Apr 2026 16:10:27 +0800 Subject: [PATCH] =?UTF-8?q?refactor(auth):=20=E9=87=8D=E6=9E=84=E8=AE=A4?= =?UTF-8?q?=E8=AF=81=E6=B5=81=E7=A8=8B=E5=92=8C=E4=BC=9A=E8=AF=9D=E7=AE=A1?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/addressBook/useAddressBook.ts | 8 +-- src/features/authGuide.tsx | 27 ++------ src/features/goods/useGoodsRedeem.ts | 13 ++++ src/lib/authGuard.ts | 34 +++++++++++ src/locale/en.ts | 2 + src/locale/ms.ts | 2 + src/locale/zh.ts | 2 + src/store/user.ts | 2 +- src/types/address.type.ts | 2 - src/views/account/index.tsx | 24 +++++++- src/views/home/index.tsx | 71 +++++----------------- src/views/record/index.tsx | 16 +++++ 12 files changed, 117 insertions(+), 86 deletions(-) create mode 100644 src/lib/authGuard.ts diff --git a/src/features/addressBook/useAddressBook.ts b/src/features/addressBook/useAddressBook.ts index 2b3b03d..c79a04b 100644 --- a/src/features/addressBook/useAddressBook.ts +++ b/src/features/addressBook/useAddressBook.ts @@ -4,6 +4,7 @@ import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query' import {addressAdd, addressDelete, addressEdit, addressList} from '@/api/address.ts' import {validateAddressFormSubmission} from '@/features/addressBook/addressValidation' import {notifyError} from '@/features/notifications' +import {ensureSessionForAction} from '@/lib/authGuard' import {queryKeys} from '@/lib/queryKeys.ts' import {useUserStore} from '@/store/user.ts' import type {AddressListItem} from '@/types/address.type.ts' @@ -65,7 +66,6 @@ export function useAddressBook(options?: UseAddressBookOptions) { session_id: sessionId!, receiver_name: addressForm.name.trim(), phone: addressForm.phone.trim(), - region: editingAddress ? editingAddress.region.map((part) => part.trim()).filter(Boolean).join(', ') : '', detail_address: addressForm.detailedAddress.trim(), default_setting: addressForm.isDefault ? '1' : '0', } as const @@ -138,7 +138,7 @@ export function useAddressBook(options?: UseAddressBookOptions) { } const saveAddress = async (editingAddress?: AddressListItem | null) => { - if (!sessionId) { + if (!ensureSessionForAction() || !sessionId) { return null } @@ -163,7 +163,7 @@ export function useAddressBook(options?: UseAddressBookOptions) { } const removeAddress = async (addressId: string) => { - if (!sessionId) { + if (!ensureSessionForAction() || !sessionId) { return null } @@ -182,7 +182,7 @@ export function useAddressBook(options?: UseAddressBookOptions) { sessionId, addresses, addressOptions, - loading: addressListQuery.isPending || loadAddressesLoading, + loading: Boolean(sessionId) && (addressListQuery.isPending || loadAddressesLoading), addressForm, isAddressFormValid, submitLoading: saveAddressMutation.isPending, diff --git a/src/features/authGuide.tsx b/src/features/authGuide.tsx index 21c22aa..b67cbbb 100644 --- a/src/features/authGuide.tsx +++ b/src/features/authGuide.tsx @@ -3,7 +3,6 @@ import {type PropsWithChildren, useEffect, useRef, useState} from 'react' import {validateToken} from '@/api/auth.ts' import {userAssets} from '@/api/user.ts' -import {useTranslation} from 'react-i18next' import {normalizeLanguage} from '@/lib/i18n' import {queryKeys} from '@/lib/queryKeys.ts' import {useUserStore} from '@/store/user.ts' @@ -16,7 +15,6 @@ const HOST_READY_RETRY_LIMIT = 20 const HOST_HEIGHT_REPORT_INTERVAL = 250 export function AuthGuide({children}: PropsWithChildren) { - const {t} = useTranslation() const queryClient = useQueryClient() const [hostToken, setHostToken] = useState('') const setUserInfo = useUserStore((state) => state.setUserInfo) @@ -73,6 +71,12 @@ export function AuthGuide({children}: PropsWithChildren) { hasHostContextRef.current = true activeTokenRef.current = normalizedToken setHostToken(normalizedToken) + } else { + hasHostContextRef.current = true + activeTokenRef.current = '' + setHostToken('') + clearUserInfo() + queryClient.removeQueries({queryKey: ['auth-bootstrap']}) } if (typeof language === 'string' && language.trim()) { @@ -236,24 +240,5 @@ export function AuthGuide({children}: PropsWithChildren) { } }, []) - if (!hostToken || authBootstrapQuery.isPending) { - return ( -
- {!hostToken ? t('auth.waitingForHostContext') : t('auth.loadingAccountData')} -
- ) - } - - if (authBootstrapQuery.isError) { - return ( -
-
-
{t('auth.authenticationFailed')}
-
{t('auth.refreshAndTryAgain')}
-
-
- ) - } - return <>{children} } diff --git a/src/features/goods/useGoodsRedeem.ts b/src/features/goods/useGoodsRedeem.ts index 46cff84..efb77b0 100644 --- a/src/features/goods/useGoodsRedeem.ts +++ b/src/features/goods/useGoodsRedeem.ts @@ -4,6 +4,7 @@ import {useState} from 'react' import {bonusRedeem, physicalRedeem, withdrawApply} from '@/api/business.ts' import {useAddressBook} from '@/features/addressBook' import {notifyError, notifySuccess} from '@/features/notifications' +import {ensureSessionForAction} from '@/lib/authGuard' import i18n from '@/lib/i18n' import {queryKeys} from '@/lib/queryKeys.ts' import {validateAddAddressSubmission, validateRedeemSubmission} from '@/features/goods/redeemValidation' @@ -45,6 +46,10 @@ export function useGoodsRedeem() { }) const openRedeemModal = async (product: ProductItem, categoryId: ProductCategory['id']) => { + if (!ensureSessionForAction()) { + return + } + setSelectedProduct({ product, categoryId, @@ -70,6 +75,10 @@ export function useGoodsRedeem() { } const openAddAddress = () => { + if (!ensureSessionForAction()) { + return + } + setModalMode('add-address') } @@ -80,6 +89,10 @@ export function useGoodsRedeem() { const isAddAddressFormValid = addressBook.isAddressFormValid const confirmRedeem = async () => { + if (!ensureSessionForAction()) { + return + } + if (modalMode === 'add-address') { const addAddressValidation = validateAddAddressSubmission({ isAddAddressFormValid, diff --git a/src/lib/authGuard.ts b/src/lib/authGuard.ts new file mode 100644 index 0000000..f4cc8dc --- /dev/null +++ b/src/lib/authGuard.ts @@ -0,0 +1,34 @@ +import {notifyError} from '@/features/notifications' +import i18n from '@/lib/i18n' +import {useUserStore} from '@/store/user.ts' + +export function hasHostToken() { + return Boolean(useUserStore.getState().userInfo?.token?.trim()) +} + +export function hasSessionAuth() { + return Boolean(useUserStore.getState().authInfo?.session_id) +} + +export function ensureTokenForAction() { + if (hasHostToken()) { + return true + } + + notifyError(i18n.t('validation.verificationRequired')) + return false +} + +export function ensureSessionForAction() { + if (!hasHostToken()) { + notifyError(i18n.t('validation.verificationRequired')) + return false + } + + if (!hasSessionAuth()) { + notifyError(i18n.t('validation.verificationInProgress')) + return false + } + + return true +} diff --git a/src/locale/en.ts b/src/locale/en.ts index 4295536..e7a76cf 100644 --- a/src/locale/en.ts +++ b/src/locale/en.ts @@ -137,6 +137,8 @@ const en = { }, validation: { sessionExpired: 'Session expired. Please log in again.', + verificationRequired: 'Please log in first.', + verificationInProgress: 'Verification in progress. Please try again shortly.', noProductSelected: 'No product selected.', pleaseSelectShippingAddress: 'Please select a shipping address.', pleaseCompleteAddressFields: 'Please complete all required address fields.', diff --git a/src/locale/ms.ts b/src/locale/ms.ts index f3e3586..679b76e 100644 --- a/src/locale/ms.ts +++ b/src/locale/ms.ts @@ -137,6 +137,8 @@ const ms = { }, validation: { sessionExpired: 'Sesi telah tamat. Sila log masuk semula.', + verificationRequired: 'Sila log masuk dahulu.', + verificationInProgress: 'Pengesahan sedang diproses. Sila cuba sebentar lagi.', noProductSelected: 'Tiada barangan dipilih.', pleaseSelectShippingAddress: 'Sila pilih alamat penghantaran.', pleaseCompleteAddressFields: 'Sila lengkapkan semua maklumat alamat.', diff --git a/src/locale/zh.ts b/src/locale/zh.ts index 1773e53..17dbc5d 100644 --- a/src/locale/zh.ts +++ b/src/locale/zh.ts @@ -137,6 +137,8 @@ const zh = { }, validation: { sessionExpired: '登录态已过期,请重新登录。', + verificationRequired: '请先登录。', + verificationInProgress: '验证中,请稍后重试。', noProductSelected: '未选择商品。', pleaseSelectShippingAddress: '请选择收货地址。', pleaseCompleteAddressFields: '请填写完整地址信息。', diff --git a/src/store/user.ts b/src/store/user.ts index 053da06..75d65cf 100644 --- a/src/store/user.ts +++ b/src/store/user.ts @@ -31,7 +31,7 @@ export const useUserStore = create()( }), { name: 'playx-user', - storage: createJSONStorage(() => localStorage), + storage: createJSONStorage(() => sessionStorage), partialize: (state) => ({ userInfo: state.userInfo, authInfo: state.authInfo, diff --git a/src/types/address.type.ts b/src/types/address.type.ts index 745474c..0dbb963 100644 --- a/src/types/address.type.ts +++ b/src/types/address.type.ts @@ -3,8 +3,6 @@ export interface AddressInfo { receiver_name: string, /**@description 电话 */ phone: string, - /**@description 地区(数组或逗号分隔字符串) */ - region: string, /**@description 详细地址 */ detail_address: string, /**@description 1 设为默认地址 */ diff --git a/src/views/account/index.tsx b/src/views/account/index.tsx index 29e114b..88dc741 100644 --- a/src/views/account/index.tsx +++ b/src/views/account/index.tsx @@ -9,12 +9,14 @@ import {ArrowLeft, BadgeCheck, MapPinHouse, PencilLine, Plus, Trash2} from 'luci import {useAddressBook} from '@/features/addressBook' import {GoodsRedeemModal} from '@/features/goods' import {notifySuccess} from '@/features/notifications' +import {ensureSessionForAction} from '@/lib/authGuard' import {MotionButton, MotionDiv, tapMotionProps, tapMotionPropsIcon, tapMotionPropsSoft} from '@/lib/motion' import type {AddressListItem} from '@/types/address.type.ts' function AccountPage() { const {t} = useTranslation() const addressBook = useAddressBook({autoLoad: true}) + const sessionId = addressBook.sessionId const [addressModalOpen, setAddressModalOpen] = useState(false) const [editingAddress, setEditingAddress] = useState(null) const [deleteTarget, setDeleteTarget] = useState(null) @@ -22,12 +24,20 @@ function AccountPage() { const isAddressFormValid = addressBook.isAddressFormValid const handleOpenAddAddress = () => { + if (!ensureSessionForAction()) { + return + } + setEditingAddress(null) addressBook.resetAddressForm() setAddressModalOpen(true) } const handleOpenEditAddress = (address: AddressListItem) => { + if (!ensureSessionForAction()) { + return + } + setEditingAddress(address) addressBook.fillAddressForm(address) setAddressModalOpen(true) @@ -102,7 +112,11 @@ function AccountPage() {
- {addressBook.loading ? ( + {!sessionId ? ( +
+ {t('validation.verificationRequired')} +
+ ) : addressBook.loading ? (
{t('account.loadingAddressList')}
@@ -156,7 +170,13 @@ function AccountPage() { setDeleteTarget(address)} + onClick={() => { + if (!ensureSessionForAction()) { + return + } + + setDeleteTarget(address) + }} {...tapMotionPropsSoft} >