KennethCheng a2fc215ef9 docs: README 补全 Docker 部署章节(拓扑图 / 镜像要点 / 持久化 / 反代)
上条 commit (846b1a7) 把 deploy/ 目录实际文件都铺好了,但 README 的
"🐳 部署"还停留在"待完成"占位,新成员(以及未来的自己)看到 README
会以为 Docker 还没做。这条 commit 把 README 与实际状态对齐。

新增 / 改动 README.md:
- 仓库结构树:deploy/ 从"(待) docker-compose + nginx"改成
  "Docker 部署文件",列出 4 个子文件
- 替换 "🐳 部署(待完成)" 占位为完整章节:
  * 快速部署:一条 docker compose up -d
  * 部署拓扑:ASCII 图展示 web 容器 (nginx) 反代 /api 到 api 容器
    (GORM + SQLite),数据卷持久化
  * 镜像要点:表格列出 api / web 两个镜像的 base image + 关键配置
    (CGO=0 / tini / 非 root / corepack pnpm / gzip + 30 天 immutable cache)
  * 数据持久化:DB_PATH 路径 / volume 名 / 备份命令(docker run --rm 模板)
  * 反代建议:不要直接 8080 暴露公网,前面挡 NPM / Traefik,符合
    plan §17 "容器内 API 默认只监听内网"
  * 健康检查:API + web 都用 wget,compose 用 condition: service_healthy
  * 开发模式:不想装 Go 也能跑,流程:pnpm build -> docker compose up
  * 链回 deploy/README.md
- 路线图:Phase 5 标 done,Phase 6/7/8 标 pending(对应 Import/Export /
  Auth / 扩展)

未做(留待后续):
- README 截图(Docker 跑起来后再补)
- Traefik / NPM 自动 HTTPS 的具体配置(留给 plan §17 后续)
- 数据备份 cron / 灾备演练

风格保持与之前一致:中文为主,技术名词留英文,emoji 章节标题。
2026-09-13 17:20:50 +08:00

FuelLog 加油记

轻量、精美、移动端优先的个人加油记录与车辆燃油成本分析工具。

Go Vue License


✨ 特性

  • 10 秒录入一笔加油:加油时间 → 里程 → 升数 → 总金额 → 油品 → 加油站 → 加满 → 备注,移动端底部固定保存按钮,实测从打开到保存 < 10s。
  • 服务端实时算实际油价:price_per_liter = total_amount / liters,前端只入 amount + liters,前端同步显示"实际油价 ¥X.XX/L"作为 UX 反馈,服务端是权威。
  • 标准百公里油耗:只在"连续两次 is_full=true"之间计算,半箱油不污染指标 —— 与 plan §6.3 / §19 严格对齐。
  • 每公里成本 + 月度趋势:Dashboard 主指标 + Statistics 4 张图(油价 / 油耗 / 月度费用 / 每公里成本)。
  • PWA:可"安装"到手机主屏,离线时已访问页面仍可用;Service Worker 预缓存所有静态资源,API 走 NetworkFirst 兜底 cache。
  • 多车辆 + 唯一默认:可加多辆,默认车辆唯一,切换走事务。

🛠 技术栈

层 选型 理由
前端 Vue 3 + Vite + TypeScript + Pinia + Vue Router + Tailwind CSS + Naive UI + ECharts plan §3 锁定;轻量、Mobile First、TypeScript 严格、Naive UI 适配暗色
后端 Go 1.22 + Gin + GORM plan §3 锁定;单文件、编译快、goroutine 适合个人项目
数据库 SQLite (glebarez/sqlite 纯 Go) plan §3 锁定;零依赖、文件级持久化;WAL + 外键 + busy_timeout
精度 shopspring/decimal plan §16;金额 / 油耗 / 成本用 decimal 避免 float 累计误差
部署 Docker + Docker Compose + Nginx plan §17;反代 + volume 持久化 SQLite

🚀 快速开始(开发期)

前置

  • Node.js ≥ 20(已测 24.15)
  • pnpm ≥ 9(已测 9.7)或 npm
  • Go ≥ 1.22(已测 1.27)
  • git,可选:make

1. 克隆 + 装依赖

git clone https://gitea.kennethcheng.cc/KennethCheng/FuelLog.git
cd FuelLog

# 后端
cd fuellog-api
go mod tidy
cp .env.example .env

