diff --git a/src/app.ts b/src/app.ts index aef2b49..1093ef5 100644 --- a/src/app.ts +++ b/src/app.ts @@ -6,6 +6,7 @@ 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"; interface AppProps { children: ReactNode; @@ -24,6 +25,9 @@ class App extends Component { // 这里可以自定义弹窗(或直接默认同意) resolve({ event: 'agree' }); // 同意隐私协议 }); + + // 移除这里的静默登录调用,避免重复调用 + // 静默登录在 home_pages/index.tsx 中统一执行 } componentDidMount() { diff --git a/src/components/Auth/index.tsx b/src/components/Auth/index.tsx index c4354c3..2432976 100644 --- a/src/components/Auth/index.tsx +++ b/src/components/Auth/index.tsx @@ -1,85 +1,14 @@ -import React, { useEffect, useState } from "react"; -import { View } from "@tarojs/components"; -import Taro from "@tarojs/taro"; -import { getCurrentFullPath } from '@/utils'; -import { check_login_status, clear_login_state } from "@/services/loginService"; +import React from "react"; +import { check_login_status } from "@/services/loginService"; +// withAuth 现在改为可选登录检查,不强制跳转 +// 如果需要强制登录,请在组件内部使用 requireLogin() 工具函数 export default function withAuth

