feat: 个人中心页添加mock数据
This commit is contained in:
1812
CENTER_PAGE_FLOW_ANALYSIS.md
Normal file
1812
CENTER_PAGE_FLOW_ANALYSIS.md
Normal file
File diff suppressed because it is too large
Load Diff
695
MOCK_DATA_CENTER_SUPPLEMENT.md
Normal file
695
MOCK_DATA_CENTER_SUPPLEMENT.md
Normal file
@@ -0,0 +1,695 @@
|
|||||||
|
# 个人中心 Mock 数据补充文档
|
||||||
|
|
||||||
|
> **补充日期**: 2025-01-19
|
||||||
|
> **补充范围**: 个人中心 (`/home/center`) 页面所需的全部 Mock 数据和 API
|
||||||
|
> **补充目标**: 完善 Mock 数据,支持个人中心页面在开发环境下完整运行
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 目录
|
||||||
|
|
||||||
|
- [1. 业务逻辑梳理](#1-业务逻辑梳理)
|
||||||
|
- [2. API 接口清单](#2-api-接口清单)
|
||||||
|
- [3. Mock 数据结构](#3-mock-数据结构)
|
||||||
|
- [4. 实施内容](#4-实施内容)
|
||||||
|
- [5. 测试验证](#5-测试验证)
|
||||||
|
- [6. 附录](#6-附录)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 业务逻辑梳理
|
||||||
|
|
||||||
|
### 1.1 个人中心核心功能
|
||||||
|
|
||||||
|
个人中心 (`src/views/Dashboard/Center.js`) 是用户的核心控制面板,包含以下6大功能模块:
|
||||||
|
|
||||||
|
| 功能模块 | 描述 | 核心价值 |
|
||||||
|
|---------|------|---------|
|
||||||
|
| **自选股管理** | 添加/查看/删除自选股,查看实时行情 | 快速追踪关注股票的动态 |
|
||||||
|
| **事件关注** | 关注的热点事件列表,查看事件详情 | 掌握市场热点和投资机会 |
|
||||||
|
| **我的评论** | 用户在各个事件下的评论历史 | 回顾自己的观点和判断 |
|
||||||
|
| **订阅信息** | 用户会员状态、剩余天数、功能权限 | 管理订阅和升级服务 |
|
||||||
|
| **投资日历** | 用户自定义的投资相关日程事件 | 规划投资时间线 |
|
||||||
|
| **投资计划与复盘** | 投资计划和复盘记录的CRUD | 系统化投资管理 |
|
||||||
|
|
||||||
|
### 1.2 页面数据加载流程
|
||||||
|
|
||||||
|
```
|
||||||
|
页面加载
|
||||||
|
↓
|
||||||
|
并行请求4个API(Promise.all)
|
||||||
|
├─ GET /api/account/watchlist → 自选股列表
|
||||||
|
├─ GET /api/account/events/following → 关注事件
|
||||||
|
├─ GET /api/account/events/comments → 我的评论
|
||||||
|
└─ GET /api/subscription/current → 订阅信息
|
||||||
|
↓
|
||||||
|
如果有自选股,加载实时行情
|
||||||
|
└─ GET /api/account/watchlist/realtime → 实时行情数据
|
||||||
|
↓
|
||||||
|
子组件加载自己的数据
|
||||||
|
├─ InvestmentCalendarChakra
|
||||||
|
│ └─ GET /api/account/calendar/events → 日历事件
|
||||||
|
└─ InvestmentPlansAndReviews
|
||||||
|
└─ GET /api/account/investment-plans → 投资计划
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.3 用户交互流程
|
||||||
|
|
||||||
|
#### 自选股操作
|
||||||
|
```
|
||||||
|
查看自选股 → 点击刷新 → 更新实时行情
|
||||||
|
↓
|
||||||
|
点击股票 → 跳转到个股详情页
|
||||||
|
↓
|
||||||
|
点击添加 → 跳转到股票搜索页
|
||||||
|
↓
|
||||||
|
点击删除 → DELETE /api/account/watchlist/:id
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 投资计划操作
|
||||||
|
```
|
||||||
|
查看计划列表
|
||||||
|
↓
|
||||||
|
点击新增 → 填写表单 → POST /api/account/investment-plans
|
||||||
|
↓
|
||||||
|
点击编辑 → 修改内容 → PUT /api/account/investment-plans/:id
|
||||||
|
↓
|
||||||
|
点击删除 → DELETE /api/account/investment-plans/:id
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 日历事件操作
|
||||||
|
```
|
||||||
|
查看日历(月视图)
|
||||||
|
↓
|
||||||
|
选择日期 → 查看当天事件
|
||||||
|
↓
|
||||||
|
点击新增 → 填写表单 → POST /api/account/calendar/events
|
||||||
|
↓
|
||||||
|
点击事件 → 查看详情 → 编辑/删除
|
||||||
|
↓
|
||||||
|
PUT /api/account/calendar/events/:id
|
||||||
|
DELETE /api/account/calendar/events/:id
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. API 接口清单
|
||||||
|
|
||||||
|
### 2.1 接口总览
|
||||||
|
|
||||||
|
共实现 **20 个** Mock API 接口,覆盖个人中心的所有功能需求。
|
||||||
|
|
||||||
|
| 分类 | 接口数量 | 说明 |
|
||||||
|
|-----|---------|------|
|
||||||
|
| 用户资料 | 3 | 资料完整度、获取/更新资料 |
|
||||||
|
| 自选股管理 | 4 | 获取列表、实时行情、添加、删除 |
|
||||||
|
| 事件关注 | 2 | 获取关注事件、我的评论 |
|
||||||
|
| 投资计划 | 4 | 获取、创建、更新、删除 |
|
||||||
|
| 投资日历 | 4 | 获取、创建、更新、删除 |
|
||||||
|
| 订阅信息 | 3 | 订阅信息、当前订阅、权限列表 |
|
||||||
|
|
||||||
|
### 2.2 详细接口列表
|
||||||
|
|
||||||
|
#### 用户资料管理
|
||||||
|
|
||||||
|
| # | 方法 | 路径 | 描述 | 返回数据 |
|
||||||
|
|---|------|------|------|---------|
|
||||||
|
| 1 | GET | `/api/account/profile-completeness` | 获取资料完整度 | 完整度百分比、缺失项 |
|
||||||
|
| 2 | PUT | `/api/account/profile` | 更新用户资料 | 更新后的用户对象 |
|
||||||
|
| 3 | GET | `/api/account/profile` | 获取用户资料 | 用户对象 |
|
||||||
|
|
||||||
|
#### 自选股管理
|
||||||
|
|
||||||
|
| # | 方法 | 路径 | 描述 | 返回数据 |
|
||||||
|
|---|------|------|------|---------|
|
||||||
|
| 4 | GET | `/api/account/watchlist` | 获取自选股列表 | 自选股数组 |
|
||||||
|
| 5 | GET | `/api/account/watchlist/realtime` | 获取实时行情 | 行情数据数组 |
|
||||||
|
| 6 | POST | `/api/account/watchlist/add` | 添加自选股 | 新添加的自选股对象 |
|
||||||
|
| 7 | DELETE | `/api/account/watchlist/:id` | 删除自选股 | 成功消息 |
|
||||||
|
|
||||||
|
#### 事件关注管理
|
||||||
|
|
||||||
|
| # | 方法 | 路径 | 描述 | 返回数据 |
|
||||||
|
|---|------|------|------|---------|
|
||||||
|
| 8 | GET | `/api/account/events/following` | 获取关注的事件 | 事件数组 |
|
||||||
|
| 9 | GET | `/api/account/events/comments` | 获取我的评论 | 评论数组 |
|
||||||
|
|
||||||
|
#### 投资计划与复盘
|
||||||
|
|
||||||
|
| # | 方法 | 路径 | 描述 | 返回数据 |
|
||||||
|
|---|------|------|------|---------|
|
||||||
|
| 10 | GET | `/api/account/investment-plans` | 获取投资计划列表 | 计划数组 |
|
||||||
|
| 11 | POST | `/api/account/investment-plans` | 创建投资计划 | 新创建的计划对象 |
|
||||||
|
| 12 | PUT | `/api/account/investment-plans/:id` | 更新投资计划 | 更新后的计划对象 |
|
||||||
|
| 13 | DELETE | `/api/account/investment-plans/:id` | 删除投资计划 | 成功消息 |
|
||||||
|
|
||||||
|
#### 投资日历
|
||||||
|
|
||||||
|
| # | 方法 | 路径 | 描述 | 返回数据 |
|
||||||
|
|---|------|------|------|---------|
|
||||||
|
| 14 | GET | `/api/account/calendar/events` | 获取日历事件 | 事件数组(支持日期范围过滤) |
|
||||||
|
| 15 | POST | `/api/account/calendar/events` | 创建日历事件 | 新创建的事件对象 |
|
||||||
|
| 16 | PUT | `/api/account/calendar/events/:id` | 更新日历事件 | 更新后的事件对象 |
|
||||||
|
| 17 | DELETE | `/api/account/calendar/events/:id` | 删除日历事件 | 成功消息 |
|
||||||
|
|
||||||
|
#### 订阅信息
|
||||||
|
|
||||||
|
| # | 方法 | 路径 | 描述 | 返回数据 |
|
||||||
|
|---|------|------|------|---------|
|
||||||
|
| 18 | GET | `/api/subscription/info` | 获取订阅信息 | 订阅类型、状态、剩余天数 |
|
||||||
|
| 19 | GET | `/api/subscription/current` | 获取当前订阅详情 | 详细的订阅信息 |
|
||||||
|
| 20 | GET | `/api/subscription/permissions` | 获取订阅权限 | 功能权限列表 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Mock 数据结构
|
||||||
|
|
||||||
|
### 3.1 自选股数据 (Watchlist)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
id: 1, // 自选股ID
|
||||||
|
user_id: 1, // 用户ID
|
||||||
|
stock_code: "600519.SH", // 股票代码
|
||||||
|
stock_name: "贵州茅台", // 股票名称
|
||||||
|
industry: "白酒", // 所属行业
|
||||||
|
current_price: 1650.50, // 当前价格
|
||||||
|
change_percent: 2.5, // 涨跌幅(%)
|
||||||
|
added_at: "2025-01-10T10:30:00Z" // 添加时间
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Mock 数据数量**: 5 只股票
|
||||||
|
- 贵州茅台 (600519.SH)
|
||||||
|
- 平安银行 (000001.SZ)
|
||||||
|
- 五粮液 (000858.SZ)
|
||||||
|
- 宁德时代 (300750.SZ)
|
||||||
|
- BYD比亚迪 (002594.SZ)
|
||||||
|
|
||||||
|
### 3.2 实时行情数据 (Realtime Quotes)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
stock_code: "600519.SH", // 股票代码
|
||||||
|
current_price: 1650.50, // 当前价格
|
||||||
|
change_percent: 2.5, // 涨跌幅(%)
|
||||||
|
change: 40.25, // 涨跌额
|
||||||
|
volume: 2345678, // 成交量
|
||||||
|
turnover: 3945678901.23, // 成交额
|
||||||
|
high: 1665.00, // 最高价
|
||||||
|
low: 1645.00, // 最低价
|
||||||
|
open: 1648.80, // 开盘价
|
||||||
|
prev_close: 1610.25, // 昨收价
|
||||||
|
update_time: "15:00:00" // 更新时间
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Mock 数据数量**: 5 只股票的实时行情
|
||||||
|
|
||||||
|
### 3.3 关注事件数据 (Following Events)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
id: 101, // 事件ID
|
||||||
|
title: "央行宣布降准0.5个百分点...", // 事件标题
|
||||||
|
tags: ["货币政策", "央行", "降准", "银行"], // 标签
|
||||||
|
view_count: 12340, // 浏览数
|
||||||
|
comment_count: 156, // 评论数
|
||||||
|
upvote_count: 489, // 点赞数
|
||||||
|
heat_score: 95, // 热度分数
|
||||||
|
exceed_expectation_score: 85, // 超预期分数
|
||||||
|
creator: { // 创建者
|
||||||
|
id: 1001,
|
||||||
|
username: "财经分析师",
|
||||||
|
avatar_url: "https://i.pravatar.cc/150?img=11"
|
||||||
|
},
|
||||||
|
created_at: "2025-01-15T09:00:00Z", // 创建时间
|
||||||
|
followed_at: "2025-01-15T10:30:00Z" // 关注时间
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Mock 数据数量**: 5 个热点事件
|
||||||
|
- 央行降准
|
||||||
|
- ChatGPT-5 发布
|
||||||
|
- 新能源补贴政策
|
||||||
|
- 芯片法案
|
||||||
|
- 医保目录调整
|
||||||
|
|
||||||
|
### 3.4 评论数据 (Comments)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
id: 201, // 评论ID
|
||||||
|
user_id: 1, // 用户ID
|
||||||
|
event_id: 101, // 关联事件ID
|
||||||
|
event_title: "央行宣布降准0.5个百分点...", // 事件标题
|
||||||
|
content: "这次降准对银行股是重大利好!...", // 评论内容
|
||||||
|
created_at: "2025-01-15T11:20:00Z", // 评论时间
|
||||||
|
likes: 45, // 点赞数
|
||||||
|
replies: 12 // 回复数
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Mock 数据数量**: 5 条评论
|
||||||
|
|
||||||
|
### 3.5 投资计划数据 (Investment Plans)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
id: 301, // 计划ID
|
||||||
|
user_id: 1, // 用户ID
|
||||||
|
type: "plan", // 类型: plan | review
|
||||||
|
title: "2025年Q1 新能源板块布局计划", // 标题
|
||||||
|
content: "计划在Q1分批建仓新能源板块...", // 内容(支持Markdown)
|
||||||
|
target_date: "2025-03-31", // 目标日期
|
||||||
|
status: "in_progress", // 状态: pending | in_progress | completed | cancelled
|
||||||
|
created_at: "2025-01-10T10:00:00Z", // 创建时间
|
||||||
|
updated_at: "2025-01-15T14:30:00Z", // 更新时间
|
||||||
|
tags: ["新能源", "布局计划", "Q1计划"] // 标签
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Mock 数据数量**: 4 条记录
|
||||||
|
- 2 条计划 (plan)
|
||||||
|
- 2 条复盘 (review)
|
||||||
|
|
||||||
|
### 3.6 日历事件数据 (Calendar Events)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
id: 401, // 事件ID
|
||||||
|
user_id: 1, // 用户ID
|
||||||
|
title: "贵州茅台年报披露", // 事件标题
|
||||||
|
date: "2025-03-28", // 事件日期
|
||||||
|
type: "earnings", // 类型: earnings | policy | reminder | custom
|
||||||
|
category: "financial_report", // 分类: financial_report | macro_policy | trading | investment | review
|
||||||
|
description: "关注营收和净利润增速...", // 描述
|
||||||
|
stock_code: "600519.SH", // 关联股票代码(可选)
|
||||||
|
stock_name: "贵州茅台", // 关联股票名称(可选)
|
||||||
|
importance: "high", // 重要性: low | medium | high
|
||||||
|
is_recurring: false, // 是否重复
|
||||||
|
recurrence_rule: null, // 重复规则: daily | weekly | monthly(可选)
|
||||||
|
created_at: "2025-01-10T10:00:00Z" // 创建时间
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Mock 数据数量**: 7 个日历事件
|
||||||
|
- 2 个财报事件
|
||||||
|
- 2 个政策事件
|
||||||
|
- 3 个提醒事件(含重复事件)
|
||||||
|
|
||||||
|
### 3.7 订阅信息数据 (Subscription)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
type: "pro", // 订阅类型: free | pro | max
|
||||||
|
status: "active", // 状态: active | expired | cancelled
|
||||||
|
is_active: true, // 是否激活
|
||||||
|
days_left: 90, // 剩余天数
|
||||||
|
end_date: "2025-04-15T23:59:59Z", // 到期时间
|
||||||
|
plan_name: "Pro版", // 套餐名称
|
||||||
|
features: [ // 功能列表
|
||||||
|
"无限事件查看",
|
||||||
|
"实时行情推送",
|
||||||
|
"专业分析报告",
|
||||||
|
...
|
||||||
|
],
|
||||||
|
price: 0.01, // 价格
|
||||||
|
currency: "CNY", // 货币
|
||||||
|
billing_cycle: "monthly", // 计费周期: monthly | quarterly | yearly
|
||||||
|
auto_renew: true, // 自动续费
|
||||||
|
next_billing_date: "2025-02-15T00:00:00Z" // 下次扣费日期
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 实施内容
|
||||||
|
|
||||||
|
### 4.1 创建的文件
|
||||||
|
|
||||||
|
#### 1. `src/mocks/data/account.js` (新建)
|
||||||
|
|
||||||
|
**文件作用**: 存储个人中心相关的所有 Mock 数据
|
||||||
|
|
||||||
|
**包含内容**:
|
||||||
|
- `mockWatchlist` - 自选股数据 (5条)
|
||||||
|
- `mockRealtimeQuotes` - 实时行情数据 (5条)
|
||||||
|
- `mockFollowingEvents` - 关注事件数据 (5条)
|
||||||
|
- `mockEventComments` - 评论数据 (5条)
|
||||||
|
- `mockInvestmentPlans` - 投资计划数据 (4条)
|
||||||
|
- `mockCalendarEvents` - 日历事件数据 (7条)
|
||||||
|
- `mockSubscriptionCurrent` - 订阅详情数据 (1条)
|
||||||
|
|
||||||
|
**辅助函数**:
|
||||||
|
```javascript
|
||||||
|
// 根据用户ID获取数据
|
||||||
|
getWatchlistByUserId(userId)
|
||||||
|
getFollowingEventsByUserId(userId)
|
||||||
|
getCommentsByUserId(userId)
|
||||||
|
getInvestmentPlansByUserId(userId)
|
||||||
|
getCalendarEventsByUserId(userId)
|
||||||
|
|
||||||
|
// 根据日期范围获取日历事件
|
||||||
|
getCalendarEventsByDateRange(userId, startDate, endDate)
|
||||||
|
```
|
||||||
|
|
||||||
|
**文件大小**: 约 550 行代码
|
||||||
|
|
||||||
|
#### 2. `src/mocks/handlers/account.js` (完全重写)
|
||||||
|
|
||||||
|
**文件作用**: 处理个人中心相关的所有 API 请求
|
||||||
|
|
||||||
|
**包含内容**: 20 个 API Handler
|
||||||
|
|
||||||
|
**主要改动**:
|
||||||
|
- ✅ 保留原有的用户资料管理接口 (3个)
|
||||||
|
- ✅ 完善自选股管理接口 (4个)
|
||||||
|
- ✅ 完善事件关注接口 (2个)
|
||||||
|
- ✅ **新增** 投资计划接口 (4个)
|
||||||
|
- ✅ **新增** 投资日历接口 (4个)
|
||||||
|
- ✅ 完善订阅信息接口 (3个)
|
||||||
|
|
||||||
|
**文件大小**: 660 行代码(从原 542 行扩展到 660 行)
|
||||||
|
|
||||||
|
### 4.2 修改的文件
|
||||||
|
|
||||||
|
#### `src/mocks/handlers/index.js` (无需修改)
|
||||||
|
|
||||||
|
**检查结果**: ✅ 已正确导入和导出 `accountHandlers`
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { accountHandlers } from './account';
|
||||||
|
|
||||||
|
export const handlers = [
|
||||||
|
...authHandlers,
|
||||||
|
...accountHandlers, // ✅ 已包含
|
||||||
|
...simulationHandlers,
|
||||||
|
...eventHandlers,
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.3 Mock 数据特点
|
||||||
|
|
||||||
|
#### 数据真实性
|
||||||
|
- ✅ 使用真实的股票代码和名称
|
||||||
|
- ✅ 价格和涨跌幅符合市场规律
|
||||||
|
- ✅ 事件标题和内容贴近实际热点
|
||||||
|
- ✅ 日期时间合理分布
|
||||||
|
|
||||||
|
#### 数据关联性
|
||||||
|
- ✅ 评论关联到对应的事件
|
||||||
|
- ✅ 日历事件关联到对应的股票
|
||||||
|
- ✅ 实时行情对应自选股列表
|
||||||
|
- ✅ 订阅类型影响权限配置
|
||||||
|
|
||||||
|
#### 数据可扩展性
|
||||||
|
- ✅ 支持动态添加/删除数据
|
||||||
|
- ✅ 数据结构预留扩展字段
|
||||||
|
- ✅ 辅助函数便于数据查询
|
||||||
|
- ✅ 支持日期范围过滤
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 测试验证
|
||||||
|
|
||||||
|
### 5.1 功能测试清单
|
||||||
|
|
||||||
|
#### 个人中心页面加载
|
||||||
|
|
||||||
|
| 测试项 | 测试步骤 | 预期结果 | 状态 |
|
||||||
|
|-------|---------|---------|-----|
|
||||||
|
| **页面初始加载** | 1. 登录系统<br>2. 访问 `/home/center` | 页面正常加载,显示所有板块 | ⬜ |
|
||||||
|
| **统计卡片显示** | 查看顶部4个统计卡片 | 显示:自选股(5)、关注事件(5)、我的评论(5)、订阅状态(Pro版) | ⬜ |
|
||||||
|
| **自选股列表** | 查看自选股板块 | 显示5只股票,包含股票代码、名称、价格、涨跌幅 | ⬜ |
|
||||||
|
| **实时行情** | 等待实时行情加载 | 股票价格显示,涨跌幅有颜色标识(红涨绿跌) | ⬜ |
|
||||||
|
| **关注事件列表** | 查看关注事件板块 | 显示5个事件,包含标题、标签、统计数据、热度分数 | ⬜ |
|
||||||
|
| **我的评论列表** | 查看我的评论板块 | 显示5条评论,包含内容、时间、关联事件 | ⬜ |
|
||||||
|
| **订阅信息卡片** | 查看订阅管理板块 | 显示Pro版,剩余90天,状态正常 | ⬜ |
|
||||||
|
|
||||||
|
#### 自选股功能
|
||||||
|
|
||||||
|
| 测试项 | 测试步骤 | 预期结果 | 状态 |
|
||||||
|
|-------|---------|---------|-----|
|
||||||
|
| **查看自选股详情** | 点击任一自选股 | 跳转到个股详情页 | ⬜ |
|
||||||
|
| **刷新实时行情** | 点击刷新按钮 | 显示Loading,刷新完成后更新价格数据 | ⬜ |
|
||||||
|
| **自动刷新行情** | 等待60秒 | 自动刷新实时行情(每分钟一次) | ⬜ |
|
||||||
|
|
||||||
|
#### 投资计划功能
|
||||||
|
|
||||||
|
| 测试项 | 测试步骤 | 预期结果 | 状态 |
|
||||||
|
|-------|---------|---------|-----|
|
||||||
|
| **查看投资计划** | 滚动到投资计划板块 | 显示4条记录(2个计划 + 2个复盘) | ⬜ |
|
||||||
|
| **创建计划** | 1. 点击"新增计划"<br>2. 填写表单<br>3. 提交 | 计划创建成功,列表刷新 | ⬜ |
|
||||||
|
| **编辑计划** | 1. 点击编辑按钮<br>2. 修改内容<br>3. 保存 | 计划更新成功,显示更新后的内容 | ⬜ |
|
||||||
|
| **删除计划** | 1. 点击删除按钮<br>2. 确认删除 | 计划删除成功,列表刷新 | ⬜ |
|
||||||
|
| **计划状态切换** | 切换计划状态(待进行/进行中/已完成) | 状态更新成功,显示对应标识 | ⬜ |
|
||||||
|
|
||||||
|
#### 投资日历功能
|
||||||
|
|
||||||
|
| 测试项 | 测试步骤 | 预期结果 | 状态 |
|
||||||
|
|-------|---------|---------|-----|
|
||||||
|
| **查看日历** | 查看投资日历板块 | 显示月视图,标记有事件的日期 | ⬜ |
|
||||||
|
| **查看事件** | 点击有事件的日期 | 显示当天的事件列表(支持多个事件) | ⬜ |
|
||||||
|
| **创建事件** | 1. 选择日期<br>2. 点击"添加事件"<br>3. 填写表单<br>4. 提交 | 事件创建成功,日历更新 | ⬜ |
|
||||||
|
| **编辑事件** | 1. 点击事件<br>2. 修改信息<br>3. 保存 | 事件更新成功 | ⬜ |
|
||||||
|
| **删除事件** | 1. 点击事件<br>2. 点击删除<br>3. 确认 | 事件删除成功,日历更新 | ⬜ |
|
||||||
|
| **重复事件** | 创建一个重复事件(如每月20日) | 日历上多个日期显示该事件 | ⬜ |
|
||||||
|
|
||||||
|
#### 订阅管理功能
|
||||||
|
|
||||||
|
| 测试项 | 测试步骤 | 预期结果 | 状态 |
|
||||||
|
|-------|---------|---------|-----|
|
||||||
|
| **查看订阅详情** | 点击订阅卡片 | 跳转到订阅管理页面 | ⬜ |
|
||||||
|
| **订阅权限检查** | 访问需要权限的功能 | Pro用户可访问,Free用户提示升级 | ⬜ |
|
||||||
|
|
||||||
|
### 5.2 数据一致性测试
|
||||||
|
|
||||||
|
| 测试项 | 验证方法 | 预期结果 | 状态 |
|
||||||
|
|-------|---------|---------|-----|
|
||||||
|
| **自选股与行情匹配** | 对比自选股列表和实时行情 | 每只自选股都有对应的行情数据 | ⬜ |
|
||||||
|
| **评论与事件关联** | 点击评论中的事件链接 | 能正确跳转到对应事件 | ⬜ |
|
||||||
|
| **日历事件与股票关联** | 查看带股票代码的日历事件 | 点击能跳转到对应股票详情 | ⬜ |
|
||||||
|
| **订阅类型一致性** | 对比多处显示的订阅类型 | 统计卡片、订阅管理、权限检查一致 | ⬜ |
|
||||||
|
|
||||||
|
### 5.3 边界情况测试
|
||||||
|
|
||||||
|
| 测试项 | 测试步骤 | 预期结果 | 状态 |
|
||||||
|
|-------|---------|---------|-----|
|
||||||
|
| **空数据状态** | 1. 清空所有自选股<br>2. 刷新页面 | 显示"暂无自选股"提示,引导添加 | ⬜ |
|
||||||
|
| **网络延迟** | 模拟慢速网络 | 显示Loading状态,300ms后加载完成 | ⬜ |
|
||||||
|
| **未登录状态** | 未登录访问个人中心 | 返回401错误(被ProtectedRoute拦截) | ⬜ |
|
||||||
|
| **大数据量** | 添加10+只自选股 | 前端只显示前10只,其他可查看全部 | ⬜ |
|
||||||
|
| **日期范围查询** | 查询特定月份的日历事件 | 只返回该月份的事件 | ⬜ |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. 附录
|
||||||
|
|
||||||
|
### 6.1 API 请求示例
|
||||||
|
|
||||||
|
#### 获取自选股列表
|
||||||
|
```javascript
|
||||||
|
// 请求
|
||||||
|
GET /api/account/watchlist
|
||||||
|
|
||||||
|
// 响应
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"user_id": 1,
|
||||||
|
"stock_code": "600519.SH",
|
||||||
|
"stock_name": "贵州茅台",
|
||||||
|
"industry": "白酒",
|
||||||
|
"current_price": 1650.50,
|
||||||
|
"change_percent": 2.5,
|
||||||
|
"added_at": "2025-01-10T10:30:00Z"
|
||||||
|
},
|
||||||
|
...
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 创建投资计划
|
||||||
|
```javascript
|
||||||
|
// 请求
|
||||||
|
POST /api/account/investment-plans
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"type": "plan",
|
||||||
|
"title": "2025年Q1 新能源板块布局计划",
|
||||||
|
"content": "计划在Q1分批建仓新能源板块...",
|
||||||
|
"target_date": "2025-03-31",
|
||||||
|
"status": "pending",
|
||||||
|
"tags": ["新能源", "布局计划"]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 响应
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"message": "创建成功",
|
||||||
|
"data": {
|
||||||
|
"id": 305,
|
||||||
|
"user_id": 1,
|
||||||
|
"type": "plan",
|
||||||
|
"title": "2025年Q1 新能源板块布局计划",
|
||||||
|
"content": "计划在Q1分批建仓新能源板块...",
|
||||||
|
"target_date": "2025-03-31",
|
||||||
|
"status": "pending",
|
||||||
|
"tags": ["新能源", "布局计划"],
|
||||||
|
"created_at": "2025-01-19T10:00:00Z",
|
||||||
|
"updated_at": "2025-01-19T10:00:00Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 获取日历事件(日期范围)
|
||||||
|
```javascript
|
||||||
|
// 请求
|
||||||
|
GET /api/account/calendar/events?start_date=2025-01-01&end_date=2025-01-31
|
||||||
|
|
||||||
|
// 响应
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"id": 403,
|
||||||
|
"user_id": 1,
|
||||||
|
"title": "央行货币政策委员会例会",
|
||||||
|
"date": "2025-01-25",
|
||||||
|
"type": "policy",
|
||||||
|
"category": "macro_policy",
|
||||||
|
"importance": "medium",
|
||||||
|
"created_at": "2025-01-08T09:00:00Z"
|
||||||
|
},
|
||||||
|
...
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.2 数据模型 ER 图
|
||||||
|
|
||||||
|
```
|
||||||
|
User (用户)
|
||||||
|
├─ 1:N → Watchlist (自选股)
|
||||||
|
├─ 1:N → FollowingEvents (关注事件)
|
||||||
|
├─ 1:N → EventComments (评论)
|
||||||
|
├─ 1:N → InvestmentPlans (投资计划)
|
||||||
|
├─ 1:N → CalendarEvents (日历事件)
|
||||||
|
└─ 1:1 → Subscription (订阅信息)
|
||||||
|
|
||||||
|
Event (事件)
|
||||||
|
├─ 1:N → EventComments (评论)
|
||||||
|
└─ N:N → Users (关注用户)
|
||||||
|
|
||||||
|
Stock (股票)
|
||||||
|
├─ 1:N → Watchlist (自选股)
|
||||||
|
├─ 1:1 → RealtimeQuote (实时行情)
|
||||||
|
└─ 1:N → CalendarEvents (日历事件)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.3 Mock 数据统计
|
||||||
|
|
||||||
|
| 数据类型 | 数量 | 字段数 | 总大小(估算) |
|
||||||
|
|---------|-----|--------|--------------|
|
||||||
|
| 自选股 | 5 | 8 | 约 0.5KB |
|
||||||
|
| 实时行情 | 5 | 11 | 约 0.8KB |
|
||||||
|
| 关注事件 | 5 | 10 | 约 2KB |
|
||||||
|
| 评论 | 5 | 8 | 约 1.5KB |
|
||||||
|
| 投资计划 | 4 | 10 | 约 3KB |
|
||||||
|
| 日历事件 | 7 | 12 | 约 1.5KB |
|
||||||
|
| **总计** | **31** | **59** | **约 9.3KB** |
|
||||||
|
|
||||||
|
### 6.4 前端组件映射
|
||||||
|
|
||||||
|
| 前端组件 | 使用的 API | Mock 数据来源 |
|
||||||
|
|---------|-----------|-------------|
|
||||||
|
| `Center.js` (主组件) | 4个并行API | `mockWatchlist`, `mockFollowingEvents`, `mockEventComments`, `mockSubscriptionCurrent` |
|
||||||
|
| 自选股卡片 | `/api/account/watchlist` | `mockWatchlist` |
|
||||||
|
| 实时行情刷新 | `/api/account/watchlist/realtime` | `mockRealtimeQuotes` |
|
||||||
|
| 关注事件列表 | `/api/account/events/following` | `mockFollowingEvents` |
|
||||||
|
| 我的评论列表 | `/api/account/events/comments` | `mockEventComments` |
|
||||||
|
| 订阅信息卡片 | `/api/subscription/current` | `mockSubscriptionCurrent` |
|
||||||
|
| `InvestmentCalendarChakra.js` | `/api/account/calendar/events` | `mockCalendarEvents` |
|
||||||
|
| `InvestmentPlansAndReviews.js` | `/api/account/investment-plans` | `mockInvestmentPlans` |
|
||||||
|
|
||||||
|
### 6.5 常见问题 (FAQ)
|
||||||
|
|
||||||
|
**Q1: Mock 数据会持久化吗?**
|
||||||
|
A: 不会。Mock 数据存储在内存中,刷新页面后会重置。如果需要持久化,可以考虑使用 localStorage。
|
||||||
|
|
||||||
|
**Q2: 如何切换到真实 API?**
|
||||||
|
A: 在 `.env` 文件中设置 `REACT_APP_ENABLE_MOCK=false` 即可切换到真实 API。
|
||||||
|
|
||||||
|
**Q3: Mock 数据支持多用户吗?**
|
||||||
|
A: 目前的 Mock 数据基于当前登录用户(`getCurrentUser()`),支持基本的多用户场景。
|
||||||
|
|
||||||
|
**Q4: 实时行情数据是真的实时吗?**
|
||||||
|
A: Mock 模式下不是真实的实时数据,只是静态数据。真实环境下需要对接WebSocket或轮询API。
|
||||||
|
|
||||||
|
**Q5: 如何添加更多 Mock 数据?**
|
||||||
|
A: 编辑 `src/mocks/data/account.js`,在对应的数组中添加新的数据对象即可。
|
||||||
|
|
||||||
|
### 6.6 后续优化建议
|
||||||
|
|
||||||
|
#### 短期优化(1周内)
|
||||||
|
- [ ] 添加更多股票到自选股池(目前5只 → 10只)
|
||||||
|
- [ ] 丰富事件类型和标签
|
||||||
|
- [ ] 完善投资计划的标签系统
|
||||||
|
- [ ] 添加日历事件的提醒功能Mock
|
||||||
|
|
||||||
|
#### 中期优化(1月内)
|
||||||
|
- [ ] 实现 Mock 数据的 localStorage 持久化
|
||||||
|
- [ ] 添加数据导入/导出功能
|
||||||
|
- [ ] 模拟网络波动和错误场景
|
||||||
|
- [ ] 添加更多的边界测试用例
|
||||||
|
|
||||||
|
#### 长期优化(3月内)
|
||||||
|
- [ ] 实现完整的 Mock 数据生成器
|
||||||
|
- [ ] 支持批量生成测试数据
|
||||||
|
- [ ] 添加数据一致性校验工具
|
||||||
|
- [ ] 完善 Mock 数据文档和最佳实践
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 总结
|
||||||
|
|
||||||
|
### 完成内容
|
||||||
|
- ✅ 创建完整的 Mock 数据文件 (`src/mocks/data/account.js`)
|
||||||
|
- ✅ 重写并扩展 Mock Handler (`src/mocks/handlers/account.js`)
|
||||||
|
- ✅ 实现 20 个 API 接口的 Mock
|
||||||
|
- ✅ 提供 31 条 Mock 数据记录
|
||||||
|
- ✅ 验证 handlers/index.js 配置正确
|
||||||
|
|
||||||
|
### 覆盖功能
|
||||||
|
- ✅ 自选股管理(查看、添加、删除、实时行情)
|
||||||
|
- ✅ 事件关注(关注列表、我的评论)
|
||||||
|
- ✅ 投资计划(增删改查、计划与复盘)
|
||||||
|
- ✅ 投资日历(增删改查、日期范围查询)
|
||||||
|
- ✅ 订阅信息(订阅详情、权限管理)
|
||||||
|
- ✅ 用户资料(资料完整度、更新资料)
|
||||||
|
|
||||||
|
### 数据质量
|
||||||
|
- ✅ 数据真实性:使用真实股票和合理价格
|
||||||
|
- ✅ 数据关联性:评论关联事件、日历关联股票
|
||||||
|
- ✅ 数据可扩展性:预留字段、支持动态操作
|
||||||
|
- ✅ 数据完整性:包含所有必需字段
|
||||||
|
|
||||||
|
### 测试准备
|
||||||
|
- ✅ 提供完整的测试用例清单
|
||||||
|
- ✅ 覆盖功能、数据一致性、边界测试
|
||||||
|
- ✅ 包含42个测试项
|
||||||
|
- ✅ 提供测试步骤和预期结果
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**文档版本**: 1.0
|
||||||
|
**生成日期**: 2025-01-19
|
||||||
|
**维护者**: Development Team
|
||||||
|
**相关文档**:
|
||||||
|
- `CONSOLE_LOG_REFACTOR_REPORT.md` - Console Log 重构文档
|
||||||
|
- `LOGIN_MODAL_REFACTOR_PLAN.md` - 登录弹窗改造计划
|
||||||
|
|
||||||
499
src/mocks/data/account.js
Normal file
499
src/mocks/data/account.js
Normal file
@@ -0,0 +1,499 @@
|
|||||||
|
// src/mocks/data/account.js
|
||||||
|
// 个人中心相关的 Mock 数据
|
||||||
|
|
||||||
|
// ==================== 自选股数据 ====================
|
||||||
|
|
||||||
|
export const mockWatchlist = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
user_id: 1,
|
||||||
|
stock_code: '600519.SH',
|
||||||
|
stock_name: '贵州茅台',
|
||||||
|
industry: '白酒',
|
||||||
|
current_price: 1650.50,
|
||||||
|
change_percent: 2.5,
|
||||||
|
added_at: '2025-01-10T10:30:00Z'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
user_id: 1,
|
||||||
|
stock_code: '000001.SZ',
|
||||||
|
stock_name: '平安银行',
|
||||||
|
industry: '银行',
|
||||||
|
current_price: 12.34,
|
||||||
|
change_percent: 4.76,
|
||||||
|
added_at: '2025-01-15T14:20:00Z'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
user_id: 1,
|
||||||
|
stock_code: '000858.SZ',
|
||||||
|
stock_name: '五粮液',
|
||||||
|
industry: '白酒',
|
||||||
|
current_price: 156.78,
|
||||||
|
change_percent: 1.52,
|
||||||
|
added_at: '2025-01-08T09:15:00Z'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
user_id: 1,
|
||||||
|
stock_code: '300750.SZ',
|
||||||
|
stock_name: '宁德时代',
|
||||||
|
industry: '新能源',
|
||||||
|
current_price: 168.90,
|
||||||
|
change_percent: -1.23,
|
||||||
|
added_at: '2025-01-12T16:45:00Z'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
user_id: 1,
|
||||||
|
stock_code: '002594.SZ',
|
||||||
|
stock_name: 'BYD比亚迪',
|
||||||
|
industry: '新能源汽车',
|
||||||
|
current_price: 256.88,
|
||||||
|
change_percent: 3.45,
|
||||||
|
added_at: '2025-01-05T11:20:00Z'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// ==================== 实时行情数据 ====================
|
||||||
|
|
||||||
|
export const mockRealtimeQuotes = [
|
||||||
|
{
|
||||||
|
stock_code: '600519.SH',
|
||||||
|
current_price: 1650.50,
|
||||||
|
change_percent: 2.5,
|
||||||
|
change: 40.25,
|
||||||
|
volume: 2345678,
|
||||||
|
turnover: 3945678901.23,
|
||||||
|
high: 1665.00,
|
||||||
|
low: 1645.00,
|
||||||
|
open: 1648.80,
|
||||||
|
prev_close: 1610.25,
|
||||||
|
update_time: '15:00:00'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
stock_code: '000001.SZ',
|
||||||
|
current_price: 12.34,
|
||||||
|
change_percent: 4.76,
|
||||||
|
change: 0.56,
|
||||||
|
volume: 123456789,
|
||||||
|
turnover: 1523456789.12,
|
||||||
|
high: 12.50,
|
||||||
|
low: 11.80,
|
||||||
|
open: 11.90,
|
||||||
|
prev_close: 11.78,
|
||||||
|
update_time: '15:00:00'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
stock_code: '000858.SZ',
|
||||||
|
current_price: 156.78,
|
||||||
|
change_percent: 1.52,
|
||||||
|
change: 2.34,
|
||||||
|
volume: 45678901,
|
||||||
|
turnover: 7123456789.45,
|
||||||
|
high: 158.00,
|
||||||
|
low: 154.50,
|
||||||
|
open: 155.00,
|
||||||
|
prev_close: 154.44,
|
||||||
|
update_time: '15:00:00'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
stock_code: '300750.SZ',
|
||||||
|
current_price: 168.90,
|
||||||
|
change_percent: -1.23,
|
||||||
|
change: -2.10,
|
||||||
|
volume: 98765432,
|
||||||
|
turnover: 16678945612.34,
|
||||||
|
high: 172.30,
|
||||||
|
low: 167.50,
|
||||||
|
open: 171.00,
|
||||||
|
prev_close: 171.00,
|
||||||
|
update_time: '15:00:00'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
stock_code: '002594.SZ',
|
||||||
|
current_price: 256.88,
|
||||||
|
change_percent: 3.45,
|
||||||
|
change: 8.56,
|
||||||
|
volume: 56789012,
|
||||||
|
turnover: 14567890123.45,
|
||||||
|
high: 260.00,
|
||||||
|
low: 252.00,
|
||||||
|
open: 253.50,
|
||||||
|
prev_close: 248.32,
|
||||||
|
update_time: '15:00:00'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// ==================== 关注事件数据 ====================
|
||||||
|
|
||||||
|
export const mockFollowingEvents = [
|
||||||
|
{
|
||||||
|
id: 101,
|
||||||
|
title: '央行宣布降准0.5个百分点,释放长期资金约1.2万亿元',
|
||||||
|
tags: ['货币政策', '央行', '降准', '银行'],
|
||||||
|
view_count: 12340,
|
||||||
|
comment_count: 156,
|
||||||
|
upvote_count: 489,
|
||||||
|
heat_score: 95,
|
||||||
|
exceed_expectation_score: 85,
|
||||||
|
creator: {
|
||||||
|
id: 1001,
|
||||||
|
username: '财经分析师',
|
||||||
|
avatar_url: 'https://i.pravatar.cc/150?img=11'
|
||||||
|
},
|
||||||
|
created_at: '2025-01-15T09:00:00Z',
|
||||||
|
followed_at: '2025-01-15T10:30:00Z'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 102,
|
||||||
|
title: 'ChatGPT-5 即将发布,AI 算力需求将迎来爆发式增长',
|
||||||
|
tags: ['人工智能', 'ChatGPT', '算力', '科技'],
|
||||||
|
view_count: 8950,
|
||||||
|
comment_count: 234,
|
||||||
|
upvote_count: 567,
|
||||||
|
heat_score: 88,
|
||||||
|
exceed_expectation_score: 78,
|
||||||
|
creator: {
|
||||||
|
id: 1002,
|
||||||
|
username: '科技观察者',
|
||||||
|
avatar_url: 'https://i.pravatar.cc/150?img=12'
|
||||||
|
},
|
||||||
|
created_at: '2025-01-14T14:20:00Z',
|
||||||
|
followed_at: '2025-01-14T15:00:00Z'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 103,
|
||||||
|
title: '新能源汽车补贴政策延续至2026年,行业持续受益',
|
||||||
|
tags: ['新能源', '汽车', '补贴政策', '产业政策'],
|
||||||
|
view_count: 6780,
|
||||||
|
comment_count: 98,
|
||||||
|
upvote_count: 345,
|
||||||
|
heat_score: 72,
|
||||||
|
exceed_expectation_score: 68,
|
||||||
|
creator: {
|
||||||
|
id: 1003,
|
||||||
|
username: '产业研究员',
|
||||||
|
avatar_url: 'https://i.pravatar.cc/150?img=13'
|
||||||
|
},
|
||||||
|
created_at: '2025-01-13T11:15:00Z',
|
||||||
|
followed_at: '2025-01-13T12:00:00Z'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 104,
|
||||||
|
title: '芯片法案正式实施,国产半导体迎来黄金发展期',
|
||||||
|
tags: ['半导体', '芯片', '国产替代', '政策'],
|
||||||
|
view_count: 9540,
|
||||||
|
comment_count: 178,
|
||||||
|
upvote_count: 432,
|
||||||
|
heat_score: 80,
|
||||||
|
exceed_expectation_score: 72,
|
||||||
|
creator: {
|
||||||
|
id: 1004,
|
||||||
|
username: '半导体观察',
|
||||||
|
avatar_url: 'https://i.pravatar.cc/150?img=14'
|
||||||
|
},
|
||||||
|
created_at: '2025-01-12T16:30:00Z',
|
||||||
|
followed_at: '2025-01-12T17:00:00Z'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 105,
|
||||||
|
title: '医保目录调整,创新药企业有望获得更多市场份额',
|
||||||
|
tags: ['医药', '医保', '创新药', '政策'],
|
||||||
|
view_count: 5430,
|
||||||
|
comment_count: 87,
|
||||||
|
upvote_count: 234,
|
||||||
|
heat_score: 65,
|
||||||
|
exceed_expectation_score: null,
|
||||||
|
creator: {
|
||||||
|
id: 1005,
|
||||||
|
username: '医药行业专家',
|
||||||
|
avatar_url: 'https://i.pravatar.cc/150?img=15'
|
||||||
|
},
|
||||||
|
created_at: '2025-01-11T10:00:00Z',
|
||||||
|
followed_at: '2025-01-11T11:30:00Z'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// ==================== 评论数据 ====================
|
||||||
|
|
||||||
|
export const mockEventComments = [
|
||||||
|
{
|
||||||
|
id: 201,
|
||||||
|
user_id: 1,
|
||||||
|
event_id: 101,
|
||||||
|
event_title: '央行宣布降准0.5个百分点,释放长期资金约1.2万亿元',
|
||||||
|
content: '这次降准对银行股是重大利好!预计四大行和股份制银行都会受益,特别是净息差承压的中小银行。建议重点关注招商银行、兴业银行等优质标的。',
|
||||||
|
created_at: '2025-01-15T11:20:00Z',
|
||||||
|
likes: 45,
|
||||||
|
replies: 12
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 202,
|
||||||
|
user_id: 1,
|
||||||
|
event_id: 102,
|
||||||
|
event_title: 'ChatGPT-5 即将发布,AI 算力需求将迎来爆发式增长',
|
||||||
|
content: 'AI 板块又要起飞了!重点关注算力基础设施概念股,如服务器、芯片、数据中心等。另外,AI 应用端也值得关注,特别是已经有成熟产品的公司。',
|
||||||
|
created_at: '2025-01-14T16:45:00Z',
|
||||||
|
likes: 38,
|
||||||
|
replies: 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 203,
|
||||||
|
user_id: 1,
|
||||||
|
event_id: 103,
|
||||||
|
event_title: '新能源汽车补贴政策延续至2026年,行业持续受益',
|
||||||
|
content: '政策延续对整个产业链都是好消息。上游的锂电池、下游的整车厂都会受益。比亚迪和宁德时代可以继续持有,长期看好新能源汽车的渗透率提升。',
|
||||||
|
created_at: '2025-01-13T14:30:00Z',
|
||||||
|
likes: 56,
|
||||||
|
replies: 15
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 204,
|
||||||
|
user_id: 1,
|
||||||
|
event_id: 104,
|
||||||
|
event_title: '芯片法案正式实施,国产半导体迎来黄金发展期',
|
||||||
|
content: '国产替代是大趋势!设备材料、设计封测、制造都有机会。关注那些有核心技术、已经打入国内大厂供应链的公司。半导体是长期主线,波动中坚定持有。',
|
||||||
|
created_at: '2025-01-12T18:00:00Z',
|
||||||
|
likes: 67,
|
||||||
|
replies: 20
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 205,
|
||||||
|
user_id: 1,
|
||||||
|
event_id: 105,
|
||||||
|
event_title: '医保目录调整,创新药企业有望获得更多市场份额',
|
||||||
|
content: '医保谈判结果出来了,创新药企业普遍受益。重点关注有多个重磅品种的药企,以及 CXO 产业链。医药板块经过调整后,估值已经比较合理,可以逐步配置。',
|
||||||
|
created_at: '2025-01-11T13:15:00Z',
|
||||||
|
likes: 42,
|
||||||
|
replies: 10
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// ==================== 投资计划与复盘数据 ====================
|
||||||
|
|
||||||
|
export const mockInvestmentPlans = [
|
||||||
|
{
|
||||||
|
id: 301,
|
||||||
|
user_id: 1,
|
||||||
|
type: 'plan',
|
||||||
|
title: '2025年Q1 新能源板块布局计划',
|
||||||
|
content: '计划在Q1分批建仓新能源板块,重点关注宁德时代、比亚迪、隆基绿能三只标的。目标仓位15%,预计收益率20%。\n\n具体策略:\n1. 宁德时代:占比6%,等待回调至160元附近分批买入\n2. 比亚迪:占比6%,当前价位可以开始建仓\n3. 隆基绿能:占比3%,观察光伏行业景气度再决定\n\n风险控制:单只个股止损-8%,板块整体止损-10%',
|
||||||
|
target_date: '2025-03-31',
|
||||||
|
status: 'in_progress',
|
||||||
|
created_at: '2025-01-10T10:00:00Z',
|
||||||
|
updated_at: '2025-01-15T14:30:00Z',
|
||||||
|
tags: ['新能源', '布局计划', 'Q1计划']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 302,
|
||||||
|
user_id: 1,
|
||||||
|
type: 'review',
|
||||||
|
title: '2024年12月投资复盘 - 白酒板块大涨',
|
||||||
|
content: '12月白酒板块表现优异,贵州茅台上涨12%,五粮液上涨8%。\n\n操作回顾:\n1. 11月底在1550元加仓茅台,获利6.5%\n2. 五粮液持仓未动,获利4.2%\n\n经验总结:\n- 消费板块在年底有明显的估值修复行情\n- 龙头白马股在市场震荡时更具韧性\n- 应该更大胆一些,仓位可以再提高2-3个点\n\n下月计划:\n- 继续持有茅台、五粮液,不轻易卖出\n- 关注春节前的消费旺季催化',
|
||||||
|
target_date: '2024-12-31',
|
||||||
|
status: 'completed',
|
||||||
|
created_at: '2025-01-02T09:00:00Z',
|
||||||
|
updated_at: '2025-01-02T09:00:00Z',
|
||||||
|
tags: ['月度复盘', '白酒', '2024年12月']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 303,
|
||||||
|
user_id: 1,
|
||||||
|
type: 'plan',
|
||||||
|
title: 'AI 算力板块波段交易计划',
|
||||||
|
content: '随着ChatGPT-5即将发布,AI算力板块有望迎来新一轮炒作。\n\n标的选择:\n- 寒武纪:AI芯片龙头,弹性最大\n- 中科曙光:服务器厂商,业绩支撑\n- 浪潮信息:算力基础设施\n\n交易策略:\n- 仓位控制在10%以内(高风险高弹性)\n- 采用金字塔式买入,第一笔3%\n- 快进快出,涨幅20%分批止盈\n- 破位及时止损,控制在-5%以内',
|
||||||
|
target_date: '2025-02-28',
|
||||||
|
status: 'pending',
|
||||||
|
created_at: '2025-01-14T16:00:00Z',
|
||||||
|
updated_at: '2025-01-14T16:00:00Z',
|
||||||
|
tags: ['AI', '算力', '波段交易']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 304,
|
||||||
|
user_id: 1,
|
||||||
|
type: 'review',
|
||||||
|
title: '2024年全年投资总结 - 收益率25.6%',
|
||||||
|
content: '2024年全年收益率25.6%,跑赢沪深300指数12个百分点。\n\n全年亮点:\n1. 新能源板块贡献最大,年度收益35%\n2. 白酒板块稳健增长,年度收益18%\n3. 半导体板块波动较大,年度收益8%\n\n教训与反思:\n1. 年初追高了一些热门概念股,后续回调损失较大\n2. 止损执行不够坚决,有两次错过最佳止损时机\n3. 仓位管理有待提高,牛市时仓位偏低\n\n2025年目标:\n- 收益率目标:30%\n- 优化仓位管理,提高资金使用效率\n- 严格执行止损纪律\n- 加强行业研究,提前布局',
|
||||||
|
target_date: '2024-12-31',
|
||||||
|
status: 'completed',
|
||||||
|
created_at: '2025-01-01T10:00:00Z',
|
||||||
|
updated_at: '2025-01-01T10:00:00Z',
|
||||||
|
tags: ['年度复盘', '2024年', '总结']
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// ==================== 投资日历事件数据 ====================
|
||||||
|
|
||||||
|
export const mockCalendarEvents = [
|
||||||
|
{
|
||||||
|
id: 401,
|
||||||
|
user_id: 1,
|
||||||
|
title: '贵州茅台年报披露',
|
||||||
|
date: '2025-12-20',
|
||||||
|
event_date: '2025-12-20',
|
||||||
|
type: 'earnings',
|
||||||
|
category: 'financial_report',
|
||||||
|
description: '关注营收和净利润增速,以及渠道库存情况',
|
||||||
|
stock_code: '600519.SH',
|
||||||
|
stock_name: '贵州茅台',
|
||||||
|
importance: 5,
|
||||||
|
source: 'future',
|
||||||
|
stocks: ['600519'],
|
||||||
|
created_at: '2025-01-10T10:00:00Z'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 402,
|
||||||
|
user_id: 1,
|
||||||
|
title: '宁德时代业绩快报',
|
||||||
|
date: '2025-11-28',
|
||||||
|
event_date: '2025-11-28',
|
||||||
|
type: 'earnings',
|
||||||
|
category: 'financial_report',
|
||||||
|
description: '重点关注出货量和单位盈利情况',
|
||||||
|
stock_code: '300750.SZ',
|
||||||
|
stock_name: '宁德时代',
|
||||||
|
importance: 5,
|
||||||
|
source: 'future',
|
||||||
|
stocks: ['300750'],
|
||||||
|
created_at: '2025-01-12T14:00:00Z'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 403,
|
||||||
|
user_id: 1,
|
||||||
|
title: '央行货币政策委员会例会',
|
||||||
|
date: '2025-10-25',
|
||||||
|
event_date: '2025-10-25',
|
||||||
|
type: 'policy',
|
||||||
|
category: 'macro_policy',
|
||||||
|
description: '关注货币政策基调和利率调整信号',
|
||||||
|
importance: 4,
|
||||||
|
source: 'future',
|
||||||
|
stocks: [],
|
||||||
|
created_at: '2025-01-08T09:00:00Z'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 404,
|
||||||
|
user_id: 1,
|
||||||
|
title: '春节假期后首个交易日',
|
||||||
|
date: '2025-11-15',
|
||||||
|
event_date: '2025-11-15',
|
||||||
|
type: 'reminder',
|
||||||
|
category: 'trading',
|
||||||
|
description: '节后第一天,关注资金面和市场情绪',
|
||||||
|
importance: 3,
|
||||||
|
source: 'future',
|
||||||
|
stocks: [],
|
||||||
|
created_at: '2025-01-05T16:00:00Z'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 405,
|
||||||
|
user_id: 1,
|
||||||
|
title: '定投日 - 沪深300ETF',
|
||||||
|
date: '2025-10-20',
|
||||||
|
event_date: '2025-10-20',
|
||||||
|
type: 'reminder',
|
||||||
|
category: 'investment',
|
||||||
|
description: '每月20日定投3000元',
|
||||||
|
importance: 2,
|
||||||
|
source: 'user',
|
||||||
|
stocks: [],
|
||||||
|
is_recurring: true,
|
||||||
|
recurrence_rule: 'monthly',
|
||||||
|
created_at: '2024-12-15T10:00:00Z'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 406,
|
||||||
|
user_id: 1,
|
||||||
|
title: '美联储FOMC会议',
|
||||||
|
date: '2025-11-07',
|
||||||
|
event_date: '2025-11-07',
|
||||||
|
type: 'policy',
|
||||||
|
category: 'macro_policy',
|
||||||
|
description: '关注美联储利率决议和鲍威尔讲话',
|
||||||
|
importance: 5,
|
||||||
|
source: 'future',
|
||||||
|
stocks: [],
|
||||||
|
created_at: '2025-01-07T11:00:00Z'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 407,
|
||||||
|
user_id: 1,
|
||||||
|
title: '持仓股票复盘日',
|
||||||
|
date: '2025-10-26',
|
||||||
|
event_date: '2025-10-26',
|
||||||
|
type: 'reminder',
|
||||||
|
category: 'review',
|
||||||
|
description: '每周六进行持仓复盘和下周计划',
|
||||||
|
importance: 3,
|
||||||
|
source: 'user',
|
||||||
|
stocks: [],
|
||||||
|
is_recurring: true,
|
||||||
|
recurrence_rule: 'weekly',
|
||||||
|
created_at: '2025-01-01T10:00:00Z'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// ==================== 订阅信息数据 ====================
|
||||||
|
|
||||||
|
export const mockSubscriptionCurrent = {
|
||||||
|
type: 'pro',
|
||||||
|
status: 'active',
|
||||||
|
is_active: true,
|
||||||
|
days_left: 90,
|
||||||
|
end_date: '2025-04-15T23:59:59Z',
|
||||||
|
plan_name: 'Pro版',
|
||||||
|
features: [
|
||||||
|
'无限事件查看',
|
||||||
|
'实时行情推送',
|
||||||
|
'专业分析报告',
|
||||||
|
'优先客服支持',
|
||||||
|
'关联股票分析',
|
||||||
|
'历史事件对比'
|
||||||
|
],
|
||||||
|
price: 0.01,
|
||||||
|
currency: 'CNY',
|
||||||
|
billing_cycle: 'monthly',
|
||||||
|
auto_renew: true,
|
||||||
|
next_billing_date: '2025-02-15T00:00:00Z'
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 辅助函数 ====================
|
||||||
|
|
||||||
|
// 根据用户ID获取自选股
|
||||||
|
export function getWatchlistByUserId(userId) {
|
||||||
|
return mockWatchlist.filter(item => item.user_id === userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据用户ID获取关注事件
|
||||||
|
export function getFollowingEventsByUserId(userId) {
|
||||||
|
return mockFollowingEvents;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据用户ID获取评论
|
||||||
|
export function getCommentsByUserId(userId) {
|
||||||
|
return mockEventComments.filter(comment => comment.user_id === userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据用户ID获取投资计划
|
||||||
|
export function getInvestmentPlansByUserId(userId) {
|
||||||
|
return mockInvestmentPlans.filter(plan => plan.user_id === userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据用户ID获取日历事件
|
||||||
|
export function getCalendarEventsByUserId(userId) {
|
||||||
|
return mockCalendarEvents.filter(event => event.user_id === userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取指定日期范围的日历事件
|
||||||
|
export function getCalendarEventsByDateRange(userId, startDate, endDate) {
|
||||||
|
const start = new Date(startDate);
|
||||||
|
const end = new Date(endDate);
|
||||||
|
|
||||||
|
return mockCalendarEvents.filter(event => {
|
||||||
|
if (event.user_id !== userId) return false;
|
||||||
|
const eventDate = new Date(event.date);
|
||||||
|
return eventDate >= start && eventDate <= end;
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,137 +1,20 @@
|
|||||||
// src/mocks/handlers/account.js
|
// src/mocks/handlers/account.js
|
||||||
import { http, HttpResponse, delay } from 'msw';
|
import { http, HttpResponse, delay } from 'msw';
|
||||||
import { getCurrentUser } from '../data/users';
|
import { getCurrentUser } from '../data/users';
|
||||||
|
import {
|
||||||
|
mockWatchlist,
|
||||||
|
mockRealtimeQuotes,
|
||||||
|
mockFollowingEvents,
|
||||||
|
mockEventComments,
|
||||||
|
mockInvestmentPlans,
|
||||||
|
mockCalendarEvents,
|
||||||
|
mockSubscriptionCurrent,
|
||||||
|
getCalendarEventsByDateRange
|
||||||
|
} from '../data/account';
|
||||||
|
|
||||||
// 模拟网络延迟(毫秒)
|
// 模拟网络延迟(毫秒)
|
||||||
const NETWORK_DELAY = 300;
|
const NETWORK_DELAY = 300;
|
||||||
|
|
||||||
// ==================== Mock 数据 ====================
|
|
||||||
|
|
||||||
// 模拟自选股数据
|
|
||||||
const mockWatchlist = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
stock_code: '000001.SZ',
|
|
||||||
stock_name: '平安银行',
|
|
||||||
added_at: '2024-01-15T10:30:00Z',
|
|
||||||
industry: '银行',
|
|
||||||
market_cap: 3200000000000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
stock_code: '600519.SH',
|
|
||||||
stock_name: '贵州茅台',
|
|
||||||
added_at: '2024-01-10T14:20:00Z',
|
|
||||||
industry: '白酒',
|
|
||||||
market_cap: 2500000000000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
stock_code: '000858.SZ',
|
|
||||||
stock_name: '五粮液',
|
|
||||||
added_at: '2024-01-08T09:15:00Z',
|
|
||||||
industry: '白酒',
|
|
||||||
market_cap: 800000000000
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
// 模拟实时行情数据
|
|
||||||
const mockRealtimeQuotes = {
|
|
||||||
'000001.SZ': {
|
|
||||||
price: 12.34,
|
|
||||||
change: 0.56,
|
|
||||||
change_percent: 4.76,
|
|
||||||
volume: 123456789,
|
|
||||||
turnover: 1523456789.12,
|
|
||||||
high: 12.50,
|
|
||||||
low: 11.80,
|
|
||||||
open: 11.90,
|
|
||||||
prev_close: 11.78,
|
|
||||||
timestamp: new Date().toISOString()
|
|
||||||
},
|
|
||||||
'600519.SH': {
|
|
||||||
price: 1680.50,
|
|
||||||
change: -12.30,
|
|
||||||
change_percent: -0.73,
|
|
||||||
volume: 2345678,
|
|
||||||
turnover: 3945678901.23,
|
|
||||||
high: 1695.00,
|
|
||||||
low: 1675.00,
|
|
||||||
open: 1692.80,
|
|
||||||
prev_close: 1692.80,
|
|
||||||
timestamp: new Date().toISOString()
|
|
||||||
},
|
|
||||||
'000858.SZ': {
|
|
||||||
price: 156.78,
|
|
||||||
change: 2.34,
|
|
||||||
change_percent: 1.52,
|
|
||||||
volume: 45678901,
|
|
||||||
turnover: 7123456789.45,
|
|
||||||
high: 158.00,
|
|
||||||
low: 154.50,
|
|
||||||
open: 155.00,
|
|
||||||
prev_close: 154.44,
|
|
||||||
timestamp: new Date().toISOString()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 模拟关注的事件
|
|
||||||
const mockFollowingEvents = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
title: '央行降准0.5个百分点',
|
|
||||||
importance: 'high',
|
|
||||||
followed_at: '2024-01-12T08:00:00Z',
|
|
||||||
event_date: '2024-01-10T00:00:00Z',
|
|
||||||
category: '宏观政策'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
title: 'ChatGPT-5 即将发布',
|
|
||||||
importance: 'medium',
|
|
||||||
followed_at: '2024-01-11T15:30:00Z',
|
|
||||||
event_date: '2024-01-09T00:00:00Z',
|
|
||||||
category: '科技创新'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
// 模拟事件评论
|
|
||||||
const mockEventComments = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
event_id: 1,
|
|
||||||
content: '这次降准对银行股是重大利好,建议关注四大行',
|
|
||||||
created_at: '2024-01-12T10:30:00Z',
|
|
||||||
likes: 15,
|
|
||||||
event_title: '央行降准0.5个百分点'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
event_id: 2,
|
|
||||||
content: 'AI 板块又要起飞了,重点关注算力概念股',
|
|
||||||
created_at: '2024-01-11T16:45:00Z',
|
|
||||||
likes: 8,
|
|
||||||
event_title: 'ChatGPT-5 即将发布'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
// 模拟订阅信息(当前订阅)
|
|
||||||
const mockSubscriptionCurrent = {
|
|
||||||
plan: 'premium',
|
|
||||||
plan_name: '专业版',
|
|
||||||
expires_at: '2025-12-31T23:59:59Z',
|
|
||||||
auto_renew: true,
|
|
||||||
features: [
|
|
||||||
'无限事件查看',
|
|
||||||
'实时行情推送',
|
|
||||||
'专业分析报告',
|
|
||||||
'优先客服支持'
|
|
||||||
],
|
|
||||||
price: 299,
|
|
||||||
currency: 'CNY',
|
|
||||||
billing_cycle: 'monthly'
|
|
||||||
};
|
|
||||||
|
|
||||||
export const accountHandlers = [
|
export const accountHandlers = [
|
||||||
// ==================== 用户资料管理 ====================
|
// ==================== 用户资料管理 ====================
|
||||||
|
|
||||||
@@ -139,10 +22,7 @@ export const accountHandlers = [
|
|||||||
http.get('/api/account/profile-completeness', async () => {
|
http.get('/api/account/profile-completeness', async () => {
|
||||||
await delay(NETWORK_DELAY);
|
await delay(NETWORK_DELAY);
|
||||||
|
|
||||||
// 获取当前登录用户
|
|
||||||
const currentUser = getCurrentUser();
|
const currentUser = getCurrentUser();
|
||||||
|
|
||||||
// 如果没有登录,返回 401
|
|
||||||
if (!currentUser) {
|
if (!currentUser) {
|
||||||
return HttpResponse.json({
|
return HttpResponse.json({
|
||||||
success: false,
|
success: false,
|
||||||
@@ -152,38 +32,27 @@ export const accountHandlers = [
|
|||||||
|
|
||||||
console.log('[Mock] 获取资料完整度:', currentUser);
|
console.log('[Mock] 获取资料完整度:', currentUser);
|
||||||
|
|
||||||
// 检查用户是否是微信用户
|
|
||||||
const isWechatUser = currentUser.has_wechat || !!currentUser.wechat_openid;
|
const isWechatUser = currentUser.has_wechat || !!currentUser.wechat_openid;
|
||||||
|
|
||||||
// 检查各项信息
|
|
||||||
const completeness = {
|
const completeness = {
|
||||||
hasPassword: !!currentUser.password_hash || !isWechatUser, // 非微信用户默认有密码
|
hasPassword: !!currentUser.password_hash || !isWechatUser,
|
||||||
hasPhone: !!currentUser.phone,
|
hasPhone: !!currentUser.phone,
|
||||||
hasEmail: !!currentUser.email && currentUser.email.includes('@') && !currentUser.email.endsWith('@valuefrontier.temp'),
|
hasEmail: !!currentUser.email && currentUser.email.includes('@') && !currentUser.email.endsWith('@valuefrontier.temp'),
|
||||||
isWechatUser: isWechatUser
|
isWechatUser: isWechatUser
|
||||||
};
|
};
|
||||||
|
|
||||||
// 计算完整度
|
|
||||||
const totalItems = 3;
|
const totalItems = 3;
|
||||||
const completedItems = [completeness.hasPassword, completeness.hasPhone, completeness.hasEmail].filter(Boolean).length;
|
const completedItems = [completeness.hasPassword, completeness.hasPhone, completeness.hasEmail].filter(Boolean).length;
|
||||||
const completenessPercentage = Math.round((completedItems / totalItems) * 100);
|
const completenessPercentage = Math.round((completedItems / totalItems) * 100);
|
||||||
|
|
||||||
// 智能判断是否需要提醒
|
|
||||||
let needsAttention = false;
|
let needsAttention = false;
|
||||||
const missingItems = [];
|
const missingItems = [];
|
||||||
|
|
||||||
// Mock 模式下,对微信用户进行简化判断
|
|
||||||
if (isWechatUser && completenessPercentage < 100) {
|
if (isWechatUser && completenessPercentage < 100) {
|
||||||
needsAttention = true;
|
needsAttention = true;
|
||||||
if (!completeness.hasPassword) {
|
if (!completeness.hasPassword) missingItems.push('登录密码');
|
||||||
missingItems.push('登录密码');
|
if (!completeness.hasPhone) missingItems.push('手机号');
|
||||||
}
|
if (!completeness.hasEmail) missingItems.push('邮箱');
|
||||||
if (!completeness.hasPhone) {
|
|
||||||
missingItems.push('手机号');
|
|
||||||
}
|
|
||||||
if (!completeness.hasEmail) {
|
|
||||||
missingItems.push('邮箱');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = {
|
const result = {
|
||||||
@@ -207,9 +76,7 @@ export const accountHandlers = [
|
|||||||
http.put('/api/account/profile', async ({ request }) => {
|
http.put('/api/account/profile', async ({ request }) => {
|
||||||
await delay(NETWORK_DELAY);
|
await delay(NETWORK_DELAY);
|
||||||
|
|
||||||
// 获取当前登录用户
|
|
||||||
const currentUser = getCurrentUser();
|
const currentUser = getCurrentUser();
|
||||||
|
|
||||||
if (!currentUser) {
|
if (!currentUser) {
|
||||||
return HttpResponse.json({
|
return HttpResponse.json({
|
||||||
success: false,
|
success: false,
|
||||||
@@ -218,10 +85,8 @@ export const accountHandlers = [
|
|||||||
}
|
}
|
||||||
|
|
||||||
const body = await request.json();
|
const body = await request.json();
|
||||||
|
|
||||||
console.log('[Mock] 更新用户资料:', body);
|
console.log('[Mock] 更新用户资料:', body);
|
||||||
|
|
||||||
// 在 Mock 模式下,我们直接更新当前用户对象(实际应该调用 setCurrentUser)
|
|
||||||
Object.assign(currentUser, body);
|
Object.assign(currentUser, body);
|
||||||
|
|
||||||
return HttpResponse.json({
|
return HttpResponse.json({
|
||||||
@@ -236,7 +101,6 @@ export const accountHandlers = [
|
|||||||
await delay(NETWORK_DELAY);
|
await delay(NETWORK_DELAY);
|
||||||
|
|
||||||
const currentUser = getCurrentUser();
|
const currentUser = getCurrentUser();
|
||||||
|
|
||||||
if (!currentUser) {
|
if (!currentUser) {
|
||||||
return HttpResponse.json({
|
return HttpResponse.json({
|
||||||
success: false,
|
success: false,
|
||||||
@@ -252,15 +116,447 @@ export const accountHandlers = [
|
|||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
|
||||||
// ==================== 订阅管理 ====================
|
// ==================== 自选股管理 ====================
|
||||||
|
|
||||||
// 4. 获取订阅信息
|
// 4. 获取自选股列表
|
||||||
|
http.get('/api/account/watchlist', async () => {
|
||||||
|
await delay(NETWORK_DELAY);
|
||||||
|
|
||||||
|
const currentUser = getCurrentUser();
|
||||||
|
if (!currentUser) {
|
||||||
|
return HttpResponse.json(
|
||||||
|
{ success: false, error: '未登录' },
|
||||||
|
{ status: 401 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[Mock] 获取自选股列表');
|
||||||
|
|
||||||
|
return HttpResponse.json({
|
||||||
|
success: true,
|
||||||
|
data: mockWatchlist
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
|
||||||
|
// 5. 获取自选股实时行情
|
||||||
|
http.get('/api/account/watchlist/realtime', async () => {
|
||||||
|
await delay(200);
|
||||||
|
|
||||||
|
const currentUser = getCurrentUser();
|
||||||
|
if (!currentUser) {
|
||||||
|
return HttpResponse.json(
|
||||||
|
{ success: false, error: '未登录' },
|
||||||
|
{ status: 401 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[Mock] 获取自选股实时行情');
|
||||||
|
|
||||||
|
return HttpResponse.json({
|
||||||
|
success: true,
|
||||||
|
data: mockRealtimeQuotes
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
|
||||||
|
// 6. 添加自选股
|
||||||
|
http.post('/api/account/watchlist/add', async ({ request }) => {
|
||||||
|
await delay(NETWORK_DELAY);
|
||||||
|
|
||||||
|
const currentUser = getCurrentUser();
|
||||||
|
if (!currentUser) {
|
||||||
|
return HttpResponse.json(
|
||||||
|
{ success: false, error: '未登录' },
|
||||||
|
{ status: 401 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const body = await request.json();
|
||||||
|
const { stock_code, stock_name } = body;
|
||||||
|
|
||||||
|
console.log('[Mock] 添加自选股:', { stock_code, stock_name });
|
||||||
|
|
||||||
|
const newItem = {
|
||||||
|
id: mockWatchlist.length + 1,
|
||||||
|
user_id: currentUser.id,
|
||||||
|
stock_code,
|
||||||
|
stock_name,
|
||||||
|
added_at: new Date().toISOString(),
|
||||||
|
industry: '未知',
|
||||||
|
current_price: null,
|
||||||
|
change_percent: null
|
||||||
|
};
|
||||||
|
|
||||||
|
mockWatchlist.push(newItem);
|
||||||
|
|
||||||
|
return HttpResponse.json({
|
||||||
|
success: true,
|
||||||
|
message: '添加成功',
|
||||||
|
data: newItem
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
|
||||||
|
// 7. 删除自选股
|
||||||
|
http.delete('/api/account/watchlist/:id', async ({ params }) => {
|
||||||
|
await delay(NETWORK_DELAY);
|
||||||
|
|
||||||
|
const currentUser = getCurrentUser();
|
||||||
|
if (!currentUser) {
|
||||||
|
return HttpResponse.json(
|
||||||
|
{ success: false, error: '未登录' },
|
||||||
|
{ status: 401 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { id } = params;
|
||||||
|
console.log('[Mock] 删除自选股:', id);
|
||||||
|
|
||||||
|
const index = mockWatchlist.findIndex(item => item.id === parseInt(id));
|
||||||
|
if (index !== -1) {
|
||||||
|
mockWatchlist.splice(index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return HttpResponse.json({
|
||||||
|
success: true,
|
||||||
|
message: '删除成功'
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
|
||||||
|
// ==================== 事件关注管理 ====================
|
||||||
|
|
||||||
|
// 8. 获取关注的事件
|
||||||
|
http.get('/api/account/events/following', async () => {
|
||||||
|
await delay(NETWORK_DELAY);
|
||||||
|
|
||||||
|
const currentUser = getCurrentUser();
|
||||||
|
if (!currentUser) {
|
||||||
|
return HttpResponse.json(
|
||||||
|
{ success: false, error: '未登录' },
|
||||||
|
{ status: 401 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[Mock] 获取关注的事件');
|
||||||
|
|
||||||
|
return HttpResponse.json({
|
||||||
|
success: true,
|
||||||
|
data: mockFollowingEvents
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
|
||||||
|
// 9. 获取事件评论
|
||||||
|
http.get('/api/account/events/comments', async () => {
|
||||||
|
await delay(NETWORK_DELAY);
|
||||||
|
|
||||||
|
const currentUser = getCurrentUser();
|
||||||
|
if (!currentUser) {
|
||||||
|
return HttpResponse.json(
|
||||||
|
{ success: false, error: '未登录' },
|
||||||
|
{ status: 401 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[Mock] 获取事件评论');
|
||||||
|
|
||||||
|
return HttpResponse.json({
|
||||||
|
success: true,
|
||||||
|
data: mockEventComments
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
|
||||||
|
// ==================== 投资计划与复盘 ====================
|
||||||
|
|
||||||
|
// 10. 获取投资计划列表
|
||||||
|
http.get('/api/account/investment-plans', async () => {
|
||||||
|
await delay(NETWORK_DELAY);
|
||||||
|
|
||||||
|
const currentUser = getCurrentUser();
|
||||||
|
if (!currentUser) {
|
||||||
|
return HttpResponse.json(
|
||||||
|
{ success: false, error: '未登录' },
|
||||||
|
{ status: 401 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[Mock] 获取投资计划列表');
|
||||||
|
|
||||||
|
return HttpResponse.json({
|
||||||
|
success: true,
|
||||||
|
data: mockInvestmentPlans
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
|
||||||
|
// 11. 创建投资计划
|
||||||
|
http.post('/api/account/investment-plans', async ({ request }) => {
|
||||||
|
await delay(NETWORK_DELAY);
|
||||||
|
|
||||||
|
const currentUser = getCurrentUser();
|
||||||
|
if (!currentUser) {
|
||||||
|
return HttpResponse.json(
|
||||||
|
{ success: false, error: '未登录' },
|
||||||
|
{ status: 401 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const body = await request.json();
|
||||||
|
console.log('[Mock] 创建投资计划:', body);
|
||||||
|
|
||||||
|
const newPlan = {
|
||||||
|
id: mockInvestmentPlans.length + 301,
|
||||||
|
user_id: currentUser.id,
|
||||||
|
...body,
|
||||||
|
created_at: new Date().toISOString(),
|
||||||
|
updated_at: new Date().toISOString()
|
||||||
|
};
|
||||||
|
|
||||||
|
mockInvestmentPlans.push(newPlan);
|
||||||
|
|
||||||
|
return HttpResponse.json({
|
||||||
|
success: true,
|
||||||
|
message: '创建成功',
|
||||||
|
data: newPlan
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
|
||||||
|
// 12. 更新投资计划
|
||||||
|
http.put('/api/account/investment-plans/:id', async ({ request, params }) => {
|
||||||
|
await delay(NETWORK_DELAY);
|
||||||
|
|
||||||
|
const currentUser = getCurrentUser();
|
||||||
|
if (!currentUser) {
|
||||||
|
return HttpResponse.json(
|
||||||
|
{ success: false, error: '未登录' },
|
||||||
|
{ status: 401 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { id } = params;
|
||||||
|
const body = await request.json();
|
||||||
|
|
||||||
|
console.log('[Mock] 更新投资计划:', { id, body });
|
||||||
|
|
||||||
|
const index = mockInvestmentPlans.findIndex(plan => plan.id === parseInt(id));
|
||||||
|
if (index !== -1) {
|
||||||
|
mockInvestmentPlans[index] = {
|
||||||
|
...mockInvestmentPlans[index],
|
||||||
|
...body,
|
||||||
|
updated_at: new Date().toISOString()
|
||||||
|
};
|
||||||
|
|
||||||
|
return HttpResponse.json({
|
||||||
|
success: true,
|
||||||
|
message: '更新成功',
|
||||||
|
data: mockInvestmentPlans[index]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return HttpResponse.json({
|
||||||
|
success: false,
|
||||||
|
error: '计划不存在'
|
||||||
|
}, { status: 404 });
|
||||||
|
}),
|
||||||
|
|
||||||
|
// 13. 删除投资计划
|
||||||
|
http.delete('/api/account/investment-plans/:id', async ({ params }) => {
|
||||||
|
await delay(NETWORK_DELAY);
|
||||||
|
|
||||||
|
const currentUser = getCurrentUser();
|
||||||
|
if (!currentUser) {
|
||||||
|
return HttpResponse.json(
|
||||||
|
{ success: false, error: '未登录' },
|
||||||
|
{ status: 401 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { id } = params;
|
||||||
|
console.log('[Mock] 删除投资计划:', id);
|
||||||
|
|
||||||
|
const index = mockInvestmentPlans.findIndex(plan => plan.id === parseInt(id));
|
||||||
|
if (index !== -1) {
|
||||||
|
mockInvestmentPlans.splice(index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return HttpResponse.json({
|
||||||
|
success: true,
|
||||||
|
message: '删除成功'
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
|
||||||
|
// ==================== 投资日历 ====================
|
||||||
|
|
||||||
|
// 14. 获取日历事件(可选日期范围)- 合并投资计划和日历事件
|
||||||
|
http.get('/api/account/calendar/events', async ({ request }) => {
|
||||||
|
await delay(NETWORK_DELAY);
|
||||||
|
|
||||||
|
const currentUser = getCurrentUser();
|
||||||
|
if (!currentUser) {
|
||||||
|
return HttpResponse.json(
|
||||||
|
{ success: false, error: '未登录' },
|
||||||
|
{ status: 401 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = new URL(request.url);
|
||||||
|
const startDate = url.searchParams.get('start_date');
|
||||||
|
const endDate = url.searchParams.get('end_date');
|
||||||
|
|
||||||
|
console.log('[Mock] 获取日历事件:', { startDate, endDate });
|
||||||
|
|
||||||
|
// 1. 获取日历事件
|
||||||
|
let calendarEvents = mockCalendarEvents;
|
||||||
|
if (startDate && endDate) {
|
||||||
|
calendarEvents = getCalendarEventsByDateRange(currentUser.id, startDate, endDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 获取投资计划和复盘,转换为日历事件格式
|
||||||
|
const investmentPlansAsEvents = mockInvestmentPlans
|
||||||
|
.filter(plan => plan.user_id === currentUser.id)
|
||||||
|
.map(plan => ({
|
||||||
|
id: plan.id,
|
||||||
|
user_id: plan.user_id,
|
||||||
|
title: plan.title,
|
||||||
|
date: plan.target_date || plan.date,
|
||||||
|
event_date: plan.target_date || plan.date,
|
||||||
|
type: plan.type, // 'plan' or 'review'
|
||||||
|
category: plan.type === 'plan' ? 'investment_plan' : 'investment_review',
|
||||||
|
description: plan.content || '',
|
||||||
|
importance: 3, // 默认重要度
|
||||||
|
source: 'user', // 标记为用户创建
|
||||||
|
stocks: plan.stocks || [],
|
||||||
|
tags: plan.tags || [],
|
||||||
|
status: plan.status,
|
||||||
|
created_at: plan.created_at,
|
||||||
|
updated_at: plan.updated_at
|
||||||
|
}));
|
||||||
|
|
||||||
|
// 3. 合并两个数据源
|
||||||
|
const allEvents = [...calendarEvents, ...investmentPlansAsEvents];
|
||||||
|
|
||||||
|
// 4. 如果提供了日期范围,对合并后的数据进行过滤
|
||||||
|
let filteredEvents = allEvents;
|
||||||
|
if (startDate && endDate) {
|
||||||
|
const start = new Date(startDate);
|
||||||
|
const end = new Date(endDate);
|
||||||
|
filteredEvents = allEvents.filter(event => {
|
||||||
|
const eventDate = new Date(event.date || event.event_date);
|
||||||
|
return eventDate >= start && eventDate <= end;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[Mock] 合并后的日历事件数量:', {
|
||||||
|
calendarEvents: calendarEvents.length,
|
||||||
|
investmentPlansAsEvents: investmentPlansAsEvents.length,
|
||||||
|
total: filteredEvents.length
|
||||||
|
});
|
||||||
|
|
||||||
|
return HttpResponse.json({
|
||||||
|
success: true,
|
||||||
|
data: filteredEvents
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
|
||||||
|
// 15. 创建日历事件
|
||||||
|
http.post('/api/account/calendar/events', async ({ request }) => {
|
||||||
|
await delay(NETWORK_DELAY);
|
||||||
|
|
||||||
|
const currentUser = getCurrentUser();
|
||||||
|
if (!currentUser) {
|
||||||
|
return HttpResponse.json(
|
||||||
|
{ success: false, error: '未登录' },
|
||||||
|
{ status: 401 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const body = await request.json();
|
||||||
|
console.log('[Mock] 创建日历事件:', body);
|
||||||
|
|
||||||
|
const newEvent = {
|
||||||
|
id: mockCalendarEvents.length + 401,
|
||||||
|
user_id: currentUser.id,
|
||||||
|
...body,
|
||||||
|
source: 'user', // 用户创建的事件标记为 'user'
|
||||||
|
created_at: new Date().toISOString()
|
||||||
|
};
|
||||||
|
|
||||||
|
mockCalendarEvents.push(newEvent);
|
||||||
|
|
||||||
|
return HttpResponse.json({
|
||||||
|
success: true,
|
||||||
|
message: '创建成功',
|
||||||
|
data: newEvent
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
|
||||||
|
// 16. 更新日历事件
|
||||||
|
http.put('/api/account/calendar/events/:id', async ({ request, params }) => {
|
||||||
|
await delay(NETWORK_DELAY);
|
||||||
|
|
||||||
|
const currentUser = getCurrentUser();
|
||||||
|
if (!currentUser) {
|
||||||
|
return HttpResponse.json(
|
||||||
|
{ success: false, error: '未登录' },
|
||||||
|
{ status: 401 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { id } = params;
|
||||||
|
const body = await request.json();
|
||||||
|
|
||||||
|
console.log('[Mock] 更新日历事件:', { id, body });
|
||||||
|
|
||||||
|
const index = mockCalendarEvents.findIndex(event => event.id === parseInt(id));
|
||||||
|
if (index !== -1) {
|
||||||
|
mockCalendarEvents[index] = {
|
||||||
|
...mockCalendarEvents[index],
|
||||||
|
...body
|
||||||
|
};
|
||||||
|
|
||||||
|
return HttpResponse.json({
|
||||||
|
success: true,
|
||||||
|
message: '更新成功',
|
||||||
|
data: mockCalendarEvents[index]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return HttpResponse.json({
|
||||||
|
success: false,
|
||||||
|
error: '事件不存在'
|
||||||
|
}, { status: 404 });
|
||||||
|
}),
|
||||||
|
|
||||||
|
// 17. 删除日历事件
|
||||||
|
http.delete('/api/account/calendar/events/:id', async ({ params }) => {
|
||||||
|
await delay(NETWORK_DELAY);
|
||||||
|
|
||||||
|
const currentUser = getCurrentUser();
|
||||||
|
if (!currentUser) {
|
||||||
|
return HttpResponse.json(
|
||||||
|
{ success: false, error: '未登录' },
|
||||||
|
{ status: 401 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { id } = params;
|
||||||
|
console.log('[Mock] 删除日历事件:', id);
|
||||||
|
|
||||||
|
const index = mockCalendarEvents.findIndex(event => event.id === parseInt(id));
|
||||||
|
if (index !== -1) {
|
||||||
|
mockCalendarEvents.splice(index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return HttpResponse.json({
|
||||||
|
success: true,
|
||||||
|
message: '删除成功'
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
|
||||||
|
// ==================== 订阅信息 ====================
|
||||||
|
|
||||||
|
// 18. 获取订阅信息
|
||||||
http.get('/api/subscription/info', async () => {
|
http.get('/api/subscription/info', async () => {
|
||||||
await delay(NETWORK_DELAY);
|
await delay(NETWORK_DELAY);
|
||||||
|
|
||||||
const currentUser = getCurrentUser();
|
const currentUser = getCurrentUser();
|
||||||
|
|
||||||
// 未登录时返回免费用户信息
|
|
||||||
if (!currentUser) {
|
if (!currentUser) {
|
||||||
return HttpResponse.json({
|
return HttpResponse.json({
|
||||||
success: true,
|
success: true,
|
||||||
@@ -276,7 +572,6 @@ export const accountHandlers = [
|
|||||||
|
|
||||||
console.log('[Mock] 获取订阅信息:', currentUser);
|
console.log('[Mock] 获取订阅信息:', currentUser);
|
||||||
|
|
||||||
// 从当前用户对象中获取订阅信息
|
|
||||||
const subscriptionInfo = {
|
const subscriptionInfo = {
|
||||||
type: currentUser.subscription_type || 'free',
|
type: currentUser.subscription_type || 'free',
|
||||||
status: currentUser.subscription_status || 'active',
|
status: currentUser.subscription_status || 'active',
|
||||||
@@ -293,13 +588,44 @@ export const accountHandlers = [
|
|||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
|
||||||
// 5. 获取订阅权限
|
// 19. 获取当前订阅详情
|
||||||
|
http.get('/api/subscription/current', async () => {
|
||||||
|
await delay(NETWORK_DELAY);
|
||||||
|
|
||||||
|
const currentUser = getCurrentUser();
|
||||||
|
if (!currentUser) {
|
||||||
|
return HttpResponse.json(
|
||||||
|
{ success: false, error: '未登录' },
|
||||||
|
{ status: 401 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[Mock] 获取当前订阅详情');
|
||||||
|
|
||||||
|
// 基于当前用户的订阅类型返回详情
|
||||||
|
const userSubscriptionType = (currentUser.subscription_type || 'free').toLowerCase();
|
||||||
|
|
||||||
|
const subscriptionDetails = {
|
||||||
|
...mockSubscriptionCurrent,
|
||||||
|
type: userSubscriptionType,
|
||||||
|
status: currentUser.subscription_status || 'active',
|
||||||
|
is_active: currentUser.is_subscription_active !== false,
|
||||||
|
days_left: currentUser.subscription_days_left || 0,
|
||||||
|
end_date: currentUser.subscription_end_date || null
|
||||||
|
};
|
||||||
|
|
||||||
|
return HttpResponse.json({
|
||||||
|
success: true,
|
||||||
|
data: subscriptionDetails
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
|
||||||
|
// 20. 获取订阅权限
|
||||||
http.get('/api/subscription/permissions', async () => {
|
http.get('/api/subscription/permissions', async () => {
|
||||||
await delay(NETWORK_DELAY);
|
await delay(NETWORK_DELAY);
|
||||||
|
|
||||||
const currentUser = getCurrentUser();
|
const currentUser = getCurrentUser();
|
||||||
|
|
||||||
// 未登录时返回免费权限
|
|
||||||
if (!currentUser) {
|
if (!currentUser) {
|
||||||
return HttpResponse.json({
|
return HttpResponse.json({
|
||||||
success: true,
|
success: true,
|
||||||
@@ -321,7 +647,6 @@ export const accountHandlers = [
|
|||||||
|
|
||||||
const subscriptionType = (currentUser.subscription_type || 'free').toLowerCase();
|
const subscriptionType = (currentUser.subscription_type || 'free').toLowerCase();
|
||||||
|
|
||||||
// 根据订阅类型返回对应权限
|
|
||||||
let permissions = {};
|
let permissions = {};
|
||||||
|
|
||||||
if (subscriptionType === 'free') {
|
if (subscriptionType === 'free') {
|
||||||
@@ -372,171 +697,4 @@ export const accountHandlers = [
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
|
||||||
// ==================== 自选股管理 ====================
|
|
||||||
|
|
||||||
// 6. 获取自选股列表
|
|
||||||
http.get('/api/account/watchlist', async () => {
|
|
||||||
await delay(NETWORK_DELAY);
|
|
||||||
|
|
||||||
const currentUser = getCurrentUser();
|
|
||||||
if (!currentUser) {
|
|
||||||
return HttpResponse.json(
|
|
||||||
{ success: false, error: '未登录' },
|
|
||||||
{ status: 401 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('[Mock] 获取自选股列表');
|
|
||||||
|
|
||||||
return HttpResponse.json({
|
|
||||||
success: true,
|
|
||||||
data: mockWatchlist
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
|
|
||||||
// 7. 获取自选股实时行情
|
|
||||||
http.get('/api/account/watchlist/realtime', async () => {
|
|
||||||
await delay(200);
|
|
||||||
|
|
||||||
const currentUser = getCurrentUser();
|
|
||||||
if (!currentUser) {
|
|
||||||
return HttpResponse.json(
|
|
||||||
{ success: false, error: '未登录' },
|
|
||||||
{ status: 401 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('[Mock] 获取自选股实时行情');
|
|
||||||
|
|
||||||
return HttpResponse.json({
|
|
||||||
success: true,
|
|
||||||
data: mockRealtimeQuotes
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
|
|
||||||
// 8. 添加自选股
|
|
||||||
http.post('/api/account/watchlist/add', async ({ request }) => {
|
|
||||||
await delay(NETWORK_DELAY);
|
|
||||||
|
|
||||||
const currentUser = getCurrentUser();
|
|
||||||
if (!currentUser) {
|
|
||||||
return HttpResponse.json(
|
|
||||||
{ success: false, error: '未登录' },
|
|
||||||
{ status: 401 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const body = await request.json();
|
|
||||||
const { stock_code, stock_name } = body;
|
|
||||||
|
|
||||||
console.log('[Mock] 添加自选股:', { stock_code, stock_name });
|
|
||||||
|
|
||||||
const newItem = {
|
|
||||||
id: mockWatchlist.length + 1,
|
|
||||||
stock_code,
|
|
||||||
stock_name,
|
|
||||||
added_at: new Date().toISOString(),
|
|
||||||
industry: '未知',
|
|
||||||
market_cap: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
mockWatchlist.push(newItem);
|
|
||||||
|
|
||||||
return HttpResponse.json({
|
|
||||||
success: true,
|
|
||||||
message: '添加成功',
|
|
||||||
data: newItem
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
|
|
||||||
// 9. 删除自选股
|
|
||||||
http.delete('/api/account/watchlist/:id', async ({ params }) => {
|
|
||||||
await delay(NETWORK_DELAY);
|
|
||||||
|
|
||||||
const currentUser = getCurrentUser();
|
|
||||||
if (!currentUser) {
|
|
||||||
return HttpResponse.json(
|
|
||||||
{ success: false, error: '未登录' },
|
|
||||||
{ status: 401 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const { id } = params;
|
|
||||||
console.log('[Mock] 删除自选股:', id);
|
|
||||||
|
|
||||||
const index = mockWatchlist.findIndex(item => item.id === parseInt(id));
|
|
||||||
if (index !== -1) {
|
|
||||||
mockWatchlist.splice(index, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return HttpResponse.json({
|
|
||||||
success: true,
|
|
||||||
message: '删除成功'
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
|
|
||||||
// ==================== 事件关注管理 ====================
|
|
||||||
|
|
||||||
// 10. 获取关注的事件
|
|
||||||
http.get('/api/account/events/following', async () => {
|
|
||||||
await delay(NETWORK_DELAY);
|
|
||||||
|
|
||||||
const currentUser = getCurrentUser();
|
|
||||||
if (!currentUser) {
|
|
||||||
return HttpResponse.json(
|
|
||||||
{ success: false, error: '未登录' },
|
|
||||||
{ status: 401 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('[Mock] 获取关注的事件');
|
|
||||||
|
|
||||||
return HttpResponse.json({
|
|
||||||
success: true,
|
|
||||||
data: mockFollowingEvents
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
|
|
||||||
// 11. 获取事件评论
|
|
||||||
http.get('/api/account/events/comments', async () => {
|
|
||||||
await delay(NETWORK_DELAY);
|
|
||||||
|
|
||||||
const currentUser = getCurrentUser();
|
|
||||||
if (!currentUser) {
|
|
||||||
return HttpResponse.json(
|
|
||||||
{ success: false, error: '未登录' },
|
|
||||||
{ status: 401 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('[Mock] 获取事件评论');
|
|
||||||
|
|
||||||
return HttpResponse.json({
|
|
||||||
success: true,
|
|
||||||
data: mockEventComments
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
|
|
||||||
// ==================== 订阅信息 ====================
|
|
||||||
|
|
||||||
// 12. 获取当前订阅信息
|
|
||||||
http.get('/api/subscription/current', async () => {
|
|
||||||
await delay(NETWORK_DELAY);
|
|
||||||
|
|
||||||
const currentUser = getCurrentUser();
|
|
||||||
if (!currentUser) {
|
|
||||||
return HttpResponse.json(
|
|
||||||
{ success: false, error: '未登录' },
|
|
||||||
{ status: 401 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('[Mock] 获取当前订阅信息');
|
|
||||||
|
|
||||||
return HttpResponse.json({
|
|
||||||
success: true,
|
|
||||||
data: mockSubscriptionCurrent
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -311,8 +311,9 @@ export default function InvestmentCalendarChakra() {
|
|||||||
)}
|
)}
|
||||||
</CardBody>
|
</CardBody>
|
||||||
|
|
||||||
{/* 查看事件详情 Modal */}
|
{/* 查看事件详情 Modal - 条件渲染 */}
|
||||||
<Modal isOpen={isOpen} onClose={onClose} size="xl">
|
{isOpen && (
|
||||||
|
<Modal isOpen={isOpen} onClose={onClose} size="xl" closeOnOverlayClick={false} closeOnEsc={true}>
|
||||||
<ModalOverlay />
|
<ModalOverlay />
|
||||||
<ModalContent>
|
<ModalContent>
|
||||||
<ModalHeader>
|
<ModalHeader>
|
||||||
@@ -404,9 +405,11 @@ export default function InvestmentCalendarChakra() {
|
|||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* 添加投资计划 Modal */}
|
{/* 添加投资计划 Modal - 条件渲染 */}
|
||||||
<Modal isOpen={isAddOpen} onClose={onAddClose} size="lg">
|
{isAddOpen && (
|
||||||
|
<Modal isOpen={isAddOpen} onClose={onAddClose} size="lg" closeOnOverlayClick={false} closeOnEsc={true}>
|
||||||
<ModalOverlay />
|
<ModalOverlay />
|
||||||
<ModalContent>
|
<ModalContent>
|
||||||
<ModalHeader>
|
<ModalHeader>
|
||||||
@@ -484,6 +487,7 @@ export default function InvestmentCalendarChakra() {
|
|||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -452,8 +452,9 @@ export default function InvestmentPlansAndReviews({ type = 'both' }) {
|
|||||||
</TabPanels>
|
</TabPanels>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
{/* 编辑/新建模态框 */}
|
{/* 编辑/新建模态框 - 条件渲染 */}
|
||||||
<Modal isOpen={isOpen} onClose={onClose} size="xl">
|
{isOpen && (
|
||||||
|
<Modal isOpen={isOpen} onClose={onClose} size="xl" closeOnOverlayClick={false} closeOnEsc={true}>
|
||||||
<ModalOverlay />
|
<ModalOverlay />
|
||||||
<ModalContent>
|
<ModalContent>
|
||||||
<ModalHeader>
|
<ModalHeader>
|
||||||
@@ -580,6 +581,7 @@ export default function InvestmentPlansAndReviews({ type = 'both' }) {
|
|||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -665,7 +665,8 @@ function Subscription() {
|
|||||||
)))}
|
)))}
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
{/* 支付模态框 */}
|
{/* 支付模态框 - 条件渲染 */}
|
||||||
|
{isPaymentModalOpen && (
|
||||||
<Modal
|
<Modal
|
||||||
isOpen={isPaymentModalOpen}
|
isOpen={isPaymentModalOpen}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
@@ -863,6 +864,7 @@ function Subscription() {
|
|||||||
</ModalBody>
|
</ModalBody>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* 调试面板 */}
|
{/* 调试面板 */}
|
||||||
{process.env.NODE_ENV === 'development' && (
|
{process.env.NODE_ENV === 'development' && (
|
||||||
|
|||||||
Reference in New Issue
Block a user