Files
FuelLog/docs/api.md
T
KennethCheng f24e6639e3 docs: 仓库根 README + docs/api.md 完整 API 文档
补上交付物必备的文档:
- README.md:项目介绍 / 特性 / 技术栈 / 快速开始 / 仓库结构 /
  核心计算公式 / 测试覆盖 / 设计原则 / 路线图 / License
- docs/api.md:6 个 endpoint 完整契约(请求参数 / 响应 / 错误码 /
  业务规则),含 Statistics 字段含义与采样逻辑说明

为什么现在补:
- 之前的 12 个 commit 都是代码,GitHub / Gitea 仓库首页空白
- 新成员(或者未来的自己)来看仓库,无法在 5 分钟内知道这是个啥 / 怎么跑
- API 文档从代码里的 dto.go / handler.go 反推,容易漏边界条件;集中写一遍
  把所有约束沉淀下来

README 设计:
- 顶上一段简介 + badge
- ✨ 特性(7 条,每条对应 plan §3 / §6 / §12)
- 🛠 技术栈:表格化展示 plan 锁定选型
- 🚀 快速开始:5 步(克隆 / 装依赖 / 启后端 / 启前端 / 冒烟)
- 📁 仓库结构:树状展示两个子工程的完整布局
- 🔢 核心计算规则:4 个公式表(从 plan §6 提炼)
- 🧪 测试覆盖:列出 22+5+13 的具体来源文件
- 🐳 部署 / 🗺 路线图:占位 + 标 done
- 📐 设计原则:把 plan §12 + AGENTS.md 关键约束翻译成"为什么这么做"
- 📎 相关:链回 AGENTS.md / docs/api.md / FuelLog_PLAN.md

docs/api.md 设计:
- 通用约定:UTC / decimal / 列表空数组 / 错误码字典
- 6 个 endpoint 一段一个,含:curl-style 示例、参数表、Response 200 示例、
  业务规则(例如 RefuelCreate.price_per_liter 不入参 / RefuelUpdate
  patch 语义 / Statistics 只在 is_full 序列内采样)
- 错误码字典表格(INTERNAL / INVALID_ARGUMENT / NOT_FOUND / CONFLICT / ...)
- 索引说明

风格:中文为主,与 AGENTS.md / commit message 保持一致;技术名词留英文
(endpoint / Vehicle / RefuelRecord 等);代码示例保持 JSON 风格。

未做(留待后续):
- README 截图:得真起服务跑,等 Docker 起来再补
- README 英文版:暂只中文,MVP 上线后再翻
2026-09-13 17:06:39 +08:00

4.8 KiB

FuelLog API 文档

Base path: /api 错误统一格式: { "code": "INVALID_ARGUMENT", "message": "..." } 鉴权: MVP 阶段不要求,MVP 之后会在 Authorization: Bearer <token> 头里加。


通用约定

  • 所有时间字段统一 UTC 存库;请求时传 RFC3339 字符串,响应回 RFC3339。
  • 金额 / 加油量 / 里程 / 油耗:浮点;金额计算在服务端用 shopspring/decimal 避免累计误差。
  • 列表接口默认空数组(不返回 null)。
  • 删除为硬删(数据库里 DELETE);二次确认在前端做。
  • 默认车辆唯一,切换走事务,避免出现两个 default。

错误码字典(在 pkg/apperror):

code HTTP 含义
INTERNAL 500 兜底,未预期
INVALID_ARGUMENT 400 参数校验失败
NOT_FOUND 404 资源不存在
CONFLICT 409 资源冲突(预留)
UNAUTHORIZED 401 未鉴权(预留)
FORBIDDEN 403 无权限(预留)
UNAVAILABLE 503 暂不可用(预留)

健康检查

GET /health

{ "status": "ok", "time": "2026-09-13T16:11:32+08:00" }

车辆 /api/vehicles

GET /api/vehicles

列出所有车辆,按 default_vehicle DESC, id ASC 排序。

Response 200

[
  {
    "id": 1,
    "name": "我的夏",
    "brand": "比亚迪",
    "model": "秦 PLUS DM-i",
    "plate_number": "京A12345",
    "fuel_type": "hybrid",
    "tank_capacity": 48,
    "current_odometer": 10000,
    "default_vehicle": true,
    "created_at": "2026-09-13T16:00:00+08:00",
    "updated_at": "2026-09-13T16:00:00+08:00"
  }
]

POST /api/vehicles

创建车辆。

Request

字段 类型 必填 说明
name string ✅ 车辆名称,max 64
brand string max 64
model string max 64
plate_number string max 32
fuel_type string ✅ gasoline / diesel / hybrid / electric / plug_in_hybrid
tank_capacity number 升,>= 0
current_odometer number 公里,>= 0
default_vehicle bool 设为默认时,旧默认会被清掉(事务)

Response 200 — 完整 Vehicle 对象

GET /api/vehicles/:id

返回单辆;不存在返 404。

PUT /api/vehicles/:id

Patch 语义:*string / *number / *bool 表示可选,未传的字段不更新。 其它校验同 POST。

Response 200 — 更新后的 Vehicle

DELETE /api/vehicles/:id

硬删。返 204 No Content。

POST /api/vehicles/:id/default

设为唯一默认。事务内先清旧默认,再置新默认。

Response 200 — 更新后的 Vehicle


加油记录 /api/refuels

POST /api/refuels

Request

字段 类型 必填 说明
vehicle_id uint64 ✅ 关联车辆
refuel_time RFC3339 ✅ 加油时间,UTC 存库
odometer number ✅ 加油时里程,>= 0
fuel_type string ✅ 油品
liters number ✅ 加油量,> 0
total_amount number ✅ 总金额,>= 0
station_name string max 128
is_full bool 是否加满(决定是否进入标准油耗)
remark string max 512

注意:price_per_liter 不入参,由服务端用 total_amount / liters 实时算出,落库保留历史真实单价。

Response 200 — 完整 RefuelRecord

GET /api/refuels/:id

PUT /api/refuels/:id

Patch 语义。liters / total_amount 任一更新都会重新算 price_per_liter。

DELETE /api/refuels/:id

返 204 No Content。

GET /api/refuels?vehicle_id=...&start_date=...&end_date=...

参数 必填 说明
vehicle_id ✅ uint64
start_date RFC3339,只返 refuel_time >= 它的
end_date RFC3339,只返 refuel_time <= 它的

按 refuel_time DESC, id DESC 排序。

Response 200 — RefuelRecord 数组


统计 /api/vehicles/:id/statistics

单车的关键统计指标,服务端正序遍历 refuel_time 倒序列表,只在 "连续两次 is_full=true"之间采样油耗 / 每公里成本;半箱油记录不会污染。

Response 200

{
  "vehicle_id": 1,
  "total_records": 3,
  "total_liters": 82.0,
  "total_amount": 656.0,
  "avg_price": 8.0,
  "avg_consumption": 6.4,
  "cost_per_km": 0.51,
  "last_refuel": { /* RefuelRecord 或 null */ }
}

字段含义:

  • avg_price 全部记录的实际油价算术平均
  • avg_consumption 标准百公里油耗 L/100km(仅 is_full 序列的样本平均)
  • cost_per_km 每公里燃油成本 元/km(仅 is_full 序列的样本平均)
  • last_refuel 最近一次加油记录(按 refuel_time DESC 第一个)

空车辆返 { total_records: 0, ...全 0, last_refuel: null }。


索引

  • 车辆:vehicle.id PK
  • 加油记录:refuel_record.id PK;(vehicle_id, refuel_time) 与 vehicle_id 各自索引