# 价值前沿认证系统完整文档 > **版本**: 2.0 > **更新日期**: 2025-01-16 > **作者**: Claude Code > **适用范围**: 前端 React + 后端 Flask --- ## 📖 目录 1. [系统架构概览](#1-系统架构概览) 2. [认证流程详解](#2-认证流程详解) - [2.1 手机验证码登录](#21-手机验证码登录) - [2.2 微信PC扫码登录](#22-微信pc扫码登录) - [2.3 微信H5网页授权](#23-微信h5网页授权) 3. [路由配置与跳转逻辑](#3-路由配置与跳转逻辑) 4. [API接口文档](#4-api接口文档) 5. [前端组件架构](#5-前端组件架构) 6. [状态管理机制](#6-状态管理机制) 7. [Session持久化](#7-session持久化) 8. [错误处理策略](#8-错误处理策略) 9. [安全机制](#9-安全机制) 10. [调试指南](#10-调试指南) --- ## 1. 系统架构概览 ### 1.1 技术栈 **前端**: - React 18.3.1 - Chakra UI 2.8.2 - React Router 6.x - Context API (状态管理) **后端**: - Flask (Python) - Session-based Authentication - HttpOnly Cookies - Flask-Session **认证方式**: 1. **手机验证码登录** (短信验证码) 2. **微信PC扫码登录** (二维码扫码) 3. **微信H5网页授权** (移动端跳转授权) ### 1.2 架构图 ``` ┌─────────────────────────────────────────────────────────────┐ │ 用户界面层 (UI) │ │ ┌───────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ HomeNavbar │ │ AuthModal │ │ProtectedRoute│ │ │ │ (登录按钮) │ │ (认证弹窗) │ │ (路由保护) │ │ │ └───────────────┘ └──────────────┘ └──────────────┘ │ └─────────────────────────────────────────────────────────────┘ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 状态管理层 (Context) │ │ ┌──────────────────────┐ ┌───────────────────────┐ │ │ │ AuthContext │ │ AuthModalContext │ │ │ │ - user │ │ - isAuthModalOpen │ │ │ │ - isAuthenticated │ │ - openAuthModal() │ │ │ │ - checkSession() │ │ - handleLoginSuccess()│ │ │ └──────────────────────┘ └───────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 业务逻辑层 (Components) │ │ ┌──────────────────┐ ┌─────────────┐ ┌────────────┐ │ │ │ AuthFormContent │ │WechatRegister│ │VerifyCodeInput│ │ │ │ (认证表单) │ │ (微信扫码) │ │ (验证码输入)│ │ │ └──────────────────┘ └─────────────┘ └────────────┘ │ └─────────────────────────────────────────────────────────────┘ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 服务层 (API Service) │ │ ┌──────────────┐ │ │ │ authService │ │ │ │ - getWechatQRCode() │ │ │ - checkWechatStatus() │ │ │ - loginWithWechat() │ │ └──────────────┘ │ └─────────────────────────────────────────────────────────────┘ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 后端 API (Flask) │ │ ┌─────────────────────────────────────────────────┐ │ │ │ /api/auth/session - Session检查 │ │ │ │ /api/auth/send-verification-code - 发送验证码 │ │ │ │ /api/auth/login-with-code - 验证码登录 │ │ │ │ /api/auth/wechat/qrcode - 获取微信二维码 │ │ │ │ /api/auth/wechat/check - 检查扫码状态 │ │ │ │ /api/auth/login/wechat - 微信登录 │ │ │ │ /api/auth/wechat/h5-auth - 微信H5授权 │ │ │ │ /api/auth/wechat/h5-callback - 微信H5回调 │ │ │ │ /api/auth/logout - 退出登录 │ │ │ └─────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 数据存储层 (Database) │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ User表 │ │ Session存储 │ │ 验证码缓存 │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ └─────────────────────────────────────────────────────────────┘ ``` --- ## 2. 认证流程详解 ### 2.1 手机验证码登录 **适用场景**: PC端和移动端通用登录方式 #### 流程图 ``` 用户点击"登录/注册"按钮 ↓ 打开 AuthModal 弹窗 ↓ 输入手机号 → 点击"获取验证码" ↓ 前端: AuthFormContent.sendVerificationCode() ↓ POST /api/auth/send-verification-code Body: { credential: "13800138000", type: "phone", purpose: "login" } ↓ 后端: 生成6位验证码 → 发送短信 → 存入Session/Redis (5分钟有效期) ↓ 返回: { success: true } ↓ 前端显示: "验证码已发送" + 60秒倒计时 ↓ 用户输入验证码 → 点击"登录/注册" ↓ 前端: AuthFormContent.handleSubmit() ↓ POST /api/auth/login-with-code Body: { credential: "13800138000", verification_code: "123456", login_type: "phone" } ↓ 后端验证: 1. 验证码是否存在 ✓ 2. 验证码是否过期 ✓ 3. 验证码是否匹配 ✓ 4. 查询用户是否存在 - 存在: 登录 (isNewUser: false) - 不存在: 自动注册 (isNewUser: true) ↓ 设置 Session Cookie (HttpOnly) ↓ 返回: { success: true, isNewUser: true/false, user: { id, phone, nickname, ... } } ↓ 前端: 1. checkSession() 更新全局状态 2. 显示成功提示 3. 如果 isNewUser=true → 显示昵称设置引导 4. 关闭弹窗,留在当前页面 ↓ 登录完成 ✅ ``` #### 关键代码位置 **前端**: - `src/components/Auth/AuthFormContent.js:130` - 发送验证码 - `src/components/Auth/AuthFormContent.js:207` - 提交登录 - `src/components/Auth/VerificationCodeInput.js` - 验证码输入组件 **后端**: - `app.py:1826` - POST /api/auth/send-verification-code - `app.py:1884` - POST /api/auth/login-with-code --- ### 2.2 微信PC扫码登录 **适用场景**: PC端桌面浏览器 #### 流程图 ``` 用户打开登录弹窗 (桌面端) ↓ 右侧显示微信二维码区域 (WechatRegister组件) ↓ 初始状态: 灰色二维码图标 + "获取二维码"按钮 ↓ 用户点击"获取二维码" ↓ 前端: WechatRegister.getWechatQRCode() ↓ GET /api/auth/wechat/qrcode ↓ 后端: 1. 生成唯一 session_id (UUID) 2. 构建微信开放平台授权URL 3. 存储到临时状态 (5分钟有效期) ↓ 返回: { code: 0, data: { auth_url: "https://open.weixin.qq.com/connect/qrconnect?...", session_id: "uuid-xxxxx" } } ↓ 前端: 1. 在 iframe 中显示微信二维码 2. 启动轮询: 每2秒检查扫码状态 3. 启动备用轮询: 每3秒检查 (防止丢失) 4. 设置超时: 5分钟后二维码过期 ↓ 【轮询检查】 POST /api/auth/wechat/check Body: { session_id: "uuid-xxxxx" } ↓ 后端返回状态: - waiting: 等待扫码 - scanned: 已扫码,等待确认 - authorized: 已授权 - login_success: 登录成功 (老用户) - register_success: 注册成功 (新用户) - expired: 二维码过期 ↓ 如果状态 = login_success / register_success: ↓ 前端: WechatRegister.handleLoginSuccess() ↓ POST /api/auth/login/wechat Body: { session_id: "uuid-xxxxx" } ↓ 后端: 1. 从临时状态获取微信用户信息 2. 查询数据库是否存在该微信用户 - 存在: 登录 - 不存在: 自动注册 3. 设置 Session Cookie ↓ 返回: { success: true, user: { id, nickname, avatar_url, ... }, token: "optional-token" } ↓ 前端: 1. 停止轮询 2. checkSession() 更新状态 3. 显示成功提示 4. 1秒后跳转 /home ↓ 登录完成 ✅ ``` #### 关键代码位置 **前端**: - `src/components/Auth/WechatRegister.js:199` - 获取二维码 - `src/components/Auth/WechatRegister.js:120` - 检查扫码状态 - `src/components/Auth/WechatRegister.js:85` - 登录成功处理 - `src/services/authService.js:69` - API服务 **后端**: - `app.py:2487` - GET /api/auth/wechat/qrcode - `app.py:2560` - POST /api/auth/wechat/check - `app.py:2743` - POST /api/auth/login/wechat --- ### 2.3 微信H5网页授权 **适用场景**: 移动端浏览器中打开 #### 流程图 ``` 移动端用户点击验证码输入框下方的微信图标 ↓ 前端: AuthFormContent.handleWechatH5Login() ↓ 构建回调URL: https://yourdomain.com/home/wechat-callback ↓ POST /api/auth/wechat/h5-auth Body: { redirect_url: "https://..." } ↓ 后端: 1. 构建微信网页授权URL (snsapi_userinfo) 2. 生成 state 参数防止CSRF ↓ 返回: { auth_url: "https://open.weixin.qq.com/connect/oauth2/authorize?..." } ↓ 前端: 延迟500ms后跳转到微信授权页面 ↓ window.location.href = auth_url ↓ 【用户在微信授权页面确认】 ↓ 微信回调: https://yourdomain.com/home/wechat-callback?code=xxx&state=yyy ↓ 前端: WechatCallback 组件接收回调 ↓ POST /api/auth/wechat/h5-callback Body: { code: "xxx", state: "yyy" } ↓ 后端: 1. 验证 state 参数 2. 使用 code 换取 access_token 3. 使用 access_token 获取微信用户信息 4. 查询数据库 - 存在: 登录 - 不存在: 自动注册 5. 设置 Session Cookie ↓ 返回: { success: true, user: { ... }, token: "optional" } ↓ 前端: 1. 存储 token (可选) 2. checkSession() 更新状态 3. 显示"登录成功" 4. 1.5秒后跳转 /home ↓ 登录完成 ✅ ``` #### 关键代码位置 **前端**: - `src/components/Auth/AuthFormContent.js:308` - 发起H5授权 - `src/views/Pages/WechatCallback.js:34` - 处理回调 - `src/services/authService.js:78` - H5授权API - `src/services/authService.js:91` - H5回调API **后端**: - `app.py:2487+` - POST /api/auth/wechat/h5-auth (需确认) - `app.py:2610+` - POST /api/auth/wechat/h5-callback (需确认) --- ## 3. 路由配置与跳转逻辑 ### 3.1 路由结构 ```javascript // src/App.js {/* 公开路由 - 无需登录 */} } /> } /> {/* 受保护路由 - 需要登录 */} } /> // src/layouts/Home.js (公开路由) } /> } /> } /> } /> {/* 需要登录的页面 */} } /> } /> ``` ### 3.2 路由保护机制 **ProtectedRoute 组件** (`src/components/ProtectedRoute.js`) ```javascript const ProtectedRoute = ({ children }) => { const { isAuthenticated, isLoading, user } = useAuth(); const { openAuthModal, isAuthModalOpen } = useAuthModal(); // 未登录时自动弹出认证窗口 useEffect(() => { if (!isLoading && !isAuthenticated && !user && !isAuthModalOpen) { openAuthModal(currentPath); // 记录当前路径 } }, [isAuthenticated, user, isLoading]); // 加载中: 显示 Spinner if (isLoading) { return ; } // 未登录: 显示页面 + 自动打开弹窗 (非阻断式) // 已登录: 正常显示页面 return children; }; ``` **特点**: - ✅ 非阻断式保护 (弹窗而非重定向) - ✅ 记录原始路径 (登录后可返回) - ✅ 避免白屏 (先显示页面骨架) ### 3.3 跳转逻辑总结 | 场景 | 触发位置 | 跳转目标 | 说明 | |------|---------|---------|------| | 未登录访问受保护页面 | ProtectedRoute | 留在当前页 + 弹窗 | 非阻断式 | | 点击导航栏登录按钮 | HomeNavbar:740 | 打开 AuthModal | 弹窗形式 | | 手机验证码登录成功 | AuthFormContent:284 | 留在当前页 | 关闭弹窗即可 | | 微信扫码登录成功 | WechatRegister:106 | 跳转 /home | 1秒延迟 | | 微信H5授权成功 | WechatCallback:69 | 跳转 /home | 1.5秒延迟 | | 用户点击退出登录 | HomeNavbar:227 | 跳转 /home | 清除Session | | 关闭弹窗未登录 | AuthModalContext:58 | 跳转 /home | 防止停留受保护页 | --- ## 4. API接口文档 ### 4.1 Session检查 **接口**: `GET /api/auth/session` **用途**: 检查当前用户登录状态 **请求**: ```http GET /api/auth/session HTTP/1.1 Cookie: session=xxx ``` **响应**: ```json { "isAuthenticated": true, "user": { "id": 123, "phone": "13800138000", "nickname": "价小前用户", "email": "user@example.com", "avatar_url": "https://...", "has_wechat": true } } ``` **前端调用**: ```javascript // src/contexts/AuthContext.js:85 const checkSession = async () => { const response = await fetch('/api/auth/session', { credentials: 'include' }); const data = await response.json(); if (data.isAuthenticated) { setUser(data.user); setIsAuthenticated(true); } }; ``` --- ### 4.2 发送验证码 **接口**: `POST /api/auth/send-verification-code` **用途**: 发送手机验证码 **请求**: ```http POST /api/auth/send-verification-code HTTP/1.1 Content-Type: application/json { "credential": "13800138000", "type": "phone", "purpose": "login" } ``` **响应**: ```json { "success": true, "message": "验证码已发送" } ``` **错误响应**: ```json { "success": false, "error": "手机号格式不正确" } ``` **前端调用**: ```javascript // src/components/Auth/AuthFormContent.js:153 const response = await fetch('/api/auth/send-verification-code', { method: 'POST', credentials: 'include', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ credential: phone, type: 'phone', purpose: 'login' }) }); ``` **限流规则**: - 同一手机号: 60秒内只能发送1次 - 验证码有效期: 5分钟 --- ### 4.3 验证码登录 **接口**: `POST /api/auth/login-with-code` **用途**: 使用验证码登录/注册 **请求**: ```http POST /api/auth/login-with-code HTTP/1.1 Content-Type: application/json { "credential": "13800138000", "verification_code": "123456", "login_type": "phone" } ``` **响应 (登录成功)**: ```json { "success": true, "isNewUser": false, "user": { "id": 123, "phone": "13800138000", "nickname": "价小前用户" } } ``` **响应 (注册成功)**: ```json { "success": true, "isNewUser": true, "user": { "id": 124, "phone": "13900139000", "nickname": "用户13900139000" } } ``` **错误响应**: ```json { "success": false, "error": "验证码已过期或不存在" } ``` **自动注册逻辑**: - 如果手机号不存在 → 自动创建新用户 - 默认昵称: `用户{手机号}` - isNewUser 标记用于前端引导设置昵称 --- ### 4.4 获取微信二维码 **接口**: `GET /api/auth/wechat/qrcode` **用途**: 获取微信PC扫码登录二维码 **请求**: ```http GET /api/auth/wechat/qrcode HTTP/1.1 ``` **响应**: ```json { "code": 0, "data": { "auth_url": "https://open.weixin.qq.com/connect/qrconnect?appid=xxx&redirect_uri=xxx&response_type=code&scope=snsapi_login&state=yyy#wechat_redirect", "session_id": "550e8400-e29b-41d4-a716-446655440000" }, "message": "success" } ``` **前端调用**: ```javascript // src/services/authService.js:69 getWechatQRCode: async () => { return await apiRequest('/api/auth/wechat/qrcode'); } ``` **二维码有效期**: 5分钟 --- ### 4.5 检查微信扫码状态 **接口**: `POST /api/auth/wechat/check` **用途**: 轮询检查微信扫码状态 **请求**: ```http POST /api/auth/wechat/check HTTP/1.1 Content-Type: application/json { "session_id": "550e8400-e29b-41d4-a716-446655440000" } ``` **响应状态**: | status | 说明 | 前端行为 | |--------|------|---------| | waiting | 等待扫码 | 继续轮询 | | scanned | 已扫码,等待确认 | 显示提示 + 继续轮询 | | authorized | 已授权 | 继续轮询 | | login_success | 登录成功 (老用户) | 停止轮询 + 调用登录接口 | | register_success | 注册成功 (新用户) | 停止轮询 + 调用登录接口 | | expired | 二维码过期 | 停止轮询 + 显示刷新按钮 | **响应示例**: ```json { "status": "login_success", "user_info": { "openid": "xxx", "nickname": "微信用户", "avatar_url": "https://..." } } ``` **前端轮询**: ```javascript // src/components/Auth/WechatRegister.js:180 pollIntervalRef.current = setInterval(() => { checkWechatStatus(); }, 2000); // 每2秒检查一次 ``` --- ### 4.6 微信登录 **接口**: `POST /api/auth/login/wechat` **用途**: 使用微信 session_id 完成登录 **请求**: ```http POST /api/auth/login/wechat HTTP/1.1 Content-Type: application/json { "session_id": "550e8400-e29b-41d4-a716-446655440000" } ``` **响应**: ```json { "success": true, "user": { "id": 123, "nickname": "微信用户", "avatar_url": "https://...", "has_wechat": true }, "token": "optional-token" } ``` **前端调用**: ```javascript // src/components/Auth/WechatRegister.js:87 const response = await authService.loginWithWechat(sessionId); if (response?.success) { // 存储用户信息 if (response.user) { localStorage.setItem('user', JSON.stringify(response.user)); } // 跳转首页 navigate('/home'); } ``` --- ### 4.7 微信H5授权 **接口**: `POST /api/auth/wechat/h5-auth` **用途**: 获取微信H5网页授权链接 **请求**: ```http POST /api/auth/wechat/h5-auth HTTP/1.1 Content-Type: application/json { "redirect_url": "https://yourdomain.com/home/wechat-callback" } ``` **响应**: ```json { "auth_url": "https://open.weixin.qq.com/connect/oauth2/authorize?appid=xxx&redirect_uri=xxx&response_type=code&scope=snsapi_userinfo&state=yyy#wechat_redirect" } ``` **前端调用**: ```javascript // src/components/Auth/AuthFormContent.js:323 const response = await authService.getWechatH5AuthUrl(redirectUrl); if (response?.auth_url) { window.location.href = response.auth_url; } ``` --- ### 4.8 微信H5回调 **接口**: `POST /api/auth/wechat/h5-callback` **用途**: 处理微信H5授权回调 **请求**: ```http POST /api/auth/wechat/h5-callback HTTP/1.1 Content-Type: application/json { "code": "wechat-code-xxx", "state": "csrf-state-yyy" } ``` **响应**: ```json { "success": true, "user": { "id": 123, "nickname": "微信用户", "avatar_url": "https://..." }, "token": "optional-token" } ``` **前端调用**: ```javascript // src/views/Pages/WechatCallback.js:46 const response = await authService.handleWechatH5Callback(code, state); if (response?.success) { await checkSession(); navigate('/home'); } ``` --- ### 4.9 退出登录 **接口**: `POST /api/auth/logout` **用途**: 清除用户Session **请求**: ```http POST /api/auth/logout HTTP/1.1 Cookie: session=xxx ``` **响应**: ```json { "success": true, "message": "已退出登录" } ``` **前端调用**: ```javascript // src/contexts/AuthContext.js:120 const logout = async () => { await fetch('/api/auth/logout', { method: 'POST', credentials: 'include' }); setUser(null); setIsAuthenticated(false); navigate('/home'); }; ``` --- ## 5. 前端组件架构 ### 5.1 核心组件关系图 ``` App.js (根组件) └── AuthProvider (全局认证状态) └── AuthModalProvider (弹窗管理) ├── HomeNavbar (导航栏) │ └── Button onClick={openAuthModal} (登录按钮) │ ├── AuthModalManager (弹窗管理器) │ └── Modal │ └── AuthFormContent (认证表单) │ ├── VerificationCodeInput (验证码输入) │ └── WechatRegister (微信二维码) │ └── ProtectedRoute (路由保护) └── 受保护的页面组件 ``` ### 5.2 组件详解 #### AuthContext (`src/contexts/AuthContext.js`) **职责**: 全局认证状态管理 **状态**: ```javascript { user: { id: 123, phone: "13800138000", nickname: "价小前用户", email: "user@example.com", avatar_url: "https://...", has_wechat: true }, isAuthenticated: true, isLoading: false } ``` **方法**: - `checkSession()` - 检查当前登录状态 - `logout()` - 退出登录 **使用**: ```javascript import { useAuth } from '../../contexts/AuthContext'; const { user, isAuthenticated, isLoading, checkSession, logout } = useAuth(); ``` --- #### AuthModalContext (`src/contexts/AuthModalContext.js`) **职责**: 认证弹窗状态管理 **状态**: ```javascript { isAuthModalOpen: false, redirectUrl: null, // 登录成功后跳转的URL (可选) } ``` **方法**: - `openAuthModal(url, callback)` - 打开认证弹窗 - `closeModal()` - 关闭弹窗 (未登录则跳转首页) - `handleLoginSuccess(user)` - 登录成功处理 **使用**: ```javascript import { useAuthModal } from '../../contexts/AuthModalContext'; const { isAuthModalOpen, openAuthModal, handleLoginSuccess } = useAuthModal(); // 打开弹窗 openAuthModal(); // 登录成功 handleLoginSuccess(userData); ``` --- #### AuthModalManager (`src/components/Auth/AuthModalManager.js`) **职责**: 渲染认证弹窗 UI **特点**: - 响应式尺寸 (移动端 md, 桌面端 xl) - 条件渲染 (仅在打开时渲染,避免不必要的Portal) - 半透明背景 + 模糊效果 - 禁止点击背景关闭 (防止误操作) **响应式配置**: ```javascript const modalSize = useBreakpointValue({ base: "md", // 移动端 md: "lg", // 中屏 lg: "xl" // 大屏 }); const modalMaxW = useBreakpointValue({ base: "90%", // 移动端占90%宽度 md: "700px" // 桌面端固定700px }); ``` --- #### AuthFormContent (`src/components/Auth/AuthFormContent.js`) **职责**: 认证表单主体内容 **功能**: 1. 手机号输入 2. 验证码输入 + 发送验证码 3. 登录/注册按钮 4. 微信扫码 (桌面端右侧) 5. 微信H5登录 (移动端验证码下方图标) 6. 隐私协议链接 **布局**: ``` 桌面端 (Desktop): ┌─────────────────────────────────────┐ │ 价值前沿 - 开启您的投资之旅 │ ├──────────────────┬──────────────────┤ │ 登陆/注册 │ 微信扫码 │ │ │ │ │ 手机号输入框 │ [二维码区域] │ │ 验证码输入框 │ │ │ [获取验证码] │ 请使用微信扫码 │ │ │ │ │ [登录/注册按钮] │ │ │ │ │ │ 《隐私政策》 │ │ └──────────────────┴──────────────────┘ 移动端 (Mobile): ┌─────────────────────┐ │ 价值前沿 │ │ 开启您的投资之旅 │ ├─────────────────────┤ │ 登陆/注册 │ │ │ │ 手机号输入框 │ │ │ │ 验证码输入框 │ │ [获取验证码] │ │ 其他登录方式: [微信] │ │ │ │ [登录/注册按钮] │ │ │ │ 《隐私政策》 │ └─────────────────────┘ ``` **关键逻辑**: ```javascript // 发送验证码 (60秒倒计时) const sendVerificationCode = async () => { const response = await fetch('/api/auth/send-verification-code', { method: 'POST', credentials: 'include', body: JSON.stringify({ credential: phone, type: 'phone', purpose: 'login' }) }); if (response.ok) { setCountdown(60); // 启动60秒倒计时 } }; // 提交登录 const handleSubmit = async (e) => { e.preventDefault(); const response = await fetch('/api/auth/login-with-code', { method: 'POST', credentials: 'include', body: JSON.stringify({ credential: phone, verification_code: verificationCode, login_type: 'phone' }) }); if (response.ok) { await checkSession(); // 更新全局状态 if (data.isNewUser) { // 新用户引导设置昵称 setShowNicknamePrompt(true); } else { handleLoginSuccess(); // 关闭弹窗 } } }; ``` --- #### WechatRegister (`src/components/Auth/WechatRegister.js`) **职责**: 微信PC扫码登录组件 **状态机**: ``` NONE (初始) → 点击"获取二维码" ↓ WAITING (显示二维码 + 轮询) ↓ SCANNED (提示"请在手机上确认") ↓ LOGIN_SUCCESS / REGISTER_SUCCESS ↓ 调用登录接口 → 跳转首页 EXPIRED (二维码过期 → 显示"点击刷新") ``` **轮询机制**: ```javascript // 主轮询: 每2秒检查 pollIntervalRef.current = setInterval(() => { checkWechatStatus(); }, 2000); // 备用轮询: 每3秒检查 (防止主轮询失败) backupPollIntervalRef.current = setInterval(() => { checkWechatStatus().catch(error => { console.warn('备用轮询检查失败(静默处理):', error); }); }, 3000); // 超时机制: 5分钟后停止轮询 timeoutRef.current = setTimeout(() => { clearTimers(); setWechatStatus(WECHAT_STATUS.EXPIRED); }, 300000); ``` **二维码缩放**: ```javascript // 使用 ResizeObserver 监听容器尺寸变化 const calculateScale = () => { const { width, height } = containerRef.current.getBoundingClientRect(); const scaleX = width / 300; // 二维码原始宽度300px const scaleY = height / 350; // 二维码原始高度350px const newScale = Math.min(scaleX, scaleY, 1.0); setScale(Math.max(newScale, 0.3)); // 最小0.3,最大1.0 }; // iframe 缩放