Files
oil-price-monitoring/docs/40.记账概要设计.md
maxf 28fcfc6aec ## 1️⃣ 文档与代码一致性 ✅(修复 4 处滞后)
**发现并已修复的文档滞后**(后期代码先于文档,审计时补齐):

| 滞后点 | 修复 |
|---|---|
| `24.附录` users 表缺 `wechat_openid` 列(二期微信登录加的) | ✅ 已补 |
| `24.附录` 缺 `refuel_records` 表(三期记账) | ✅ 已补 |
| `24.附录` 错误码缺 `1012/1013`(三期) | ✅ 已补 |
| `34.附录` 小程序页面目录缺 `calc/refuels`(三期) | ✅ 已补 |

**核对一致项**:
- API 路由 **A1-A23 + `/api/auth/wechat`** 全部注册,与 22/33/43 文档逐项对应
- `schema.sql` 11 表 = 24 附录(10 + refuel)+ 45 附录
- 小程序 8 页文件齐全(js/wxml/wxss/json × 8),`refuels.wxml` 数据绑定与 js 匹配
- H5 8 页面全部可访问

## 2️⃣ 后期变更对前期的影响 ✅ 无破坏

综合回归 **20 项全 PASS**:
- **一期**:账号密码登录 / Cookie 会话 / dashboard / provinces(31省) / history ✅
- **二期**:Bearer token 认证(与 Cookie **双通道共存**,互不冲突)✅
- **三期**:refuel 首条保存 / 区间油耗 8.25 / 统计 / 预填 ✅
- **前端**:9 个资源(8 页 + 静态 JS)全部 200 ✅

关键:二期的 `users` 加列是**幂等增量迁移**(ALTER),未动一期任何表结构;一期 API 与前端全部正常。

## 3️⃣ 整体可行性 ✅ 在可控范围

| 维度 | 状态 |
|---|---|
| 构建/静态检查 | `go build / vet` 全绿 |
| 单元测试 | 6 包 17 单测全过(auth/crawler/cron/db/push/service) |
| 规模 | 11 表 · 23+ 接口 · 8 H5 页面 · 8 小程序页面 · 17 份文档 · ~90 代码文件 |
| 三阶段功能 | 监控预测提醒 → 微信小程序 → 加油记账/真实油耗,全部落地 |

**非阻塞小项**(可后续优化,不影响运行):
1. `refuels.wxml` 第 11 行 `wx:for` 与 `wx:if` 同节点——功能正常,写法可规范
2. api 包 handler 层无独立单测——由端到端回归覆盖(service 层已有单测)

---

**结论:项目整体健康、文档与代码对齐、三阶段功能完整可用。** 4 处文档滞后已修复,无功能性问题。可以进入部署/上线阶段。
2026-09-25 16:56:07 +08:00

9.5 KiB
Raw Permalink Blame History

⛽ 三期 · 记账功能 — 概要设计

上游:docs/10.概要设计.md(v1.2)、docs/22.API详细设计.md、docs/24.附录.md 定位:加油记账 + 真实油耗计算 + 独立油耗计算器 + 油耗趋势折线图(H5 与微信小程序双端) 版本:v1.1 · 2026-09-25


目录


一、业务目标与核心问题

1.1 业务目标

记录每次加油,算出真实油耗(元/km、L/100km),展示油耗趋势折线图;并提供独立油耗计算器(无需登录/车辆也能算)。

1.2 必须解决的漏洞(用户指出)

"万一用户中间忘记一次记录,会产生很大的误差"

简单"每次记一笔"模型下,漏记会使后续油耗计算错误。

解决思路:补记模式(快照 + 差值)

  • 每次加油记录是状态快照(里程 + 油价 + 实付金额 + 加油量)
  • 油耗 = 相邻两次快照的差值计算,不依赖"每次必记"
  • 漏记后自动以最近有效快照为基准(平均化,不报废)
  • 异常检测:本次油价或油耗超历史平均 2 倍 → 提示"耗油量异常,是否漏记?"

