修改用户授权调用位置和逻辑

This commit is contained in:
张成
2025-11-18 08:00:38 +08:00
parent 4568e758a7
commit 988f57aa5e
12 changed files with 343 additions and 81 deletions

81
fetchUserInfo_analysis.md Normal file
View File

@@ -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 后的逻辑中调用

View File

@@ -5,8 +5,7 @@ import "./app.scss";
import "./scss/qweather-icons/qweather-icons.css"; import "./scss/qweather-icons/qweather-icons.css";
import { useGlobalStore } from "./store/global"; import { useGlobalStore } from "./store/global";
import { removeStorage } from "./store/storage"; import { removeStorage } from "./store/storage";
import { sceneRedirectLogic } from './utils/helper' import { sceneRedirectLogic } from './utils/helper';
import { silentLogin } from "./services/loginService";
interface AppProps { interface AppProps {
children: ReactNode; children: ReactNode;
@@ -26,8 +25,8 @@ class App extends Component<AppProps> {
resolve({ event: 'agree' }); // 同意隐私协议 resolve({ event: 'agree' }); // 同意隐私协议
}); });
// 移除这里的静默登录调用,避免重复调用 // 微信授权逻辑已转移到 main_pages/index.tsx 中
// 静默登录在 home_pages/index.tsx统一执行 // 不再在 app.ts 中执行静默登录
} }
componentDidMount() { componentDidMount() {
@@ -40,6 +39,7 @@ class App extends Component<AppProps> {
componentDidShow(options) { componentDidShow(options) {
sceneRedirectLogic(options.query, options.path); sceneRedirectLogic(options.query, options.path);
// 微信授权逻辑已转移到 main_pages/index.tsx 中
} }
componentDidHide() { } componentDidHide() { }

View File

@@ -4,6 +4,7 @@ import Taro from "@tarojs/taro";
import { useUserInfo, useUserActions } from "@/store/userStore"; import { useUserInfo, useUserActions } from "@/store/userStore";
// import { getCurrentFullPath } from "@/utils"; // import { getCurrentFullPath } from "@/utils";
import evaluateService, { StageType } from "@/services/evaluateService"; import evaluateService, { StageType } from "@/services/evaluateService";
import { waitForAuthInit } from "@/utils/authInit";
import DocCopy from "@/static/ntrp/ntrp_doc_copy.svg"; import DocCopy from "@/static/ntrp/ntrp_doc_copy.svg";
import ArrowRight from "@/static/ntrp/ntrp_arrow_right_color.svg"; import ArrowRight from "@/static/ntrp/ntrp_arrow_right_color.svg";
import { import {
@@ -26,12 +27,18 @@ function NTRPTestEntryCard(props: {
console.log(userInfo); console.log(userInfo);
useEffect(() => { useEffect(() => {
if (!userInfo.id) { const init = async () => {
fetchUserInfo(); // 先等待静默登录完成
} await waitForAuthInit();
evaluateService.getLastResult().then((res) => { // 然后再获取用户信息
if (!userInfo.id) {
await fetchUserInfo();
}
// 获取测试结果
const res = await evaluateService.getLastResult();
setTestFlag(res.code === 0 && res.data.has_ntrp_level); setTestFlag(res.code === 0 && res.data.has_ntrp_level);
}); };
init();
}, [userInfo.id]); }, [userInfo.id]);
const handleTest = useCallback( const handleTest = useCallback(

View File

@@ -7,6 +7,7 @@ import OrderService from "@/services/orderService";
import { EvaluateCallback, EvaluateScene } from "@/store/evaluateStore"; import { EvaluateCallback, EvaluateScene } from "@/store/evaluateStore";
import { MATCH_STATUS, IsSubstituteSupported } from "@/services/detailService"; import { MATCH_STATUS, IsSubstituteSupported } from "@/services/detailService";
import { GameManagePopup, NTRPEvaluatePopup } from "@/components"; import { GameManagePopup, NTRPEvaluatePopup } from "@/components";
import { useUserInfo } from "@/store/userStore";
import img from "@/config/images"; import img from "@/config/images";
import RMB_ICON from "@/static/detail/rmb.svg"; import RMB_ICON from "@/static/detail/rmb.svg";
import { toast, navto } from "@/utils/helper"; import { toast, navto } from "@/utils/helper";
@@ -76,6 +77,7 @@ export default function StickyButton(props) {
currentUserInfo, currentUserInfo,
} = props; } = props;
const [commentCount, setCommentCount] = useState(0); const [commentCount, setCommentCount] = useState(0);
const userInfo = useUserInfo();
const ntrpRef = useRef<{ const ntrpRef = useRef<{
show: (evaluateCallback: EvaluateCallback) => void; show: (evaluateCallback: EvaluateCallback) => void;
}>({ show: () => {} }); }>({ show: () => {} });
@@ -93,6 +95,36 @@ export default function StickyButton(props) {
const { ntrp_level } = currentUserInfo || {}; 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( const matchNtrpReq = matchNtrpRequestment(
ntrp_level, ntrp_level,
skill_level_min, skill_level_min,
@@ -224,14 +256,14 @@ export default function StickyButton(props) {
<Text className={styles.btnText}></Text> <Text className={styles.btnText}></Text>
</> </>
), ),
action: async () => { action: checkPhoneAndExecute(async () => {
const res = await OrderService.getUnpaidOrder(id); const res = await OrderService.getUnpaidOrder(id);
if (res.code === 0) { if (res.code === 0) {
navto( navto(
`/order_pages/orderDetail/index?id=${res.data.order_info.order_id}` `/order_pages/orderDetail/index?id=${res.data.order_info.order_id}`
); );
} }
}, }),
}; };
} else if (!matchNtrpReq) { } else if (!matchNtrpReq) {
return { return {
@@ -255,7 +287,7 @@ export default function StickyButton(props) {
<Text className={styles.btnText}></Text> <Text className={styles.btnText}></Text>
</> </>
), ),
action: handleJoinGame, action: checkPhoneAndExecute(handleJoinGame),
}; };
} else if (can_join) { } else if (can_join) {
return { return {
@@ -268,7 +300,7 @@ export default function StickyButton(props) {
</> </>
); );
}, },
action: handleJoinGame, action: checkPhoneAndExecute(handleJoinGame),
}; };
} else if (can_assess) { } else if (can_assess) {
return { return {
@@ -279,7 +311,7 @@ export default function StickyButton(props) {
<Text className={styles.btnText}></Text> <Text className={styles.btnText}></Text>
</> </>
), ),
action: handleSelfEvaluate, action: checkPhoneAndExecute(handleSelfEvaluate),
}; };
} }
return { return {

View File

@@ -10,6 +10,7 @@ import * as LoginService from "@/services/loginService";
import { getCurrentLocation } from "@/utils/locationUtils"; import { getCurrentLocation } from "@/utils/locationUtils";
import { useUserInfo, useUserActions } from "@/store/userStore"; import { useUserInfo, useUserActions } from "@/store/userStore";
import { useGlobalState } from "@/store/global"; import { useGlobalState } from "@/store/global";
import { waitForAuthInit } from "@/utils/authInit";
import { requireLoginWithPhone } from "@/utils/helper"; import { requireLoginWithPhone } from "@/utils/helper";
import GameTags from "./components/GameTags"; import GameTags from "./components/GameTags";
import Carousel from "./components/Carousel"; import Carousel from "./components/Carousel";
@@ -46,8 +47,14 @@ function Index() {
const commentRef = useRef(); const commentRef = useRef();
useEffect(() => { useEffect(() => {
updateLocation(); const init = async () => {
fetchUserInfo(); updateLocation();
// 先等待静默登录完成
await waitForAuthInit();
// 然后再获取用户信息
await fetchUserInfo();
};
init();
}, []); }, []);
useDidShow(() => { useDidShow(() => {

View File

@@ -16,6 +16,7 @@ import { updateUserLocation } from "@/services/userService";
import { useUserActions } from "@/store/userStore"; import { useUserActions } from "@/store/userStore";
import { useDictionaryStore } from "@/store/dictionaryStore"; import { useDictionaryStore } from "@/store/dictionaryStore";
import { saveImage } from "@/utils"; import { saveImage } from "@/utils";
import { waitForAuthInit } from "@/utils/authInit";
const ListPage = () => { const ListPage = () => {
// 从 store 获取数据和方法 // 从 store 获取数据和方法
@@ -220,11 +221,14 @@ const ListPage = () => {
getCities(); getCities();
getCityQrCode(); getCityQrCode();
// 2. 延迟执行:获取用户信息(不阻塞渲染) // 2. 延迟执行:等待静默登录完成后获取用户信息
requestAnimationFrame(() => { requestAnimationFrame(async () => {
fetchUserInfo().catch((error) => { try {
await waitForAuthInit();
await fetchUserInfo();
} catch (error) {
console.error('获取用户信息失败:', error); console.error('获取用户信息失败:', error);
}); }
}); });
// 3. 延迟执行:获取位置信息(可能较慢,不阻塞首屏) // 3. 延迟执行:获取位置信息(可能较慢,不阻塞首屏)

View File

@@ -14,6 +14,7 @@ import WechatTimeline from "@/static/detail/wechat_timeline.svg";
import { useUserActions } from "@/store/userStore"; import { useUserActions } from "@/store/userStore";
import { DayOfWeekMap } from "../detail/config"; import { DayOfWeekMap } from "../detail/config";
import { genNTRPRequirementText } from "@/utils/helper"; import { genNTRPRequirementText } from "@/utils/helper";
import { waitForAuthInit } from "@/utils/authInit";
import styles from "./index.module.scss"; import styles from "./index.module.scss";
function SharePoster(props) { function SharePoster(props) {
@@ -41,6 +42,8 @@ function SharePoster(props) {
image_list, image_list,
title, title,
} = detail || {}; } = detail || {};
// 先等待静默登录完成
await waitForAuthInit();
const userInfo = await fetchUserInfo(); const userInfo = await fetchUserInfo();
const { avatar_url, nickname } = userInfo; const { avatar_url, nickname } = userInfo;
const startTime = dayjs(start_time); const startTime = dayjs(start_time);

View File

@@ -9,7 +9,6 @@ import { View, Image, Text, ScrollView } from "@tarojs/components";
import ListContainer from "@/container/listContainer"; import ListContainer from "@/container/listContainer";
import DistanceQuickFilter from "@/components/DistanceQuickFilter"; import DistanceQuickFilter from "@/components/DistanceQuickFilter";
import { updateUserLocation } from "@/services/userService"; import { updateUserLocation } from "@/services/userService";
import { useUserActions } from "@/store/userStore";
import { useDictionaryStore } from "@/store/dictionaryStore"; import { useDictionaryStore } from "@/store/dictionaryStore";
import { saveImage, navigateTo } from "@/utils"; import { saveImage, navigateTo } from "@/utils";
@@ -35,7 +34,6 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
onFilterPopupVisibleChange, onFilterPopupVisibleChange,
}) => { }) => {
const store = useListStore() || {}; const store = useListStore() || {};
const { fetchUserInfo } = useUserActions();
const { statusNavbarHeightInfo, getCurrentLocationInfo } = const { statusNavbarHeightInfo, getCurrentLocationInfo } =
useGlobalState() || {}; useGlobalState() || {};
const { totalHeight = 98 } = statusNavbarHeightInfo || {}; const { totalHeight = 98 } = statusNavbarHeightInfo || {};
@@ -196,12 +194,8 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
getCities(); getCities();
getCityQrCode(); getCityQrCode();
// 2. 延迟执行:获取用户信息(不阻塞渲染) // 2. 移除 fetchUserInfo 调用,因为父组件 main_pages/index.tsx 已经在授权成功后调用了
requestAnimationFrame(() => { // 这里直接使用 store 中的用户信息即可
fetchUserInfo().catch((error) => {
console.error('获取用户信息失败:', error);
});
});
// 3. 延迟执行:获取位置信息(可能较慢,不阻塞首屏) // 3. 延迟执行:获取位置信息(可能较慢,不阻塞首屏)
requestAnimationFrame(() => { requestAnimationFrame(() => {

View File

@@ -1,8 +1,9 @@
import React, { useState, useEffect, useCallback } from "react"; import React, { useState, useEffect, useCallback } from "react";
import { View } from "@tarojs/components"; import { View } from "@tarojs/components";
import Taro from "@tarojs/taro"; 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 { useUserActions } from "@/store/userStore";
import tokenManager from "@/utils/tokenManager";
import GuideBar from "@/components/GuideBar"; import GuideBar from "@/components/GuideBar";
import { GeneralNavbar } from "@/components"; import { GeneralNavbar } from "@/components";
import HomeNavbar from "@/components/HomeNavbar"; import HomeNavbar from "@/components/HomeNavbar";
@@ -24,41 +25,66 @@ const MainPage: React.FC = () => {
const [isShowInputCustomerNavBar, setIsShowInputCustomerNavBar] = useState(false); const [isShowInputCustomerNavBar, setIsShowInputCustomerNavBar] = useState(false);
const [listPageScrollToTopTrigger, setListPageScrollToTopTrigger] = useState(0); const [listPageScrollToTopTrigger, setListPageScrollToTopTrigger] = useState(0);
const [showGuideBar, setShowGuideBar] = useState(true); const [showGuideBar, setShowGuideBar] = useState(true);
const [showAuthError, setShowAuthError] = useState(false);
const [authErrorMessage, setAuthErrorMessage] = useState('');
const { fetchUserInfo } = useUserActions(); const { fetchUserInfo } = useUserActions();
// 初始化:尝试静默登录并获取用户信息 // 初始化:自动微信授权并获取用户信息
useEffect(() => { useEffect(() => {
const init = async () => { const init = async () => {
// 先检查是否已登录 const hasValidToken = tokenManager.hasValidToken();
const login_status = check_login_status();
if (login_status) { 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 { try {
await fetchUserInfo(); await fetchUserInfo();
} catch (error) { } catch (error) {
console.error('获取用户信息失败:', 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(); init();
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, []); // fetchUserInfo 是稳定的函数引用,不需要加入依赖项 }, []); // fetchUserInfo 是稳定的函数引用,不需要加入依赖项
// 显示授权错误提示
useEffect(() => {
if (showAuthError) {
Taro.showModal({
title: '授权失败',
content: `${authErrorMessage}\n\n请重启小程序后重试`,
showCancel: false,
confirmText: '我知道了',
success: () => {
setShowAuthError(false);
}
});
}
}, [showAuthError, authErrorMessage]);
// 处理标签切换 // 处理标签切换
const handleTabChange = useCallback((code: string) => { const handleTabChange = useCallback((code: string) => {
if (code === currentTab) { if (code === currentTab) {

View File

@@ -15,6 +15,7 @@ import { useEvaluate, EvaluateScene } from "@/store/evaluateStore";
import { useGlobalState } from "@/store/global"; import { useGlobalState } from "@/store/global";
import { delay, getCurrentFullPath } from "@/utils"; import { delay, getCurrentFullPath } from "@/utils";
import { formatNtrpDisplay } from "@/utils/helper"; import { formatNtrpDisplay } from "@/utils/helper";
import { waitForAuthInit } from "@/utils/authInit";
import CloseIcon from "@/static/ntrp/ntrp_close_icon.svg"; import CloseIcon from "@/static/ntrp/ntrp_close_icon.svg";
import DocCopy from "@/static/ntrp/ntrp_doc_copy.svg"; import DocCopy from "@/static/ntrp/ntrp_doc_copy.svg";
import ArrowRight from "@/static/ntrp/ntrp_arrow_right.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 { ntrp_level, create_time, id } = last_test_result || {};
const lastTestTime = create_time ? dayjs(create_time).format("YYYY年M月D日") : ""; const lastTestTime = create_time ? dayjs(create_time).format("YYYY年M月D日") : "";
// 组件初始化时立即获取用户信息
useEffect(() => { useEffect(() => {
// 如果用户信息为空,立即获取 const init = async () => {
if (!userInfo || Object.keys(userInfo).length === 0) { // 先等待静默登录完成
fetchUserInfo(); await waitForAuthInit();
} // 然后再调用接口
}, []); await getLastResult();
};
useEffect(() => { init();
getLastResult();
}, []); }, []);
async function getLastResult() { async function getLastResult() {
@@ -193,9 +192,8 @@ function Intro() {
}); });
} }
Taro.redirectTo({ Taro.redirectTo({
url: `/other_pages/ntrp-evaluate/index?stage=${type}${ url: `/other_pages/ntrp-evaluate/index?stage=${type}${type === StageType.RESULT ? `&id=${id}` : ""
type === StageType.RESULT ? `&id=${id}` : "" }`,
}`,
}); });
} }
@@ -450,20 +448,18 @@ function Result() {
[propName: string, prop: number][] [propName: string, prop: number][]
>([]); >([]);
// 组件初始化时立即获取用户信息
useEffect(() => { useEffect(() => {
// 如果用户信息为空,立即获取 const init = async () => {
if (!userInfo || Object.keys(userInfo).length === 0) { // 先等待静默登录完成
fetchUserInfo(); await waitForAuthInit();
} // 然后再调用接口
}, []); await getResultById();
// 确保用户信息已加载
useEffect(() => { if (!userInfo || Object.keys(userInfo).length === 0) {
getResultById(); await fetchUserInfo();
// 确保用户信息已加载 }
if (!userInfo || Object.keys(userInfo).length === 0) { };
fetchUserInfo(); init();
}
}, [id]); }, [id]);
async function getResultById() { async function getResultById() {
@@ -624,9 +620,8 @@ function Result() {
} }
const currentPage = getCurrentFullPath(); const currentPage = getCurrentFullPath();
Taro.redirectTo({ Taro.redirectTo({
url: `/login_pages/index/index${ url: `/login_pages/index/index${currentPage ? `?redirect=${encodeURIComponent(currentPage)}` : ""
currentPage ? `?redirect=${encodeURIComponent(currentPage)}` : "" }`,
}`,
}); });
} }

View File

@@ -80,6 +80,7 @@ export interface UserInfoType {
} }
// 微信授权登录 // 微信授权登录
// phone_code: 可选参数,如果提供则绑定手机号,否则只进行微信授权
export const wechat_auth_login = async ( export const wechat_auth_login = async (
phone_code?: string, phone_code?: string,
): Promise<LoginResponse> => { ): Promise<LoginResponse> => {
@@ -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, 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) { if (auth_response.code === 0) {
return { return {

105
src/utils/authInit.ts Normal file
View File

@@ -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<void> | null = null;
// 初始化用户授权的核心逻辑
const initUserAuthCore = async (): Promise<void> => {
// 如果正在初始化,直接返回现有的 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<void> => {
// 检查是否已经有有效的 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();
};