diff --git a/README.md b/README.md index e215bc4..9c3d5cb 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,223 @@ -This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). +# Omniledger 跨界记账中枢 -## Getting Started +
-First, run the development server: +![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.3.3-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) + +**跨境外汇投资组合追踪系统 | Cross-Border Portfolio Tracker** + +资产管理 · 交易记录 · 持仓分析 · 多币种支持 + +[功能介绍](#功能特性) · [技术栈](#技术栈) · [快速开始](#快速开始) · [项目结构](#项目结构) · [数据库设计](#数据库设计) + +
+ +--- + +## 项目介绍 + +Omniledger 是一款专业的**跨境外汇投资组合追踪应用**,帮助用户管理多币种资产组合,支持股票、加密货币和现金的全面持仓管理。系统采用高精度数值计算,确保金融数据计算的准确性。 + +### 核心优势 + +- **多资产类型支持** - 股票、加密货币、现金全覆盖 +- **多币种管理** - 支持不同货币的交易和换算 +- **高精度计算** - 采用 Big.js 确保金融计算精度 +- **实时持仓计算** - 自动聚合交易记录生成持仓报告 + +--- + +## 功能特性 + +### 资产管理 +- 添加/查看资产(支持 STOCK/CRYPTO/CASH 三种类型) +- 统一的资产符号体系,防止重复 +- 资产基础货币设置 + +### 交易记录 +- 支持多种交易类型:买入(BUY)、卖出(SELL)、分红(DIVIDEND)、空投(AIRDROP)、手续费(FEE) +- 高精度数值存储(36位精度,18位小数) +- 交易手续费精确记录 +- 实时汇率支持 + +### 持仓总览 +- 实时持仓计算 +- 自动聚合同资产交易 +- 持仓卡片直观展示 + +### 交易历史 +- 完整的交易流水账 +- 按执行时间排序 +- 交易详情一览无余 + +### 主题切换 +- 浅色/深色模式 +- 系统主题自动检测 +- 流畅的过渡动画 + +--- + +## 技术栈 + +### 前端 + +| 技术 | 版本 | 用途 | +|------|------|------| +| Next.js | 16.2.4 | React 框架 | +| React | 19.2.4 | UI 库 | +| TypeScript | 5.3.3 | 类型安全 | +| Tailwind CSS | 3.4.17 | 样式框架 | +| shadcn/ui | - | UI 组件库 | +| React Hook Form | 7.74.0 | 表单处理 | +| Zod | 4.3.6 | 数据验证 | +| Lucide React | 1.11.0 | 图标库 | + +### 后端 + +| 技术 | 版本 | 用途 | +|------|------|------| +| 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.local.example .env.local +# 编辑 .env.local,配置数据库连接 + +# 数据库初始化 +npm run db:generate # 生成迁移文件 +npm run db:push # 推送 schema 到数据库 + +# 启动开发服务器 npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev ``` -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. +打开 [http://localhost:3000](http://localhost:3000) 即可访问。 -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. +### 可用命令 -This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. +| 命令 | 说明 | +|------|------| +| `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 | -## Learn More +--- -To learn more about Next.js, take a look at the following resources: +## 项目结构 -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. +``` +├── 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 # 持仓计算 +│ ├── components/ +│ │ ├── assets/ # 资产组件 +│ │ ├── transactions/ # 交易组件 +│ │ └── ui/ # UI 基础组件 +│ ├── db/ # 数据库层 +│ │ ├── index.ts # Drizzle 客户端 +│ │ └── schema.ts # 数据库 Schema +│ └── lib/ +│ └── formatters.ts # 格式化工具 +├── drizzle/ # 数据库迁移 +├── public/ # 静态资源 +└── package.json +``` -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! +--- -## Deploy on Vercel +## 数据库设计 -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. +### 表结构 -Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. +#### assets 资产表 +| 字段 | 类型 | 说明 | +|------|------|------| +| id | UUID | 主键 | +| symbol | VARCHAR(20) | 资产符号(唯一) | +| type | ENUM | STOCK/CRYPTO/CASH | +| baseCurrency | VARCHAR(10) | 基础货币 | +| 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 | 创建时间 | + +--- + +## 开发指南 + +### 添加新资产 + +1. 进入「资产」页面 +2. 点击「添加资产」按钮 +3. 填写资产信息(符号、类型、基础货币) +4. 提交保存 + +### 记录交易 + +1. 进入「持仓总览」或「交易历史」页面 +2. 点击「记录交易」按钮 +3. 选择资产和交易类型 +4. 填写交易详情(数量、价格、手续费等) +5. 提交保存 + +### 主题切换 + +点击页面右上角主题切换按钮,可在浅色/深色模式间切换。 + +--- + +## 许可证 + +MIT License diff --git a/src/actions/portfolio.ts b/src/actions/portfolio.ts index 69ccae4..9f1e622 100644 --- a/src/actions/portfolio.ts +++ b/src/actions/portfolio.ts @@ -3,7 +3,7 @@ import { db } from '@/db'; import { transactions, assets } from '@/db/schema'; import Big from 'big.js'; -import { desc } from 'drizzle-orm'; +import { desc, eq } from 'drizzle-orm'; export async function getPortfolioPositions() { const allTransactions = await db @@ -16,7 +16,7 @@ export async function getPortfolioPositions() { assetBaseCurrency: assets.baseCurrency, }) .from(transactions) - .leftJoin(assets, assets.id.eq(transactions.assetId)) + .leftJoin(assets, eq(assets.id, transactions.assetId)) .orderBy(desc(transactions.executedAt)); const holdings = new Map