1.3 设计约束

约束 说明
车辆维度 加油记录关联 vehicle_id(必填,无车先添加);每辆车独立油耗/里程/统计
加满为标签 full_tank 复选框仅作标签展示,不参与油耗计算
历史可修正 可编辑/删除/补录(补录可改加油日期),并级联重算受影响区间
双端一致 H5 与小程序共用同一套 API 与计算规则
计算器独立 计算真实油耗的页面无需登录/车辆;有车绑定保存,未登录不保存

二、核心模型:补记模式(快照 + 差值)

2.1 一次加油的完整生命周期(登录 + 有车)

用户加完油 → 进入"记一笔"页(或独立计算器)
  ├─ 后端回显(可修改):
  │    ├─ 当前油价 price        ← 默认取该车省份最新价(按车油号)
  │    ├─ 上次码表里程 last_odo ← 该车最近一条记录
  │    └─ 加油日期 refuel_at    ← 默认当前日期,可改(补录场景)
  ├─ 用户输入(必填):支付金额 amount / 当前码表里程 odometer
  │    可选:优惠金额 discount(默认0) / 本次是否加满 full_tank(复选框,标签) / 备注
  └─ 点击"完成"
       ├─ 加油量 liters = (amount - discount) / price
       ├─ 找上次有效记录 → distance = odometer - last_odo
       ├─ 若上次存在且 distance > 0:L/100km、元/km
       ├─ 存历史(refuel_records,油耗字段记在"本次"上)
       └─ 异常检测:本次油价或油耗超历史平均 2 倍 → possible_missing 提示

2.2 快照 vs 区间

R1 ──── R2 ──── R3(快照:里程/油量/日期)
R1→R2 区间油耗记在 R2;R2→R3 记在 R3。首条无"上次",油耗 NULL。

2.3 漏记/异常行为

实际:R1 ──(漏记)── R3
记录:R1 ──────── R3
  R3 的 distance = R3.odometer - R1.odometer(跨漏记区间)
  liters = R3.liters(只有本次量)→ 平均油耗(偏低但可用)
  • 异常检测:本次 price 或 calc_l_per_100km 超历史平均 2 倍 → possible_missing=true → 前端提示"耗油量异常,是否有漏记?"
    • 是 → 跳历史页补录(填漏记那次的日期/里程/金额/油价)→ 级联重算恢复精确
    • 否 → 保留记录(平均油耗),继续
  • 无历史平均(首条)→ 不检测

三、独立油耗计算器

定位:把"计算真实油耗"单独拎出,有车无车 / 登录未登录均可进入。

3.1 访问矩阵

场景 回填 保存
未登录 无回填(全手输) 不保存(仅展示计算结果)
已登录 + 有车 回填该车:当前油价 / 上次里程 / 上次日期 计算后可选"保存到车辆" → POST /api/refuels
已登录 + 无车 无回填 提示"请先添加车辆",引导到爱车页
已登录 + 有车 + 无记录 无上次里程(提示"首次加油,无上次基准") 保存为首条记录(油耗 NULL,后续可算)

3.2 流程

进入计算器
  ├─ 已登录 + 有车 → 页面顶部展示【当前车辆】可切换(A4 车辆列表)
  │    ├─ 切换车辆 → 请求 A22 prefill → 回填当前油价 / 上次里程 / 上次日期(可改)
  │    └─ 该车无记录 → 展示"暂无历史"(无上次基准)
  ├─ 未登录 → 顶部展示登录入口"登录后可实时追踪真实油耗"(点击登录)
  │    └─ 输入框:油价 / 上次里程 / 上次加油日期(可空,用户手输)
  ├─ 输入:本次里程 / 支付金额 / 优惠金额(0) / 是否加满(标签) / 加油日期(默认当前)
  └─ 点"计算油耗" → 本地公式算出 L/100km、元/km → 展示结果卡片
       ├─ 已登录 + 有车 → 按钮"保存到车辆" → POST A19 → 保存 + 异常检测
       └─ 未登录 → 结果卡片下方展示"登录后可实时追踪真实油耗"(点击登录)

