# 思投录数据库设计文档 ## 一、数据库选型分析 ### 1.1 业务特点分析 根据业务需求,系统需要处理以下类型的数据: 1. **结构化数据** - 用户账户、持仓、交易记录 - 交易计划、计划步骤 - 资产净值、收益率等数值数据 2. **时间序列数据** - 每日单位净值记录 - 资产快照(每日) - 价格历史数据 3. **文本内容数据** - 交易思考(交易记录时填写) - 复盘内容(定期复盘时填写) - 计划思考(创建计划时填写) 4. **关联关系** - 交易记录与持仓的关联 - 交易计划与交易记录的关联 - 复盘与交易记录的关联 - 计划步骤与计划的关联 5. **查询需求** - 复杂的时间范围查询 - 多表关联查询 - 聚合统计查询 - 时间线查询(按时间排序) ### 1.2 数据库选型推荐 #### 推荐方案:PostgreSQL **推荐理由:** 1. **关系型数据库优势** - ✅ 数据结构清晰,关系明确 - ✅ 支持复杂查询和统计(JOIN、聚合函数) - ✅ 支持事务,保证数据一致性 - ✅ ACID特性,数据可靠性高 2. **PostgreSQL特有优势** - ✅ 支持JSON/JSONB类型,可灵活存储思考内容等文本 - ✅ 支持数组类型,适合存储计划步骤等 - ✅ 强大的时间序列查询能力 - ✅ 支持全文搜索(可用于搜索思考内容) - ✅ 性能优秀,适合中小型应用 - ✅ 开源免费,社区活跃 3. **其他考虑** - 如果团队更熟悉MySQL,MySQL 8.0+ 也是不错的选择 - 对于时间序列数据,可以考虑用TimescaleDB(基于PostgreSQL的扩展) #### 备选方案:MySQL 8.0+ **适用场景:** - 团队更熟悉MySQL - 需要更好的云服务支持(如阿里云RDS) - 数据量不是特别大 **MySQL优势:** - ✅ 生态成熟,工具丰富 - ✅ 云服务支持好 - ✅ 性能稳定 - ⚠️ JSON支持不如PostgreSQL强大 - ⚠️ 复杂查询性能略逊于PostgreSQL #### 不推荐方案 - **MongoDB等NoSQL**:虽然可以存储JSON,但关联查询复杂,不适合这种关系明确的数据结构 - **Redis**:只适合缓存,不适合持久化存储 - **SQLite**:适合单机应用,不支持多用户并发 ### 1.3 最终推荐 **推荐使用 PostgreSQL 14+** 理由: 1. 数据结构关系明确,适合关系型数据库 2. JSONB类型可以灵活存储思考内容 3. 强大的查询能力,适合复杂统计 4. 时间序列查询性能好 5. 支持全文搜索,方便搜索思考内容 --- ## 二、数据库表设计 ### 2.1 表结构总览 ``` 用户相关 ├── users (用户表) └── user_settings (用户设置表) 账户相关 ├── accounts (账户表) ├── positions (持仓表) ├── cash_accounts (现金账户表) └── position_warnings (持仓预警表) 交易相关 ├── transactions (交易记录表) ├── transaction_thoughts (交易思考表) └── transaction_reviews (交易复盘表) 计划相关 ├── trading_plans (交易计划表) ├── plan_steps (计划步骤表) └── plan_thoughts (计划思考表) 净值相关 ├── net_value_snapshots (净值快照表) └── daily_net_values (每日净值表) 市场数据 └── market_prices (市场价格表) ``` ### 2.2 详细表结构设计 #### 2.2.1 用户相关表 ##### users (用户表) ```sql CREATE TABLE users ( id BIGSERIAL PRIMARY KEY, username VARCHAR(50) UNIQUE NOT NULL, email VARCHAR(100) UNIQUE, phone VARCHAR(20) UNIQUE, password_hash VARCHAR(255) NOT NULL, nickname VARCHAR(50), avatar_url VARCHAR(255), created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, last_login_at TIMESTAMP, status VARCHAR(20) DEFAULT 'active' -- active, inactive, deleted ); CREATE INDEX idx_users_email ON users(email); CREATE INDEX idx_users_phone ON users(phone); CREATE INDEX idx_users_status ON users(status); ``` ##### user_settings (用户设置表) ```sql CREATE TABLE user_settings ( id BIGSERIAL PRIMARY KEY, user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, setting_key VARCHAR(50) NOT NULL, setting_value TEXT, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE(user_id, setting_key) ); CREATE INDEX idx_user_settings_user_id ON user_settings(user_id); ``` **常用设置项:** - `position_limit_per_stock`: 单只股票仓位上限(百分比) - `currency`: 默认货币 - `theme`: 主题设置 - `notification_enabled`: 是否启用通知 #### 2.2.2 账户相关表 ##### accounts (账户表) ```sql CREATE TABLE accounts ( id BIGSERIAL PRIMARY KEY, user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, name VARCHAR(100) NOT NULL, type VARCHAR(20) NOT NULL, -- stock, fund, cash, mixed currency VARCHAR(10) NOT NULL DEFAULT 'CNY', -- CNY, USD, HKD description TEXT, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, status VARCHAR(20) DEFAULT 'active' -- active, archived, deleted ); CREATE INDEX idx_accounts_user_id ON accounts(user_id); CREATE INDEX idx_accounts_status ON accounts(status); ``` ##### positions (持仓表) ```sql CREATE TABLE positions ( id BIGSERIAL PRIMARY KEY, account_id BIGINT NOT NULL REFERENCES accounts(id) ON DELETE CASCADE, symbol VARCHAR(20) NOT NULL, -- 股票/基金代码 name VARCHAR(100) NOT NULL, -- 股票/基金名称 market VARCHAR(20) NOT NULL, -- A股, 港股, 美股 shares DECIMAL(18, 4) NOT NULL DEFAULT 0, -- 持仓份额 cost_price DECIMAL(18, 4) NOT NULL, -- 成本价(每股/每份) current_price DECIMAL(18, 4), -- 当前价格 currency VARCHAR(10) NOT NULL DEFAULT 'CNY', status VARCHAR(20) DEFAULT 'active', -- active, suspended, delisted created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE(account_id, symbol, market) -- 同一账户同一股票只能有一条持仓 ); CREATE INDEX idx_positions_account_id ON positions(account_id); CREATE INDEX idx_positions_symbol ON positions(symbol); CREATE INDEX idx_positions_status ON positions(status); CREATE INDEX idx_positions_updated_at ON positions(updated_at); ``` ##### cash_accounts (现金账户表) ```sql CREATE TABLE cash_accounts ( id BIGSERIAL PRIMARY KEY, account_id BIGINT NOT NULL REFERENCES accounts(id) ON DELETE CASCADE, balance DECIMAL(18, 2) NOT NULL DEFAULT 0, -- 余额 currency VARCHAR(10) NOT NULL DEFAULT 'CNY', interest_rate DECIMAL(5, 4) DEFAULT 0, -- 年化利率 updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE(account_id, currency) -- 同一账户同一货币只能有一条记录 ); CREATE INDEX idx_cash_accounts_account_id ON cash_accounts(account_id); ``` ##### position_warnings (持仓预警表) ```sql CREATE TABLE position_warnings ( id BIGSERIAL PRIMARY KEY, user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, position_id BIGINT NOT NULL REFERENCES positions(id) ON DELETE CASCADE, warning_type VARCHAR(20) NOT NULL, -- position_limit, price_alert threshold_value DECIMAL(18, 4), -- 阈值 is_triggered BOOLEAN DEFAULT FALSE, triggered_at TIMESTAMP, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX idx_position_warnings_user_id ON position_warnings(user_id); CREATE INDEX idx_position_warnings_position_id ON position_warnings(position_id); ``` #### 2.2.3 交易相关表 ##### transactions (交易记录表) ```sql CREATE TABLE transactions ( id BIGSERIAL PRIMARY KEY, user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, account_id BIGINT NOT NULL REFERENCES accounts(id) ON DELETE CASCADE, position_id BIGINT REFERENCES positions(id) ON DELETE SET NULL, -- 可为空,因为可能是现金交易 trading_plan_id BIGINT REFERENCES trading_plans(id) ON DELETE SET NULL, -- 关联交易计划 type VARCHAR(20) NOT NULL, -- buy, sell, dividend, split, bonus, rights, deposit, withdraw date DATE NOT NULL, -- 交易日期 symbol VARCHAR(20), -- 股票/基金代码 name VARCHAR(100), -- 股票/基金名称 market VARCHAR(20), -- 市场 shares DECIMAL(18, 4), -- 交易份额 price DECIMAL(18, 4), -- 交易价格 amount DECIMAL(18, 2) NOT NULL, -- 交易金额(正数表示收入,负数表示支出) fee DECIMAL(18, 2) DEFAULT 0, -- 交易费用 currency VARCHAR(10) NOT NULL DEFAULT 'CNY', exchange_rate DECIMAL(10, 6) DEFAULT 1, -- 汇率(用于多货币) metadata JSONB, -- 存储额外信息,如拆股比例、分红金额等 created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX idx_transactions_user_id ON transactions(user_id); CREATE INDEX idx_transactions_account_id ON transactions(account_id); CREATE INDEX idx_transactions_position_id ON transactions(position_id); CREATE INDEX idx_transactions_trading_plan_id ON transactions(trading_plan_id); CREATE INDEX idx_transactions_date ON transactions(date); CREATE INDEX idx_transactions_type ON transactions(type); CREATE INDEX idx_transactions_symbol ON transactions(symbol); CREATE INDEX idx_transactions_date_desc ON transactions(date DESC); -- 用于时间线查询 ``` **metadata字段示例:** ```json { "split_ratio": 2, // 拆股比例 "dividend_per_share": 0.5, // 每股分红 "bonus_ratio": 0.5, // 送股比例 "rights_price": 10.5, // 配股价格 "rights_shares": 100 // 配股数量 } ``` ##### transaction_thoughts (交易思考表) ```sql CREATE TABLE transaction_thoughts ( id BIGSERIAL PRIMARY KEY, transaction_id BIGINT NOT NULL REFERENCES transactions(id) ON DELETE CASCADE, content TEXT NOT NULL, -- 思考内容 created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE(transaction_id) -- 每笔交易只能有一条思考 ); CREATE INDEX idx_transaction_thoughts_transaction_id ON transaction_thoughts(transaction_id); CREATE INDEX idx_transaction_thoughts_content ON transaction_thoughts USING gin(to_tsvector('jiebacfg', content)); -- 全文搜索索引(需要安装pg_trgm扩展) ``` ##### transaction_reviews (交易复盘表) ```sql CREATE TABLE transaction_reviews ( id BIGSERIAL PRIMARY KEY, transaction_id BIGINT NOT NULL REFERENCES transactions(id) ON DELETE CASCADE, review_type VARCHAR(20) DEFAULT 'manual', -- manual, scheduled content TEXT NOT NULL, -- 复盘内容 review_date DATE NOT NULL, -- 复盘日期 created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX idx_transaction_reviews_transaction_id ON transaction_reviews(transaction_id); CREATE INDEX idx_transaction_reviews_review_date ON transaction_reviews(review_date); CREATE INDEX idx_transaction_reviews_content ON transaction_reviews USING gin(to_tsvector('jiebacfg', content)); -- 全文搜索索引 ``` **说明:** - 一笔交易可以有多次复盘(不同时间点) - `review_type`: manual(手动复盘)、scheduled(定期提醒复盘) #### 2.2.4 计划相关表 ##### trading_plans (交易计划表) ```sql CREATE TABLE trading_plans ( id BIGSERIAL PRIMARY KEY, user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, symbol VARCHAR(20) NOT NULL, -- 股票代码 name VARCHAR(100) NOT NULL, -- 股票名称 market VARCHAR(20) NOT NULL, -- 市场 target_price DECIMAL(18, 4) NOT NULL, -- 目标价格 target_amount DECIMAL(18, 2), -- 目标金额 target_shares DECIMAL(18, 4), -- 目标份额(金额和份额二选一) deadline DATE, -- 截止日期 status VARCHAR(20) DEFAULT 'pending', -- pending, in_progress, completed, cancelled progress DECIMAL(5, 2) DEFAULT 0, -- 完成进度(百分比) current_price DECIMAL(18, 4), -- 当前价格(用于提醒) is_price_alert_enabled BOOLEAN DEFAULT TRUE, -- 是否启用价格提醒 created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, completed_at TIMESTAMP ); CREATE INDEX idx_trading_plans_user_id ON trading_plans(user_id); CREATE INDEX idx_trading_plans_status ON trading_plans(status); CREATE INDEX idx_trading_plans_deadline ON trading_plans(deadline); CREATE INDEX idx_trading_plans_symbol ON trading_plans(symbol); ``` ##### plan_steps (计划步骤表) ```sql CREATE TABLE plan_steps ( id BIGSERIAL PRIMARY KEY, trading_plan_id BIGINT NOT NULL REFERENCES trading_plans(id) ON DELETE CASCADE, step_order INTEGER NOT NULL, -- 步骤顺序(1, 2, 3...) target_price DECIMAL(18, 4) NOT NULL, -- 该步骤的目标价格 target_amount DECIMAL(18, 2), -- 该步骤的目标金额 target_shares DECIMAL(18, 4), -- 该步骤的目标份额 status VARCHAR(20) DEFAULT 'pending', -- pending, completed completed_at TIMESTAMP, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE(trading_plan_id, step_order) ); CREATE INDEX idx_plan_steps_trading_plan_id ON plan_steps(trading_plan_id); CREATE INDEX idx_plan_steps_status ON plan_steps(status); ``` ##### plan_thoughts (计划思考表) ```sql CREATE TABLE plan_thoughts ( id BIGSERIAL PRIMARY KEY, trading_plan_id BIGINT NOT NULL REFERENCES trading_plans(id) ON DELETE CASCADE, content TEXT NOT NULL, -- 思考内容 created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE(trading_plan_id) -- 每个计划只能有一条思考 ); CREATE INDEX idx_plan_thoughts_trading_plan_id ON plan_thoughts(trading_plan_id); CREATE INDEX idx_plan_thoughts_content ON plan_thoughts USING gin(to_tsvector('jiebacfg', content)); -- 全文搜索索引 ``` #### 2.2.5 净值相关表 ##### net_value_snapshots (净值快照表) ```sql CREATE TABLE net_value_snapshots ( id BIGSERIAL PRIMARY KEY, user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, account_id BIGINT REFERENCES accounts(id) ON DELETE CASCADE, -- 可为空,表示总账户 snapshot_date DATE NOT NULL, -- 快照日期 total_asset DECIMAL(18, 2) NOT NULL, -- 总资产 total_cost DECIMAL(18, 2) NOT NULL, -- 总成本(累计投入) total_profit DECIMAL(18, 2) NOT NULL, -- 总收益 net_value DECIMAL(18, 6) NOT NULL, -- 单位净值 cumulative_return DECIMAL(10, 6) NOT NULL, -- 累计收益率 annualized_return DECIMAL(10, 6), -- 年化收益率 year_to_date_return DECIMAL(10, 6), -- 当年收益率 positions_data JSONB, -- 持仓明细(JSON格式,便于查询) cash_data JSONB, -- 现金明细 created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE(user_id, account_id, snapshot_date) -- 同一用户同一账户同一日期只能有一条快照 ); CREATE INDEX idx_net_value_snapshots_user_id ON net_value_snapshots(user_id); CREATE INDEX idx_net_value_snapshots_account_id ON net_value_snapshots(account_id); CREATE INDEX idx_net_value_snapshots_date ON net_value_snapshots(snapshot_date); CREATE INDEX idx_net_value_snapshots_user_date ON net_value_snapshots(user_id, snapshot_date DESC); ``` **positions_data字段示例:** ```json [ { "position_id": 1, "symbol": "600519", "name": "贵州茅台", "shares": 100, "cost_price": 1600, "current_price": 1850, "market_value": 185000, "profit": 25000 } ] ``` ##### daily_net_values (每日净值表) ```sql CREATE TABLE daily_net_values ( id BIGSERIAL PRIMARY KEY, user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, account_id BIGINT REFERENCES accounts(id) ON DELETE CASCADE, -- 可为空,表示总账户 value_date DATE NOT NULL, -- 净值日期 net_value DECIMAL(18, 6) NOT NULL, -- 单位净值 total_asset DECIMAL(18, 2) NOT NULL, -- 总资产 total_cost DECIMAL(18, 2) NOT NULL, -- 总成本 daily_return DECIMAL(10, 6), -- 日收益率 created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE(user_id, account_id, value_date) ); CREATE INDEX idx_daily_net_values_user_id ON daily_net_values(user_id); CREATE INDEX idx_daily_net_values_account_id ON daily_net_values(account_id); CREATE INDEX idx_daily_net_values_date ON daily_net_values(value_date); CREATE INDEX idx_daily_net_values_user_date ON daily_net_values(user_id, account_id, value_date DESC); -- 用于净值曲线查询 ``` #### 2.2.6 市场数据表 ##### market_prices (市场价格表) ```sql CREATE TABLE market_prices ( id BIGSERIAL PRIMARY KEY, symbol VARCHAR(20) NOT NULL, market VARCHAR(20) NOT NULL, price_date DATE NOT NULL, open_price DECIMAL(18, 4), -- 开盘价 close_price DECIMAL(18, 4) NOT NULL, -- 收盘价 high_price DECIMAL(18, 4), -- 最高价 low_price DECIMAL(18, 4), -- 最低价 volume BIGINT, -- 成交量 amount DECIMAL(18, 2), -- 成交额 currency VARCHAR(10) DEFAULT 'CNY', created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE(symbol, market, price_date) ); CREATE INDEX idx_market_prices_symbol ON market_prices(symbol, market); CREATE INDEX idx_market_prices_date ON market_prices(price_date); CREATE INDEX idx_market_prices_symbol_date ON market_prices(symbol, market, price_date DESC); ``` --- ## 三、关键业务逻辑实现 ### 3.1 基金净值法计算 #### 3.1.1 每日净值计算流程 ```sql -- 1. 计算当日总资产 -- 总资产 = 所有持仓市值 + 现金余额 -- 2. 计算累计投入资金 -- 累计投入 = 所有买入交易的金额总和 - 所有卖出交易的金额总和 + 初始资金 -- 3. 计算单位净值 -- 单位净值 = 总资产 / 累计投入资金 -- 4. 计算收益率 -- 累计收益率 = (当前净值 - 初始净值) / 初始净值 -- 日收益率 = (今日净值 - 昨日净值) / 昨日净值 ``` #### 3.1.2 净值计算SQL示例 ```sql -- 计算用户某账户的当日净值 WITH asset_value AS ( -- 计算持仓市值 SELECT COALESCE(SUM(p.shares * p.current_price), 0) as position_value FROM positions p WHERE p.account_id = :account_id AND p.status = 'active' ), cash_value AS ( -- 计算现金余额 SELECT COALESCE(SUM(c.balance), 0) as cash_balance FROM cash_accounts c WHERE c.account_id = :account_id ), total_cost AS ( -- 计算累计投入 SELECT COALESCE(SUM( CASE WHEN t.type IN ('buy', 'deposit') THEN t.amount WHEN t.type IN ('sell', 'withdraw') THEN -t.amount ELSE 0 END ), 0) as total_invested FROM transactions t WHERE t.account_id = :account_id ) SELECT (av.position_value + cv.cash_balance) as total_asset, tc.total_invested, CASE WHEN tc.total_invested > 0 THEN (av.position_value + cv.cash_balance) / tc.total_invested ELSE 1.0 END as net_value FROM asset_value av, cash_value cv, total_cost tc; ``` ### 3.2 交易记录与思考关联查询 ```sql -- 查询交易记录及其思考(时间线) SELECT t.id, t.date, t.type, t.symbol, t.name, t.shares, t.price, t.amount, tt.content as thought_content, tt.created_at as thought_created_at FROM transactions t LEFT JOIN transaction_thoughts tt ON t.id = tt.transaction_id WHERE t.user_id = :user_id ORDER BY t.date DESC, t.created_at DESC LIMIT :limit OFFSET :offset; ``` ### 3.3 交易复盘查询 ```sql -- 查询交易及其所有复盘记录 SELECT t.id, t.date, t.symbol, t.name, t.type, tr.id as review_id, tr.review_date, tr.content as review_content, tr.created_at as review_created_at FROM transactions t LEFT JOIN transaction_reviews tr ON t.id = tr.transaction_id WHERE t.user_id = :user_id AND t.id = :transaction_id ORDER BY tr.review_date DESC; ``` ### 3.4 交易计划进度更新 ```sql -- 更新计划进度 UPDATE trading_plans tp SET progress = ( SELECT CASE WHEN tp.target_amount > 0 THEN LEAST(100, (COALESCE(SUM(CASE WHEN t.type = 'buy' THEN t.amount ELSE 0 END), 0) / tp.target_amount) * 100) WHEN tp.target_shares > 0 THEN LEAST(100, (COALESCE(SUM(CASE WHEN t.type = 'buy' THEN t.shares ELSE 0 END), 0) / tp.target_shares) * 100) ELSE 0 END FROM transactions t WHERE t.trading_plan_id = tp.id ), status = CASE WHEN progress >= 100 THEN 'completed' WHEN progress > 0 THEN 'in_progress' ELSE 'pending' END, updated_at = CURRENT_TIMESTAMP WHERE tp.id = :plan_id; ``` --- ## 四、索引优化建议 ### 4.1 必须创建的索引 1. **外键索引**:所有外键字段都应创建索引 2. **查询字段索引**:经常用于WHERE、JOIN、ORDER BY的字段 3. **时间字段索引**:date、created_at等时间字段 ### 4.2 全文搜索索引 对于思考内容、复盘内容等文本字段,使用PostgreSQL的全文搜索功能: ```sql -- 安装扩展(需要管理员权限) CREATE EXTENSION IF NOT EXISTS pg_trgm; CREATE EXTENSION IF NOT EXISTS jieba; -- 中文分词(如果可用) -- 创建全文搜索索引 CREATE INDEX idx_transaction_thoughts_content_search ON transaction_thoughts USING gin(to_tsvector('jiebacfg', content)); ``` ### 4.3 复合索引 根据查询模式创建复合索引: ```sql -- 用户时间线查询(最常用) CREATE INDEX idx_transactions_user_date ON transactions(user_id, date DESC, created_at DESC); -- 净值查询 CREATE INDEX idx_daily_net_values_user_account_date ON daily_net_values(user_id, account_id, value_date DESC); -- 持仓查询 CREATE INDEX idx_positions_account_status ON positions(account_id, status); ``` --- ## 五、数据维护建议 ### 5.1 定期任务 1. **每日净值计算** - 每日收盘后(如18:00)自动计算并保存净值 - 更新持仓的当前价格 2. **价格数据更新** - 每日更新市场价格表 - 清理过期的价格数据(保留最近2年) 3. **计划提醒** - 检查到达目标价格的计划 - 发送提醒通知 4. **复盘提醒** - 定期(如每月)提醒用户进行复盘 ### 5.2 数据清理 ```sql -- 清理过期的市场价格数据(保留最近2年) DELETE FROM market_prices WHERE price_date < CURRENT_DATE - INTERVAL '2 years'; -- 归档已删除的数据(软删除的数据) -- 可以考虑定期归档到历史表 ``` ### 5.3 数据备份 1. **定期全量备份**:每日备份 2. **增量备份**:每小时备份事务日志 3. **重要数据备份**:交易记录、思考内容等关键数据单独备份 --- ## 六、性能优化建议 ### 6.1 查询优化 1. **分页查询**:所有列表查询都应使用LIMIT和OFFSET 2. **避免N+1查询**:使用JOIN一次性获取关联数据 3. **使用EXPLAIN分析**:定期分析慢查询 ### 6.2 缓存策略 1. **净值数据缓存**:最近30天的净值数据可以缓存 2. **持仓数据缓存**:当前持仓数据可以缓存,更新时失效 3. **市场价格缓存**:使用Redis缓存常用股票的最新价格 ### 6.3 读写分离 如果数据量大,可以考虑: - 主库:写操作 - 从库:读操作(查询、统计) --- ## 七、数据迁移方案 ### 7.1 初始化数据 ```sql -- 创建初始用户 INSERT INTO users (username, password_hash, nickname) VALUES ('admin', 'hashed_password', '管理员'); -- 创建初始账户 INSERT INTO accounts (user_id, name, type, currency) VALUES (1, '主账户', 'mixed', 'CNY'); -- 设置初始净值 INSERT INTO daily_net_values (user_id, account_id, value_date, net_value, total_asset, total_cost) VALUES (1, 1, CURRENT_DATE, 1.0, 0, 0); ``` ### 7.2 数据导入 如果从其他系统迁移数据,需要: 1. 清洗数据格式 2. 按依赖关系顺序导入(先用户,再账户,再交易) 3. 重新计算净值历史 --- ## 八、安全考虑 ### 8.1 数据安全 1. **敏感数据加密**:密码使用bcrypt等加密 2. **SQL注入防护**:使用参数化查询 3. **权限控制**:用户只能访问自己的数据 ### 8.2 数据完整性 1. **外键约束**:确保数据关联正确 2. **唯一约束**:防止重复数据 3. **检查约束**:确保数据有效性 ```sql -- 示例:检查约束 ALTER TABLE transactions ADD CONSTRAINT check_transaction_amount CHECK (amount != 0); ALTER TABLE positions ADD CONSTRAINT check_shares_positive CHECK (shares >= 0); ``` --- ## 九、总结 ### 9.1 数据库选型 **推荐使用 PostgreSQL 14+** 优势: - ✅ 关系型数据库,结构清晰 - ✅ JSONB支持,灵活存储思考内容 - ✅ 强大的查询能力 - ✅ 全文搜索支持 - ✅ 时间序列查询性能好 ### 9.2 核心表设计 1. **用户体系**:users, user_settings 2. **账户体系**:accounts, positions, cash_accounts 3. **交易体系**:transactions, transaction_thoughts, transaction_reviews 4. **计划体系**:trading_plans, plan_steps, plan_thoughts 5. **净值体系**:daily_net_values, net_value_snapshots 6. **市场数据**:market_prices ### 9.3 关键特性 1. **基金净值法**:通过daily_net_values表记录每日净值 2. **思考记录**:transaction_thoughts和plan_thoughts表 3. **复盘功能**:transaction_reviews表支持多次复盘 4. **时间线查询**:通过索引优化时间线查询性能 ### 9.4 后续优化 1. 根据实际使用情况调整索引 2. 监控慢查询,持续优化 3. 考虑使用TimescaleDB处理时间序列数据 4. 根据数据量考虑分表策略 --- **文档版本**:v1.0 **创建日期**:2024年 **数据库版本**:PostgreSQL 14+