数据库与迁移(SQLite / schema_version / Repository)
1) 快速定位
- 初始化入口:
src/main.rs→db::init_database() - DB 单例:
src/db/mod.rs(OnceLock<Database>+get_database()) - 连接与迁移:
src/db/connection.rs(Database::new/run_migrations/ensure_default_plan) - Repository:
src/db/asset_repo.rs、src/db/snapshot_repo.rs、src/db/plan_repo.rs
2) DB 文件路径与环境变量
路径规则见:src/db/connection.rs → Database::db_path()
- 默认:
dirs::data_local_dir()下的asset-light/data.db - 可覆盖:环境变量
ASSET_LIGHT_DB_PATH=/path/to/data.db
建议:
- 调试/测试优先用
ASSET_LIGHT_DB_PATH指向临时 DB,避免污染真实数据。
3) 迁移策略(重要:当前包含破坏性升级)
迁移入口:Database::run_migrations()(src/db/connection.rs)
实现要点:
schema_meta表维护schema_version- 当
schema_version < 2时,会执行DROP TABLE ...重建表结构(会清空历史数据)
适用场景:
- 学习期/快速迭代期:允许破坏性升级以快速调整数据模型
不适用场景:
- 长期使用:需要演进为“非破坏性迁移”(新增列/表、回填数据、保留历史记录)
4) 表结构概览(以 connection.rs 为准)
4.1 assets
资产条目。字段类型以 TEXT 为主:
id:UUID stringscope:INVESTABLE/NON_INVESTABLEcategory:CASH/FI/EQ/...sub_asset_class:稳定编码(如EQ_BROAD)vehicle_type:ETF/MUTUAL_FUND/...current_value:Decimal stringcreated_at/updated_at:RFC3339
4.2 snapshots / snapshot_items
快照主表 + 明细表:
- 主表记录盘点时间与总额
- 明细表冗余存储资产当时的分类/口径/工具类型与价值(保证历史可审计)
4.3 allocation_plans / allocations
方案主表 + 配置项表:
set_active()会强制保证“仅一个激活方案”。
5) 编码与类型转换(Rust ⇄ SQLite)
本项目采用"稳定字符串协议"的策略:
Uuid⇄ TEXTDecimal⇄ TEXT(避免浮点误差)DateTime<Utc>⇄ RFC3339 TEXTNaiveDate⇄YYYY-MM-DDTEXT- enum ⇄ 稳定编码 TEXT(
as_str/from_str)
转换逻辑集中在 Repository 的 row_to_xxx 与模型的 as_str/from_str。
已完成改进:Repository 层现已使用 AppResult<T>(定义在 src/error.rs),DB 错误自动转换为 AppError::Database。
待改进:row_to_xxx 内部仍使用 unwrap_or_default() 静默吞解析失败,建议升级为 AppError::Parse 传播(详见 docs/rust-guide/06-error-handling.md)。
6) 原子性与事务(建议改进点)
SnapshotRepository::create() 会写入 1 条主表 + N 条明细。
建议:
- 使用 rusqlite transaction,保证要么全部成功,要么全部失败
7) 数据重置/备份建议
学习期最简单的“重置数据”方式:
- 换一个 DB 路径(
ASSET_LIGHT_DB_PATH指向新文件)
长期使用期建议:
- 增加导出(JSON/CSV)与备份策略
- 将破坏性迁移替换为非破坏性迁移