feat: 添加身份一致性检查,更新登录逻辑以清除不一致的缓存

This commit is contained in:
2026-06-25 18:39:08 +08:00
parent 95f5879c9a
commit 1d8b6936f2

View File

@@ -1,76 +1,91 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useNavigate } from "react-router"; import { useNavigate } from "react-router";
import { motion } from "motion/react"; import { motion } from "motion/react";
import { Heart } from "lucide-react"; import { Heart } from "lucide-react";
import { useUser } from "../context/UserContext"; import { useUser } from "../context/UserContext";
import { devLogin, fetchApi } from "../services/api"; import { devLogin, fetchApi, getToken, clearToken, decodeToken } from "../services/api";
import { debugLog, clearDebugLogs } from "../services/debug"; import { debugLog, clearDebugLogs } from "../services/debug";
export function LoginPage() { export function LoginPage() {
const navigate = useNavigate(); const navigate = useNavigate();
const { setTokenAndUser, fetchProfile } = useUser(); const { setTokenAndUser, fetchProfile } = useUser();
const [error, setError] = useState(""); const [error, setError] = useState("");
useEffect(() => { useEffect(() => {
clearDebugLogs(); clearDebugLogs();
const params = new URLSearchParams(window.location.search); // 身份一致性检查:URL 中的 openid 与缓存 token 不一致时清除缓存
debugLog(`URL: ${window.location.href.substring(0, 100)}`); const urlOpenid = new URLSearchParams(window.location.search).get('openid');
debugLog(`search: ${window.location.search}`); const cachedToken = getToken();
if (urlOpenid && cachedToken) {
const openidParam = params.get('openid'); try {
if (openidParam && openidParam.trim()) { const payload = decodeToken(cachedToken);
debugLog(`openid: ${openidParam}`); if (payload.openid !== urlOpenid) {
fetchApi("/auth/openid-login", { debugLog(`[auth] openid 不一致: URL=${urlOpenid}, cache=${payload.openid},清除缓存`);
method: "POST", clearToken();
body: JSON.stringify({ openid: openidParam }), }
}).then((res: any) => { } catch (e) {
debugLog(`openid-login OK: id=${res.user_id}, phone=${res.mobile}`); debugLog(`[auth] token 解码失败: ${e}`);
setTokenAndUser(res.token, { id: res.user_id, openid: res.openid, phone: res.mobile }); }
fetchProfile().then((dto) => { }
if (dto) debugLog(`fetchProfile: name=${dto.name}, phone=${dto.phone}, onboarded=${dto.is_onboarded}`);
navigate(dto?.is_onboarded ? "/home" : "/onboarding", { replace: true }); const params = new URLSearchParams(window.location.search);
}).catch((e: any) => { debugLog(`URL: ${window.location.href.substring(0, 100)}`);
debugLog(`fetchProfile error: ${e?.message}`); debugLog(`search: ${window.location.search}`);
setError("登录已失效,请在小程序重新进入");
}); const openidParam = params.get('openid');
}).catch((e: any) => { if (openidParam && openidParam.trim()) {
debugLog(`openid-login error: ${e?.message}`); debugLog(`openid: ${openidParam}`);
setError("登录失败: " + (e?.message || "")); fetchApi("/auth/openid-login", {
}); method: "POST",
return; body: JSON.stringify({ openid: openidParam }),
} }).then((res: any) => {
debugLog(`openid-login OK: id=${res.user_id}, phone=${res.mobile}`);
debugLog(`no openid param (val=${openidParam || '(empty)'}), fallback to dev-login`); setTokenAndUser(res.token, { id: res.user_id, openid: res.openid, phone: res.mobile });
devLogin().then((res) => { fetchProfile().then((dto) => {
debugLog(`dev-login: openid=${res.openid}, phone=${res.mobile}`); if (dto) debugLog(`fetchProfile: name=${dto.name}, phone=${dto.phone}, onboarded=${dto.is_onboarded}`);
setTokenAndUser(res.token, { id: res.user_id, openid: res.openid, phone: res.mobile }); navigate(dto?.is_onboarded ? "/home" : "/onboarding", { replace: true });
fetchProfile().then((dto) => { }).catch((e: any) => {
if (dto) debugLog(`fetchProfile: name=${dto.name}, phone=${dto.phone}`); debugLog(`fetchProfile error: ${e?.message}`);
navigate(dto?.is_onboarded ? "/home" : "/onboarding", { replace: true }); setError("登录已失效,请在小程序重新进入");
}); });
}).catch((err) => { }).catch((e: any) => {
debugLog(`dev-login error: ${err?.message}`); debugLog(`openid-login error: ${e?.message}`);
setError(err?.message || "登录失败,请刷新重试"); setError("登录失败: " + (e?.message || ""));
}); });
}, []); // eslint-disable-line react-hooks/exhaustive-deps return;
}
return (
<div className="min-h-dvh bg-morandi-50 flex items-center justify-center p-4"> debugLog(`no openid param (val=${openidParam || '(empty)'}), fallback to dev-login`);
<div className="text-center"> devLogin().then((res) => {
<motion.div debugLog(`dev-login: openid=${res.openid}, phone=${res.mobile}`);
animate={{ rotate: 360 }} setTokenAndUser(res.token, { id: res.user_id, openid: res.openid, phone: res.mobile });
transition={{ repeat: Infinity, duration: 1.5, ease: "linear" }} fetchProfile().then((dto) => {
className="inline-flex items-center justify-center w-16 h-16 bg-gradient-to-br from-morandi-300 to-morandi-400 rounded-full mb-4" if (dto) debugLog(`fetchProfile: name=${dto.name}, phone=${dto.phone}`);
> navigate(dto?.is_onboarded ? "/home" : "/onboarding", { replace: true });
<Heart className="w-8 h-8 text-white fill-white" /> });
</motion.div> }).catch((err) => {
{error ? ( debugLog(`dev-login error: ${err?.message}`);
<p className="text-sm text-red-500">{error}</p> setError(err?.message || "登录失败,请刷新重试");
) : ( });
<p className="text-sm text-gray-500">加载中...</p> }, []); // eslint-disable-line react-hooks/exhaustive-deps
)}
</div> return (
</div> <div className="min-h-dvh bg-morandi-50 flex items-center justify-center p-4">
); <div className="text-center">
} <motion.div
animate={{ rotate: 360 }}
transition={{ repeat: Infinity, duration: 1.5, ease: "linear" }}
className="inline-flex items-center justify-center w-16 h-16 bg-gradient-to-br from-morandi-300 to-morandi-400 rounded-full mb-4"
>
<Heart className="w-8 h-8 text-white fill-white" />
</motion.div>
{error ? (
<p className="text-sm text-red-500">{error}</p>
) : (
<p className="text-sm text-gray-500">加载中...</p>
)}
</div>
</div>
);
}