# 财富自由指南灯 · 设计方案

> **核心隐喻**：把每一笔收入翻译成"买到的自由生命"，把每一笔支出翻译成"被磨损的自由时间"。
> 让财富自由从一个抽象目标变成一张**可视化的人生方格图**。

---

## 1. 第一性原理

传统记账只回答"我今天花了多少 / 赚了多少"。
本系统回答**一个更本质的问题**：

> 如果维持我**过往平均**的生活成本，我目前的财富能支撑我**自由地活多少天**？

每一笔收入 = 给自己买到 N 天自由 → **点亮 N 个人生方格**
每一笔支出 = 抬高平均日花销 → **熄灭已点亮的方格**
当所有"剩余日方格"被点亮 = **走完财富自由之旅**

这不是预算工具，是**一面财富自由的进度灯**。

---

## 2. 核心数学模型

### 2.1 定义

- `tracking_start_date`：开始记账的第一天（首笔交易日期）
- `today`：当前日期
- `tracking_days = (today - tracking_start_date).days + 1` （含起点 +1，避免 0 除）
- `total_income = Σ income`
- `total_expense = Σ expense`
- `avg_daily_expense = total_expense / tracking_days`
- `net_savings = total_income - total_expense`
- `freedom_days_bought = floor(net_savings / avg_daily_expense)` ← **扣除过往生存成本后的净储蓄换得的自由天数**

### 2.2 方格总数（人生剩余日方格）

- `birth_date`：用户生日
- `target_age = 80`（可调）
- `total_cells = (birth_date + target_age年 - today).days`

例：某用户 26 岁，剩余 ≈ 54×365 ≈ 19,710 格。

### 2.3 状态映射

| 方格状态 | 含义 | 数量 |
|---------|------|------|
| 🟡 lit | 已点亮 = 已经买到的自由日 | `min(freedom_days_bought, total_cells)` |
| ⚫ unlit | 未点亮 = 还要继续努力的日子 | `total_cells - lit` |
| ✨ overflow | 财富自由超额（lit > total_cells） | `freedom_days_bought - total_cells` |

### 2.4 增量动画规则

每次新增交易后重新算 `freedom_days_bought_new` 与 `lit_count_new`：

- **新增收入 → 净增 N 格**：动画"点亮"末尾 N 格（从当前最后一格之后开始）
- **新增支出 → 抬高 avg → 净减 M 格**：动画"熄灭"末尾 M 格（从最后已点亮格往回熄）
- **支出但不引起熄灭**：仅刷新统计，无方格动画
- **特殊**：极端情况下 N 或 M 跨越 overflow 边界，按段分别处理

---

## 3. 数据模型 (SQLite)

```sql
CREATE TABLE settings (
  id INTEGER PRIMARY KEY CHECK (id = 1),
  birth_date TEXT NOT NULL,        -- 'YYYY-MM-DD'
  target_age INTEGER NOT NULL DEFAULT 80,
  currency TEXT DEFAULT 'CNY',
  created_at TEXT NOT NULL
);

CREATE TABLE transactions (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  occurred_on TEXT NOT NULL,       -- 'YYYY-MM-DD'
  type TEXT NOT NULL,              -- 'income' | 'expense'
  amount REAL NOT NULL CHECK (amount > 0),
  note TEXT DEFAULT '',
  created_at TEXT NOT NULL
);
CREATE INDEX idx_tx_date ON transactions(occurred_on);
```

---

## 4. 后端 API

技术栈：**Python 3 + FastAPI + sqlite3 (stdlib) + uvicorn**

| Method | Path | 说明 |
|--------|------|------|
| GET    | `/api/state` | 返回完整状态（settings + stats + 最近交易 + lit_count） |
| POST   | `/api/settings` | 首次设置 / 修改 birth_date target_age |
| POST   | `/api/transactions` | 新增交易，**返回**：前后 lit_count、delta、动画类型 |
| GET    | `/api/transactions` | 分页交易列表 |
| DELETE | `/api/transactions/{id}` | 删除（也返回 delta，触发反向动画） |

`POST /api/transactions` 响应：

```json
{
  "transaction": {...},
  "stats": {
    "total_income": 12000,
    "total_expense": 4500,
    "tracking_days": 90,
    "avg_daily_expense": 50.0,
    "freedom_days_bought": 240,
    "lit_count_before": 210,
    "lit_count_after": 240,
    "delta": 30,
    "animation": "light_up"   // 'light_up' | 'extinguish' | 'none'
  }
}
```

前端拿到 delta + 类型即可启动仪式动画。

---

## 5. 前端架构

**纯静态 HTML/CSS/JS**，Canvas 渲染方格（万级元素 DOM 会卡）。

### 5.1 布局

