# FuelLog API 文档 > Base path: `/api` > 错误统一格式: `{ "code": "INVALID_ARGUMENT", "message": "..." }` > 鉴权: MVP 阶段不要求,MVP 之后会在 `Authorization: Bearer ` 头里加。 --- ## 通用约定 - 所有时间字段统一 **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` ```json { "status": "ok", "time": "2026-09-13T16:11:32+08:00" } ``` --- ## 车辆 `/api/vehicles` ### `GET /api/vehicles` 列出所有车辆,按 `default_vehicle DESC, id ASC` 排序。 **Response 200** ```json [ { "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** ```json { "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` 各自索引