( WrappedComponent: React.ComponentType

, ) { const ComponentWithAuth: React.FC

= (props: P) => { - const [authed, setAuthed] = useState(null); // null表示未检查 - const [isChecking, setIsChecking] = useState(true); - - useEffect(() => { - const checkAuth = async () => { - setIsChecking(true); - try { - const is_login = check_login_status(); - - if (!is_login) { - // 未登录,清除可能过期的状态 - clear_login_state(); - const currentPage = getCurrentFullPath(); - // 跳转到登录页,并保存当前页面路径用于登录后跳转 - (Taro as any).redirectTo({ - url: `/login_pages/index/index${ - currentPage ? `?redirect=${encodeURIComponent(currentPage)}` : "" - }`, - }); - setAuthed(false); - return; - } - - setAuthed(true); - } catch (error) { - console.error('检查登录状态失败:', error); - clear_login_state(); - (Taro as any).redirectTo({ url: '/login_pages/index/index' }); - setAuthed(false); - } finally { - setIsChecking(false); - } - }; - - checkAuth(); - }, []); - - // 正在检查登录状态,显示空白页面避免闪烁 - if (isChecking || authed === null) { - return ( - - ); - } - - // 未登录,显示空白页面(已跳转,这里只是防止渲染) - if (!authed) { - return ( - - ); - } - - // 已登录,正常渲染组件 + // 不再强制检查登录状态,直接渲染组件 + // 组件内部可以在需要时调用 requireLogin() 进行检查 return ; }; diff --git a/src/game_pages/detail/index.tsx b/src/game_pages/detail/index.tsx index a1656c5..1e53ba1 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 { requireLoginWithPhone } from "@/utils/helper"; import GameTags from "./components/GameTags"; import Carousel from "./components/Carousel"; import StickyBottom from "./components/StickyBottom"; @@ -112,6 +113,11 @@ function Index() { } const handleJoinGame = async () => { + // 检查登录状态和手机号 + if (!requireLoginWithPhone()) { + return; // 未登录或未绑定手机号,已跳转到登录页 + } + if (isMyOwn) { const res = await DetailService.organizerJoin(Number(id)); if (res.code === 0) { @@ -274,6 +280,4 @@ function Index() { ); } -// export default withAuth(Index); -const ExportPage = withAuth(Index); -export default ExportPage; +export default Index; diff --git a/src/game_pages/list/index.tsx b/src/game_pages/list/index.tsx index 9d25852..be4c15a 100644 --- a/src/game_pages/list/index.tsx +++ b/src/game_pages/list/index.tsx @@ -606,4 +606,4 @@ const ListPage = () => { ); }; -export default withAuth(ListPage); +export default ListPage; diff --git a/src/home_pages/index.tsx b/src/home_pages/index.tsx index 4e0c191..48bf7fb 100644 --- a/src/home_pages/index.tsx +++ b/src/home_pages/index.tsx @@ -1,6 +1,6 @@ import React, { useEffect } from 'react'; import { View, } from '@tarojs/components'; -import { check_login_status } from '@/services/loginService'; +import { silentLogin } from '@/services/loginService'; import { useUserActions } from '@/store/userStore'; import Taro from '@tarojs/taro'; import "./index.scss"; @@ -9,18 +9,25 @@ const HomePage: React.FC = () => { useEffect(() => { - const handleLoginRedirect = () => { - const login_status = check_login_status(); - if (login_status) { - // 先跳转,不阻塞启动 - Taro.redirectTo({ url: '/main_pages/index' }); - // 异步获取用户信息,不阻塞跳转 - fetchUserInfo().catch((error) => { - console.error('获取用户信息失败:', error); - }); - } else { - Taro.redirectTo({ url: '/login_pages/index/index' }); + const handleLoginRedirect = async () => { + // 先执行静默登录,然后再跳转 + try { + console.log('开始静默登录...'); + const loginResult = await silentLogin(); + console.log('静默登录结果:', loginResult); + if (loginResult.success) { + // 静默登录成功,获取用户信息 + fetchUserInfo().catch((error) => { + console.error('获取用户信息失败:', error); + }); + } + } catch (error) { + console.error('静默登录失败:', error); + // 静默登录失败不影响使用 } + + // 无论静默登录是否成功,都跳转到主页面 + Taro.redirectTo({ url: '/main_pages/index' }); }; handleLoginRedirect(); diff --git a/src/main_pages/index.tsx b/src/main_pages/index.tsx index 2beec5a..382ec92 100644 --- a/src/main_pages/index.tsx +++ b/src/main_pages/index.tsx @@ -1,10 +1,10 @@ import React, { useState, useEffect, useCallback } from "react"; import { View } from "@tarojs/components"; import Taro from "@tarojs/taro"; -import { check_login_status } from "@/services/loginService"; +import { check_login_status, silentLogin } from "@/services/loginService"; import { useUserActions } from "@/store/userStore"; import GuideBar from "@/components/GuideBar"; -import { withAuth, GeneralNavbar } from "@/components"; +import { GeneralNavbar } from "@/components"; import HomeNavbar from "@/components/HomeNavbar"; import ListPageContent from "./components/ListPageContent"; import MessagePageContent from "./components/MessagePageContent"; @@ -27,16 +27,32 @@ const MainPage: React.FC = () => { const { fetchUserInfo } = useUserActions(); - // 初始化:检查登录状态并获取用户信息 + // 初始化:尝试静默登录并获取用户信息 useEffect(() => { const init = async () => { + // 先检查是否已登录 const login_status = check_login_status(); if (login_status) { + // 已登录,获取用户信息 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(); @@ -213,5 +229,5 @@ const MainPage: React.FC = () => { ); }; -export default withAuth(MainPage); +export default MainPage; diff --git a/src/order_pages/orderDetail/index.tsx b/src/order_pages/orderDetail/index.tsx index 42b0653..8c49f2a 100644 --- a/src/order_pages/orderDetail/index.tsx +++ b/src/order_pages/orderDetail/index.tsx @@ -27,6 +27,7 @@ import { withAuth, RefundPopup, GeneralNavbar } from "@/components"; import img from "@/config/images"; import CustomerIcon from "@/static/order/customer.svg"; import { handleCustomerService } from "@/services/userService"; +import { requireLoginWithPhone } from "@/utils/helper"; import { DECLAIMER } from "./config"; import styles from "./index.module.scss"; @@ -583,6 +584,11 @@ const OrderCheck = () => { } async function getPaymentParams() { + // 检查登录状态和手机号(创建订单前检查) + if (!requireLoginWithPhone()) { + throw new Error("请先绑定手机号"); + } + const unPaidRes = await orderService.getUnpaidOrder(detail.id); if (unPaidRes.code === 0 && unPaidRes.data.has_unpaid_order) { return unPaidRes.data.payment_params; @@ -596,6 +602,10 @@ const OrderCheck = () => { //TODO: get order msg from id const handlePay = async () => { + // 检查登录状态和手机号 + if (!requireLoginWithPhone()) { + return; // 未登录或未绑定手机号,已跳转到登录页 + } setPaying(true); Taro.showLoading({ title: "支付中...", diff --git a/src/order_pages/orderList/index.tsx b/src/order_pages/orderList/index.tsx index 89baf9f..f555fb0 100644 --- a/src/order_pages/orderList/index.tsx +++ b/src/order_pages/orderList/index.tsx @@ -17,7 +17,7 @@ import { withAuth, RefundPopup, GeneralNavbar } from "@/components"; import { payOrder, generateOrderActions } from "@/utils"; import emptyContent from "@/static/emptyStatus/publish-empty.png"; import CustomerIcon from "@/static/order/customer.svg"; -import { insertDotInTags, genNTRPRequirementText } from "@/utils/helper"; +import { insertDotInTags, genNTRPRequirementText, requireLoginWithPhone } from "@/utils/helper"; import styles from "./index.module.scss"; dayjs.locale("zh-cn"); @@ -111,6 +111,11 @@ const OrderList = () => { } async function handlePayNow(item) { + // 检查登录状态和手机号 + if (!requireLoginWithPhone()) { + return; // 未登录或未绑定手机号,已跳转到登录页 + } + try { const unPaidRes = await orderService.getUnpaidOrder(item.game_info?.id); if (unPaidRes.code === 0 && unPaidRes.data.has_unpaid_order) { diff --git a/src/publish_pages/publishBall/index.tsx b/src/publish_pages/publishBall/index.tsx index ff0854f..0f23282 100644 --- a/src/publish_pages/publishBall/index.tsx +++ b/src/publish_pages/publishBall/index.tsx @@ -13,7 +13,7 @@ import { } from "../../config/formSchema/publishBallFormSchema"; import { PublishBallFormData } from "../../../types/publishBall"; import PublishService from "@/services/publishService"; -import { getNextHourTime, getEndTime, delay } from "@/utils"; +import { getNextHourTime, getEndTime, delay, requireLoginWithPhone } from "@/utils"; import { useGlobalState } from "@/store/global"; import GeneralNavbar from "@/components/GeneralNavbar"; import images from "@/config/images"; @@ -344,6 +344,10 @@ const PublishBall: React.FC = () => { }; // 提交表单 const handleSubmit = async () => { + // 检查登录状态和手机号 + if (!requireLoginWithPhone()) { + return; // 未登录或未绑定手机号,已跳转到登录页 + } // 基础验证 console.log(formData, "formData"); const params = getParams(); diff --git a/src/services/loginService.ts b/src/services/loginService.ts index 6b17fe8..0305657 100644 --- a/src/services/loginService.ts +++ b/src/services/loginService.ts @@ -420,3 +420,93 @@ export const unFollowUser = async (following_id) => { throw error; } }; + +// 静默登录锁,防止并发调用 +let silentLoginPromise: Promise | null = null; + +// 静默登录:只获取用户ID和token,不需要手机号 +export const silentLogin = async (): Promise => { + // 如果已经有正在进行的静默登录,直接返回该 Promise + if (silentLoginPromise) { + console.log('静默登录正在进行中,等待结果...'); + return silentLoginPromise; + } + + // 先检查是否已经登录 + if (check_login_status()) { + console.log('已登录,跳过静默登录'); + return { + success: true, + message: "已登录", + token: get_user_token() || undefined, + user_info: get_user_info() || undefined, + }; + } + + // 创建静默登录 Promise + silentLoginPromise = (async (): Promise => { + try { + console.log('开始执行静默登录...'); + // 调用微信登录获取code + const login_result = await Taro.login(); + console.log('微信登录结果:', login_result); + + if (!login_result.code) { + console.error('微信登录失败:未获取到code'); + return { + success: false, + message: "微信登录失败", + }; + } + + // 调用微信授权接口,不传 phone_code(静默登录) + console.log('调用后端接口进行静默登录...'); + const auth_response = await httpService.post("user/wx_auth", { + code: login_result.code, + // 不传 phone_code,实现静默登录 + }, { + showLoading: false, // 静默登录不显示loading + }); + + console.log('后端接口响应:', auth_response); + + if (auth_response.code === 0) { + const token = auth_response.data?.token || ""; + const user_info = auth_response.data?.userInfo; + + // 保存登录状态 + if (token && user_info) { + console.log('保存登录状态...'); + save_login_state(token, user_info); + console.log('静默登录成功,已保存登录状态'); + } else { + console.warn('静默登录成功,但token或user_info为空'); + } + + return { + success: true, + message: "静默登录成功", + token, + user_info, + }; + } else { + console.error('静默登录失败:', auth_response.message); + return { + success: false, + message: auth_response.message || "静默登录失败", + }; + } + } catch (error) { + console.error("静默登录异常:", error); + return { + success: false, + message: "静默登录失败,请重试", + }; + } finally { + // 清除锁,允许下次调用 + silentLoginPromise = null; + } + })(); + + return silentLoginPromise; +}; \ No newline at end of file diff --git a/src/utils/helper.ts b/src/utils/helper.ts index 0b8b4cf..24d062e 100644 --- a/src/utils/helper.ts +++ b/src/utils/helper.ts @@ -1,4 +1,6 @@ import Taro from "@tarojs/taro"; +import { check_login_status, get_user_info } from "@/services/loginService"; +import { useUser } from "@/store/userStore"; // 普通函数,不调用 useLoad export const sceneRedirectLogic = (options, defaultPage: string) => { @@ -25,6 +27,64 @@ export const sceneRedirectLogic = (options, defaultPage: string) => { } }; +// 获取当前页面路径 +const getCurrentPagePath = (): string => { + const pages = Taro.getCurrentPages(); + const currentPage = pages[pages.length - 1]; + return currentPage ? `/${currentPage.route}${currentPage.options ? '?' + Object.entries(currentPage.options).map(([k, v]) => `${k}=${encodeURIComponent(v as string)}`).join('&') : ''}` : ''; +}; + +// 跳转到登录页 +const navigateToLogin = (currentPath?: string) => { + const path = currentPath || getCurrentPagePath(); + (Taro as any).navigateTo({ + url: `/login_pages/index/index${path ? `?redirect=${encodeURIComponent(path)}` : ''}`, + }); +}; + +// 检查登录状态,如果未登录则跳转到登录页 +export const requireLogin = (): boolean => { + const isLoggedIn = check_login_status(); + + if (!isLoggedIn) { + navigateToLogin(); + return false; + } + + return true; +}; + +// 检查登录状态和手机号,如果未登录或没有手机号则跳转到登录页绑定手机号 +export const requireLoginWithPhone = (): boolean => { + // 先检查登录状态 + const isLoggedIn = check_login_status(); + + if (!isLoggedIn) { + navigateToLogin(); + return false; + } + + // 检查是否有手机号 + // 优先从 store 中获取用户信息 + const userInfo = useUser.getState().user; + let phone = (userInfo as any)?.phone; + + // 如果 store 中没有,尝试从本地存储获取 + if (!phone || phone.trim() === '') { + const localUserInfo = get_user_info(); + phone = localUserInfo?.phone; + } + + // 如果用户信息中没有手机号,或者手机号为空 + if (!phone || phone.trim() === '') { + console.log('用户未绑定手机号,跳转到登录页绑定'); + navigateToLogin(); + return false; + } + + return true; +}; + export function navto(url) { Taro.navigateTo({ url: url, diff --git a/src/utils/index.ts b/src/utils/index.ts index 2ad0bec..bffda04 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -11,3 +11,4 @@ export * from './share' export * from './genPoster' export * from './wx_helper' export * from './navigation'; +export * from './helper';