# 系统架构 ## 核心不变量 `.bean` 文件是唯一事实来源;SQLite 仅为可丢弃的读缓存。 ## 数据流 ```mermaid flowchart LR subgraph DataSource[数据源] A[".bean 文件"] B["OCR / 无障碍 / 通知 / 短信"] C[“手动录入 / CSV 导入"] end subgraph Pipeline[处理层] D["BillPipeline 串行互斥锁"] E[“转账识别"] F[“去重"] G[“规则分类"] end subgraph Storage[存储层] H["main.bean 追加写入"] I["SQLite 缓存"] J["Zustand Store 内存索引"] end subgraph UI[展示层] K["Expo Router 页面"] L[“报表 / 图表"] end A -->|parseLedger| J B -->|原始事件| D C -->|TransactionDraft| D D --> E --> F --> G G -->|确认写入| H H -->|reparse| J J --> K J --> L I -.->|搜索查询| J ``` ## 模块分层 ```mermaid graph TB subgraph UILayer["UI 层: src/app + src/components"] R[“Expo Router 页面"] CO[“可复用组件"] end subgraph StateLayer["状态层: src/store"] S[“Zustand Stores"] end subgraph DomainLayer["领域层: src/domain — 纯 TS 零 RN 依赖"] LE["ledger.ts 解析器"] P["billPipeline.ts"] D2["dedup / transfer / rules"] O["ocrProcessor.ts"] end subgraph Infra[“基础设施"] FS["expo-file-system"] DB["expo-sqlite"] NAT["plugins/ 原生模块"] end R --> S CO --> S S --> LE S --> P P --> D2 P --> O LE --> FS D2 --> DB O --> NAT ``` ## BillPipeline 处理顺序 所有导入渠道(手动、CSV、OCR、无障碍、通知、短信、截图)统一经过 `BillPipeline`,严格按序执行: 1. **转账识别** — 配对收入+支出 → 单笔转账(必须先于去重) 2. **批内去重** — 时间窗口 + 金额 + 交易对手 3. **历史去重** — 按日期索引比对已提交交易 4. **规则分类** — 规则匹配 → 关键词 → `Uncategorized` 兆底 ## 原生模块 原生功能以 Expo Config Plugin 形式封装在 `plugins//`,详见 [plugins/README.md](../plugins/README.md)。 原生服务**不直接写入**数据库或文件,而是通过 `NativeEventEmitter` 将原始事件推送到 JS 层管道。 ## OCR 三层级联 ```mermaid flowchart LR A[“图像输入"] --> B["Layer 1: 正则规则"] B -->|未命中| C["Layer 2: 本地 OCR PP-OCRv6"] C -->|未命中| D["Layer 3: AI Vision 付费"] ``` ## 双轨映射 Beancount 无原生“分类/预算”概念,应用在本地 SQLite 维护 UI 元数据,写入时映射回 Beancount 语义: | 概念 | 本地表 | 映射到 `.bean` | |------|--------|----------------| | 分类 | `categories` | `linkedAccount` → posting 账户 | | 标签 | `tags` | narration 中的 `#tag` | | 预算 | `budgets` | 仅本地,不影响余额 | | 信用卡 | `credit_cards` | 账户在 `.bean`,UI 字段本地 |