修改用户授权调用位置和逻辑
This commit is contained in:
81
fetchUserInfo_analysis.md
Normal file
81
fetchUserInfo_analysis.md
Normal 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 后的逻辑中调用
|
||||||
|
|
||||||
@@ -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() { }
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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(() => {
|
||||||
|
|||||||
@@ -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. 延迟执行:获取位置信息(可能较慢,不阻塞首屏)
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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(() => {
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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)}` : ""
|
}`,
|
||||||
}`,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
105
src/utils/authInit.ts
Normal 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();
|
||||||
|
};
|
||||||
|
|
||||||
Reference in New Issue
Block a user