From 988f57aa5edfc56c0b0721770af0867771746622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=88=90?= Date: Tue, 18 Nov 2025 08:00:38 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=94=A8=E6=88=B7=E6=8E=88?= =?UTF-8?q?=E6=9D=83=E8=B0=83=E7=94=A8=E4=BD=8D=E7=BD=AE=E5=92=8C=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fetchUserInfo_analysis.md | 81 ++++++++++++++ src/app.ts | 10 +- src/components/NTRPTestEntryCard/index.tsx | 17 ++- .../detail/components/StickyBottom/index.tsx | 42 ++++++- src/game_pages/detail/index.tsx | 11 +- src/game_pages/list/index.tsx | 12 +- src/game_pages/sharePoster/index.tsx | 3 + src/main_pages/components/ListPageContent.tsx | 10 +- src/main_pages/index.tsx | 66 +++++++---- src/other_pages/ntrp-evaluate/index.tsx | 51 ++++----- src/services/loginService.ts | 16 ++- src/utils/authInit.ts | 105 ++++++++++++++++++ 12 files changed, 343 insertions(+), 81 deletions(-) create mode 100644 fetchUserInfo_analysis.md create mode 100644 src/utils/authInit.ts diff --git a/fetchUserInfo_analysis.md b/fetchUserInfo_analysis.md new file mode 100644 index 0000000..bc10391 --- /dev/null +++ b/fetchUserInfo_analysis.md @@ -0,0 +1,81 @@ +# fetchUserInfo 调用分析 + +## 调用位置汇总 + +### ✅ 合理的调用 + +1. **src/services/loginService.ts** (第160行) + - 登录成功后调用,确保用户信息同步到 store + - ✅ 合理 + +2. **src/main_pages/index.tsx** (第63行) + - 微信授权成功后调用 + - ✅ 合理(主入口,需要确保用户信息加载) + +3. **src/game_pages/list/index.tsx** (第228行) + - 在 useEffect 中,等待 waitForAuthInit 后调用 + - ✅ 合理 + +4. **src/game_pages/detail/index.tsx** (第55行) + - 在 useEffect 中,等待 waitForAuthInit 后调用 + - ✅ 合理 + +5. **src/game_pages/sharePoster/index.tsx** (第47行) + - 在 handleGenPoster 中,等待 waitForAuthInit 后调用 + - ✅ 合理(需要用户信息生成海报) + +6. **src/components/NTRPTestEntryCard/index.tsx** (第35行) + - 在 useEffect 中,等待 waitForAuthInit 后调用 + - ✅ 合理 + +7. **src/utils/authInit.ts** (第29、39行) + - 在静默登录成功后调用 + - ✅ 合理(核心授权逻辑) + +8. **src/home_pages/index.tsx** (第20行) + - 在静默登录成功后调用 + - ✅ 合理 + +### ⚠️ 可能重复的调用 + +1. **src/main_pages/components/ListPageContent.tsx** (第204行) + - 在 useEffect 中调用,等待 waitForAuthInit + - ⚠️ **问题**:这是 `main_pages/index.tsx` 的子组件 + - `main_pages/index.tsx` 已经在授权成功后调用了 `fetchUserInfo` + - **建议**:移除这里的调用,因为父组件已经调用了 + +2. **src/other_pages/ntrp-evaluate/index.tsx - Intro组件** (第159、180行) + - 第159行:在 useEffect 中检查 userInfo 为空时调用 + - 第180行:在 getLastResult 中检查 userInfo 为空时调用 + - ⚠️ **问题**:两个地方都可能调用,有重复风险 + - **建议**:移除第159行的调用,只在 getLastResult 中调用(因为已经等待了 waitForAuthInit) + +3. **src/other_pages/ntrp-evaluate/index.tsx - Result组件** (第463、475行) + - 第463行:在 useEffect 中检查 userInfo 为空时调用 + - 第475行:在 init 中检查 userInfo 为空时调用 + - ⚠️ **问题**:两个地方都可能调用,有重复风险 + - **建议**:移除第463行的调用,只在 init 中调用(因为已经等待了 waitForAuthInit) + +## 优化建议 + +### 1. 移除重复调用 +- `main_pages/components/ListPageContent.tsx` - 移除 fetchUserInfo 调用(父组件已调用) +- `ntrp-evaluate/index.tsx` - Intro 组件:移除第一个 useEffect 中的调用 +- `ntrp-evaluate/index.tsx` - Result 组件:移除第一个 useEffect 中的调用 + +### 2. 调用原则 +- ✅ 主入口页面(main_pages/index.tsx)应该在授权成功后调用 +- ✅ 子组件不应该重复调用,应该依赖父组件或 store 中的数据 +- ✅ 独立页面(如 game_pages/*)可以调用,但应该等待 waitForAuthInit +- ✅ 工具函数(authInit.ts)中的调用是必要的 + +## 总结 + +**当前问题**: +1. `main_pages/components/ListPageContent.tsx` 与父组件重复调用 +2. `ntrp-evaluate/index.tsx` 中 Intro 和 Result 组件都有重复调用 + +**建议修复**: +- 移除子组件中的重复调用 +- 统一在等待 waitForAuthInit 后的逻辑中调用 + diff --git a/src/app.ts b/src/app.ts index 1093ef5..55566e7 100644 --- a/src/app.ts +++ b/src/app.ts @@ -5,8 +5,7 @@ import "./app.scss"; import "./scss/qweather-icons/qweather-icons.css"; import { useGlobalStore } from "./store/global"; import { removeStorage } from "./store/storage"; -import { sceneRedirectLogic } from './utils/helper' -import { silentLogin } from "./services/loginService"; +import { sceneRedirectLogic } from './utils/helper'; interface AppProps { children: ReactNode; @@ -25,9 +24,9 @@ class App extends Component { // 这里可以自定义弹窗(或直接默认同意) resolve({ event: 'agree' }); // 同意隐私协议 }); - - // 移除这里的静默登录调用,避免重复调用 - // 静默登录在 home_pages/index.tsx 中统一执行 + + // 微信授权逻辑已转移到 main_pages/index.tsx 中 + // 不再在 app.ts 中执行静默登录 } componentDidMount() { @@ -40,6 +39,7 @@ class App extends Component { componentDidShow(options) { sceneRedirectLogic(options.query, options.path); + // 微信授权逻辑已转移到 main_pages/index.tsx 中 } componentDidHide() { } diff --git a/src/components/NTRPTestEntryCard/index.tsx b/src/components/NTRPTestEntryCard/index.tsx index e433ab6..209ffe4 100644 --- a/src/components/NTRPTestEntryCard/index.tsx +++ b/src/components/NTRPTestEntryCard/index.tsx @@ -4,6 +4,7 @@ import Taro from "@tarojs/taro"; import { useUserInfo, useUserActions } from "@/store/userStore"; // import { getCurrentFullPath } from "@/utils"; import evaluateService, { StageType } from "@/services/evaluateService"; +import { waitForAuthInit } from "@/utils/authInit"; import DocCopy from "@/static/ntrp/ntrp_doc_copy.svg"; import ArrowRight from "@/static/ntrp/ntrp_arrow_right_color.svg"; import { @@ -26,12 +27,18 @@ function NTRPTestEntryCard(props: { console.log(userInfo); useEffect(() => { - if (!userInfo.id) { - fetchUserInfo(); - } - evaluateService.getLastResult().then((res) => { + const init = async () => { + // 先等待静默登录完成 + await waitForAuthInit(); + // 然后再获取用户信息 + if (!userInfo.id) { + await fetchUserInfo(); + } + // 获取测试结果 + const res = await evaluateService.getLastResult(); setTestFlag(res.code === 0 && res.data.has_ntrp_level); - }); + }; + init(); }, [userInfo.id]); const handleTest = useCallback( diff --git a/src/game_pages/detail/components/StickyBottom/index.tsx b/src/game_pages/detail/components/StickyBottom/index.tsx index 0f232be..696eed0 100644 --- a/src/game_pages/detail/components/StickyBottom/index.tsx +++ b/src/game_pages/detail/components/StickyBottom/index.tsx @@ -7,6 +7,7 @@ import OrderService from "@/services/orderService"; import { EvaluateCallback, EvaluateScene } from "@/store/evaluateStore"; import { MATCH_STATUS, IsSubstituteSupported } from "@/services/detailService"; import { GameManagePopup, NTRPEvaluatePopup } from "@/components"; +import { useUserInfo } from "@/store/userStore"; import img from "@/config/images"; import RMB_ICON from "@/static/detail/rmb.svg"; import { toast, navto } from "@/utils/helper"; @@ -76,6 +77,7 @@ export default function StickyButton(props) { currentUserInfo, } = props; const [commentCount, setCommentCount] = useState(0); + const userInfo = useUserInfo(); const ntrpRef = useRef<{ show: (evaluateCallback: EvaluateCallback) => void; }>({ show: () => {} }); @@ -93,6 +95,36 @@ export default function StickyButton(props) { const { ntrp_level } = currentUserInfo || {}; + // 检查手机号绑定的包装函数 + const checkPhoneAndExecute = (action: () => void) => { + return () => { + if (!userInfo?.phone) { + Taro.showModal({ + title: '提示', + content: '该功能需要绑定手机号', + confirmText: '去绑定', + cancelText: '取消', + success: (res) => { + if (res.confirm) { + const currentPath = Taro.getCurrentInstance().router?.path || ''; + const currentParams = Taro.getCurrentInstance().router?.params || {}; + const queryString = Object.keys(currentParams) + .map(key => `${key}=${currentParams[key]}`) + .join('&'); + const fullPath = queryString ? `${currentPath}?${queryString}` : currentPath; + + Taro.navigateTo({ + url: `/login_pages/index/index?redirect=${encodeURIComponent(fullPath)}`, + }); + } + } + }); + return; + } + action(); + }; + }; + const matchNtrpReq = matchNtrpRequestment( ntrp_level, skill_level_min, @@ -224,14 +256,14 @@ export default function StickyButton(props) { 继续支付 ), - action: async () => { + action: checkPhoneAndExecute(async () => { const res = await OrderService.getUnpaidOrder(id); if (res.code === 0) { navto( `/order_pages/orderDetail/index?id=${res.data.order_info.order_id}` ); } - }, + }), }; } else if (!matchNtrpReq) { return { @@ -255,7 +287,7 @@ export default function StickyButton(props) { 我要候补 ), - action: handleJoinGame, + action: checkPhoneAndExecute(handleJoinGame), }; } else if (can_join) { return { @@ -268,7 +300,7 @@ export default function StickyButton(props) { ); }, - action: handleJoinGame, + action: checkPhoneAndExecute(handleJoinGame), }; } else if (can_assess) { return { @@ -279,7 +311,7 @@ export default function StickyButton(props) { 立即加入 ), - action: handleSelfEvaluate, + action: checkPhoneAndExecute(handleSelfEvaluate), }; } return { diff --git a/src/game_pages/detail/index.tsx b/src/game_pages/detail/index.tsx index 1e53ba1..ad5391c 100644 --- a/src/game_pages/detail/index.tsx +++ b/src/game_pages/detail/index.tsx @@ -10,6 +10,7 @@ import * as LoginService from "@/services/loginService"; import { getCurrentLocation } from "@/utils/locationUtils"; import { useUserInfo, useUserActions } from "@/store/userStore"; import { useGlobalState } from "@/store/global"; +import { waitForAuthInit } from "@/utils/authInit"; import { requireLoginWithPhone } from "@/utils/helper"; import GameTags from "./components/GameTags"; import Carousel from "./components/Carousel"; @@ -46,8 +47,14 @@ function Index() { const commentRef = useRef(); useEffect(() => { - updateLocation(); - fetchUserInfo(); + const init = async () => { + updateLocation(); + // 先等待静默登录完成 + await waitForAuthInit(); + // 然后再获取用户信息 + await fetchUserInfo(); + }; + init(); }, []); useDidShow(() => { diff --git a/src/game_pages/list/index.tsx b/src/game_pages/list/index.tsx index be4c15a..d47dfe0 100644 --- a/src/game_pages/list/index.tsx +++ b/src/game_pages/list/index.tsx @@ -16,6 +16,7 @@ import { updateUserLocation } from "@/services/userService"; import { useUserActions } from "@/store/userStore"; import { useDictionaryStore } from "@/store/dictionaryStore"; import { saveImage } from "@/utils"; +import { waitForAuthInit } from "@/utils/authInit"; const ListPage = () => { // 从 store 获取数据和方法 @@ -220,11 +221,14 @@ const ListPage = () => { getCities(); getCityQrCode(); - // 2. 延迟执行:获取用户信息(不阻塞渲染) - requestAnimationFrame(() => { - fetchUserInfo().catch((error) => { + // 2. 延迟执行:等待静默登录完成后获取用户信息 + requestAnimationFrame(async () => { + try { + await waitForAuthInit(); + await fetchUserInfo(); + } catch (error) { console.error('获取用户信息失败:', error); - }); + } }); // 3. 延迟执行:获取位置信息(可能较慢,不阻塞首屏) diff --git a/src/game_pages/sharePoster/index.tsx b/src/game_pages/sharePoster/index.tsx index 1933994..3f32c63 100644 --- a/src/game_pages/sharePoster/index.tsx +++ b/src/game_pages/sharePoster/index.tsx @@ -14,6 +14,7 @@ import WechatTimeline from "@/static/detail/wechat_timeline.svg"; import { useUserActions } from "@/store/userStore"; import { DayOfWeekMap } from "../detail/config"; import { genNTRPRequirementText } from "@/utils/helper"; +import { waitForAuthInit } from "@/utils/authInit"; import styles from "./index.module.scss"; function SharePoster(props) { @@ -41,6 +42,8 @@ function SharePoster(props) { image_list, title, } = detail || {}; + // 先等待静默登录完成 + await waitForAuthInit(); const userInfo = await fetchUserInfo(); const { avatar_url, nickname } = userInfo; const startTime = dayjs(start_time); diff --git a/src/main_pages/components/ListPageContent.tsx b/src/main_pages/components/ListPageContent.tsx index d1027fe..f97207b 100644 --- a/src/main_pages/components/ListPageContent.tsx +++ b/src/main_pages/components/ListPageContent.tsx @@ -9,7 +9,6 @@ import { View, Image, Text, ScrollView } from "@tarojs/components"; import ListContainer from "@/container/listContainer"; import DistanceQuickFilter from "@/components/DistanceQuickFilter"; import { updateUserLocation } from "@/services/userService"; -import { useUserActions } from "@/store/userStore"; import { useDictionaryStore } from "@/store/dictionaryStore"; import { saveImage, navigateTo } from "@/utils"; @@ -35,7 +34,6 @@ const ListPageContent: React.FC = ({ onFilterPopupVisibleChange, }) => { const store = useListStore() || {}; - const { fetchUserInfo } = useUserActions(); const { statusNavbarHeightInfo, getCurrentLocationInfo } = useGlobalState() || {}; const { totalHeight = 98 } = statusNavbarHeightInfo || {}; @@ -196,12 +194,8 @@ const ListPageContent: React.FC = ({ getCities(); getCityQrCode(); - // 2. 延迟执行:获取用户信息(不阻塞渲染) - requestAnimationFrame(() => { - fetchUserInfo().catch((error) => { - console.error('获取用户信息失败:', error); - }); - }); + // 2. 移除 fetchUserInfo 调用,因为父组件 main_pages/index.tsx 已经在授权成功后调用了 + // 这里直接使用 store 中的用户信息即可 // 3. 延迟执行:获取位置信息(可能较慢,不阻塞首屏) requestAnimationFrame(() => { diff --git a/src/main_pages/index.tsx b/src/main_pages/index.tsx index 382ec92..0d1ee72 100644 --- a/src/main_pages/index.tsx +++ b/src/main_pages/index.tsx @@ -1,8 +1,9 @@ import React, { useState, useEffect, useCallback } from "react"; import { View } from "@tarojs/components"; import Taro from "@tarojs/taro"; -import { check_login_status, silentLogin } from "@/services/loginService"; +import { wechat_auth_login, save_login_state } from "@/services/loginService"; import { useUserActions } from "@/store/userStore"; +import tokenManager from "@/utils/tokenManager"; import GuideBar from "@/components/GuideBar"; import { GeneralNavbar } from "@/components"; import HomeNavbar from "@/components/HomeNavbar"; @@ -24,41 +25,66 @@ const MainPage: React.FC = () => { const [isShowInputCustomerNavBar, setIsShowInputCustomerNavBar] = useState(false); const [listPageScrollToTopTrigger, setListPageScrollToTopTrigger] = useState(0); const [showGuideBar, setShowGuideBar] = useState(true); + const [showAuthError, setShowAuthError] = useState(false); + const [authErrorMessage, setAuthErrorMessage] = useState(''); const { fetchUserInfo } = useUserActions(); - // 初始化:尝试静默登录并获取用户信息 + // 初始化:自动微信授权并获取用户信息 useEffect(() => { const init = async () => { - // 先检查是否已登录 - const login_status = check_login_status(); - if (login_status) { - // 已登录,获取用户信息 + const hasValidToken = tokenManager.hasValidToken(); + + if (!hasValidToken) { + try { + console.log('开始微信授权...'); + const loginRes = await wechat_auth_login(); + + if (loginRes.success && loginRes.token) { + save_login_state(loginRes.token, loginRes.user_info); + console.log('微信授权成功'); + } else { + // 显示错误提示 + setAuthErrorMessage(loginRes.message || '微信授权失败'); + setShowAuthError(true); + return; + } + } catch (error) { + console.error('微信授权异常:', error); + setAuthErrorMessage('微信授权失败,请重试'); + setShowAuthError(true); + return; + } + } + + // 如果有有效token,获取用户详细信息 + if (tokenManager.hasValidToken()) { try { await fetchUserInfo(); } catch (error) { console.error('获取用户信息失败:', error); } - } else { - // 未登录,尝试静默登录 - try { - const loginResult = await silentLogin(); - if (loginResult.success) { - // 静默登录成功,获取用户信息 - fetchUserInfo().catch((error) => { - console.error('获取用户信息失败:', error); - }); - } - } catch (error) { - console.error('静默登录失败:', error); - // 静默登录失败不影响使用 - } } }; init(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); // fetchUserInfo 是稳定的函数引用,不需要加入依赖项 + // 显示授权错误提示 + useEffect(() => { + if (showAuthError) { + Taro.showModal({ + title: '授权失败', + content: `${authErrorMessage}\n\n请重启小程序后重试`, + showCancel: false, + confirmText: '我知道了', + success: () => { + setShowAuthError(false); + } + }); + } + }, [showAuthError, authErrorMessage]); + // 处理标签切换 const handleTabChange = useCallback((code: string) => { if (code === currentTab) { diff --git a/src/other_pages/ntrp-evaluate/index.tsx b/src/other_pages/ntrp-evaluate/index.tsx index 8469bf1..b0d2d0f 100644 --- a/src/other_pages/ntrp-evaluate/index.tsx +++ b/src/other_pages/ntrp-evaluate/index.tsx @@ -15,6 +15,7 @@ import { useEvaluate, EvaluateScene } from "@/store/evaluateStore"; import { useGlobalState } from "@/store/global"; import { delay, getCurrentFullPath } from "@/utils"; import { formatNtrpDisplay } from "@/utils/helper"; +import { waitForAuthInit } from "@/utils/authInit"; import CloseIcon from "@/static/ntrp/ntrp_close_icon.svg"; import DocCopy from "@/static/ntrp/ntrp_doc_copy.svg"; import ArrowRight from "@/static/ntrp/ntrp_arrow_right.svg"; @@ -151,16 +152,14 @@ function Intro() { const { ntrp_level, create_time, id } = last_test_result || {}; const lastTestTime = create_time ? dayjs(create_time).format("YYYY年M月D日") : ""; - // 组件初始化时立即获取用户信息 useEffect(() => { - // 如果用户信息为空,立即获取 - if (!userInfo || Object.keys(userInfo).length === 0) { - fetchUserInfo(); - } - }, []); - - useEffect(() => { - getLastResult(); + const init = async () => { + // 先等待静默登录完成 + await waitForAuthInit(); + // 然后再调用接口 + await getLastResult(); + }; + init(); }, []); async function getLastResult() { @@ -193,9 +192,8 @@ function Intro() { }); } Taro.redirectTo({ - url: `/other_pages/ntrp-evaluate/index?stage=${type}${ - type === StageType.RESULT ? `&id=${id}` : "" - }`, + url: `/other_pages/ntrp-evaluate/index?stage=${type}${type === StageType.RESULT ? `&id=${id}` : "" + }`, }); } @@ -450,20 +448,18 @@ function Result() { [propName: string, prop: number][] >([]); - // 组件初始化时立即获取用户信息 useEffect(() => { - // 如果用户信息为空,立即获取 - if (!userInfo || Object.keys(userInfo).length === 0) { - fetchUserInfo(); - } - }, []); - - useEffect(() => { - getResultById(); - // 确保用户信息已加载 - if (!userInfo || Object.keys(userInfo).length === 0) { - fetchUserInfo(); - } + const init = async () => { + // 先等待静默登录完成 + await waitForAuthInit(); + // 然后再调用接口 + await getResultById(); + // 确保用户信息已加载 + if (!userInfo || Object.keys(userInfo).length === 0) { + await fetchUserInfo(); + } + }; + init(); }, [id]); async function getResultById() { @@ -624,9 +620,8 @@ function Result() { } const currentPage = getCurrentFullPath(); Taro.redirectTo({ - url: `/login_pages/index/index${ - currentPage ? `?redirect=${encodeURIComponent(currentPage)}` : "" - }`, + url: `/login_pages/index/index${currentPage ? `?redirect=${encodeURIComponent(currentPage)}` : "" + }`, }); } diff --git a/src/services/loginService.ts b/src/services/loginService.ts index 0305657..db085de 100644 --- a/src/services/loginService.ts +++ b/src/services/loginService.ts @@ -80,6 +80,7 @@ export interface UserInfoType { } // 微信授权登录 +// phone_code: 可选参数,如果提供则绑定手机号,否则只进行微信授权 export const wechat_auth_login = async ( phone_code?: string, ): Promise => { @@ -94,11 +95,18 @@ export const wechat_auth_login = async ( }; } - // 使用 httpService 调用微信授权接口,传递手机号code - const auth_response = await httpService.post("user/wx_auth", { + // 构建请求参数 + const requestData: any = { code: login_result.code, - phone_code: phone_code, // 传递手机号加密code - }); + }; + + // 只有在提供 phone_code 时才添加到请求参数中 + if (phone_code) { + requestData.phone_code = phone_code; + } + + // 使用 httpService 调用微信授权接口 + const auth_response = await httpService.post("user/wx_auth", requestData); if (auth_response.code === 0) { return { diff --git a/src/utils/authInit.ts b/src/utils/authInit.ts new file mode 100644 index 0000000..7aa474c --- /dev/null +++ b/src/utils/authInit.ts @@ -0,0 +1,105 @@ +import { check_login_status, get_user_token, silentLogin } from "@/services/loginService"; +import { useUser } from "@/store/userStore"; +import tokenManager from "@/utils/tokenManager"; + +// 防止重复调用静默登录 +let isInitializingAuth = false; +let authInitPromise: Promise | null = null; + +// 初始化用户授权的核心逻辑 +const initUserAuthCore = async (): Promise => { + // 如果正在初始化,直接返回现有的 Promise + if (isInitializingAuth && authInitPromise) { + return authInitPromise; + } + + // 如果已经登录且有用户信息,跳过 + const login_status = check_login_status(); + const { user, fetchUserInfo } = useUser.getState(); + if (login_status && user && Object.keys(user).length > 0) { + return Promise.resolve(); + } + + isInitializingAuth = true; + authInitPromise = (async () => { + try { + if (login_status) { + // 已登录,获取用户信息 + try { + await fetchUserInfo(); + } catch (error) { + console.error('获取用户信息失败:', error); + } + } else { + // 未登录,尝试静默登录 + try { + const loginResult = await silentLogin(); + if (loginResult.success) { + // 静默登录成功,获取用户信息 + await fetchUserInfo(); + } + } catch (error) { + console.error('静默登录失败:', error); + // 静默登录失败不影响使用 + } + } + } catch (error) { + console.error('初始化用户授权失败:', error); + } finally { + isInitializingAuth = false; + authInitPromise = null; + } + })(); + + return authInitPromise; +}; + +// 导出等待静默登录完成的函数,供页面组件使用 +export const waitForAuthInit = async (): Promise => { + // 检查是否已经有有效的 token(最优先检查) + if (tokenManager.hasValidToken() || get_user_token()) { + return Promise.resolve(); + } + + // 如果正在初始化,等待完成 + if (authInitPromise) { + await authInitPromise; + // 等待完成后再次检查 token + if (tokenManager.hasValidToken() || get_user_token()) { + return; + } + } + + // 检查是否已经登录且有用户信息 + const login_status = check_login_status(); + const { user } = useUser.getState(); + if (login_status && user && Object.keys(user).length > 0) { + // 即使有登录状态,也确保 token 存在 + if (tokenManager.hasValidToken() || get_user_token()) { + return Promise.resolve(); + } + } + + // 触发初始化 + const promise = initUserAuthCore(); + await promise; + + // 等待 token 真正存在(最多等待 2 秒) + let retryCount = 0; + const maxRetries = 20; // 20 * 100ms = 2秒 + while (retryCount < maxRetries && !tokenManager.hasValidToken() && !get_user_token()) { + await new Promise(resolve => setTimeout(resolve, 100)); + retryCount++; + } + + // 如果还是没有 token,记录警告但继续执行 + if (!tokenManager.hasValidToken() && !get_user_token()) { + console.warn('等待静默登录完成,但未获取到 token'); + } +}; + +// 导出初始化函数供 app.ts 使用 +export const initUserAuth = () => { + return initUserAuthCore(); +}; +