一个基于 Beancount 和 Flutter 构建的现代化的个人记账系统
2.6K
跑在 NAS 个人云上的家庭协作复式记账应用,数据模型对标 Beancount。 前端为 Flutter Web(PWA,浏览器访问),后端为 Go + SQLite(可平滑切换 Postgres)。
.beancount 文本,通过 bean-check 校验。owner / editor / viewer 三级角色权限,成员管理内置。VACUUM INTO 一致性快照(含未 checkpoint 的 WAL 数据),附带 per-账本 Beancount 导出;保留最近 N 份每日 + M 个月月度,手动备份永不清理。main.bean + accounts/ + date/)两种导出,解压后可直接 bean-check。| 层 | 技术 |
|---|---|
| 后端 | Go 1.26 + 标准库 + modernc.org/sqlite(纯 Go,免 CGO) |
| 前端 | Flutter Web 3.35+(MVVM 分层)+ sembast_web(IndexedDB) |
| 导出校验 | 项目内 Python venv + beancount |
| 部署 | Docker 多阶段构建(amd64 / arm64),单容器单端口 |
# 1. 构建(或使用 CI 发布的镜像)
docker build -t yourname/money-mint:0.5.0 .
# 2. 准备数据目录
mkdir -p ./data
# 3. 启动(数据 / 备份 / 前端 / API 全在这个容器里)
docker run -d --name famben -p 8080:8080 \
-e TZ=Asia/Shanghai \
-e LEDGER_OWNER_USER=awen \
-e LEDGER_OWNER_PASSWORD=你的密码 \
-v $(pwd)/data:/app/data \
yourname/money-mint:0.5.0
# 4. 浏览器打开 http://<主机IP>:8080 ,用预设的 owner 账号登录
仓库内附带 docker-compose.yml(含健康检查 / 备份保留 / 可选第二块盘异地备份),
配合 .env.example 使用更方便。
空库 + 未设置
LEDGER_OWNER_USER时,首个注册的账号自动成为系统管理员; 设置了则跳过注册直接用预设账号登录(见下文「账号与权限」)。
.venv,不污染系统)# 后端(前端与 API 同域托管,需先构建前端)
(cd frontend && flutter build web --release)
LEDGER_DB=data/ledger.db LEDGER_WEB_ROOT=frontend/build/web go run ./cmd/server
# 前端开发模式(dev server 与后端端口不同,需注入 API 地址)
cd frontend && flutter run -d chrome --dart-define=API_BASE=http://localhost:8080
健康检查:curl http://127.0.0.1:8080/api/health → {"status":"ok"}
| 变量 | 默认值 | 说明 |
|---|---|---|
LEDGER_DB | data/ledger.db | SQLite 数据库路径 |
LISTEN | :8080 | HTTP 监听地址 |
LEDGER_WEB_ROOT | ./web | 前端产物目录;不存在则只提供 API |
LEDGER_OWNER_USER / LEDGER_OWNER_PASSWORD | 空 | 预设 owner 账号(空库时生效,自动成为系统管理员) |
LEDGER_REGISTRATION_ENABLED | true | 注册开关初始值(之后可在界面修改并持久化) |
LEDGER_BACKUP_ENABLED | true | 自动备份开关 |
LEDGER_BACKUP_DIR | {DB 目录}/backups | 备份目录(可指向第二块盘) |
LEDGER_BACKUP_HOUR | 3 | 每日备份触发小时 |
LEDGER_BACKUP_KEEP_DAILY / LEDGER_BACKUP_KEEP_MONTHLY | 14 / 12 | 备份保留策略 |
TZ | Asia/Shanghai | 时区(交易日与备份时间依赖它) |
| 角色 | 管什么 | 典型操作 |
|---|---|---|
系统管理员(users.is_admin,全局唯一层级) | 全库级 | 数据备份 / 下载 / 还原、注册开关 |
账本 owner(ledger_members.role,账本级) | 单个账本 | 成员邀请 / 改角色 / 移除 / 重置成员密码、账本设置 |
首个账号 / 预设的 LEDGER_OWNER_USER 自动成为系统管理员兼账本 owner。
LEDGER_REGISTRATION_ENABLED=false(初始值)| 谁 | 怎么办 |
|---|---|
| 普通成员 | 请账本 owner 在成员管理中重置(自动注销其全部会话) |
| owner / 管理员 | 由另一位系统管理员重置(互救);单人家庭的兜底:到服务器执行 CLI |
# 服务器上直接执行(不经 HTTP,无需登录)
./server -reset-password awen -new-password 新密码
# Docker:
docker exec famben /app/server -reset-password awen -new-password 新密码
重置后该用户全部会话立即失效,用新密码重新登录。
LEDGER_BACKUP_HOUR 点(启动时若超过 24h 未备份会补跑一次)。VACUUM INTO 生成,完整性校验 + 行数比对后才写入清单;
同目录附每账本一份 .beancount 文本。GET /api/backup/list(仅系统管理员)。ledger.db 文件当备份——WAL 模式下会丢数据且不报错。uuid;改账 = 新增冲正交易,绝不覆盖;删除 = 软删 + op=delete 事件。sync_log 全局水位;pull 增量拉取,push 逐条幂等提交(单条失败不阻塞其余)。完整端点清单(认证 / 账户 / 交易 / 成员 / 账本 / 同步 / 导出 / 备份 / 设置)见 AGENTS.md §7。
快速验证示例:
B=http://127.0.0.1:8080
TOKEN=$(curl -s -X POST $B/api/auth/register -H 'Content-Type: application/json' \
-d '{"username":"alice","password":"secret123"}' | sed 's/.*"token":"\([^"]*\)".*/\1/')
AUTH="Authorization: Bearer $TOKEN"
# 建账户(记下返回的 id)
curl -s -X POST $B/api/accounts -H "$AUTH" -H 'Content-Type: application/json' \
-d '{"name":"Assets:Cash:CNY","type":"Assets","open_date":"2026-08-01","commodity":"CNY"}'
# 记一笔(postings 用 account_id;借贷必须按币种平衡)
curl -s -X POST $B/api/transactions -H "$AUTH" -H 'Content-Type: application/json' \
-d '{"date":"2026-08-02","description":"早餐","postings":[
{"account_id":1,"commodity":"CNY","amount":"-10.00"},
{"account_id":2,"commodity":"CNY","amount":"10.00"}]}'
# 导出并校验
curl -s $B/api/export -H "$AUTH" -o data/family.beancount
.venv/Scripts/python.exe -m beancount.scripts.check data/family.beancount
字段提醒:在线
POST /api/transactions的 posting 用account_id(整数);离线POST /api/sync/push的交易 posting 用account_uuid(字符串),两者勿混。
.
├── schema.sql # 数据库 DDL(含 Beancount 映射注释)
├── cmd/server/ # Go 后端入口(含 CLI:密码重置)
├── internal/ # 后端分层
│ ├── config/ # 集中配置(环境变量 + 启动校验)
│ ├── domain/ # 领域模型 + 哨兵错误
│ ├── db/ # 开库 + 增量迁移
│ ├── repository/ # 数据访问层(SQL 集中于此)
│ ├── service/ # 业务逻辑层(校验/事务/同步/权限/备份调度)
│ ├── handler/ # HTTP 适配层
│ ├── server/ # 路由 + CORS/鉴权中间件 + 前端静态托管
│ ├── auth/ # 认证纯函数(bcrypt/token/context)
│ ├── backup/ # 一致性快照 + 定时调度 + 保留清理
│ └── export/ # DB → .beancount 导出器(单文件 + 多文件目录)
├── frontend/ # Flutter Web 应用(MVVM 分层)
├── scripts/ # check-bean / verify-version / smoke / local.sh
├── Dockerfile # 三阶段构建(Flutter → Go → alpine)
├── docker-compose.yml # 家用部署示例
├── data/ # 运行时 SQLite 与 backups/(git 忽略)
└── docs/ # 设计规格 / 同步协议 / 原型 / 任务书
前端 frontend/lib/ 按 app / core / viewmodels / widgets / screens 分层(详见 AGENTS.md §3.1)。
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o familyledger ./cmd/server # ARM64 NAS
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o familyledger ./cmd/server # x86_64 NAS
前端与 API 同端口部署(Go 托管 Flutter 产物),反向代理只需把 / 转到后端端口,
HTTPS / 换端口 / 域名均零配置;前后端分离部署时用
flutter build web --dart-define=API_BASE=https://api.example.com 注入。
建议用反向代理(Nginx / Caddy / Cloudflare)加 HTTPS——账本数据不要明文跑公网。
AGENTS.md — 开发上下文:架构 / 约定 / Schema / 端点清单(面向开发者与 AI 助手)docs/design-spec.md — 产品设计规格docs/sync-design.md — 离线同步协议设计docs/m2-plan.md — 备份还原 + Beancount 导入方案docs/frontend-revamp-tasks.md — 前端改版任务清单CHANGELOG.md — 版本变更记录License 暂未指定。
Content type
Image
Digest
sha256:f5aefe825…
Size
33.6 MB
Last updated
1 day ago
docker pull quewen08/money-mint