# Omniledger 跨界记账中枢
![Next.js](https://img.shields.io/badge/Next.js-16.2.4-black?style=flat-square&logo=next.js) ![React](https://img.shields.io/badge/React-19.2.4-61DAFB?style=flat-square&logo=react) ![TypeScript](https://img.shields.io/badge/TypeScript-5.7-3178C6?style=flat-square&logo=typescript) ![PostgreSQL](https://img.shields.io/badge/PostgreSQL-16.0-336791?style=flat-square&logo=postgresql) ![Tailwind CSS](https://img.shields.io/badge/Tailwind_CSS-3.4.17-06B6D4?style=flat-square&logo=tailwindcss) ![Drizzle ORM](https://img.shields.io/badge/Drizzle_ORM-0.45.2-f5f5f5?style=flat-square&logo=drizzle) **跨境外汇投资组合追踪系统 | Cross-Border Portfolio Tracker** 资产管理 · 交易记录 · 持仓分析 · 多币种支持 · 实时汇率 [![Docker](https://img.shields.io/badge/Docker-Ready-2496ED?logo=docker&style=flat-square)](#快速部署)
--- ## 项目介绍 Omniledger 是一款专业的**跨境外汇投资组合追踪应用**,帮助用户管理多币种资产组合,支持股票、加密货币和现金的全面持仓管理。系统采用高精度数值计算,确保金融数据计算的准确性。 ### 核心优势 | 特性 | 说明 | |------|------| | **多资产类型** | 股票、加密货币、现金全覆盖 | | **多币种管理** | 支持 USD/HKD/CNY/JPY 等多币种交易和换算 | | **高精度计算** | 采用 Big.js 确保金融计算精度(36位精度,18位小数)| | **实时持仓** | 自动聚合交易记录生成持仓报告 | | **历史走势** | 每日快照记录,净值曲线可视化 | | **主题切换** | 浅色/深色模式,流畅过渡动画 | --- ## 功能特性 ### 仪表盘 - 总资产概览(人民币计价) - 持仓盈亏与总盈亏实时计算 - 净值走势图表(Recharts 实现) - 资产分布饼图 ### 持仓明细 - 支持展开/收起每项资产的流水明细 - 显示:现价、市值、持仓量、摊薄成本、平均成本 - 浮动盈亏与累计盈亏(金额 + 百分比) - 一键导出 CSV ### 交易记录 - 支持交易类型:买入(BUY)、卖出(SELL)、分红(DIVIDEND)、空投(AIRDROP)、手续费(FEE) - 高精度数值存储 - 支持修改和删除交易 ### 资产管理 - 添加/查看资产(STOCK/CRYPTO/CASH 三种类型) - 统一的资产符号体系 - 批量导入历史价格 ### 历史数据 - 自动记录每日组合快照 - 重构历史走势功能 - 支持导入历史价格数据 --- ## 技术栈 ### 前端 | 技术 | 版本 | 用途 | |------|------|------| | Next.js | 16.2.4 | React 框架 | | React | 19.2.4 | UI 库 | | TypeScript | 5.x | 类型安全 | | Tailwind CSS | 3.4.17 | 样式框架 | | Radix UI | - | UI 组件底层 | | React Hook Form | 7.74.0 | 表单处理 | | Zod | 4.3.6 | 数据验证 | | Recharts | 3.8.1 | 图表库 | | Lucide React | 1.11.0 | 图标库 | | Sonner | 2.0.7 | Toast 提示 | ### 后端 | 技术 | 版本 | 用途 | |------|------|------| | PostgreSQL | 16.0 | 数据库 | | Drizzle ORM | 0.45.2 | ORM 框架 | | Big.js | 7.0.1 | 高精度计算 | | Drizzle Kit | 0.31.10 | 数据库迁移 | --- ## 快速开始 ### 环境要求 - Node.js 20+ - PostgreSQL 数据库 - npm / yarn / pnpm / bun ### 安装步骤 ```bash # 克隆项目 git clone cd stock-portfolio_byQwen3.6 # 安装依赖 npm install # 配置环境变量 cp .env.example .env.local # 编辑 .env.local,配置数据库连接 # 数据库初始化 npm run db:generate # 生成迁移文件 npm run db:push # 推送 schema 到数据库 # 启动开发服务器 npm run dev ``` 打开 [http://localhost:3000](http://localhost:3000) 即可访问。 ### 可用命令 | 命令 | 说明 | |------|------| | `npm run dev` | 启动开发服务器 | | `npm run build` | 构建生产版本 | | `npm run start` | 启动生产服务器 | | `npm run lint` | 运行 ESLint 检查 | | `npm run db:generate` | 生成数据库迁移 | | `npm run db:push` | 推送数据库 Schema | | `npm run db:studio` | 打开 Drizzle Studio | --- ## 快速部署 ### Docker Compose ```bash # 构建并启动 docker-compose up -d # 查看日志 docker-compose logs -f ``` 访问 [http://localhost:8080](http://localhost:8080) ### 环境变量 ```env DATABASE_URL=postgresql://user:password@localhost:5432/omniledger ``` --- ## 项目结构 ``` ├── app/ # Next.js App Router │ ├── dashboard/ # 仪表盘页面 │ │ ├── page.tsx # 持仓总览 │ │ ├── assets/page.tsx # 资产管理 │ │ ├── transactions/page.tsx # 交易历史 │ │ └── layout.tsx # 仪表盘布局 │ ├── layout.tsx # 根布局 │ ├── page.tsx # 根页面(重定向) │ └── globals.css # 全局样式 ├── src/ │ ├── actions/ # Server Actions │ │ ├── asset.ts # 资产操作 │ │ ├── transaction.ts # 交易操作 │ │ ├── portfolio.ts # 持仓计算 │ │ ├── snapshots.ts # 组合快照 │ │ ├── exchange.ts # 汇率 │ │ └── market.ts # 市场数据 │ ├── components/ │ │ ├── dashboard/ # 仪表盘组件 │ │ │ ├── allocation-chart.tsx │ │ │ └── net-worth-chart.tsx │ │ ├── assets/ # 资产相关组件 │ │ ├── transactions/ # 交易相关组件 │ │ └── ui/ # shadcn/ui 组件 │ ├── db/ │ │ ├── index.ts # Drizzle 客户端 │ │ └── schema.ts # 数据库 Schema │ └── lib/ │ ├── formatters.ts # 格式化工具 │ └── utils.ts # 通用工具 ├── drizzle/ # 数据库迁移文件 ├── scripts/ # 辅助脚本 ├── public/ # 静态资源 └── package.json ``` --- ## 数据库设计 ### 表结构 #### users 用户表 | 字段 | 类型 | 说明 | |------|------|------| | id | UUID | 主键 | | username | VARCHAR(50) | 用户名(唯一) | | password_hash | VARCHAR(255) | 密码哈希 | | created_at | TIMESTAMP | 创建时间 | #### assets 资产表 | 字段 | 类型 | 说明 | |------|------|------| | id | UUID | 主键 | | symbol | VARCHAR(20) | 资产符号(唯一) | | name | VARCHAR(100) | 资产名称 | | type | ENUM | STOCK/CRYPTO/CASH | | exchange | VARCHAR(10) | 交易所(默认 US)| | baseCurrency | VARCHAR(10) | 基础货币 | | latestPrice | NUMERIC(36,18) | 最新价格 | | created_at | TIMESTAMP | 创建时间 | #### transactions 交易表 | 字段 | 类型 | 说明 | |------|------|------| | id | UUID | 主键 | | assetId | UUID | 关联资产 | | txType | ENUM | BUY/SELL/DIVIDEND/AIRDROP/FEE | | quantity | NUMERIC(36,18) | 数量 | | price | NUMERIC(36,18) | 价格 | | fee | NUMERIC(36,18) | 手续费 | | txCurrency | VARCHAR(10) | 交易货币 | | exchangeRate | NUMERIC(20,8) | 汇率 | | executedAt | TIMESTAMP | 执行时间 | | createdAt | TIMESTAMP | 创建时间 | #### exchange_rates 汇率表 | 字段 | 类型 | 说明 | |------|------|------| | id | UUID | 主键 | | fromCurrency | VARCHAR(10) | 源货币 | | toCurrency | VARCHAR(10) | 目标货币 | | rate | NUMERIC(20,8) | 汇率 | | updatedAt | TIMESTAMP | 更新时间 | #### portfolio_snapshots 组合快照表 | 字段 | 类型 | 说明 | |------|------|------| | id | UUID | 主键 | | date | DATE | 日期(唯一)| | totalValueCny | NUMERIC(36,18) | 总值(CNY)| | totalCostCny | NUMERIC(36,18) | 总成本(CNY)| | createdAt | TIMESTAMP | 创建时间 | | updatedAt | TIMESTAMP | 更新时间 | #### asset_prices_history 资产价格历史 | 字段 | 类型 | 说明 | |------|------|------| | id | UUID | 主键 | | assetId | UUID | 关联资产 | | price | NUMERIC(36,18) | 价格 | | date | DATE | 日期 | | updateTime | TIMESTAMP | 更新时间 | #### exchange_rates_history 汇率历史 | 字段 | 类型 | 说明 | |------|------|------| | id | UUID | 主键 | | fromCurrency | VARCHAR(10) | 源货币 | | toCurrency | VARCHAR(10) | 目标货币 | | rate | NUMERIC(20,8) | 汇率 | | fetchTime | TIMESTAMP | 抓取时间 | --- ## 开发指南 ### 添加新资产 1. 进入「资产管理」页面 2. 点击「添加资产」按钮 3. 填写资产信息(符号、名称、类型、基础货币) 4. 提交保存 ### 记录交易 1. 在「持仓总览」中点击资产的「添加」按钮 2. 选择交易类型(买入/卖出/分红/空投/手续费) 3. 填写交易详情(数量、价格、手续费、日期等) 4. 提交保存 ### 导入历史价格 1. 在「持仓总览」中点击资产的「导入价格」按钮 2. 从 Excel 复制粘贴数据,格式:`日期, 价格`(每行一条) 3. 点击开始导入 ### 主题切换 点击页面右上角主题切换按钮,可在浅色/深色模式间切换。 --- ## 许可证 MIT License