计算逻辑为纯公式,前端本地即可完成(未登录不触达后端私有接口)。


四、公式概览

量 公式
实付金额 net net = amount - discount
加油量 liters liters = net / price(保留 2 位)
区间里程 distance distance = odometer_now - last_odometer
区间油耗 L/100km l_per_100km = liters / distance × 100(保留 1 位)
区间成本 元/km yuan_per_km = net / distance(保留 2 位)

生效条件:无上次(首条)→ NULL;distance <= 0 → anomaly=odometer(里程回退,标黄提示);加满与否不参与计算(full_tank 仅标签)。

公式完整定义与异常检测规则见 docs/45.记账附录.md。


五、数据表概览

refuel_records(加油记录表)

字段 类型 说明
id / user_id / vehicle_id — 主键 / 归属用户 / 归属车辆
refuel_at / odometer — 加油时间(默认当前,可补录)/ 码表里程(km)
price_per_liter / amount / discount / net_amount — 油价 / 支付 / 优惠 / 实付金额
liters / fuel_type / full_tank / note — 加油量 / 油号 / 是否加满(标签) / 备注
calc_distance_km / calc_l_per_100km / calc_yuan_per_km — 区间里程 / 区间油耗 / 区间成本(可空)
anomaly — odometer / NULL(里程回退标记)

完整 DDL 见 docs/45.记账附录.md。


六、API 概览

接口 方法 功能 认证
/api/refuels GET 记录列表(含油耗字段) 🔒
/api/refuels POST 新增(算油耗 + 异常检测) 🔒
/api/refuels/:id PUT 修正(级联重算) 🔒
/api/refuels/:id DELETE 删除(级联重算) 🔒
/api/refuels/prefill GET 记一笔回填(当前油价 + 上次里程) 🔒
/api/refuels/stats GET 统计(折线图 + 汇总 + 最近油耗) 🔒

完整请求/响应/字段/SQL 见 docs/43.记账API详细设计.md。计算器本地计算不新增后端接口(保存复用 A19)。


七、前端概览

端 页面 功能
H5 /fuel-calc 独立油耗计算器(无需登录/车辆)
H5 /refuels?vehicle=X 记录列表 + 记一笔 + 统计(ECharts 折线图)
H5 爱车页入口 每辆车"加油记录"按钮 + "最近一次油耗"卡片
小程序 pages/refuels/refuels 三 tab:记录 / 记一笔 / 统计(canvas 折线)
小程序 pages/calc/calc 独立油耗计算器(首页导航入口)

交互细节与折线图设计见 docs/44.记账前端详细设计.md。


八、与现有模块衔接

模块 衔接点
vehicles 表 加油记录 vehicle_id 归属(无车先添加);车辆卡片"加油记录" + "最近油耗"
province_prices A22 回填"当前油价"(该车省份 + 该车油号最新价)
api 路由 /api/refuels* 挂 authGuard
service 油耗计算/级联重算放 internal/service/refuel.go
数据库 schema.sql 增 refuel_records;db.Open 迁移兼容新旧库

九、已确认设计点(2026-09-25 用户确认)

# 确认项 定稿
1 计算器车辆选择 登录+有车:页面顶部展示当前车辆,可切换(A4 车辆列表);下方为"先算展示 → 保存到车辆"
2 未登录引导 结果卡片下方展示"登录后可实时追踪真实油耗"(点击登录)
3 无历史平均 直接展示"暂无历史"(该车无记录时提示,不弹异常)
4 补录日期 补录可改加油日期 → 按 refuel_at 排序落位 + 级联重算
5 最近油耗卡片 爱车首页展示该车最近一次 calc_l_per_100km,点击跳统计页