# 前端(另开一个 shell)
cd fuellog-web
pnpm install
cp .env.example .env.local

2. 启后端

cd fuellog-api
go run ./cmd/server          # 监听 :8080

输出形如:

time=... msg="db ready" path=./data/fuellog.db
time=... msg="server starting" addr=:8080 mode=debug

3. 启前端

cd fuellog-web
pnpm dev                    # 监听 :5173,/api 自动代理到 :8080

打开 http://127.0.0.1:5173 即可。首次进会看到"还没有车辆,先去添加"的引导。

4. (可选)冒烟测试

cd fuellog-api
pwsh -ExecutionPolicy Bypass -File scripts/smoke.ps1
# 13/13 断言:建车 / 加油 / 油价 / 油耗 / 删记录 / 删车 全部命中 plan §6 数值

5. (可选)单测

cd fuellog-api
go test ./...               # 22 decimal + 5 service
pnpm typecheck              # 前端 vue-tsc
pnpm build                  # 前端 vite build

📁 仓库结构

FuelLog/
├── fuellog-api/                # Go 后端
│   ├── cmd/server/main.go      # 入口
│   ├── internal/
│   │   ├── config/             # env 加载
│   │   ├── db/                 # GORM open + migration
│   │   ├── handler/            # HTTP 入参 / 出参
│   │   ├── service/            # 业务逻辑 + 核心计算
│   │   ├── repository/         # GORM 数据访问
│   │   ├── model/              # GORM 实体
│   │   ├── dto/                # 请求/响应 DTO
│   │   ├── middleware/         # logger / recover / cors
│   │   └── router/             # Gin 路由
│   ├── pkg/
│   │   ├── apperror/           # 统一错误类型
│   │   └── decimal/            # 金额 / 油耗精度工具
│   ├── migrations/             # 迁移(目前 GORM AutoMigrate;后续手写)
│   ├── scripts/                # smoke.ps1 等
│   └── go.mod
│
├── fuellog-web/                # Vue 前端
│   ├── src/
│   │   ├── api/                # axios + 类型化 client
│   │   ├── components/         # 业务组件
│   │   ├── layouts/            # AppLayout(顶栏 + 移动端底部 Tab)
│   │   ├── pages/              # Dashboard / Refuels / RefuelNew / Vehicles / Statistics / NotFound
│   │   ├── router/             # Vue Router 配置
│   │   ├── stores/             # Pinia
│   │   ├── styles/             # tailwind.css + theme.css(CSS Variables)
│   │   ├── utils/              # decimal 工具
│   │   ├── App.vue
│   │   └── main.ts
│   ├── public/                 # favicon / manifest / PWA icons
│   └── package.json
│
├── deploy/                     # Docker 部署文件
│   ├── docker-compose.yml      # 生产:api + web(nginx),宿主机 :8080
│   ├── docker-compose.dev.yml  # 开发:api 容器 + bind mount 本地 dist
│   ├── nginx/web.conf          # web 容器用 nginx 配置(/api 反代 + SPA fallback)
│   └── README.md               # 部署说明
├── docs/
│   └── api.md                  # 完整 API 文档
├── data/                       # SQLite 文件(默认 .gitignore,挂载 volume)
├── AGENTS.md                   # Agent 工程操作手册(规则、约束、Phase 顺序)
├── README.md                   # 本文件
└── .gitignore

🔢 核心计算规则

指标 公式 范围
实际油价 total_amount / liters 服务端权威,前端只展示
行驶里程 current.odometer - previous.odometer 负数 / 零视为无效
百公里油耗 liters / mileage × 100 只在 is_full=true 序列内采样
每公里成本 total_amount / mileage 同上

所有除法走 shopspring/decimal(后端) / 同步实现(前端 utils/decimal.ts)。


🧪 测试覆盖

  • pkg/decimal/money_test.go — 22 个子测试,覆盖 plan §6.1-6.4 全部案例
  • internal/service/refuel_record_test.go — 5 个测试,覆盖"半箱油不进入标准油耗"硬规则
  • scripts/smoke.ps1 — 13 步端到端冒烟,跑通"建车 → 加 3 次油 → 统计 → 删记录 → 删车"

🐳 部署

生产一条命令起 api + web,数据持久化到 named volume,前置 Nginx Proxy Manager 或 Traefik 接 SSL 即可对外。

快速部署

# 在仓库根跑(任选一种)
docker compose -f deploy/docker-compose.yml up -d
# 浏览器访问 http://your-host:8080