```
┌─────────────────────┬───────────────────────────────────────────────┐
│                     │                                                │
│   左 1/3            │   右 2/3                                       │
│   ─────────         │   ─────────                                    │
│   📒 账本面板         │   ✨ 人生方格视图（Canvas）                       │
│                     │                                                │
│  · 财富自由进度        │     [年份索引: 26 27 28 29 ... 80]              │
│  · 设置（生日/目标）   │     ┌────────────────────────────────────┐    │
│  · 添加交易表单        │     │ 方格 grid (按年分行/分段)            │    │
│  · 最近交易列表        │     │                                    │    │
│  · 统计仪表盘          │     │  🟡🟡🟡🟡🟡⚫⚫⚫⚫⚫⚫⚫⚫⚫⚫           │    │
│                     │     │  ⚫⚫⚫⚫⚫⚫⚫⚫⚫⚫⚫⚫⚫⚫⚫            │    │
│                     │     └────────────────────────────────────┘    │
│                     │                                                │
└─────────────────────┴───────────────────────────────────────────────┘
```

### 5.2 视觉风格

- 主色：深邃黑底（`#0a0a0f`）+ 金色光源（`#ffd166` 点亮 / `#3a3a4a` 未亮）
- 字体：等宽 + 衬线混搭（账本数字用等宽显严肃，标题用衬线显仪式感）
- 整体氛围：**夜空中点亮星辰** —— 不是 dashboard，是 ritual。

### 5.3 方格 Canvas 规格

- 自适应宽度，每行 N 格（按年自动分行：一年一行，每行 365/366 格）
- 单格尺寸：4-8px（自适应屏宽 + 总格数）
- 渲染：高 DPI（`devicePixelRatio` 适配）
- 性能：仅在状态变化时重绘；动画期间用 RAF + 离屏 buffer

### 5.4 仪式动画核心（**项目灵魂**）

#### 点亮动画 `light_up`

1. **阶段 A · Zoom In (600ms)**：camera 平滑缩放 + 平移到第一格目标位置（`lit_count_before + 1`），目标 scale = 4-6x
2. **阶段 B · Sequential Ignite**：每格点亮间隔自适应：
   - delta ≤ 10：每格 180ms（充分仪式感）
   - delta ≤ 50：每格 80ms
   - delta ≤ 500：每格 25ms
   - delta > 500：每格 8ms（保持流畅）
3. **单格点亮微动画 (300ms)**：
   - 起：暗灰 → 黄白爆发（亮度 200%）
   - 中：橙黄光晕扩散（半径 4x）
   - 末：稳定金色 + 微弱呼吸光
4. **阶段 C · Zoom Out (800ms, ease-out)**：平滑回到全局视图

#### 熄灭动画 `extinguish`

1. **阶段 A · Zoom In (600ms)**：camera 缩放到最后一格已点亮位置
2. **阶段 B · Sequential Extinguish**（从尾往头熄）：
   - 单格熄灭微动画 (400ms)：金色 → 红橙闪烁一次 → 灰烬色 → 黑暗
   - 间隔同点亮节奏，但放慢 1.3x（失去比获得更慢）
3. **阶段 C · Zoom Out**

#### 财富自由达成 ✨

- 当 `lit_count_after >= total_cells` 触发**全局烟花动画**：所有方格连锁波纹 + 中央巨型金光 + "FREE" 字样浮现
- 一次性彩蛋

---

## 6. 文件结构

```
财富自由指南灯/
├── 设计方案.md            ← 本文件
├── README.md             ← 启动说明
├── backend/
│   ├── main.py           ← FastAPI 应用 + SQLite
│   └── requirements.txt  ← fastapi, uvicorn
├── frontend/
│   ├── index.html
│   ├── style.css
│   ├── app.js            ← API client + 主控
│   └── grid.js           ← Canvas 方格 + 动画引擎
├── data/
│   └── ledger.db         ← SQLite 数据库（git ignore）
└── run.sh                ← 一键启动脚本
```

---

## 7. 启动方式

```bash
cd 财富自由指南灯
./run.sh        # 启动 FastAPI on :8765, 自动开浏览器
```

---

## 8. 实现优先级 (MVP)

- [x] 设计方案落盘
- [ ] P0：后端 API + 数据库
- [ ] P0：前端布局 + 账本面板
- [ ] P0：Canvas 方格渲染（静态）
- [ ] P0：点亮 / 熄灭 仪式动画
- [ ] P1：财富自由达成彩蛋
- [ ] P1：交易删除 / 编辑
- [ ] P2：导出 / 多账本

MVP 范围：P0 全部。

---

## 9. 关键不变量（写代码时反复对照）

1. `avg_daily_expense` 永远 = `total_expense / tracking_days`，不做平滑
2. `lit_count` 永远 = `floor((total_income - total_expense) / avg_daily_expense)`，幂等
3. 动画只是 lit_count 变化的**视觉投影**，不持久化任何中间状态
4. 删除交易 = 重算 + 反向动画（点亮变熄灭，反之亦然）

> 数据是真相，动画是仪式。仪式服从真相。
