- 📛 证券名称增强:修复 INTC 等证券名称显示为空的问题 (BUG-101) - 🔒 SELL超量校验:修复卖出数量超过持仓导致持仓变负的Bug (BUG-002) - 🔄 撤销BUY成本还原:修复撤销买入时平均成本计算公式错误 (BUG-003) - 💹 Decimal精度计算:持仓分析改用Prisma.Decimal进行金融计算,防止浮点精度丢失 (BUG-004) - 📛 证券名称显示:在持仓分析卡片、资产分布、盈亏排行等位置同时显示股票代码和名称 - 📋 证券数据库扩展:新增 Intel Corp. (INTC) 证券记录 - 🔍 回退逻辑增强:确保证券名称为空时显示代码而非空白 - 📈 腾讯行情解析升级:精准解析股票名称(索引1),支持港/A/美股及 ETF 名称自动获取 - 🔀 多市场涨跌解析修复:重构腾讯行情多市场适配层,美股(索引4/5)与港A股(索引31/32)使用差异化索引解析涨跌数据 - 🇨🇳 GBK中文解码修复:改用 `arrayBuffer() + TextDecoder('gbk')` 替代 `text()`,彻底解决A股/港股中文股票名称乱码问题 - 💱 JisuAPI实时汇率:接入 JisuAPI 获取实时汇率,缓存1小时,支持 CNY/HKD/USD 转换 - 📊 资产配置动态图:环形图改由后端实时聚合持仓数据驱动,支持 Tooltip 和百分比显示 - 🎨 资产配置图表优化:精美毛玻璃 Tooltip、颜色图标、去除生硬描边、useMemo 性能优化 - 💱 全局货币联动:资产配置图表数值随 CNY/USD/HKD 切换实时转换 - 📝 交易流水增强:新增证券名称列,显示"名称+代码"双行格式 - 💹 全局汇率展示:在导航栏实时显示 USD/CNY/HKD 汇率信息 - 🔧 BUG-202 修复:修正 `convertCurrency` 汇率换算逻辑(原逻辑除法/乘法颠倒,导致 USD→CNY 换算失效) - 🔧 BUG-201 修复:腾讯行情 API 获取失败时,`priceAvailable` 标记配合前端显示 "N/A" 替代虚假 0% - 🔧 BUG-203 增强:持仓分析 `name` 字段确保回退到 `pos.symbol`,名称永不空 - 💹 Decimal 精度保障:所有盈亏/汇率计算均使用 Prisma.Decimal,防止浮点精度丢失
796 lines
21 KiB
Markdown
796 lines
21 KiB
Markdown
# 个人投资持仓管理系统 - 需求规格说明书
|
||
|
||
> 文档版本:v1.0.0
|
||
> 日期:2026-04-12
|
||
> 状态:已完成
|
||
|
||
---
|
||
|
||
## 1. 产品概述
|
||
|
||
### 1.1 产品名称
|
||
个人投资持仓管理系统(Stock Portfolio Manager)
|
||
|
||
### 1.2 产品简介
|
||
现代化、全面化的个人投资组合管理平台,支持多市场(美股、A股、港股、加密货币)统一管理,实时获取行情数据,自动计算持仓成本与盈亏。
|
||
|
||
### 1.3 目标用户
|
||
- 拥有多个市场投资账户的个人投资者
|
||
- 需要统一管理分散在不同平台的投资组合
|
||
- 希望实时了解总体资产配置和盈亏状况
|
||
|
||
---
|
||
|
||
## 2. 技术架构
|
||
|
||
### 2.1 技术栈
|
||
|
||
| 层级 | 技术 | 版本 |
|
||
|------|------|------|
|
||
| 前端框架 | Next.js | 16 |
|
||
| UI 框架 | React | 19 |
|
||
| 类型系统 | TypeScript | 5 |
|
||
| 样式方案 | Tailwind CSS | 4 |
|
||
| 组件库 | shadcn/ui | - |
|
||
| 图表库 | Recharts | - |
|
||
| 后端框架 | Next.js API Routes | - |
|
||
| ORM | Prisma | - |
|
||
| 数据库 | PostgreSQL | 16 |
|
||
|
||
### 2.2 系统架构
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────┐
|
||
│ 前端 (Next.js) │
|
||
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
|
||
│ │ Dashboard │ │ 持仓明细 │ │ 交易流水 │ │ 分析 │ │
|
||
│ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │
|
||
└───────┼────────────┼────────────┼────────────┼───────┘
|
||
│ │ │ │
|
||
└────────────┴─────┬──────┴────────────┘
|
||
│ REST API
|
||
┌──────────────────┼──────────────────┐
|
||
│ │ │
|
||
┌────▼────┐ ┌──────▼──────┐ ┌──────▼──────┐
|
||
│ Accounts │ │ Transactions │ │ Positions │
|
||
│ API │ │ API │ │ API │
|
||
└────┬────┘ └──────┬──────┘ └──────┬──────┘
|
||
│ │ │
|
||
┌────▼──────────────────▼──────────────────▼────┐
|
||
│ Prisma ORM │
|
||
└────────────────────┬─────────────────────────┘
|
||
│
|
||
┌────────────────────▼─────────────────────────┐
|
||
│ PostgreSQL Database │
|
||
│ User │ Account │ Security │ Transaction │ │
|
||
│ Position │ ExchangeRate │
|
||
└──────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 2.3 目录结构
|
||
|
||
```
|
||
stock-portfolio/
|
||
├── prisma/
|
||
│ ├── schema.prisma # 数据模型定义
|
||
│ └── seed.ts # 初始化数据
|
||
├── src/
|
||
│ ├── app/
|
||
│ │ ├── page.tsx # 主页面(Dashboard)
|
||
│ │ └── api/ # API 路由
|
||
│ │ ├── accounts/
|
||
│ │ ├── transactions/
|
||
│ │ ├── positions/
|
||
│ │ ├── securities/
|
||
│ │ ├── exchange-rates/
|
||
│ │ ├── dashboard/
|
||
│ │ │ ├── stats/
|
||
│ │ │ └── analytics/
|
||
│ │ └── import/
|
||
│ ├── components/ # UI 组件
|
||
│ ├── lib/
|
||
│ │ ├── api.ts # API 调用封装
|
||
│ │ ├── prisma.ts # Prisma 客户端
|
||
│ │ └── import-export.ts
|
||
│ └── types/ # TypeScript 类型定义
|
||
├── .env # 环境变量
|
||
└── README.md
|
||
```
|
||
|
||
---
|
||
|
||
## 3. 功能规格
|
||
|
||
### 3.1 市场与账户
|
||
|
||
#### 3.1.1 支持的市场
|
||
|
||
| 市场代码 | 市场名称 | 结算货币 | 示例证券 |
|
||
|----------|----------|----------|----------|
|
||
| US | 美股 | USD | AAPL, GOOGL, MSFT |
|
||
| CN | A股 | CNY | 600690, 159235 |
|
||
| HK | 港股 | HKD | 00700, 09868 |
|
||
| CRYPTO | 加密货币 | USDT | BTC, ETH |
|
||
|
||
#### 3.1.2 账户管理
|
||
|
||
- 每个市场对应一个默认账户
|
||
- 账户信息包含:名称、市场类型、基准货币、余额
|
||
- 创建账户时自动设置对应市场的基准货币
|
||
|
||
### 3.2 证券管理
|
||
|
||
#### 3.2.1 证券数据结构
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| symbol | String | 证券代码(唯一) |
|
||
| name | String | 证券名称 |
|
||
| market | MarketType | 市场类型 |
|
||
| currency | String | 结算货币 |
|
||
| lotSize | Int | 每手股数(港股=100,A股=100,美股=1) |
|
||
| priceDecimals | Int | 价格精度 |
|
||
| qtyDecimals | Int | 数量精度(股票=0,数字货币=8) |
|
||
| isCrypto | Boolean | 是否为加密货币 |
|
||
|
||
#### 3.2.2 预置证券
|
||
|
||
| 代码 | 名称 | 市场 | 货币 |
|
||
|------|------|------|------|
|
||
| 00700 | 腾讯控股 | HK | HKD |
|
||
| 09868 | 小鹏汽车 | HK | HKD |
|
||
| 09988 | 阿里巴巴 | HK | HKD |
|
||
| AAPL | Apple Inc. | US | USD |
|
||
| MSFT | Microsoft Corp. | US | USD |
|
||
| NVDA | NVIDIA Corp. | US | USD |
|
||
| GOOGL | Alphabet Inc. | US | USD |
|
||
| INTC | Intel Corp. | US | USD |
|
||
| 600690 | 海尔智家 | CN | CNY |
|
||
| 159235 | 中证现金流ETF | CN | CNY |
|
||
| BTC | Bitcoin | CRYPTO | USDT |
|
||
| ETH | Ethereum | CRYPTO | USDT |
|
||
|
||
### 3.3 交易类型
|
||
|
||
| 类型代码 | 中文名称 | 说明 | 账户影响 | 持仓影响 |
|
||
|----------|----------|------|----------|----------|
|
||
| BUY | 买入 | 购买证券 | 扣减余额 | 增加持仓 |
|
||
| SELL | 卖出 | 卖出证券 | 增加余额 | 减少持仓 |
|
||
| DEPOSIT | 入金 | 资金转入 | 增加余额 | 无 |
|
||
| WITHDRAW | 出金 | 资金转出 | 扣减余额 | 无 |
|
||
| DIVIDEND | 分红 | 现金分红 | 增加余额 | 无 |
|
||
| INTEREST | 利息 | 利息收入 | 增加余额 | 无 |
|
||
| FEE | 费用 | 手续费支出 | 扣减余额 | 无 |
|
||
|
||
### 3.4 行情数据
|
||
|
||
#### 3.4.1 数据源
|
||
|
||
使用腾讯行情接口获取实时价格:
|
||
- 接口地址:`https://qt.gtimg.cn/q=`
|
||
- 支持批量查询,用逗号分隔
|
||
|
||
#### 3.4.2 代码格式转换
|
||
|
||
用户输入证券代码时,系统自动转换为腾讯接口格式:
|
||
|
||
| 市场 | 用户输入 | 腾讯接口格式 |
|
||
|------|----------|--------------|
|
||
| 港股 | 09868 | r_hk09868 |
|
||
| A股上海 | sh600690 或 600690 | sh600690 |
|
||
| A股深圳 | sz159235 或 159235 | sz159235 |
|
||
| 美股 | GOOGL | s_usGOOGL |
|
||
|
||
#### 3.4.3 解析字段(多市场差异化索引)
|
||
|
||
腾讯行情各市场返回字段索引不同,必须根据市场前缀分别解析:
|
||
|
||
| 市场 | 前缀 | 名称索引 | 当前价索引 | 涨跌额索引 | 涨跌幅索引 |
|
||
|------|------|---------|-----------|-----------|-----------|
|
||
| 美股 | `s_us` | 1 | 3 | **4** | **5** |
|
||
| 港股 | `r_hk` | 1 | 3 | **31** | **32** |
|
||
| A股(上海) | `sh` | 1 | 3 | **31** | **32** |
|
||
| A股(深圳) | `sz` | 1 | 3 | **31** | **32** |
|
||
|
||
**返回数据示例:**
|
||
```
|
||
# 美股 (s_us) - 涨跌额在索引4,涨跌幅在索引5
|
||
v_s_usGOOG="200~Alphabet-C~GOOG.OQ~315.72~-0.65~-0.21~..."
|
||
|
||
# 港股 (r_hk) - 涨跌额在索引31,涨跌幅在索引32
|
||
v_r_hk09868="100~小鹏集团-W~09868~67.600~...~2026/04/13 16:08:56~0.600~0.90~..."
|
||
|
||
# A股 (sh) - 涨跌额在索引31,涨跌幅在索引32
|
||
v_sh600690="1~海尔智家~600690~...~20260413161426~-0.14~-0.67~..."
|
||
```
|
||
|
||
**异常处理策略:**
|
||
1. 当 Security 表中存在证券记录时,优先使用 Security 表中的名称
|
||
2. 当 Security 表中无记录时,使用腾讯行情返回的名称(索引1)
|
||
3. 当前价获取失败时,降级使用持仓的平均成本价(avgCost)
|
||
4. 涨跌数据获取失败时,降级通过 `当前价 - 昨收价` 计算
|
||
5. **GBK 中文解码**:腾讯 API 返回 GBK 编码数据,必须使用 `arrayBuffer() + TextDecoder('gbk')` 解码,禁止直接使用 `text()`
|
||
|
||
### 3.5 持仓计算
|
||
|
||
#### 3.5.1 成本计算 - 平均成本法
|
||
|
||
```
|
||
平均成本 = (Σ买入金额) / 总数量
|
||
= (Q1×P1 + Q2×P2 + ...) / (Q1 + Q2 + ...)
|
||
```
|
||
|
||
#### 3.5.2 市值计算
|
||
|
||
```
|
||
市值 = 持仓数量 × 当前价格
|
||
```
|
||
|
||
#### 3.5.3 盈亏计算
|
||
|
||
```
|
||
浮动盈亏 = 市值 - 成本基数
|
||
= (数量 × 当前价) - (数量 × 平均成本)
|
||
|
||
浮动盈亏率 = (浮动盈亏 / 成本基数) × 100%
|
||
```
|
||
|
||
#### 3.5.4 货币转换
|
||
|
||
系统接入 **JisuAPI** (`https://api.jisuapi.com/exchange/convert`) 获取实时汇率。
|
||
|
||
**汇率获取策略:**
|
||
1. 优先使用 JisuAPI 实时汇率
|
||
2. 缓存周期:1 小时(防止 API 调用频率限制)
|
||
3. 降级机制:JisuAPI 失败时使用默认固定汇率
|
||
|
||
**默认固定汇率(降级用):**
|
||
|
||
| 从货币 | 到 USD 汇率 |
|
||
|--------|-------------|
|
||
| USD | 1 |
|
||
| CNY | 0.137 |
|
||
| HKD | 0.129 |
|
||
| USDT | 1 |
|
||
|
||
**汇率转换公式:**
|
||
```
|
||
目标货币金额 = 源货币金额 × 汇率
|
||
示例:1000 CNY × 0.137 = 137 USD
|
||
```
|
||
|
||
### 3.6 货币显示
|
||
|
||
#### 3.6.1 显示货币选择
|
||
|
||
顶部导航栏提供显示货币切换器:
|
||
- CNY(人民币)
|
||
- USD(美元)
|
||
- HKD(港币)
|
||
|
||
#### 3.6.2 持仓显示规则
|
||
|
||
持仓明细和分析页面根据市场显示对应货币:
|
||
- 港股持仓 → HKD
|
||
- A股持仓 → CNY
|
||
- 美股持仓 → USD
|
||
- 加密货币持仓 → USDT
|
||
|
||
#### 3.6.3 总资产显示
|
||
|
||
总资产、总盈亏等汇总数据根据用户选择的显示货币进行转换:
|
||
|
||
```
|
||
显示金额 = USD金额 × 显示货币汇率
|
||
```
|
||
|
||
**资产配置环形图联动:**
|
||
- 环形图的市值数值、Tooltip 提示金额、图例金额均随全局显示货币(CNY/USD/HKD)实时联动转换
|
||
- 使用 `useMemo` 缓存计算结果,避免重复渲染
|
||
|
||
### 3.7 数据导入导出
|
||
|
||
#### 3.7.1 CSV 导出
|
||
|
||
支持导出:
|
||
- 交易记录(transactions)
|
||
- 持仓明细(positions)
|
||
|
||
导出字段:
|
||
- 交易记录:时间、类型、证券代码、数量、价格、金额、手续费、备注
|
||
- 持仓明细:证券代码、名称、市场、数量、成本价、当前价、市值、盈亏
|
||
|
||
#### 3.7.2 CSV 导入
|
||
|
||
导入交易记录:
|
||
- 支持批量导入
|
||
- 自动验证数据格式
|
||
- 按账户批量创建交易
|
||
|
||
导入模板包含字段:
|
||
- type, symbol, quantity, price, amount, fee, currency, notes, executedAt
|
||
|
||
---
|
||
|
||
## 4. 页面与界面
|
||
|
||
### 4.1 顶部导航栏
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ [💰] 投资持仓管理 [CNY▼] [账户▼] [导入] [导出] [+记录交易] │
|
||
└─────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
功能:
|
||
- Logo 和标题
|
||
- 显示货币选择(下拉)
|
||
- 账户选择(下拉)
|
||
- 导入按钮
|
||
- 导出按钮
|
||
- 记录交易按钮
|
||
|
||
### 4.2 资产概览卡片
|
||
|
||
四个卡片并排显示:
|
||
1. **总资产** - 渐变蓝背景,显示总市值和成本
|
||
2. **浮动盈亏** - 渐变绿背景,显示盈亏金额和百分比
|
||
3. **持仓市值** - 渐变紫背景,显示持仓数量和总市值
|
||
4. **账户数量** - 渐变橙背景,显示市场数量
|
||
|
||
### 4.3 持仓分析卡片
|
||
|
||
展示前4个持仓的概览:
|
||
- 证券代码、图标和**证券名称**
|
||
- 市值(使用对应市场货币)
|
||
- 盈亏金额和百分比
|
||
|
||
### 4.3.1 证券名称显示
|
||
|
||
在以下位置均显示证券代码和对应名称:
|
||
|
||
| 位置 | 显示内容 |
|
||
|------|----------|
|
||
| 持仓分析卡片 | 代码 + 名称(truncate) |
|
||
| 资产分布条形图 | 代码 + 名称(truncate) |
|
||
| 盈亏排行榜 | 代码 + 名称(truncate,最大宽度120px) |
|
||
|
||
证券名称从数据库 Security 表中获取,与腾讯行情接口解析的实时价格配合使用。
|
||
|
||
### 4.4 标签页
|
||
|
||
#### 4.4.1 持仓明细标签页
|
||
|
||
表格列:证券 | 市场 | 数量 | 成本价 | 当前价 | 市值 | 盈亏 | 操作
|
||
|
||
功能:
|
||
- 按市场货币显示价格和市值
|
||
- 显示涨跌百分比
|
||
- 支持删除持仓(卖出全部)
|
||
|
||
#### 4.4.2 交易流水标签页
|
||
|
||
表格列:时间 | 类型 | 证券(名称+代码) | 数量 | 价格 | 金额 | 账户 | 操作
|
||
|
||
**证券列显示:**
|
||
- 上行:证券名称(加粗)
|
||
- 下行:证券代码(灰色小字)
|
||
|
||
功能:
|
||
- 支持编辑交易记录
|
||
- 支持删除交易记录
|
||
- 支持导出 CSV
|
||
- 后端通过关联查询附加证券名称
|
||
|
||
#### 4.4.3 分析标签页
|
||
|
||
包含两个卡片:
|
||
1. **资产配置环形图** - 由后端实时聚合持仓市值数据,按市场(美股/港股/A股/加密)分组显示占比
|
||
2. **盈亏排行** - 列表显示按盈亏排序的持仓
|
||
|
||
**资产配置数据来源:**
|
||
- 后端 API `/api/dashboard/analytics` 返回 `marketDistribution` 字段
|
||
- 数据结构:`{ name: string, value: number, percent: number }`
|
||
- 使用 Recharts `<PieChart>` 渲染环形图
|
||
|
||
### 4.5 对话框
|
||
|
||
#### 4.5.1 记录交易对话框
|
||
|
||
字段:
|
||
- 账户选择
|
||
- 交易类型(买入/卖出/入金/出金/分红)
|
||
- 证券代码搜索(自动补全)
|
||
- 数量(买入/卖出时显示)
|
||
- 价格(买入/卖出时显示)
|
||
- 成交总额(自动计算)
|
||
- 手续费
|
||
- 交易时间
|
||
- 备注
|
||
- 确认按钮
|
||
|
||
#### 4.5.2 交易确认对话框
|
||
|
||
显示交易摘要:
|
||
- 账户信息
|
||
- 交易类型和证券
|
||
- 成交总额
|
||
- 手续费
|
||
- 交易时间
|
||
|
||
#### 4.5.3 编辑交易对话框
|
||
|
||
与记录交易类似,但预填充现有数据。
|
||
|
||
#### 4.5.4 删除确认对话框
|
||
|
||
显示待删除项目详情,确认后执行删除。
|
||
|
||
---
|
||
|
||
## 5. API 规格
|
||
|
||
### 5.1 账户 API
|
||
|
||
#### GET /api/accounts
|
||
获取所有账户列表
|
||
|
||
Response:
|
||
```json
|
||
{
|
||
"id": "string",
|
||
"name": "string",
|
||
"marketType": "US|CN|HK|CRYPTO",
|
||
"baseCurrency": "string",
|
||
"balance": "number"
|
||
}
|
||
```
|
||
|
||
### 5.2 持仓 API
|
||
|
||
#### GET /api/positions
|
||
获取持仓列表
|
||
|
||
Query: `?accountId=xxx`
|
||
|
||
Response:
|
||
```json
|
||
{
|
||
"symbol": "string",
|
||
"quantity": "string",
|
||
"averageCost": "string",
|
||
"currency": "string",
|
||
"accountName": "string",
|
||
"marketType": "string"
|
||
}
|
||
```
|
||
|
||
### 5.3 证券 API
|
||
|
||
#### GET /api/securities
|
||
获取证券列表
|
||
|
||
Query: `?market=US&search=AAPL`
|
||
|
||
#### POST /api/securities
|
||
创建证券
|
||
|
||
Body:
|
||
```json
|
||
{
|
||
"symbol": "string",
|
||
"name": "string",
|
||
"market": "US|CN|HK|CRYPTO",
|
||
"currency": "string",
|
||
"lotSize": 100,
|
||
"priceDecimals": 2,
|
||
"qtyDecimals": 0,
|
||
"isCrypto": false
|
||
}
|
||
```
|
||
|
||
### 5.4 交易 API
|
||
|
||
#### GET /api/transactions
|
||
获取交易流水
|
||
|
||
Query: `?accountId=xxx&page=1&limit=20`
|
||
|
||
Response:
|
||
```json
|
||
{
|
||
"data": [...],
|
||
"pagination": {
|
||
"page": 1,
|
||
"limit": 20,
|
||
"total": 100,
|
||
"totalPages": 5
|
||
}
|
||
}
|
||
```
|
||
|
||
#### POST /api/transactions
|
||
创建交易
|
||
|
||
Body:
|
||
```json
|
||
{
|
||
"accountId": "string",
|
||
"type": "BUY|SELL|DEPOSIT|WITHDRAW|DIVIDEND",
|
||
"symbol": "string|null",
|
||
"quantity": "number|null",
|
||
"price": "number|null",
|
||
"amount": "number",
|
||
"fee": "number",
|
||
"currency": "string",
|
||
"notes": "string|null",
|
||
"executedAt": "ISO8601"
|
||
}
|
||
```
|
||
|
||
#### PATCH /api/transactions
|
||
更新交易
|
||
|
||
Body:
|
||
```json
|
||
{
|
||
"id": "string",
|
||
"type": "string",
|
||
"symbol": "string|null",
|
||
"quantity": "number|null",
|
||
"price": "number|null",
|
||
"amount": "number|null",
|
||
"fee": "number|null",
|
||
"currency": "string|null",
|
||
"notes": "string|null",
|
||
"executedAt": "string|null"
|
||
}
|
||
```
|
||
|
||
#### DELETE /api/transactions?id=xxx
|
||
删除交易
|
||
|
||
### 5.5 行情 API
|
||
|
||
#### GET /api/dashboard/analytics
|
||
获取持仓分析和汇总
|
||
|
||
Response:
|
||
```json
|
||
{
|
||
"prices": {
|
||
"AAPL": { "price": 150.00, "change": 2.50, "changePercent": 1.69 }
|
||
},
|
||
"positions": [
|
||
{
|
||
"symbol": "AAPL",
|
||
"name": "Apple Inc.",
|
||
"marketType": "US",
|
||
"quantity": 10,
|
||
"avgCost": 145.00,
|
||
"currentPrice": 150.00,
|
||
"change": 2.50,
|
||
"changePercent": 1.69,
|
||
"costBasis": 1450.00,
|
||
"costBasisUSD": 1450.00,
|
||
"marketValue": 1500.00,
|
||
"marketValueUSD": 1500.00,
|
||
"pnl": 50.00,
|
||
"pnlPercent": 3.45,
|
||
"pnlUSD": 50.00,
|
||
"currency": "USD"
|
||
}
|
||
],
|
||
"summary": {
|
||
"totalCostBasis": 10000.00,
|
||
"totalMarketValue": 10500.00,
|
||
"totalPnL": 500.00,
|
||
"totalPnLPercent": 5.00,
|
||
"positionCount": 3
|
||
},
|
||
"byMarket": {
|
||
"US": { "totalCost": 5000, "totalValue": 5500, "totalPnL": 500 },
|
||
"CN": { "totalCost": 3000, "totalValue": 3000, "totalPnL": 0 }
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 6. 数据库模型
|
||
|
||
### 6.1 User(用户)
|
||
```prisma
|
||
model User {
|
||
id String @id @default(cuid())
|
||
email String @unique
|
||
name String?
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
accounts Account[]
|
||
}
|
||
```
|
||
|
||
### 6.2 Account(账户)
|
||
```prisma
|
||
model Account {
|
||
id String @id @default(cuid())
|
||
userId String
|
||
user User @relation(...)
|
||
name String
|
||
marketType MarketType
|
||
baseCurrency String
|
||
balance Decimal @default(0)
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
transactions Transaction[]
|
||
positions Position[]
|
||
}
|
||
|
||
enum MarketType {
|
||
US // 美股
|
||
CN // A股
|
||
HK // 港股
|
||
CRYPTO // 加密货币
|
||
}
|
||
```
|
||
|
||
### 6.3 Security(证券)
|
||
```prisma
|
||
model Security {
|
||
id String @id @default(cuid())
|
||
symbol String @unique
|
||
name String
|
||
market MarketType
|
||
currency String
|
||
lotSize Int @default(1)
|
||
priceDecimals Int @default(2)
|
||
qtyDecimals Int @default(0)
|
||
isCrypto Boolean @default(false)
|
||
}
|
||
```
|
||
|
||
### 6.4 Transaction(交易流水)
|
||
```prisma
|
||
model Transaction {
|
||
id String @id @default(cuid())
|
||
accountId String
|
||
account Account @relation(...)
|
||
type TransactionType
|
||
symbol String?
|
||
quantity Decimal?
|
||
price Decimal?
|
||
amount Decimal
|
||
fee Decimal @default(0)
|
||
networkFee Decimal?
|
||
currency String
|
||
exchangeRate Decimal?
|
||
notes String?
|
||
executedAt DateTime
|
||
}
|
||
|
||
enum TransactionType {
|
||
DEPOSIT // 入金
|
||
WITHDRAW // 出金
|
||
BUY // 买入
|
||
SELL // 卖出
|
||
DIVIDEND // 分红
|
||
INTEREST // 利息
|
||
FEE // 费用
|
||
}
|
||
```
|
||
|
||
### 6.5 Position(持仓)
|
||
```prisma
|
||
model Position {
|
||
id String @id @default(cuid())
|
||
accountId String
|
||
account Account @relation(...)
|
||
symbol String
|
||
quantity Decimal
|
||
averageCost Decimal
|
||
currency String
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@unique([accountId, symbol])
|
||
}
|
||
```
|
||
|
||
### 6.6 ExchangeRate(汇率)
|
||
```prisma
|
||
model ExchangeRate {
|
||
id String @id @default(cuid())
|
||
fromCurrency String
|
||
toCurrency String
|
||
rate Decimal
|
||
effectiveDate DateTime
|
||
|
||
@@unique([fromCurrency, toCurrency, effectiveDate])
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 7. 汇率配置
|
||
|
||
### 7.1 初始汇率(以 USD 为基准)
|
||
|
||
| 从货币 | 到 USD 汇率 |
|
||
|--------|-------------|
|
||
| USD | 1.000 |
|
||
| CNY | 0.137 |
|
||
| HKD | 0.129 |
|
||
| USDT | 1.000 |
|
||
|
||
### 7.2 货币转换公式
|
||
|
||
```
|
||
amountInTargetCurrency = amountInUSD × targetCurrencyRate
|
||
|
||
示例:
|
||
100 USD → CNY = 100 × 7.24 = 724 CNY
|
||
100 HKD → USD = 100 × 0.129 = 12.9 USD
|
||
```
|
||
|
||
---
|
||
|
||
## 8. 非功能性需求
|
||
|
||
### 8.1 性能
|
||
- 页面加载时间 < 2秒
|
||
- API 响应时间 < 500ms
|
||
- 股价缓存时间 60 秒
|
||
|
||
### 8.2 兼容性
|
||
- 支持 Chrome、Firefox、Safari、Edge 最新版本
|
||
- 响应式设计,支持桌面和移动端
|
||
|
||
### 8.3 数据安全
|
||
- 所有敏感配置通过环境变量管理
|
||
- 数据库连接使用密码认证
|
||
|
||
---
|
||
|
||
## 9. 版本历史
|
||
|
||
| 版本 | 日期 | 说明 |
|
||
|------|------|------|
|
||
| v1.0.0 | 2026-04-12 | 初始版本,完成核心功能 |
|
||
|
||
---
|
||
|
||
## 10. 附录
|
||
|
||
### 10.1 环境变量
|
||
|
||
```env
|
||
DATABASE_URL="postgresql://user:password@host:5432/database"
|
||
ALPHA_VANTAGE_API_KEY="your_api_key"
|
||
```
|
||
|
||
### 10.2 腾讯行情接口示例
|
||
|
||
**港股(小鹏汽车):**
|
||
```
|
||
GET https://qt.gtimg.cn/q=r_hk09868
|
||
Response: v_r_hk09868="100~小鹏汽车-W~09868~67.000~66.950~..."
|
||
```
|
||
|
||
**A股(海尔智家):**
|
||
```
|
||
GET https://qt.gtimg.cn/q=sh600690
|
||
Response: v_sh600690="1~海尔智家~600690~20.88~20.75~20.79~..."
|
||
```
|
||
|
||
**美股(Google):**
|
||
```
|
||
GET https://qt.gtimg.cn/q=s_usGOOG
|
||
Response: v_s_usGOOG="200~Alphabet-C~GOOG.OQ~315.72~-0.65~-0.21~..."
|
||
```
|
||
|
||
### 10.3 批量查询示例
|
||
|
||
```
|
||
GET https://qt.gtimg.cn/q=r_hk09868,sh600690,sz159235,s_usGOOG
|
||
```
|