用 Claude Code 从零做一个 Beancount 记账 Web 应用(全程不写一行代码)

用 Claude Code 从零做一个 Beancount 记账 Web 应用(全程不写一行代码)
蔡坨坨转载请注明出处❤️
作者:测试蔡坨坨
前言
你好,我是测试蔡坨坨。
上一篇文章里,我介绍了 Claude Code 的基本使用方式:从安装、对话,到让 AI 直接在终端里修改代码。同时,还做了一个简单的记账软件作为 demo,不过当时更多只是演示流程,记的是“流水账”。
这篇文章,我们来做一次更 真实、更完整的实践:
用 Claude Code 从零搭建一个服务于 Beancount 的个人记账 Web 应用。
在开始之前,其实还有几篇背景文章可以一起看看,算是这篇实践的基础铺垫:
- 为什么要记账?
- 什么是复式记账?
- Beancount 复式记账的基本概念和账本结构
- 如何用 Python 自动解析微信、支付宝账单 CSV,并生成 Beancount 格式的交易记录
有了这些基础之后,一个新的问题就出现了:
在日常记账时,如何更方便地录入和维护 Beancount 的账务数据?
这篇文章,我们就用 Claude Code,一步一步把这个问题变成一个可以真正使用的 Web 工具。
背景
Beancount 是一套基于 纯文本文件 的复式记账系统。它的核心理念很简单:所有账务数据都保存在普通文本中,每一笔交易都必须满足严格的借贷平衡原则。
相比 Excel 或各种商业记账 App,Beancount 有几个很明显的优势:
- 数据自主:账本就是本地文件,不依赖任何云服务
- 版本控制友好:天然适合用 Git 管理,所有修改历史都清晰可追溯
- 可编程:由于是纯文本结构,可以用 Python、Shell 等任何语言进行处理和自动化
- 复式记账模型:每笔交易都包含借方和贷方,财务结构更严谨,也更容易做统计分析
不过,Beancount 本身其实只是一个 命令行工具,主要负责解析账本、校验借贷是否平衡,以及生成各种报表,并没有自带完整的图形界面。
官方提供的 Web 界面工具叫 Fava,可以用来浏览账本、查看报表和分析资产变化。
Fava 的局限性
Fava 提供了相当完善的报表和可视化能力,但在日常使用中,还是会遇到一些不太顺手的地方:
移动端体验不佳
Fava 的界面主要是为桌面端设计的,在手机上操作时,页面布局会显得拥挤,录入和修改数据都比较麻烦。
新增交易不够友好
添加一笔交易时,需要手动编写完整的 Beancount 语法,没有表单式的引导,对于日常快速记账来说不太方便。
无法直接编辑余额断言
balance 断言(用于记录账户在某一时间点的余额)通常分散在不同文件中。每次对账时,都需要手动找到对应文件并修改内容,过程比较繁琐。
理财记录管理不方便
像基金收益、利息收入这类周期性或重复性记录,在 Fava 中没有专门的录入方式,往往还是需要手动编辑账本文件。
账单导入工具与界面割裂
比如账单解压密码、对账日期等信息,通常写在独立的 Python 脚本里,与 Fava 的界面没有任何关联,整个流程比较分散。
这些痛点让日常记账的摩擦成本变高。
于是我决定用 Claude Code 从零做一个 专门服务于 Beancount 数据文件的轻量级 Web 应用,把这些高频操作集中到一个简单的界面里,让日常记账变得更顺手。
技术选型
核心挑战:浏览器如何读写本地文件?
传统的 Web 应用如果需要访问本地文件,通常必须通过后端服务来完成。但这样一来,就会引入服务器部署、接口维护等额外复杂度,这和 Beancount 本地账本的理念其实是相违背的。
好在现代浏览器已经提供了 File System Access API,允许网页在用户授权后,直接读写本地文件系统,这正好契合我们的使用场景。
1 | // 用户授权选择目录 |
用户只需要 首次选择账本所在目录并授权,页面就可以直接读取和修改其中的 .bean 文件。
为了避免每次打开页面都重新授权,可以把目录句柄(FileSystemDirectoryHandle)保存到 IndexedDB 中。这样在下次访问页面时就可以自动恢复权限,无需重复选择目录。
技术栈
整体技术方案尽量保持 简单、轻量、本地优先:
| 层级 | 选型 |
|---|---|
| 框架 | React 18 + TypeScript |
| 构建工具 | Vite |
| 图表 | Chart.js |
| 样式 | 原生 CSS(CSS Variables) |
| 本地存储 | File System Access API + IndexedDB |
整个应用 没有后端、没有数据库、也没有任何云服务依赖。
所有数据都直接存储在本地的 Beancount 账本文件中,Web 应用只负责读取、解析和编辑这些文件。
这也意味着:
只要有浏览器,就能随时打开账本进行管理。
功能介绍
1. 目录管理与权限持久化
首次使用时,用户需要通过文件选择器授权 Beancount 账本所在的目录。应用会将获得的目录句柄保存到 IndexedDB 中,之后再次打开页面时即可自动恢复权限,无需每次重新授权。
同时也支持 随时切换目录,如果有多套账本(例如个人账本、家庭账本等),可以在不同目录之间自由切换。
2. 记账流水查看
应用会按照 年月维度 展示当月的所有交易记录,并支持在不同月份之间快速切换。
为了让流水一眼可读,每条记录都会根据账户类型显示不同颜色:
- 蓝色:转账(Assets → Assets)
- 红色:支出(Expenses)
- 绿色:收入(Income)或退款
其中 退款 是一个特殊场景:当贷方账户以 Expenses: 开头时,会被识别为退款,而不是收入。这样处理后,退款金额会 从支出中扣除,而不会被统计为收入。
3. 新增交易(引导式表单)
针对 Beancount 复式记账的特点,新增交易时设计成分层引导的方式,减少手写语法的成本。
第一层:交易分类预设
提供四种常见类型:
- 支出
- 收入
- 转账
- 还款
选择后会自动填充借贷方账户的默认结构。
第二层:模板快捷选择
在分类下会显示常用模板,例如:
1 | 支出类:餐饮、交通、购物、娱乐... |
选择模板后,付款账户、借贷账户以及备注信息都会自动填充,大幅减少重复输入。
为了方便后续精确删除,每一笔交易写入文件时都会附带一个 内联 ID:
1 | 2026-03-10 * "XXX有限公司" "三月工资" ; id: abc123xyz |
4. 会计恒等式展示
页面 Header 中常驻展示经典的 会计恒等式:
1 | 资产 + 费用 = 负债 + 所有者权益 + 收入 |
这是 Beancount 复式记账的核心约束,通过持续展示这个公式,也在提醒账务结构始终保持逻辑闭环。
5. 余额断言编辑(assert.bean)
balance 断言是 Beancount 的重要对账机制,用于记录某一天各账户的真实余额,例如:
1 | 2026-03-04 balance Assets:Current:Wechat:Wallet 0.00 CNY ;微信钱包 |
应用为此提供了专门的编辑界面:
- 统一日期:所有断言共享同一个对账日期,一处修改即可全部更新
- 逐行金额编辑:每个账户都有独立输入框,资产类与负债类用不同颜色区分
- 符号保护:负债账户的 - 前缀是 Beancount 的借贷约定,无法修改,只允许编辑数值部分
保存时会 精确保留原文件的列对齐格式,避免破坏账本结构。
6. 理财收益管理(Investments.bean)
针对定期收益类记录(例如货币基金或银行活期理财),应用提供了单独的管理界面。内置常见模板,例如:
微信零钱通收益
1 | 2026-03-03 * "零钱通收益" "202603" |
主要功能包括:
- 按收益类型分组展示,清晰区分不同来源
- 新增记录时,账单期默认填入当月(例如 202603)
- 支持 年份切换,方便查看历史收益
为了避免误操作,这个界面 不提供删除功能。如果确实需要删除,可以直接编辑原始文件。
7. 账单导入工具集成
配套提供了一个 Python 脚本,用于从 QQ 邮箱自动下载支付宝和微信账单 ZIP 文件并解压。Web 应用也为这个脚本提供了辅助管理界面。
密码管理
支付宝和微信账单的解压密码会定期更换,因此可以直接在界面中修改。应用会通过正则替换 Python 文件中的对应变量:
1 | self.alipay_zip_password = '' |
已对账日期
应用会自动扫描 data/bank_statements/ 目录中的支付宝账单文件名,例如:
1 | 支付宝交易明细(20260304-20260311).csv |
并解析出最新对账日期,展示为:
1 | 2026-03-11 |
运行脚本
提供一键复制命令到剪贴板:
1 | cd /path/to/beancount-importer && python -m src.main |
由于浏览器的安全限制,网页无法直接执行本地进程,因此采用 复制命令 → 用户在终端执行 的方式作为中转。
8. 自定义 Toast 通知
应用统一替换了浏览器原生的 alert / confirm 弹窗,改为自定义 Toast 通知组件:
- 支持 info / success / error / confirm 四种类型
- 全局单例,通过函数式调用:toast(‘操作成功’, ‘success’)
- 删除等敏感操作使用 toastConfirm,避免原生对话框打断页面体验
这样既保持了交互的一致性,也让整体 UI 更加流畅自然。
小结
整个应用 没有任何后端,也没有数据库,所有数据都直接存储在本地的 Beancount 账本文件中。
其中:
- File System Access API 解决了浏览器读写本地文件的问题
- IndexedDB 用于持久化目录句柄,避免每次都重新授权
通过这两项能力,就可以在浏览器里实现一个真正 本地优先(local-first) 的 Web 应用。
对于有一定技术背景、同时使用 Beancount 管理个人财务的人来说,这套工具链提供了一种比 Fava 更贴近日常操作习惯的方式。它并不是要取代 Fava 强大的报表与分析能力,而是作为一个 专注于数据录入和维护的前端工具,与 Fava 形成互补。
而 Beancount 最迷人的地方依然没有改变:
所有数据始终是纯文本。
这意味着:
- 任何时候都可以直接打开文件查看或修改
- 可以用 Git 进行版本管理
- 可以用任何编程语言进行自动化处理
数据不会被锁在某个应用或平台里,这正是 纯文本记账最大的魅力。










