修改用户登陆认证流程

This commit is contained in:
张成
2025-11-16 23:55:49 +08:00
parent 5f8fb9b1b3
commit abbfc27479
12 changed files with 229 additions and 99 deletions

View File

@@ -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<AppProps> {
// 这里可以自定义弹窗(或直接默认同意)
resolve({ event: 'agree' }); // 同意隐私协议
});
// 移除这里的静默登录调用,避免重复调用
// 静默登录在 home_pages/index.tsx 中统一执行
}
componentDidMount() {

View File

@@ -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<P extends object>(
WrappedComponent: React.ComponentType<P>,
) {
const ComponentWithAuth: React.FC<P> = (props: P) => {
const [authed, setAuthed] = useState<boolean | null>(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 (
<View
style={{
width: "100vw",
height: "100vh",
backgroundColor: "#FAFAFA",
position: "fixed",
top: 0,
left: 0,
zIndex: 999,
}}
/>
);
}
// 未登录,显示空白页面(已跳转,这里只是防止渲染)
if (!authed) {
return (
<View
style={{
width: "100vw",
height: "100vh",
backgroundColor: "#FAFAFA",
position: "fixed",
top: 0,
left: 0,
zIndex: 999,
}}
/>
);
}
// 已登录,正常渲染组件
// 不再强制检查登录状态,直接渲染组件
// 组件内部可以在需要时调用 requireLogin() 进行检查
return <WrappedComponent {...props} />;
};

View File

@@ -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;

View File

@@ -606,4 +606,4 @@ const ListPage = () => {
);
};
export default withAuth(ListPage);
export default ListPage;

View File

@@ -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();

View File

@@ -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;

View File

@@ -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: "支付中...",

View File

@@ -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) {

View File

@@ -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();

View File

@@ -420,3 +420,93 @@ export const unFollowUser = async (following_id) => {
throw error;
}
};
// 静默登录锁,防止并发调用
let silentLoginPromise: Promise<LoginResponse> | null = null;
// 静默登录只获取用户ID和token不需要手机号
export const silentLogin = async (): Promise<LoginResponse> => {
// 如果已经有正在进行的静默登录,直接返回该 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<LoginResponse> => {
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;
};

View File

@@ -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,

View File

@@ -11,3 +11,4 @@ export * from './share'
export * from './genPoster'
export * from './wx_helper'
export * from './navigation';
export * from './helper';