From fa2d82333c2c407292b28134ada594a6696e7bd4 Mon Sep 17 00:00:00 2001 From: Nelson <1475262689@qq.com> Date: Sun, 5 Jul 2026 19:48:32 +0800 Subject: [PATCH] Add WeChat auth entry for H5 --- hy_qa_asset_backend/.env.example | 5 +++ .../frontend/components/h5/H5QAExperience.tsx | 24 +++++++++++ .../frontend/components/h5/MockAuthPanel.tsx | 33 +++++++++++--- .../frontend/lib/wechatAuth.ts | 43 +++++++++++++++++++ 4 files changed, 100 insertions(+), 5 deletions(-) create mode 100644 hy_qa_asset_backend/frontend/lib/wechatAuth.ts diff --git a/hy_qa_asset_backend/.env.example b/hy_qa_asset_backend/.env.example index 3b0939c..a58170d 100644 --- a/hy_qa_asset_backend/.env.example +++ b/hy_qa_asset_backend/.env.example @@ -16,3 +16,8 @@ SCHEDULE_CRON=0 1 * * * SCHEDULE_TIMEZONE=Asia/Shanghai MOCK_MODE=true + +# H5 WeChat OAuth. Leave empty to use the frontend demo authorization flow. +NEXT_PUBLIC_WECHAT_APP_ID= +NEXT_PUBLIC_WECHAT_AUTH_REDIRECT_URI= +NEXT_PUBLIC_WECHAT_AUTH_SCOPE=snsapi_userinfo diff --git a/hy_qa_asset_backend/frontend/components/h5/H5QAExperience.tsx b/hy_qa_asset_backend/frontend/components/h5/H5QAExperience.tsx index f7fdba3..37302d6 100644 --- a/hy_qa_asset_backend/frontend/components/h5/H5QAExperience.tsx +++ b/hy_qa_asset_backend/frontend/components/h5/H5QAExperience.tsx @@ -128,6 +128,20 @@ export default function H5QAExperience() { .finally(() => setLoadingQa(false)); }, []); + useEffect(() => { + const params = new URLSearchParams(window.location.search); + const code = params.get("code"); + const state = params.get("state"); + if (code && state === "hyqa_h5") { + setUser({ + mode: "wechat", + name: "微信已授权", + phone: "等待后端换取登录态" + }); + window.history.replaceState(null, "", window.location.pathname); + } + }, []); + useEffect(() => { bottomRef.current?.scrollIntoView({ behavior: "smooth", block: "end" }); }, [messages, activeTab]); @@ -141,6 +155,15 @@ export default function H5QAExperience() { setAuthOpen(false); } + function mockWechatLogin() { + setUser({ + mode: "wechat", + name: "微信用户", + phone: "微信授权演示" + }); + setAuthOpen(false); + } + function persistMessages(nextMessages: ChatMessage[]) { setMessages(nextMessages); setSessions((current) => { @@ -413,6 +436,7 @@ export default function H5QAExperience() { user={user} onClose={() => setAuthOpen(false)} onMockLogin={mockLogin} + onWechatMockLogin={mockWechatLogin} onLogout={() => { setUser(null); setAuthOpen(false); diff --git a/hy_qa_asset_backend/frontend/components/h5/MockAuthPanel.tsx b/hy_qa_asset_backend/frontend/components/h5/MockAuthPanel.tsx index bb7da90..26b0b18 100644 --- a/hy_qa_asset_backend/frontend/components/h5/MockAuthPanel.tsx +++ b/hy_qa_asset_backend/frontend/components/h5/MockAuthPanel.tsx @@ -1,11 +1,12 @@ "use client"; -import { LogIn, ShieldCheck, UserPlus, X } from "lucide-react"; +import { LogIn, MessageCircle, ShieldCheck, UserPlus, X } from "lucide-react"; +import { buildWechatOAuthUrl, getWechatAuthConfig, isWechatBrowser } from "@/lib/wechatAuth"; export type MockUser = { name: string; phone: string; - mode: "login" | "register"; + mode: "login" | "register" | "wechat"; }; export default function MockAuthPanel({ @@ -13,23 +14,36 @@ export default function MockAuthPanel({ user, onClose, onMockLogin, + onWechatMockLogin, onLogout }: { open: boolean; user: MockUser | null; onClose: () => void; onMockLogin: (mode: MockUser["mode"]) => void; + onWechatMockLogin: () => void; onLogout: () => void; }) { if (!open) return null; + const wechatConfig = getWechatAuthConfig(); + const inWechat = typeof navigator !== "undefined" && isWechatBrowser(navigator.userAgent); + + function startWechatAuth() { + if (wechatConfig) { + window.location.href = buildWechatOAuthUrl(wechatConfig); + return; + } + onWechatMockLogin(); + } + return (

账号入口

-

先用 mock 登录,后续可替换为微信授权或手机号登录。

+

优先预留微信授权;没有配置时使用演示账号。

+
diff --git a/hy_qa_asset_backend/frontend/lib/wechatAuth.ts b/hy_qa_asset_backend/frontend/lib/wechatAuth.ts new file mode 100644 index 0000000..b981d0b --- /dev/null +++ b/hy_qa_asset_backend/frontend/lib/wechatAuth.ts @@ -0,0 +1,43 @@ +const WECHAT_OAUTH_URL = "https://open.weixin.qq.com/connect/oauth2/authorize"; + +export type WechatAuthConfig = { + appId: string; + redirectUri: string; + scope: "snsapi_base" | "snsapi_userinfo"; + state: string; +}; + +export function getWechatAuthConfig(): WechatAuthConfig | null { + const appId = process.env.NEXT_PUBLIC_WECHAT_APP_ID; + const redirectUri = + process.env.NEXT_PUBLIC_WECHAT_AUTH_REDIRECT_URI || + (typeof window !== "undefined" ? `${window.location.origin}/h5` : ""); + const scope = (process.env.NEXT_PUBLIC_WECHAT_AUTH_SCOPE || "snsapi_userinfo") as WechatAuthConfig["scope"]; + + if (!appId || !redirectUri) { + return null; + } + + return { + appId, + redirectUri, + scope, + state: "hyqa_h5" + }; +} + +export function buildWechatOAuthUrl(config: WechatAuthConfig): string { + const params = new URLSearchParams({ + appid: config.appId, + redirect_uri: config.redirectUri, + response_type: "code", + scope: config.scope, + state: config.state + }); + + return `${WECHAT_OAUTH_URL}?${params.toString()}#wechat_redirect`; +} + +export function isWechatBrowser(userAgent: string): boolean { + return /MicroMessenger/i.test(userAgent); +}