部署拓扑

┌─────────────────┐     ┌─────────────────┐
│ Nginx Proxy Mgr │────▶│ fuellog-web:80  │
│ (TLS / 反代)    │     │  (nginx:alpine) │
└─────────────────┘     │  ├ 静态 SPA      │
                        │  └ /api/* → api │
                        └────────┬────────┘
                                 │ fuellog-net
                        ┌────────▼────────┐
                        │ fuellog-api:8080│
                        │  (golang alpine)│
                        │  ├ GORM + SQLite │
                        │  └ /data 卷      │
                        └─────────────────┘

镜像要点

镜像 基础 关键配置
fuellog/api 多阶段 golang:1.22-alpine → alpine:3.20 CGO=0 出纯静态二进制 / tini 收僵尸 / 非 root / 时区 Asia/Shanghai / HEALTHCHECK
fuellog/web 多阶段 node:20-alpine → nginx:1.27-alpine corepack 启 pnpm / pnpm build → nginx 静态 / gzip + 30 天 immutable cache

数据持久化

  • 容器内 DB_PATH=/data/fuellog.db
  • 宿主机 docker volume 名:fuellog-data
  • 容器删了重建,数据还在;真正要备份:docker run --rm -v fuellog-data:/data -v $(pwd):/backup alpine cp /data/fuellog.db /backup/

反代建议(不要直接把 8080 暴露公网)

Internet ──TLS──▶ Nginx Proxy Manager ──127.0.0.1:8080──▶ fuellog-web (容器)

容器只 listen 8080,不直接对外,符合 plan §17 "容器内 API 默认只监听内网"。

健康检查

  • api:wget http://127.0.0.1:8080/health,30s 间隔,启动 10s 后开始
  • web:wget http://127.0.0.1/,30s 间隔
  • docker-compose 用 depends_on: condition: service_healthy 保证 web 等 api 健康后才起

开发模式(不想本地装 Go 时用)

# 1. 本地 build 前端
cd fuellog-web && pnpm install && pnpm build

# 2. 启容器(nginx bind mount 上面 build 出的 dist)
cd .. && docker compose -f deploy/docker-compose.dev.yml up

# 3. 改前端 -> pnpm build -> docker compose restart web

日常前端调试还是推荐直接 pnpm dev(Vite :5173 自带 /api 代理),上面这套 是为了"不想在 Windows 装 Go 也能跑后端"准备的。

详细配置在 deploy/README.md。


🗺 路线图

  • Phase 0~1:初始化 + 后端骨架 + 数据层 + API + 单测 + 冒烟
  • Phase 2~3:前端骨架 + 4 个业务页
  • Phase 4:PWA(manifest / SW / 添加到主屏)
  • Phase 5:Docker 部署 + 反代(API / web 多阶段镜像 + nginx + volume)
  • Phase 6:数据导入导出(JSON / CSV)
  • Phase 7:多用户 / 鉴权(目前是个人单机)
  • Phase 8:充电 / 保养 / 停车等扩展

📐 设计原则

  • Mobile first:360 / 390 / 430 三档断点先验证;布局用 grid + md:,CSS Variables 切暗色。
  • 不写死颜色 / 间距 / 字号:Tailwind theme + :root CSS Variables 双轨;brand / ink / surface 都是变量,组件层一律 text-brand / bg-surface。
  • Handler 不写业务:Handler 只做参数解析、错误码映射;Service 持有 Repository 接口,便于单测替换。
  • 金额 decimal:任何除法都不直接用 float64。
  • 半箱油不污染油耗:plan §6.3 + §19 验收的硬规则,单测里有专门 case。
  • 错误统一格式:{ "code": "INVALID_ARGUMENT", "message": "..." },前端 axios 拦截器解到 Error.cause。
  • PWA 添加到主屏:display: standalone + maskable icon + SW 预缓存,真像 app。

📄 License

MIT


📎 相关

  • AGENTS.md — Agent 工程操作手册(规则、约束、Phase 顺序、Definition of Done)
  • docs/api.md — 完整 API 文档
  • FuelLog_PLAN.md — 原始产品 / 技术决策文档(仓库内)
S
Description
No description provided
Readme
316 KiB
Languages
Go 46.6%
Vue 30%
TypeScript 8.5%
PowerShell 7.5%
JavaScript 3.4%
Other 4%