diff --git a/API_DOCS_profile_completeness.md b/API_DOCS_profile_completeness.md deleted file mode 100644 index 1a1e7780..00000000 --- a/API_DOCS_profile_completeness.md +++ /dev/null @@ -1,458 +0,0 @@ -# 用户资料完整度 API 文档 - -## 接口概述 - -**接口名称**:获取用户资料完整度 -**接口路径**:`/api/account/profile-completeness` -**请求方法**:`GET` -**接口描述**:获取当前登录用户的资料完整度信息,包括各项必填信息的完成状态、完整度百分比、缺失项列表等。 -**业务场景**:用于在用户登录后提醒用户完善个人资料,提升平台服务质量。 - ---- - -## 请求参数 - -### Headers - -| 参数名 | 类型 | 必填 | 描述 | -|--------|------|------|------| -| `Cookie` | string | 是 | 包含用户会话信息(session cookie),用于身份认证 | - -### Query Parameters - -无 - -### Body Parameters - -无(GET 请求无 Body) - ---- - -## 响应格式 - -### 成功响应 (200 OK) - -**Content-Type**: `application/json` - -```json -{ - "success": true, - "data": { - "completeness": { - "hasPassword": true, - "hasPhone": true, - "hasEmail": false, - "isWechatUser": false - }, - "completenessPercentage": 66, - "needsAttention": false, - "missingItems": ["邮箱"], - "isComplete": false, - "showReminder": false - } -} -``` - -### 响应字段说明 - -#### 顶层字段 - -| 字段名 | 类型 | 描述 | -|--------|------|------| -| `success` | boolean | 请求是否成功,`true` 表示成功 | -| `data` | object | 资料完整度数据对象 | - -#### `data` 对象字段 - -| 字段名 | 类型 | 描述 | -|--------|------|------| -| `completeness` | object | 各项资料的完成状态详情 | -| `completenessPercentage` | number | 资料完整度百分比(0-100) | -| `needsAttention` | boolean | 是否需要用户注意(提醒用户完善) | -| `missingItems` | array[string] | 缺失项的中文描述列表 | -| `isComplete` | boolean | 资料是否完全完整(100%) | -| `showReminder` | boolean | 是否显示提醒横幅(同 `needsAttention`) | - -#### `completeness` 对象字段 - -| 字段名 | 类型 | 描述 | -|--------|------|------| -| `hasPassword` | boolean | 是否已设置登录密码 | -| `hasPhone` | boolean | 是否已绑定手机号 | -| `hasEmail` | boolean | 是否已设置有效邮箱(排除临时邮箱) | -| `isWechatUser` | boolean | 是否为微信登录用户 | - ---- - -## 业务逻辑说明 - -### 资料完整度计算规则 - -1. **必填项**(共 3 项): - - 登录密码(`hasPassword`) - - 手机号(`hasPhone`) - - 邮箱(`hasEmail`) - -2. **完整度计算公式**: - ``` - completenessPercentage = (已完成项数 / 3) × 100 - ``` - 示例: - - 已完成 2 项 → 66% - - 已完成 3 项 → 100% - -3. **邮箱有效性判断**: - - 必须包含 `@` 符号 - - 不能是临时邮箱(如 `*@valuefrontier.temp`) - -### 提醒逻辑(`needsAttention`) - -**仅对微信登录用户进行提醒**,需同时满足以下条件: - -1. `isWechatUser === true`(微信登录用户) -2. `completenessPercentage < 100`(资料不完整) - -**后端额外的智能提醒策略**(Mock 模式未实现): - -- 新用户(注册 7 天内):更容易触发提醒 -- 每 7 天最多提醒一次(通过 session 记录) -- 完整度低于 50% 时优先提醒 - -### 缺失项列表(`missingItems`) - -根据 `completeness` 对象生成中文描述: - -| 条件 | 添加到 `missingItems` | -|------|----------------------| -| `!hasPassword` | `"登录密码"` | -| `!hasPhone` | `"手机号"` | -| `!hasEmail` | `"邮箱"` | - ---- - -## 响应示例 - -### 示例 1:手机号登录用户,资料完整 - -**场景**:手机号登录,已设置密码和邮箱 - -```json -{ - "success": true, - "data": { - "completeness": { - "hasPassword": true, - "hasPhone": true, - "hasEmail": true, - "isWechatUser": false - }, - "completenessPercentage": 100, - "needsAttention": false, - "missingItems": [], - "isComplete": true, - "showReminder": false - } -} -``` - -### 示例 2:微信登录用户,未绑定手机号 - -**场景**:微信登录,未设置密码和手机号,触发提醒 - -```json -{ - "success": true, - "data": { - "completeness": { - "hasPassword": false, - "hasPhone": false, - "hasEmail": true, - "isWechatUser": true - }, - "completenessPercentage": 33, - "needsAttention": true, - "missingItems": ["登录密码", "手机号"], - "isComplete": false, - "showReminder": true - } -} -``` - -### 示例 3:微信登录用户,只缺邮箱 - -**场景**:微信登录,已设置密码和手机号,只缺邮箱 - -```json -{ - "success": true, - "data": { - "completeness": { - "hasPassword": true, - "hasPhone": true, - "hasEmail": false, - "isWechatUser": true - }, - "completenessPercentage": 66, - "needsAttention": true, - "missingItems": ["邮箱"], - "isComplete": false, - "showReminder": true - } -} -``` - ---- - -## 错误响应 - -### 401 Unauthorized - 未登录 - -**场景**:用户未登录或 Session 已过期 - -```json -{ - "success": false, - "error": "用户未登录" -} -``` - -**HTTP 状态码**:`401` - -### 500 Internal Server Error - 服务器错误 - -**场景**:服务器内部错误(如数据库连接失败) - -```json -{ - "success": false, - "error": "获取资料完整性错误: [错误详情]" -} -``` - -**HTTP 状态码**:`500` - ---- - -## 调用示例 - -### JavaScript (Fetch API) - -```javascript -async function checkProfileCompleteness() { - try { - const response = await fetch('/api/account/profile-completeness', { - method: 'GET', - credentials: 'include', // 重要:携带 Cookie - headers: { - 'Content-Type': 'application/json' - } - }); - - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - - const data = await response.json(); - - if (data.success) { - console.log('资料完整度:', data.data.completenessPercentage + '%'); - console.log('是否需要提醒:', data.data.needsAttention); - - if (data.data.needsAttention) { - console.log('缺失项:', data.data.missingItems.join('、')); - } - } - } catch (error) { - console.error('检查资料完整性失败:', error); - } -} -``` - -### cURL - -```bash -curl -X GET 'http://localhost:5001/api/account/profile-completeness' \ - -H 'Cookie: session=your_session_cookie_here' \ - -H 'Content-Type: application/json' -``` - -### Axios - -```javascript -import axios from 'axios'; - -async function checkProfileCompleteness() { - try { - const { data } = await axios.get('/api/account/profile-completeness', { - withCredentials: true // 携带 Cookie - }); - - if (data.success) { - return data.data; - } - } catch (error) { - if (error.response?.status === 401) { - console.error('用户未登录'); - } else { - console.error('检查失败:', error.message); - } - } -} -``` - ---- - -## 调用时机建议 - -### ✅ 推荐调用场景 - -1. **用户登录后**:首次登录或刷新页面后检查一次 -2. **资料更新后**:用户修改个人资料后重新检查 -3. **手动触发**:用户点击"检查资料完整度"按钮 - -### ❌ 避免的场景 - -1. **导航栏每次 render 时**:会导致频繁请求 -2. **组件重新渲染时**:应使用缓存或标志位避免重复 -3. **轮询调用**:此接口不需要轮询,用户资料变化频率低 - -### 最佳实践 - -```javascript -// 使用 React Hooks 的最佳实践 -function useProfileCompleteness() { - const [completeness, setCompleteness] = useState(null); - const hasChecked = useRef(false); - const { isAuthenticated, user } = useAuth(); - - const check = useCallback(async () => { - // 避免重复检查 - if (hasChecked.current) return; - - try { - const response = await fetch('/api/account/profile-completeness', { - credentials: 'include' - }); - - if (response.ok) { - const data = await response.json(); - if (data.success) { - setCompleteness(data.data); - hasChecked.current = true; // 标记已检查 - } - } - } catch (error) { - console.warn('检查失败:', error); - } - }, []); - - // 仅在登录后检查一次 - useEffect(() => { - if (isAuthenticated && user && !hasChecked.current) { - check(); - } - }, [isAuthenticated, user, check]); - - // 提供手动刷新方法 - const refresh = useCallback(() => { - hasChecked.current = false; - check(); - }, [check]); - - return { completeness, refresh }; -} -``` - ---- - -## Mock 模式说明 - -在 Mock 模式下(`REACT_APP_ENABLE_MOCK=true`),此接口由 MSW (Mock Service Worker) 拦截: - -### Mock 实现位置 - -- **Handler**: `src/mocks/handlers/account.js` -- **数据源**: `src/mocks/data/users.js` (getCurrentUser) - -### Mock 特点 - -1. **真实计算**:基于当前登录用户的实际数据计算完整度 -2. **状态同步**:与登录状态同步,登录后才返回真实用户数据 -3. **未登录返回 401**:模拟真实后端行为 -4. **延迟模拟**:300ms 网络延迟,模拟真实请求 - -### Mock 测试数据 - -| 测试账号 | 手机号 | 密码 | 邮箱 | 微信 | 完整度 | -|---------|--------|------|------|------|--------| -| 测试用户 | 13800138000 | ✅ | ❌ | ❌ | 66% | -| 投资达人 | 13900139000 | ✅ | ✅ | ✅ | 100% | - ---- - -## 前端集成示例 - -### 显示资料完整度横幅 - -```jsx -import { useProfileCompleteness } from './hooks/useProfileCompleteness'; - -function App() { - const { completeness } = useProfileCompleteness(); - - return ( - <> - {/* 资料完整度提醒横幅 */} - {completeness?.showReminder && ( - - - - 完善资料,享受更好服务 - - 您还需要设置:{completeness.missingItems.join('、')} - ({completeness.completenessPercentage}% 完成) - - - - - )} - - {/* 主要内容 */} - - - ); -} -``` - ---- - -## 版本历史 - -| 版本 | 日期 | 变更说明 | -|------|------|----------| -| v1.0 | 2024-10-17 | 初始版本,支持资料完整度检查和智能提醒 | - ---- - -## 相关接口 - -- `GET /api/auth/session` - 检查登录状态 -- `GET /api/account/profile` - 获取完整用户资料 -- `PUT /api/account/profile` - 更新用户资料 -- `POST /api/auth/logout` - 退出登录 - ---- - -## 技术支持 - -如有问题,请联系开发团队或查看: -- **Mock 配置指南**: [MOCK_GUIDE.md](./MOCK_GUIDE.md) -- **项目文档**: [CLAUDE.md](./CLAUDE.md) - ---- - -**文档生成日期**:2024-10-17 -**API 版本**:v1.0 -**Mock 支持**:✅ 已实现 diff --git a/API_ENDPOINTS.md b/API_ENDPOINTS.md deleted file mode 100644 index 632cbc54..00000000 --- a/API_ENDPOINTS.md +++ /dev/null @@ -1,415 +0,0 @@ -# API 接口文档 - -本文档记录了项目中所有 API 接口的详细信息。 - -## 目录 -- [认证相关 API](#认证相关-api) -- [个人中心相关 API](#个人中心相关-api) -- [事件相关 API](#事件相关-api) -- [股票相关 API](#股票相关-api) -- [公司相关 API](#公司相关-api) -- [订阅/支付相关 API](#订阅支付相关-api) - ---- - -## 认证相关 API - -### POST /api/auth/send-verification-code -发送验证码到手机号或邮箱 - -**请求参数**: -```json -{ - "credential": "13800138000", // 手机号或邮箱 - "type": "phone", // 'phone' | 'email' - "purpose": "login" // 'login' | 'register' -} -``` - -**响应示例**: -```json -{ - "success": true, - "message": "验证码已发送到 13800138000", - "dev_code": "123456" // 仅开发环境返回 -} -``` - -**错误响应**: -```json -{ - "success": false, - "error": "发送验证码失败" -} -``` - -**Mock 数据**: ✅ `src/mocks/handlers/auth.js` 行 21-44 - -**涉及文件**: -- `src/components/Auth/AuthFormContent.js` 行 164-207 - ---- - -### POST /api/auth/login-with-code -使用验证码登录(支持自动注册新用户) - -**请求参数**: -```json -{ - "credential": "13800138000", - "verification_code": "123456", - "login_type": "phone" // 'phone' | 'email' -} -``` - -**响应示例**: -```json -{ - "success": true, - "message": "登录成功", - "isNewUser": false, - "user": { - "id": 1, - "phone": "13800138000", - "nickname": "用户昵称", - "email": null, - "avatar_url": "https://...", - "has_wechat": false - }, - "token": "mock_token_1_1234567890" -} -``` - -**错误响应**: -```json -{ - "success": false, - "error": "验证码错误" -} -``` - -**Mock 数据**: ✅ `src/mocks/handlers/auth.js` 行 47-115 - -**涉及文件**: -- `src/components/Auth/AuthFormContent.js` 行 252-327 - -**注意事项**: -- 后端需要支持自动注册新用户(当用户不存在时) -- 前端已添加 `.trim()` 防止空格问题 - ---- - -### GET /api/auth/session -检查当前登录状态 - -**响应示例**: -```json -{ - "success": true, - "isAuthenticated": true, - "user": { - "id": 1, - "phone": "13800138000", - "nickname": "用户昵称" - } -} -``` - -**Mock 数据**: ✅ `src/mocks/handlers/auth.js` 行 269-290 - ---- - -### POST /api/auth/logout -退出登录 - -**响应示例**: -```json -{ - "success": true, - "message": "退出成功" -} -``` - -**Mock 数据**: ✅ `src/mocks/handlers/auth.js` 行 317-329 - ---- - -## 个人中心相关 API - -### GET /api/account/watchlist -获取用户自选股列表 - -**响应示例**: -```json -{ - "success": true, - "data": [ - { - "id": 1, - "stock_code": "000001.SZ", - "stock_name": "平安银行", - "added_at": "2024-01-01T00:00:00Z" - } - ] -} -``` - -**Mock 数据**: ❌ 待创建 `src/mocks/handlers/account.js` - -**涉及文件**: -- `src/views/Dashboard/Center.js` 行 94 - ---- - -### GET /api/account/watchlist/realtime -获取自选股实时行情 - -**响应示例**: -```json -{ - "success": true, - "data": { - "000001.SZ": { - "price": 12.34, - "change": 0.56, - "change_percent": 4.76, - "volume": 123456789 - } - } -} -``` - -**Mock 数据**: ❌ 待创建 - -**涉及文件**: -- `src/views/Dashboard/Center.js` 行 133 - ---- - -### GET /api/account/events/following -获取用户关注的事件列表 - -**响应示例**: -```json -{ - "success": true, - "data": [ - { - "id": 1, - "title": "事件标题", - "followed_at": "2024-01-01T00:00:00Z" - } - ] -} -``` - -**Mock 数据**: ❌ 待创建 - -**涉及文件**: -- `src/views/Dashboard/Center.js` 行 95 - ---- - -### GET /api/account/events/comments -获取用户的事件评论 - -**响应示例**: -```json -{ - "success": true, - "data": [ - { - "id": 1, - "event_id": 123, - "content": "评论内容", - "created_at": "2024-01-01T00:00:00Z" - } - ] -} -``` - -**Mock 数据**: ❌ 待创建 - -**涉及文件**: -- `src/views/Dashboard/Center.js` 行 96 - ---- - -### GET /api/subscription/current -获取当前订阅信息 - -**响应示例**: -```json -{ - "success": true, - "data": { - "plan": "premium", - "expires_at": "2025-01-01T00:00:00Z", - "auto_renew": true - } -} -``` - -**Mock 数据**: ❌ 待创建 `src/mocks/handlers/subscription.js` - -**涉及文件**: -- `src/views/Dashboard/Center.js` 行 97 - ---- - -## 事件相关 API - -### GET /api/events -获取事件列表 - -**查询参数**: -- `page`: 页码(默认 1) -- `per_page`: 每页数量(默认 10) -- `sort`: 排序方式 ('new' | 'hot' | 'returns') -- `importance`: 重要性筛选 ('all' | 'high' | 'medium' | 'low') -- `date_range`: 日期范围 -- `q`: 搜索关键词 -- `industry_classification`: 行业分类 -- `industry_code`: 行业代码 - -**响应示例**: -```json -{ - "success": true, - "data": { - "events": [ - { - "id": 1, - "title": "事件标题", - "importance": "high", - "created_at": "2024-01-01T00:00:00Z" - } - ], - "pagination": { - "page": 1, - "per_page": 10, - "total": 100 - } - } -} -``` - -**Mock 数据**: ⚠️ 部分实现(需完善) - -**涉及文件**: -- `src/views/Community/index.js` 行 148 - ---- - -### GET /api/events/:id -获取事件详情 - -**响应示例**: -```json -{ - "success": true, - "data": { - "id": 1, - "title": "事件标题", - "content": "事件内容", - "importance": "high", - "created_at": "2024-01-01T00:00:00Z" - } -} -``` - -**Mock 数据**: ❌ 待创建 - ---- - -### GET /api/events/:id/stocks -获取事件相关股票 - -**响应示例**: -```json -{ - "success": true, - "data": [ - { - "stock_code": "000001.SZ", - "stock_name": "平安银行", - "correlation": 0.85 - } - ] -} -``` - -**Mock 数据**: ✅ `src/mocks/handlers/event.js` 行 12-38 - ---- - -### GET /api/events/popular-keywords -获取热门关键词 - -**查询参数**: -- `limit`: 返回数量(默认 20) - -**响应示例**: -```json -{ - "success": true, - "data": [ - { - "keyword": "人工智能", - "count": 123, - "trend": "up" - } - ] -} -``` - -**Mock 数据**: ❌ 待创建 - -**涉及文件**: -- `src/views/Community/index.js` 行 180 - ---- - -### GET /api/events/hot -获取热点事件 - -**查询参数**: -- `days`: 天数范围(默认 5) -- `limit`: 返回数量(默认 4) - -**响应示例**: -```json -{ - "success": true, - "data": [ - { - "id": 1, - "title": "热点事件标题", - "heat_score": 95.5 - } - ] -} -``` - -**Mock 数据**: ❌ 待创建 - -**涉及文件**: -- `src/views/Community/index.js` 行 192 - ---- - -## 待补充 API - -以下 API 将在重构其他文件时逐步添加: - -- 股票相关 API -- 公司相关 API -- 订阅/支付相关 API -- 用户资料相关 API -- 行业分类相关 API - ---- - -## 更新日志 - -- 2024-XX-XX: 创建文档,记录认证和个人中心相关 API diff --git a/AUTHENTICATION_SYSTEM_GUIDE.md b/AUTHENTICATION_SYSTEM_GUIDE.md deleted file mode 100644 index c01738c4..00000000 --- a/AUTHENTICATION_SYSTEM_GUIDE.md +++ /dev/null @@ -1,1879 +0,0 @@ -# 价值前沿认证系统完整文档 - -> **版本**: 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 缩放 -