|
|
@@ -1,15 +1,25 @@
|
|
|
-import { useEffect } from 'react';
|
|
|
+import { useEffect, useRef } from 'react';
|
|
|
import { useNavigate, useLocation } from 'react-router-dom';
|
|
|
-import { hasUserId, clearUserRouteData, getUserLevel, getStorage, STORAGE_KEYS } from '@/utils/storage';
|
|
|
+import {
|
|
|
+ clearStorage,
|
|
|
+ getUserLevel,
|
|
|
+ getStorage,
|
|
|
+ getUserId,
|
|
|
+ STORAGE_KEYS,
|
|
|
+ syncUserDataFromDatabase
|
|
|
+} from '@/utils/storage';
|
|
|
+import { getUserInfo, getUserUIState } from '@/utils/api';
|
|
|
|
|
|
/**
|
|
|
* 用户ID守卫组件
|
|
|
- * 在非首页路由检查用户ID,如果不存在则清空路由数据并跳转到首页
|
|
|
- * 首页路由由 RootRedirect 组件处理用户创建逻辑
|
|
|
+ * 在访问任何界面时:
|
|
|
+ * 1. 如果没有 user_id,清空 localStorage 并跳转到 /welcome
|
|
|
+ * 2. 如果有 user_id,检查数据库中是否存在,存在则同步数据,不存在则清空并跳转
|
|
|
*/
|
|
|
export const UserGuard = ({ children }: { children: React.ReactNode }) => {
|
|
|
const navigate = useNavigate();
|
|
|
const location = useLocation();
|
|
|
+ const isCheckingRef = useRef(false); // 防止重复检查
|
|
|
|
|
|
useEffect(() => {
|
|
|
// 根路由交给 RootRedirect 处理
|
|
|
@@ -17,33 +27,85 @@ export const UserGuard = ({ children }: { children: React.ReactNode }) => {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- if (!hasUserId()) {
|
|
|
- // 当本地没有 user_id 时,仅在需要重定向时才清理并跳转
|
|
|
- // 登录流程相关页面允许无 user_id 访问
|
|
|
- const allowList = ['/welcome', '/naming', '/login', '/login-verification-code', '/login-verification-input'];
|
|
|
- if (!allowList.includes(location.pathname)) {
|
|
|
- // 清理本地路由数据,然后跳转到 /welcome
|
|
|
- clearUserRouteData();
|
|
|
+ // 允许无 user_id 访问的页面列表
|
|
|
+ const allowList = ['/welcome', '/naming', '/login', '/login-verification-code', '/login-verification-input'];
|
|
|
+
|
|
|
+ const checkAndSyncUser = async () => {
|
|
|
+ // 防止重复检查
|
|
|
+ if (isCheckingRef.current) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const userId = getUserId();
|
|
|
+
|
|
|
+ // 情况1: 没有 user_id
|
|
|
+ if (!userId) {
|
|
|
+ // 如果不在允许列表中,清空 localStorage 并跳转
|
|
|
+ if (!allowList.includes(location.pathname)) {
|
|
|
+ console.log('[UserGuard] 没有 user_id,清空 localStorage 并跳转到 /welcome');
|
|
|
+ clearStorage();
|
|
|
+ navigate('/welcome', { replace: true });
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 情况2: 有 user_id,需要检查数据库中是否存在
|
|
|
+ isCheckingRef.current = true;
|
|
|
+ try {
|
|
|
+ console.log('[UserGuard] 检查用户是否存在并同步数据:', userId);
|
|
|
+
|
|
|
+ // 并行获取用户信息和 UI 状态
|
|
|
+ const [userInfoResult, uiStateResult] = await Promise.all([
|
|
|
+ getUserInfo(userId).catch((error) => {
|
|
|
+ // 如果用户不存在(404)或其他错误,返回 null
|
|
|
+ console.warn('[UserGuard] 获取用户信息失败:', error);
|
|
|
+ return null;
|
|
|
+ }),
|
|
|
+ getUserUIState(userId).catch((error) => {
|
|
|
+ // UI 状态获取失败不影响主流程
|
|
|
+ console.warn('[UserGuard] 获取用户UI状态失败:', error);
|
|
|
+ return null;
|
|
|
+ }),
|
|
|
+ ]);
|
|
|
+
|
|
|
+ // 检查用户是否存在
|
|
|
+ if (!userInfoResult || !userInfoResult.success || !userInfoResult.data) {
|
|
|
+ // 用户不存在,清空 localStorage 并跳转
|
|
|
+ console.log('[UserGuard] 用户不存在,清空 localStorage 并跳转到 /welcome');
|
|
|
+ clearStorage();
|
|
|
+ navigate('/welcome', { replace: true });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 用户存在,同步数据到 localStorage
|
|
|
+ console.log('[UserGuard] 用户存在,同步数据到 localStorage');
|
|
|
+ syncUserDataFromDatabase(
|
|
|
+ userInfoResult.data,
|
|
|
+ uiStateResult?.success && uiStateResult.data ? uiStateResult.data : undefined
|
|
|
+ );
|
|
|
+
|
|
|
+ // 同步完成后,继续原有的等级检查逻辑
|
|
|
+ const userLevel = getUserLevel();
|
|
|
+ const isLoggedIn = getStorage(STORAGE_KEYS.IS_LOGGED_IN) === 'true';
|
|
|
+ const levelWelcomePages = ['/level5-welcome', '/level34-welcome'];
|
|
|
+ const loginFlowPages = ['/first-login', '/verification-code', '/verification-input', '/login-verification-code', '/login-verification-input', '/first-login-reward', '/login'];
|
|
|
+ const chatbotPage = '/chatbot';
|
|
|
+
|
|
|
+ if (!isLoggedIn && userLevel >= 5 && !levelWelcomePages.includes(location.pathname) && !loginFlowPages.includes(location.pathname) && location.pathname !== chatbotPage) {
|
|
|
+ // 如果用户等级 >= 5 且未登录,且当前不在等级欢迎页或登录流程页或chatbot页,则跳转到 level5-welcome
|
|
|
+ navigate('/level5-welcome', { replace: true });
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('[UserGuard] 检查用户时发生错误:', error);
|
|
|
+ // 发生错误时,清空 localStorage 并跳转
|
|
|
+ clearStorage();
|
|
|
navigate('/welcome', { replace: true });
|
|
|
+ } finally {
|
|
|
+ isCheckingRef.current = false;
|
|
|
}
|
|
|
- // 在允许列表上保留本地路由数据,避免刚写入就被清空
|
|
|
- return;
|
|
|
- }
|
|
|
+ };
|
|
|
|
|
|
- // 检查用户等级和登录状态:如果用户等级 >= 5 且未登录,跳转到 level5-welcome
|
|
|
- const userLevel = getUserLevel();
|
|
|
- const isLoggedIn = getStorage(STORAGE_KEYS.IS_LOGGED_IN) === 'true';
|
|
|
- const levelWelcomePages = ['/level5-welcome', '/level34-welcome'];
|
|
|
- // 登录流程相关页面,不应该被重定向回 level5-welcome
|
|
|
- const loginFlowPages = ['/first-login', '/verification-code', '/verification-input', '/login-verification-code', '/login-verification-input', '/first-login-reward', '/login'];
|
|
|
- // chatbot 页面由 ChatBotPage 自己处理跳转逻辑(需要等待音频播放完成)
|
|
|
- const chatbotPage = '/chatbot';
|
|
|
-
|
|
|
- if (!isLoggedIn && userLevel >= 5 && !levelWelcomePages.includes(location.pathname) && !loginFlowPages.includes(location.pathname) && location.pathname !== chatbotPage) {
|
|
|
- // 如果用户等级 >= 5 且未登录,且当前不在等级欢迎页或登录流程页或chatbot页,则跳转到 level5-welcome
|
|
|
- // 注意:chatbot 页面的跳转由 ChatBotPage 自己处理,避免中断音频播放
|
|
|
- navigate('/level5-welcome', { replace: true });
|
|
|
- }
|
|
|
+ checkAndSyncUser();
|
|
|
}, [location.pathname, navigate]);
|
|
|
|
|
|
return <>{children}</>;
|