barnesy/strictly-spending
GitHub: barnesy/strictly-spending
一款本地优先的家庭支出看板,支持多银行 CSV 导入、自动分类、周期性扣款检测和支出预测,所有数据完全离线存储。
Stars: 2 | Forks: 0
# Strictly Spending
一款本地优先的家庭支出看板。支持多银行 CSV 导入、基于规则的自动分类、周期性扣款检测、各类别预算以及快速分拣的 **Sort** 视图,全部在浏览器中完成。**无需服务器。无需云端。无需账户凭证。**

## 功能介绍
- **导入 CSV 导出文件**:支持 Chase、Bank of America(信用卡 + 借记卡)和 Truist。只需拖入文件,或指定一个文件夹,应用即可通过 File System Access API 自动检测新文件。借助 SHA-256 内容哈希,即使账单以不同名称被重新下载,也能实现去重。
- **自动分类**:通过按优先级排序的规则引擎实现自动分类,内置了约 150+ 种常见商户模式(NETFLIX、STARBUCKS、SHELL 等)。当没有规则匹配时,源 CSV 中的类别会通过每个银行专属的查找表进行标准化处理。
- **周期性扣款展示**:通过检测商户历史记录中的一致时间间隔(每月、每两周、每周、每年)来识别并展示周期性扣款。采用最近 3 次扣款的平均值,确保订阅档位的变化能在一个计费周期内同步更新。
- **下月支出预测**:将周期性支出(固定支出)与各类别预算(基于历史移动平均值)分开进行预测。你可以切换具体的周期性扣款或预算条目,从而模拟削减支出的效果。
- **Sort 视图**:将导入后令人头疼的“未分类”数据整理转变为卡片堆叠式的交互体验。只需对每个商户做出一次决定,即可对该商户过去的*所有*及*未来*交易进行批量分类。按 `Enter` 接受建议的类别,按 `1`–`9` 从网格中选择,按 `⌘Z` 撤销操作。
- **JSON 备份与恢复**:方便在不同浏览器间迁移数据,或在执行*清除站点数据*后恢复应用。采用单文件、纯 JSON 格式,无加密(请自行妥善存放)。
- **演示模式**:这是一个“设置”开关,可以隐藏真实数据,并在全应用范围内仅显示 `Demo:` 记录,非常适合用于截屏、演示或屏幕共享会议。
## 隐私与数据
Strictly Spending 是一款本地优先的桌面应用程序。所有数据都存储在系统标准应用数据目录下的本地 SQLite 数据库(`spending-viz.sqlite`)中。
本应用绝不会使用您的财务数据发起任何网络请求。没有账户、无需登录,也不收集任何遥测数据。所有 CSV 导出文件均在客户端完成解析并直接写入数据库。若要备份数据或在多台设备间迁移数据,请使用导入页面上的 JSON Backup 工具。
## 架构
Strictly Spending 采用了一种现代化的本地优先混合架构,结合了高性能的原生系统后端与功能丰富的 Web 前端:
```
graph TD
subgraph Frontend (Vite + React + TS)
UI[React Pages & Hooks] -->|Method Calls| API[API Wrapper]
end
subgraph IPC Bridge (Tauri v2)
API -->|Invoke Command| TAURI[Tauri Command Router]
end
subgraph Backend (Rust + SQLite)
TAURI -->|Invokes| COMMANDS[Rust Commands]
COMMANDS -->|Connection Pool| RUSQLITE[rusqlite Database Layer]
RUSQLITE -->|Read / Write| SQLITE[(spending-viz.sqlite)]
end
```
### 核心架构亮点:
- **原生 Rust 后端**:基于 Tauri v2 构建并由 Rust 驱动。所有的数据库连接池、事务、迁移和数据变更均直接由 Rust 通过 `rusqlite` 处理,并辅以线程安全的连接锁。
- **轻量级 API 封装**:前端组件通过一个轻量且完全类型安全的 API 封装(`src/api.ts`)与后端进行交互,该封装直接映射到 Tauri IPC 命令,从而使前端包体积极为轻巧,且完全摆脱了繁重的 ORM 库。
- **事务安全性**:数据库的变更操作(插入、更新、批量处理)均在 Rust 端的符合 ACID 标准的 SQLite 事务中执行,即使在极高负载下也能确保数据库的一致性。
- **高保真测试**:
- **单元测试**:在 Vitest 中针对 mock 配置或模拟的 API 边界运行。
- **E2E 集成测试**:在 Playwright 中使用 Chrome DevTools Protocol (CDP) 模式运行,以在 Windows 上原生测试已编译的 Tauri 应用程序。
## 技术栈
- **后端**:**Rust** + **Tauri v2** + **rusqlite** (SQLite)
- **前端**:**Vite** + **React 19** + **TypeScript**
- **UI 与样式**:**MUI v7** + **MUI X Charts**(堆叠柱状图可视化)+ **react-resizable-panels**
- **状态管理**:**Zustand**(管理筛选器、预测和 Sort 会话状态)
- **解析器**:**PapaParse**(CSV 解析)
## 快速开始
构建并运行该桌面应用程序:
```
# 安装依赖
npm install
# 在 Tauri dev mode 下运行 app
npm run tauri dev
```
随后打开 **Settings → Load demo data → Demo mode**,即可在不导入真实 CSV 文件的情况下探索应用各项功能。演示数据集涵盖了 3 个模拟账户在 5 个月内的数据,并包含真实的周期性与变动性支出模式。
## 脚本命令
| 命令 | 描述 |
|---|---|
| `npm run dev` | 运行 Vite 开发服务器 |
| `npm run build` | 编译 TypeScript 并构建生产环境 Web 资源 |
| `npm run tsc` | 运行 TypeScript 类型检查(`tsc --noEmit`) |
| `npm run test` | 运行 Vitest 单元测试 |
| `npx playwright test` | 运行 Playwright E2E 测试(在 Windows 上使用 CDP 模式) |
| `npm run tauri dev` | 以开发者模式运行 Tauri 应用程序 |
| `npm run tauri build` | 将 Tauri 应用程序打包为生产环境安装程序 |
### 个人商户规则(私有,不提交至仓库)
`src/seed.ts` 中的初始规则包经过了刻意的泛化处理,每个类别都包含许多备选项,因此任何单一品牌的存在都不会暴露规则配置者的个人信息。
如果你想在代码中添加自定义商户(例如特定的房东、健身房或当地餐厅),请复制以下模板:
```
cp src/seed.local.example.ts src/seed.local.ts
```
编辑 `src/seed.local.ts` 并添加你的规则。该文件已被加入 `.gitignore`,因此仅会保留在你的本地计算机中。构建时,系统会自动将其 `LOCAL_RULES` 数组合并到初始规则包中(Vite 会在构建阶段解析这个可选导入)。
你也可以在运行时通过 **Sort** 或 **Rules** 页面添加商户;这些规则会存储在 SQLite 中。请使用导入页面上的 **Backup** 按钮在不同设备间保留这些配置。
### 解析器与 E2E 测试
运行单元测试与集成测试:
```
# 运行单元测试
npm run test
# 运行 E2E 测试(需要 tauri app 编译并启动)
npx playwright test
```
位于 `src/parsers/parsers.test.ts` 下的 CSV 解析器测试需要真实的银行导出文件作为测试固件。如果固件不存在,这些测试将被自动跳过。要在本地运行这些测试:
```
STRICTLY_SPENDING_FIXTURES=/path/to/bank-csvs npm run test
```
## 项目结构
```
strictly-spending/
├── src-tauri/ Native Rust backend
│ ├── Cargo.toml Rust dependencies (tauri, rusqlite, serde)
│ └── src/
│ ├── lib.rs App entry point, state initialization, command routing
│ ├── db.rs rusqlite schema setup and read-only commands
│ ├── db_mut.rs rusqlite transaction-safe mutation commands
│ └── db_extra.rs rusqlite auxiliary tables and commands
├── src/ React frontend
│ ├── pages/ Top-level routes (Dashboard, Sort, Forecast, Rules, …)
│ ├── components/ Reusable UI components (SortCard, SpendChart, …)
│ ├── api.ts Tauri command invoke wrapper
│ ├── testBridge.ts E2E test bridge for injecting mock data
│ ├── main.tsx Frontend bootstrapper
│ ├── categorize.ts Rule engine + merchantKey extraction
│ ├── recurrence.ts Recurring-charge detection
│ ├── forecast.ts Next-month projection (recurring + budgets)
│ ├── seed.ts Default categories + rules
│ ├── demoData.ts Synthetic demo dataset generator
│ └── backup.ts JSON export / restore pipeline
├── e2e/ Playwright E2E specs and setup
└── playwright.config.ts Playwright configuration (configured for CDP mode on Windows)
```
## 许可证
MIT。详见 [LICENSE](LICENSE)。
本项目由 [Claude](https://claude.com/claude-code) 作为工程合作伙伴协助开发。相关开发工作流已在[案例研究](https://barnesy.me/strictly-spending.html)中详细记录。
标签:CSV处理, 个人理财, 可视化界面, 本地优先, 浏览器应用, 特征检测, 财务管理