数据库与迁移(SQLite / schema_version / Repository)

1) 快速定位

  • 初始化入口src/main.rsdb::init_database()
  • DB 单例src/db/mod.rsOnceLock<Database> + get_database()
  • 连接与迁移src/db/connection.rsDatabase::new/run_migrations/ensure_default_plan
  • Repositorysrc/db/asset_repo.rssrc/db/snapshot_repo.rssrc/db/plan_repo.rs

2) DB 文件路径与环境变量

路径规则见:src/db/connection.rsDatabase::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 string
  • scopeINVESTABLE / NON_INVESTABLE
  • categoryCASH/FI/EQ/...
  • sub_asset_class:稳定编码(如 EQ_BROAD
  • vehicle_typeETF/MUTUAL_FUND/...
  • current_value:Decimal string
  • created_at/updated_at:RFC3339

4.2 snapshots / snapshot_items

快照主表 + 明细表:

  • 主表记录盘点时间与总额
  • 明细表冗余存储资产当时的分类/口径/工具类型与价值(保证历史可审计)

4.3 allocation_plans / allocations

方案主表 + 配置项表:

set_active() 会强制保证“仅一个激活方案”。


5) 编码与类型转换(Rust ⇄ SQLite)

本项目采用"稳定字符串协议"的策略:

  • Uuid ⇄ TEXT
  • Decimal ⇄ TEXT(避免浮点误差)
  • DateTime<Utc> ⇄ RFC3339 TEXT
  • NaiveDateYYYY-MM-DD TEXT
  • 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)与备份策略
  • 将破坏性迁移替换为非破坏性迁移