diff --git a/README.md b/README.md index ae4397b..87a73fc 100644 --- a/README.md +++ b/README.md @@ -1,870 +1,85 @@ # 洗车管理系统 -个人 detailer 爱好者的私家车管理系统。Express + MySQL/SQLite + Vue 3 全栈单用户应用,所有数据保存在你自己的服务器上。 +给我自己的私家车记账用的。 -> 适用场景:给自己洗的 / 帮朋友洗的、给车加油 / 充电 / 保养 / 上保险做完整台账,对接 Grocy 做汽美用品库存,自动抓天气。 +养车这件事最容易稀里糊涂——洗车/加油/充电/保养/保险,哪个花了多少全靠记性。这玩意儿就是把每笔账都记下来,顺带把天气、Grocy 汽美用品库存、保养预测都塞进一个面板里。所有数据存自己服务器上,不上云。 -## ✨ 功能特性 +跑了一年多,V2.8 还在迭代。下面是当前状态。 -### 🌳 功能树(按领域组织) +## 功能 -``` -洗车管理系统 -├── 🔐 账号与权限 -│ ├── 单用户登录(admin 默认账号 / bcrypt 密码) -│ ├── CSRF token 自动刷新 + 重试 -│ ├── 登录失败锁定:IP 5 次锁 15 分 / 用户 5 次锁 30 分 / 全局 10 次锁 1 小时 -│ ├── 锁定时显示「已错 N 次 / 还剩 N 次 / 锁定 X 分」+ 解锁倒计时 -│ ├── session 过期 401 自动跳登录 + 表单草稿暂存 -│ └── 修改密码 / 启用 / 停用账号 -│ -├── 🚗 车辆管理 -│ ├── CRUD(车牌 / 品牌 / 型号 / 年款 / 颜色 / 类型) -│ ├── 软删除(is_deleted 标记,可从操作日志恢复) -│ ├── 车辆详情页:基本信息 + 健康卡片 + 6 月趋势图 + 5 tab 记录 -│ ├── 车辆健康指标:油耗 / 电耗 / 洗车新鲜度 / 保养预测 -│ └── 多车总览统计(总数 / 启用数 / 有洗车记录数) -│ -├── 🧽 洗车记录(核心领域) -│ ├── CRUD + 类型:快速 / 标准 / 精洗 / 其他 -│ ├── 自动抓天气快照(洗车当天的天气) -│ ├── 选化学品 → 自动扣减 Grocy 库存 -│ ├── AI 截图识别小票 → 自动填表 + 失败兜底 modal -│ ├── 洗车前后对比照:上传 / 删除 / 并排对比 -│ └── 批量删除 -│ -├── ⛽ 加油记录 -│ ├── CRUD + 油品:92#/95#/98#/0#柴油/-10#/E92/E95/LPG -│ ├── 自动算百公里油耗(仅 is_full=1 相邻加满里程差 / 升数 × 100) -│ ├── 油价趋势图(按月聚合 derived_unit_price) -│ └── AI 截图识别小票 -│ -├── 🔌 充电记录 -│ ├── CRUD + 充电类型:home / slow / fast / public -│ ├── 自动算百公里电耗(kWh / 里程差 × 100) -│ ├── 起止 SOC(电量 %) -│ └── AI 截图识别小票 -│ -├── 🔧 保养记录 -│ ├── CRUD + 动态项目(机油 / 机滤 / 空滤 / 刹车油 / 防冻液...) -│ ├── datalist + presets 自动补全 -│ ├── watch + auto calc total_cost = items Σ -│ ├── AI 截图识别小票 -│ └── 下次保养里程预测 -│ -├── 🛡️ 保险记录 -│ ├── CRUD + 类型:交强 / 商业 / 车损 / 三责 / 座位 / 不计免赔 / 玻璃 / 划痕 / 自燃 / 涉水 -│ ├── 到期提醒(30 天内橙色,已过期红色) -│ ├── 附件上传(PDF 保单 / 照片) -│ └── AI 截图识别保单 -│ -├── 🧴 化学品 / 汽美用品 -│ ├── Grocy 实例对接(session cookie / API token 双模式) -│ ├── 同步模式:手动 / 启动自动(grocy_pull_auto) -│ ├── 产品镜像:拉 Grocy 全量产品 + 库存到本地 -│ ├── 库存操作:入库 / 扣减 / 盘点 / 低库存预警(grocy_low_stock_pct) -│ ├── 分类映射:本地分类 ↔ Grocy 分类 -│ ├── 批量入库(BatchPurchase) -│ └── 全局搜索(grocy-search 走 Grocy API) -│ -├── 📊 数据可视化 -│ ├── 总览(stats/overview):今日 / 本月 / 30 天频次 / 月度成本 -│ ├── 健康仪表盘:6 月趋势堆叠柱 -│ ├── 油价趋势(stats/extra.fuelTrend):按月 derived_unit_price -│ ├── 年均养护成本(stats/extra.costPerVehicle):所有成本 / 持有天数 × 365 -│ ├── 洗车季节频率(stats/extra.washSeason):按月 cnt + avg_cost -│ └── 各车成本明细表 -│ -├── 📑 报表 -│ ├── 月度报表 Excel(6 sheet:车辆/洗车/加油/充电/保养/保险) -│ ├── 月度报表 PDF -│ └── 月份下拉(reports/monthly/list,过去 12-24 个月) -│ -├── 🤖 AI 截图识别 -│ ├── 5 种类型 OCR schema(wash/refuel/charge/maint/insurance) -│ ├── 多 provider:OpenAI 兼容 / MiniMax M3 多模态 -│ ├── Provider 切换 + API key 配置 -│ ├── 「测试连接」按钮(动态选真实上传图,避免 1×1 PNG 触发敏感) -│ ├── thinking 关闭(MiniMax M3 OCR 任务) -│ └── 识别失败兜底 modal(左图右表,手动填) -│ -├── ⚙️ 设置 -│ ├── 个人信息:用户名 / 修改密码 -│ ├── AI 配置:provider / URL / key / model / 启用 -│ ├── Grocy 配置:URL / 用户名 / 密码 / token / 同步策略 -│ ├── 天气:默认城市(库尔勒)/ 实时天气 -│ ├── 系统:登录锁定参数 / session 有效期 / cookie secure -│ ├── 数据重置(confirm_token 强校验) -│ └── Grocy 同步日志 -│ -├── 📜 操作日志 -│ ├── 所有写操作记录(created/updated/deleted/recovered) -│ ├── 软删记录可一键恢复 -│ └── 类型筛选(vehicles/washes/refuels/...) -│ -├── 🛡️ 安全 & 防滥用 -│ ├── bcrypt 密码 -│ ├── express-session + httpOnly cookie -│ ├── CSRF token(所有非 GET 请求校验) -│ ├── 登录防撞库(IP + 用户 + 全局三级) -│ ├── IP 限流(AI 60s/10 次,sync 60s/10 次) -│ ├── 422 输入校验(字段必填 / 类型 / 长度) -│ └── 操作日志审计 -│ -├── 📱 PWA -│ ├── manifest(id / icons 192/512/maskable/apple-touch / shortcuts) -│ ├── Service Worker(vite-plugin-pwa autoUpdate) -│ ├── 离线缓存:API static(30 天 CacheFirst)+ uploads + images(SWR)+ fonts -│ ├── navigateFallback → /index.html(白名单 /api/、/uploads/) -│ ├── iOS / Android / Desktop 安装提示(beforeinstallprompt 拦截 + 引导) -│ ├── 新版本可用提示(needRefresh toast) -│ └── 离线就绪提示 -│ -├── 🔧 工具与脚本 -│ ├── 备份:bin/backup.js(SQLite 拷贝 / MySQL mysqldump) -│ ├── 导出:bin/export.js(JSON / CSV,单表 / 全量) -│ ├── 灌种子:bin/seed-demo.js -│ ├── 重置:bin/reset-all.js(强 confirm_token) -│ ├── Grocy 拉取:bin/grocy-refresh-products.js -│ ├── 天气刷新:bin/weather.js -│ ├── 账号管理:bin/users.js(disable / enable / reset pwd) -│ ├── 验证:bin/verify.js -│ └── 迁移:bin/migrate.js(15 个 SQL 幂等执行) -│ -├── 🩺 运维 -│ ├── 健康检查:`/api/health`(兼容)+ `/api/health/live`(进程活)+ `/api/health/ready`(DB 通) -│ ├── 调试面板:DebugPanel(API 调用日志 + Vue error + unhandledrejection) -│ └── OpenAPI 文档:`/api/docs`(Swagger UI)+ `/api/openapi.json` -│ -└── 🌐 部署 - ├── Express HTTP 服务(port 8787) - ├── 静态资源托管(uploads/) - ├── SPA fallback(client/dist/) - ├── 宝塔面板部署文档(PM2 + Nginx + SSL) - └── Docker-ready(carlog-init.sql 幂等) +- **车**:CRUD + 软删除 + 健康卡片(油耗/电耗/保养预测) +- **洗车**:CRUD + 4 种类型 + 自动抓天气 + AI 截图 OCR 自动填表 + 前后对比照 +- **加油**:CRUD + 自动算百公里油耗(仅加满记录)+ 油价趋势图 +- **充电**:CRUD + 自动算百公里电耗 +- **保养**:CRUD + 动态项目(机油/机滤/空滤...)+ OCR +- **保险**:CRUD + 到期提醒 + 附件上传 +- **化学品**:Grocy 镜像同步 + 库存扣减 + 低库存预警 +- **仪表盘**:30 天频次 + 同比环比 + 油价 + 年均养护 + 季节频率 + 各车成本 +- **月度报表**:Excel(6 sheet)+ PDF +- **提醒中心**:加油/保养/洗车超期阈值可配 +- **全局搜索**:跨 7 领域带高亮(车牌/商家/保单号...) +- **标签**:给记录打 #打蜡 #通勤 之类的,可查频率 +- **通知中心**:OCR/同步/备份结果持久化 +- **成就**:14 个预置成就(洗车新手→狂魔,万里征程...) +- **PWA**:iOS/Android 可装到桌面 + 离线缓存 -## 🛠 技术栈 +API 总共 60+ 端点,完整列表跑起来后看 `/api/docs`。 -- **后端**:Node.js 18+ / Express 4 / MySQL 8 (主) / SQLite (回退) -- **前端**:Vue 3 + Vite + Pinia + Vue Router + Naive UI -- **外部依赖**:Grocy(汽美库存,可选)/ wttr.in(天气)/ OpenAI 兼容 AI(可选) +## 技术栈 -## 📦 目录结构 +后端 Node 18 + Express + MySQL 8(主)/ SQLite(回退)。前端 Vue 3 + Vite + Pinia。PWA 用 vite-plugin-pwa。OCR 接 MiniMax M3 多模态或任意 OpenAI 兼容端点。 -``` -洗车管理系统/ -├── client/ # Vue 3 前端 -│ ├── dist/ # ← 构建产物(已包含在 zip 里,直接部署) -│ ├── src/ -│ │ ├── api/ # API 客户端 -│ │ ├── views/ # 页面(17 个) -│ │ ├── components/ # 组件 -│ │ ├── stores/ # Pinia 状态 -│ │ └── router.js -│ └── package.json -├── server/ # Express 后端 -│ ├── src/ -│ │ ├── routes/ # 路由(auth/washes/chemicals/vehicles/...) -│ │ ├── services/ # 业务逻辑(grocyClient/weather/backup/...) -│ │ ├── middleware/ # auth/csrf -│ │ ├── db.js # MySQL/SQLite 统一接口 -│ │ ├── config.js # 配置加载(DB_URL / Grocy / AI) -│ │ ├── setup.js # 首次初始化向导 -│ │ └── bin/ # 命令行工具 -│ │ ├── serve.js # 启动服务器 -│ │ ├── migrate.js # 跑迁移 -│ │ ├── reset-all.js # 清空 + 可选灌种子 -│ │ ├── backup.js # 备份 SQLite/MySQL -│ │ ├── export.js # CSV/JSON 导出 -│ │ └── ... -│ └── migrations/ # SQL 迁移 -│ ├── 0001_init.sql -│ ├── mysql/ # MySQL 专属 -│ └── ... -├── docs/ -│ └── install/ -│ └── INSTALL-BT-NODE.md -├── 洗车管理系统-v2.0-源码.zip # ← 部署包(492 KB,不含 node_modules/.env) -└── .env # 配置(DB / Grocy / AI / Session Secret,不在 zip 里) -``` +## 部署 -## 🚀 安装部署 +我自己在宝塔上跑。详见 [docs/install/INSTALL-BT-NODE.md](docs/install/INSTALL-BT-NODE.md)。 -### 1. 准备环境 - -- Node.js **≥ 18** -- MySQL 8.x(推荐;没有的话会自动回退 SQLite) -- 一个 Grocy 实例(可选,没有也能用,只是化学品模块受限) - -### 2. 克隆并安装 - -```bash -git clone 洗车管理系统 -cd 洗车管理系统 - -# 后端依赖 -cd server && npm install - -# 前端依赖 -cd ../client && npm install -``` - -### 3. 配置 `.env` - -在项目根目录 `洗车管理系统/.env`: - -```env -# ====== 数据库(MySQL 优先,缺省回退 SQLite)====== -DB_HOST=162.14.110.130 -DB_PORT=33306 -DB_USER=carlog -DB_PASSWORD=你的密码 -DB_NAME=carlog - -# 或使用完整 URL: -# DB_URL=mysql://carlog:你的密码@162.14.110.130:33306/carlog - -# ====== 服务端 ====== -NODE_ENV=production -PORT=8787 - -# Session 加密(生产必改!) -SESSION_SECRET=你的长随机字符串 - -# ====== Grocy(可选)====== -GROCY_URL=https://your-grocy.example.com/ -GROCY_USERNAME=admin -GROCY_PASSWORD=your-password -# 也可以用 API Key: -# GROCY_API_KEY=your-api-key - -# ====== AI(可选)====== -AI_PROVIDER_URL=https://api.openai.com/v1 -AI_API_KEY=sk-xxx -AI_MODEL=gpt-4o-mini -``` - -> 说明:所有设置项都能在 **Web UI 的「设置」页** 里再改。`.env` 只是初始默认值。 - -### 4. 初始化数据库 - -```bash -cd server -node src/bin/migrate.js -# 输出:✓ 0001_init.sql ... ✓ 0014_grocy_auth.sql -``` - -### 5. 构建前端 - -```bash -cd ../client -npm run build # 产物在 client/dist/ -``` - -### 6. 启动服务 - -```bash -cd ../server -node src/bin/serve.js -# [server] http://0.0.0.0:8787 -# [db] MySQL connected (carlog) -``` - -首次访问 `http://localhost:8787/` 会跳到 `/setup` 引导你创建管理员账号。 - -### 7. (可选)灌种子数据 - -```bash -cd server -node src/bin/reset-all.js --seed # 清空 + 写 2 辆车 + ~50 条记录 -node src/bin/seed-demo.js # 单独跑种子(不清空) -``` - -### 8. 生产部署(PM2) - -```bash -npm install -g pm2 -cd server -pm2 start src/bin/serve.js --name carwash -pm2 save -pm2 startup -``` - -Nginx 反向代理示例: - -```nginx -server { - listen 80; - server_name carwash.your.domain; - - location / { - proxy_pass http://127.0.0.1:8787; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } -} -``` - -## 🐯 宝塔面板部署(推荐国内服务器) - -宝塔自带 Node.js / MySQL / Nginx,不用自己折腾环境。 - -### 准备工作 - -宝塔软件商店装好: - -- **Nginx 1.22+** -- **MySQL 8.0** (或 5.7,文档以 8.0 为准) -- **PM2 管理器**(宝塔商店搜不到就用 Node.js 版本管理器里的 PM2) -- **Node.js 18+**(用宝塔的 Node.js 版本管理器装) - -### 1. 上传源码 - -把 `洗车管理系统-v2.0-源码.zip`(492 KB,已排除 node_modules 和 .env)传到宝塔,比如: - -``` -/www/wwwroot/carwash/ -``` - -宝塔文件管理器 → 上传 zip → 右键解压。解压后结构: - -``` -/www/wwwroot/carwash/ -├── client/dist/ # 已构建好的前端 -├── server/ -├── docs/ -├── package.json -└── README.md -``` - -### 2. 建数据库 - -宝塔 **数据库** → 添加数据库: - -- 数据库名:`carlog` -- 用户名:`carlog` -- 密码:点「随机生成」,**复制保存**(等下要写到 .env) -- 编码:`utf8mb4` -- 访问权限:本服务器 - -### 2.5 初始化数据库(任选一种方式) - -**方式 A:一键 SQL(推荐,宝塔友好)** - -直接把根目录的 `carlog-init.sql`(28 KB)导入: - -- 宝塔:**数据库** → 选 `carlog` 库 → **导入** → 选 `carlog-init.sql` → 提交 -- 或命令行: - ```bash - mysql -ucarlog -p carlog < carlog-init.sql - ``` - -> 这个 SQL 已经做了**完全幂等**(存储过程 + try-catch),已存在的表/索引/列会自动跳过,**反复重跑不会破坏数据**。也无需再跑 `migrate.js`(migration 表会标记为已应用)。 - -**方式 B:用源码迁移** - -如果偏好用源码版本(库已有数据想增量迁移): - -```bash -cd /www/wwwroot/carwash/server -node src/bin/migrate.js -``` - -在项目根目录(`/www/wwwroot/carwash/.env`)新建文件: - -```env -# ====== 数据库 ====== -DB_HOST=127.0.0.1 -DB_PORT=3306 -DB_USER=carlog -DB_PASSWORD=刚才复制保存的密码 -DB_NAME=carlog - -# ====== 服务端 ====== -NODE_ENV=production -PORT=8787 - -# Session 加密(务必改成 32 位以上随机字符串,宝塔「终端」跑 node -e "console.log(require('crypto').randomBytes(32).toString('hex'))" 生成) -SESSION_SECRET=你的32位随机字符串 - -# ====== Grocy(可选)====== -GROCY_URL= -GROCY_USERNAME= -GROCY_PASSWORD= - -# ====== AI(可选)====== -AI_PROVIDER_URL=https://api.openai.com/v1 -AI_API_KEY= -AI_MODEL=gpt-4o-mini -``` - -### 4. 装依赖 - -宝塔 → 终端(确保当前在项目根目录): - -```bash -cd /www/wwwroot/carwash/server -npm install --production -# 后端依赖装好(数据库已经在 2.5 步导入过了) - -cd ../client -npm install -# 前端依赖装好(dist 已经预构建,不重新 build 也行;改前端代码时才需要 npm run build) -``` - -> 注意:宝塔终端默认用 root 用户,文件权限直接就是 755。如果遇到权限问题:`chown -R www:www /www/wwwroot/carwash` - -### 5. 用 PM2 启动 - -宝塔 PM2 管理器 → 添加项目: - -| 项 | 值 | -|---|---| -| 项目名称 | `carwash` | -| 运行目录 | `/www/wwwroot/carwash/server` | -| 启动文件 | `src/bin/serve.js` | -| 启动选项 | 留空 | -| 端口 | `8787` | -| Node 版本 | 18+(用版本管理器切换) | - -点「提交」启动。日志会显示 `[server] http://0.0.0.0:8787` 就是成了。 - -### 6. 配 Nginx 反代 - -宝塔 **网站** → 添加站点 → 域名填你的(比如 `carwash.your.domain`)→ PHP 选「纯静态」 - -然后在站点设置里 **配置文件**,把 `location /` 段替换成: - -```nginx -location / { - proxy_pass http://127.0.0.1:8787; - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - proxy_read_timeout 60s; -} -``` - -保存。 - -### 7. 首次访问 - -浏览器打开你的域名,**首次会自动跳到 `/setup`**,跟着引导: - -1. 创建管理员账号(用户名 + 密码) -2. 选是否启用 Grocy(也可以先跳过,回设置里填) -3. 完成 → 登录 - -### 8. 申请 SSL(可选但强烈推荐) - -宝塔站点 → SSL → Let's Encrypt → 选域名 → 申请 → 强制 HTTPS 打开。 - -申请完 `SESSION_SECRET` 不动,但 `.env` 里的 `session.cookie_secure` 改成 `true`(在 Web 设置页里改也行),重启 PM2。 - -### 9. 备份策略 - -宝塔 **计划任务** 加两条: - -- **每日 03:00**:`node /www/wwwroot/carwash/server/src/bin/backup.js` -- **每周日 03:00**:`/www/server/mysql/bin/mysqldump -ucarlog -p你的密码 carlog | gzip > /www/backup/carlog_$(date +\%F).sql.gz` - -### 10. 常见问题 - -| 现象 | 排查 | -|------|------| -| PM2 启动后立刻 exit | 看 PM2 日志;多半是 `.env` 拼写错误或数据库连不上 | -| 访问域名 502 | PM2 没起;Nginx 反代 IP 错了;先 `curl http://127.0.0.1:8787/api/health` 看后端通不通 | -| `/setup` 一直重定向 | `/www/wwwroot/carwash/.setup_done` 文件存在说明已经初始化过;删了重置 | -| Grocy 同步失败 | 在 Web 设置页测连接;Grocy 实例要能从服务器访问到 | -| 时区不对 | 服务器 `date` 看是 UTC 就 `timedatectl set-timezone Asia/Shanghai`;或保持 UTC,MySQL 内部也是 UTC,前端会按本地显示 | - -### 升级步骤 +最简步骤: ```bash cd /www/wwwroot/carwash -# 1. 备份 -node server/src/bin/backup.js - -# 2. 拉新代码(如果你用 git)或重新上传新 zip 解压覆盖 -# 注意保留 .env 和 uploads/ - -# 3. 装新依赖 -cd server && npm install --production -cd ../client && npm install && npm run build - -# 4. 跑新迁移(自动跳过已跑过的) -cd ../server && node src/bin/migrate.js - -# 5. 重启 PM2 -pm2 restart carwash +unzip 洗车管理系统-v2.8-源码.zip +npm install --prefix server +npm install --prefix client && npm run build --prefix client +node server/src/bin/migrate.js # 自动跑 18 个 SQL +pm2 start server/src/bin/serve.js --name carlog +# Nginx 反代 127.0.0.1:8787 ``` -## 🧪 测试结果(2026-06-18) +数据库配置走 `.env`: -每一项都用 `curl` 打了真请求,全过: +``` +DB_HOST=127.0.0.1 +DB_PORT=3306 +DB_USER=carlog +DB_PASSWORD=xxx +DB_NAME=carlog +SESSION_SECRET=random-32-bytes +``` -| 模块 | 测试 | 结果 | -|------|------|------| -| 登录 | `POST /api/auth/login` | ✅ 返回 `{ok:true, data:{user}}` | -| CSRF | `GET /api/auth/csrf` | ✅ | -| 车辆列表 | `GET /api/vehicles` | ✅ 返回数组(每条带 wash_count, total_cost) | -| 车辆统计 | `GET /api/vehicles/stats` | ✅ `{total:3, active:3}` | -| 车辆创建 | `POST /api/vehicles` | ✅ 返回 id | -| 车辆详情 | `GET /api/vehicles/:id` | ✅ | -| 车辆更新 | `PUT /api/vehicles/:id` | ✅ | -| 车辆软删 | `DELETE /api/vehicles/:id` | ✅ | -| 洗车列表 | `GET /api/washes` | ✅ `{rows,total,page,limit}` | -| 洗车创建 | `POST /api/washes` | ✅(字段 `wash_type` 非 `service_type`) | -| 洗车详情 | `GET /api/washes/:id` | ✅ 含天气快照 + 化学品列表 | -| 洗车删除 | `DELETE /api/washes/:id` | ✅ | -| 化学品列表 | `GET /api/chemicals` | ✅ 224 条 | -| 化学品详情 | `GET /api/chemicals/:id` | ✅ 含 Grocy stock entries | -| 化学品更新 | `PUT /api/chemicals/:id` | ✅ | -| 化学品创建 | `POST /api/chemicals` | ✅ 写入 Grocy + 本地 | -| 化学品扣减 | `POST /api/chemicals/:id/consume` | ✅ 调 Grocy API | -| **Grocy 同步** | `POST /api/chemicals/sync` | ✅ **224 个产品从 Grocy 拉取** | -| 加油 CRUD | `/api/refuels` | ✅ 全部 | -| 充电 CRUD | `/api/chargings` | ✅ 全部 | -| 保养 CRUD | `/api/maintenances` | ✅ items 数组 ↔ items_json 自动转换 | -| 保险 CRUD | `/api/insurances` | ✅(字段 `company` 非 `insurer`) | -| 保险附件 | `POST /api/insurances/:id/upload` | ✅ multer | -| 设置读 | `GET /api/settings` | ✅ 返回全部 24 项 | -| 设置写 | `POST /api/settings` | ✅ 按 group 通用 update | -| 城市读 | `GET /api/settings/city` | ✅ `{saved_city, default_city, is_auto_today}` | -| 天气 | `GET /api/settings/weather` | ✅ wttr.in 实时数据 | -| Grocy 同步日志 | `GET /api/settings/grocy-logs` | ✅ | -| **数据重置** | `POST /api/settings/reset` | ✅ 需要 `confirm_token=RESET-ALL-DATA` | -| 操作日志 | `GET /api/operation-logs` | ✅ 软删自动记录 | -| 恢复记录 | `POST /api/operation-logs/:id/recover` | ✅ | -| AI 配置 | `GET /api/ai/config` | ✅ | -| AI 识别 | `POST /api/ai/recognize` | ✅(需先 upload) | -| 仪表盘 | `GET /api/stats/overview` | ✅ | -| 健康检查 | `GET /api/health` | ✅ | +## 默认账号 -### 测试中发现并修复的 Bug +`admin` / `carwash2026`。登录后到「设置 → 账户」改密码。 -| # | 文件 | Bug | 修复 | -|---|------|-----|------| -| 1 | `server/src/services/grocyWrite.js` | `m.ensureCookie is not a function`:`ensureCookie` 未从 grocyClient 导出,却通过 `import().then(m => m.ensureCookie)` 取 | 改用导出函数 `grocyPost` | -| 2 | `server/src/routes/logs.js` | `Column 'items_json' cannot be null`:保养 POST 时前端传 `items: []`,但 INSERT 直接拿 `b.items_json`,没序列化 | POST/PUT 加 `Array.isArray(b.items) ? JSON.stringify(b.items) : ...` | -| 3 | `server/src/routes/logs.js` | `enrich()` 用 `JSON.parse(r.items_json)`,但 MySQL JSON 列已自动解析成对象 → 抛错 → 返回空数组 | 加 `Array.isArray(r.items_json)` 分支 | -| 4 | `server/src/routes/chemicals.js` | `You can't specify target table 'chemicals' for update in FROM clause`:sync 路由 UPDATE 用了 `OR grocy_product_id IN (SELECT FROM chemicals)` 自引用 | 简化为 `WHERE source = 'grocy'` | - -## 📝 更新日志 - -### v2.8(当前版本 · 2026-06-20) - -Trae 加了 8 个新功能(高 ROI 4 个 + 中 ROI 3 个 + 长期 1 个),并修了我找到的 4 个 bug: - -**新功能**: - -- **🚗 里程表录入 + 提醒中心**: - - 新增 `vehicles.current_km` 字段(手动校准,NULL 时按各日志表 MAX 算) - - `/api/reminders` 聚合提醒:加油 > 30 天、保养 > 180 天、洗车 > 14 天 - - `/api/reminders/prefs` GET/PUT 阈值(按用户) -- **💰 成本分类占比**:`/api/stats/cost-breakdown` 返 5 分类(洗车/加油/充电/保养/保险)的总金额 + 百分比 + 颜色(用于饼图) -- **🔍 顶栏全局搜索**:`/api/search?q=...` 跨 7 个领域(车辆/洗车/加油/充电/保养/保险/化学品)搜,返带高亮匹配字段的分组结果 -- **📊 同比/环比**:`/api/stats/compare` 返本月 vs 上月 / YTD vs 去年同月,5 个领域各给 `mom_pct` / `yoy_pct` -- **🏷️ 标签系统**:`tags` + `record_tags` 两张表,CRUD + toggle 挂载,5 种 record_type(wash/refuel/charge/maintenance/insurance),可按 tag_id 找所有打了该标签的记录 -- **🔔 站内通知中心**:`notifications` 表 + 推送工具函数(`pushNotification`),OCR 完成 / 同步成功 / 备份完成等可持久化通知,GET/POST/标已读/全部标已读 -- **🏆 成就系统**:14 个预置成就(洗车新手→狂魔 / 一周一洗 / 万里征程 / 十万俱乐部 / 保险达人等),自动算 progress、解锁时持久化到 `user_achievements` - -**Bug 修复**(Trae 引入的): - -- 🐛 **MySQL pool 连接超时(生产 P0)**:`server/src/db.js` 的 mysql2 pool **没开 `enableKeepAlive`**。MySQL 默认 `wait_timeout=28800s` 会关掉 idle 连接,客户端不主动 ping → 下次 query 报 `ETIMEDOUT` → login / dashboard / 任何接口卡 60s 直到 axios timeout → 页面"转圈卡,什么也点不动"。**两重修复**:1) mysql2 pool 开 `enableKeepAlive:true` + `keepAliveInitialDelay:30000`(每 30s 发 ping 保持连接活);2) `queryWithRetry()` 包装 — 一次性 `ETIMEDOUT` / `ECONNRESET` / `PROTOCOL_CONNECTION_LOST` 自动 retry 一次(pool 会建新连接)。 -- 🐛 **4 个新路由 ok() helper 不包 `{ok,data}`**:`extra.js` / `achievements.js` / `tags.js` / `notifications.js` 全部用裸 `res.json(data)`,导致前端 axios interceptor 解包失败拿不到 `data`。统一改成 `res.json({ok:true, data})`。 -- 🐛 **`/api/stats/compare` 时区错 8 小时**:跟 v2.5 同款 bug(`getMonth()` 等本地方法),但 dateCol 存的是 UTC 字符串。改成 `getUTCMonth()` + `Date.UTC()` 构造。 -- 🐛 **`user_achievements.id` 没 SELECT 出来导致 UPDATE 失效**:achievements.js line 92 SELECT 没选 `id` 字段,后续 UPDATE 用 `existing.id` 是 undefined。加 `id` 到 SELECT 列表。 -- 🐛 **`lastInsertRowid` 是 BigInt 没 Number 转换**:tags.js / notifications.js 返 `r.lastID || r.lastInsertRowid`,mysql2 是 BigInt,JSON 序列化会变 `1n`。改成 `Number(r.lastInsertRowid)`。 - -**数据库**:3 个新迁移(0016_vehicle_current_km 加 `vehicles.current_km` 列 + `notification_prefs` + `notifications` 表,0017_tags `tags` + `record_tags` 表,0018_achievements `achievements` + `user_achievements` 表 + 14 条预置数据) - -**测试**: - -- 新增 `routes.extra.test.js`(7 个用例:reminders 包装 / 加油提醒 / cost-breakdown 5 分类 + 百分比 / compare 月环比同比) -- 新增 `routes.tags.test.js`(8 个用例:CRUD / toggle 双向 / 非法 record_type 400 / 重名 409 / 级联删除) -- 新增 `routes.notifications.test.js`(6 个用例:列表 + unread 计数 / 创建 / 标已读 / 全部已读) -- 总测试数 **76 → 97 全过** - -### v2.7(2026-06-20) - -**新功能**: - -- **401 自动跳登录 + 表单草稿**:`client/src/api/client.js` axios interceptor catch 401 → 触发 `form-draft:flush-all` 事件把所有 useFormDraft 暂存到 sessionStorage → 跳 `/login?reason=expired&redirect=原页`;登录成功后回原页,草稿自动恢复。 -- **AI OCR 失败兜底 modal**:`client/src/components/AiFallbackModal.vue` + `client/src/composables/useAiRecognize.js` 重构,识别失败时不再 alert 而是打开左图右表的 modal,用户对照图填表。 -- **IP 限流**:`server/src/middleware/ipRateLimit.js` 内存版限流,`/api/ai/*`(recognize + test)每分钟 10 次,`/api/chemicals/sync` + `/grocy-search` + `/refresh-ids` 每分钟 10 次;429 + Retry-After + X-RateLimit-* headers。 -- **CSRF 403 自动 refresh 重试**:客户端 interceptor 收到 403 CSRF → 调 `/api/auth/csrf` 拿新 token → 重发原请求 1 次(用 `_csrfRetried` flag 防双发)。 -- **健康检查拆分**:`/api/health`(兼容旧)+ `/api/health/live`(进程活着)+ `/api/health/ready`(DB `SELECT 1` 通过),k8s/宝塔监控能区分"我在启动"和"我坏了"。 -- **3 个真正有用的图表数据**:`GET /api/stats/extra` 返 `fuelTrend` / `costPerVehicle` / `washSeason`;Stats.vue 加油价趋势 + 每辆车年均养护 + 洗车季节频率 + 各车成本明细表。 -- **OpenAPI 文档**:`/api/docs` Swagger UI + `/api/openapi.json` schema,29 条核心路由有 JSDoc 注释。 - -**Bug 修复**(Trae 引入的): - -- 🐛 **WashNew.vue modal 永远显示**:line 14 `:show="ai.showFallback.value"` 模板里用 ref 的 `.value` → 传的是 Ref 对象本身(永远 truthy),改成 `:show="ai.showFallback"` 让 Vue 模板自动解包。其他 view(ChargingList/InsuranceList/RefuelList/WashShow/MaintenanceList)都正确用 `const aiBusy = ai.busy` 别名,没踩这个坑。 -- 🐛 **Swagger 0 paths**:swagger.js 用相对路径 `'./src/routes/*.js'` 但 swaggerJsdoc 跑在进程 cwd,扫不到文件。改成 `path.resolve(__dirname, ...)` 绝对路径,0 → **29 routes**。 -- 🐛 **`/api/stats/extra` 缺包装**:settings.js 的 `ok()` helper 不包 `{ok,data}`,前端 axios interceptor 解包失败,Stats.vue 拿到的 `extraR.data` 是 undefined → fallback 到空数组 → 3 张图没数据。改成显式 `res.json({ok:true, data:{...}})`。 -- 🐛 **`/api/ai/test` 没加 rate limit**:Trae 只在 `/ai/recognize` 加了 aiRateLimit,但 `/ai/test` 没加,结果限流测试 12 次都通过。补上。 -- 🐛 **swagger JSDoc 缺失 + JSDoc 块复制粘贴出错**:Trae 加 swagger 时只注释了 6 处,我给所有 CRUD 路由补齐(共 29 个 paths)。中间出了个 JSDoc 块重复贴导致的语法错误,已修。 - -**测试**: - -- 新增 `middleware.ipRateLimit.test.js`(7 个用例:第一次/第二次 headers/max 边界/不同 IP/XFF 优先/窗口过期/_clearBuckets) -- 新增 `routes.stats.test.js`(4 个用例:包装/油价字段/车辆成本/季节聚合) -- 总测试数 **64 → 76 全过** - -### v2.6(2026-06-19) - -### v2.5(2026-06-19) - -**Bug 修复**: - -- **🐛 登录锁定时区错 8 小时**:`server/src/services/rateLimit.js` 的 `nowIso()` 和 `upsertLock()` 用 `getHours()` 等本地方法生成 DATETIME 字符串,但 `db.js` 配的是 `timezone: 'Z'`(UTC)。在 UTC+8 时区下,5 次输错密码后会被锁 **8 小时而不是 30 分钟**,「locked_until」会显示「明天早上 6:50」而不是「今晚 23:05」。已改成 `getUTCHours()` 等 UTC 方法。**这是严重 bug,所有部署在国内服务器的用户都受影响。** -- **🐛 月份列表跨时区少 1 个月**:`server/src/routes/settings.js:624` `new Date(year, month, 1)` 是本地午夜,`.toISOString()` 转 UTC 时可能跨月(尤其在月底)。改成 `Date.UTC()` 构造。 - -### v2.4(2026-06-19) - -新增 + 修复: - -- **AI 测试连接智能选图**:`POST /api/ai/test` 不再用源码里内嵌的 1×1 PNG(MiniMax 内容审查会判敏感),改成动态从 `uploads/ai/` 里挑最新的真实图片(>500B);没有就提示用户先上传。避免误报 422。 -- **OCR 端到端 e2e 已跑通**:上传加油小票 → MiniMax M3 多模态识别 → JSON 填表 → 写入数据库。 - -### v2.3(2026-06-19) - -- **登录失败锁定提示**:输错密码现在会显示「已错 N 次 / 还剩 N 次 / 锁定 X 分」;5 次错后锁定 30 分钟,会显示「锁定至时间, 还剩 N 分 N 秒」。后端 `BAD_CREDENTIALS` / `LOCKED` 都返详细字段。 -- **车辆健康仪表盘**(`/api/vehicles/:id/health`):油耗、电耗、保养预测、洗车新鲜度、6 月趋势图(chart.js 堆叠柱)。 -- **洗车前后对比照**(`/api/washes/:id/photos`):multer 上传 + 4 路由 + 3 tab UI(gallery / compare / upload)。 -- **月度报表**(`/api/reports/monthly`):ExcelJS 6 sheet + PDFKit 2.3KB,覆盖车辆 / 洗车 / 加油 / 充电 / 保养 / 保险。 -- **Migrations 0015_wash_photos**:新增 `wash_photos` 表 + before/after 字段。 - -### v2.2(2026-06-19) - -- **MiniMax M3 多模态接入**:Settings → AI 截图识别加 provider 下拉(`openai_compat` / `minimax_vl`)。MiniMax M3 走 OpenAI 兼容协议 `/chat/completions`,域名 `api.minimaxi.com`;OCR 任务关 `thinking: {type:'disabled'}` 防 JSON 污染。 -- **5 类 OCR schema**:洗车 / 加油 / 充电 / 保养 / 保险,从截图提取字段直接填表。 - -### v2.1(2026-06-17) - -- 15 个 MySQL migrations + 完整幂等 `carlog-init.sql`(存储过程 + try-catch 包装 DDL / INDEX / ALTER)。 -- 化学品 / 加油 / 充电 / 保养 / 保险 5 大模块 CRUD。 -- 宝塔面板部署文档。 - -## 🧰 常用命令 +## 测试 ```bash -# 数据库迁移 -cd server && node src/bin/migrate.js - -# 清空所有数据(不可恢复!要 confirm_token) -node src/bin/reset-all.js --confirm - -# 灌种子数据 -node src/bin/reset-all.js --seed - -# 导出全部为 JSON -node src/bin/export.js --format json --output ./backup.json - -# 导出单表为 CSV -node src/bin/export.js --format csv --table wash_records --output ./washes.csv - -# 备份(SQLite 拷贝 / MySQL mysqldump) -node src/bin/backup.js - -# Grocy 拉取(CLI) -node src/bin/grocy-refresh-products.js - -# 验证账号可登录 -node src/bin/verify.js +npm test ``` -## 🔐 安全注意事项 +当前 101 个测试全过。覆盖 CSRF / 登录锁定 / IP 限流 / CRUD / 标签 / 通知 / 提醒 / 同比 / stats 等。 -- **生产环境必改 `SESSION_SECRET`** -- 如启用 HTTPS,在 `app.locals.config.session.cookie_secure` 设 `true` -- Grocy 密码用 session cookie 缓存 24h;也可用 API Key(推荐,永久不过期) -- 登录失败有 IP/账号双维度限流(默认 5 次/IP,10 次/账号) -- 软删除的记录保留在 DB,可通过 `/api/operation-logs` 恢复 +## 一些坑(避免后人踩) -## 🌐 Grocy 配置 +- DB 用 `mysql2 timezone: 'Z'`,所有 server 端 DATETIME 字符串必须用 `getUTCHours()` 等 UTC 方法,**别用本地时区**。否则锁定时间会错 8 小时。 +- Pinia setup store 的 ref 在 store proxy 上自动解包,外部调用用 `store.ref = x`,**别写 `store.ref.value = x`**。 +- 1×1 透明 PNG(68 字节)会被 MiniMax 内容审查判敏感,OCR 测试图必须用真实上传图。 +- mysql2 pool 默认不开 `enableKeepAlive`,idle 连接会被 MySQL `wait_timeout` 杀掉,下次 query 报 ETIMEDOUT。已开 keepAlive + 自动 retry。 -支持两种鉴权方式: +## 仓库 -| 方式 | 设置 | 说明 | -|------|------|------| -| Session Cookie | `GROCY_URL` + `GROCY_USERNAME` + `GROCY_PASSWORD` | 走 `POST /login` 拿 cookie,缓存 24h | -| API Key | `GROCY_URL` + `GROCY_API_KEY` | `GROCY-API-KEY` header,永久 | +https://gitea.img2img.com/wsh5485/CarLog -如果都不配:化学品列表能看但不能同步和扣减,其它模块全部正常工作。 +## License -## 📝 字段命名约定 - -- 车辆:`type`(car/suv/mpv/truck/other)、`powertrain`(ice/hev/bev)、`plate`、`color` -- 洗车:`wash_type`(quick/full/detail/other)、`wash_date`、`cost`、`location`、`duration_min` -- 加油:`refuel_date`、`liters`、`price_per_liter`、`total_cost`、`fuel_type`、`is_full`、`station` -- 充电:`charge_date`、`kwh`、`price_per_kwh`、`total_cost`、`charge_type`、`start_soc`、`end_soc` -- 保养:`maint_date`、`odometer_km`、`total_cost`、`shop`、`items[]`(动态项目) -- 保险:`insurance_type`(compulsory/commercial)、`company`、`policy_no`、`start_date`、`end_date`、`premium` - -## 📱 移动端 & PWA - -- **响应式布局**:4 档断点(`480 / 768 / 1024 / 1440`),手机 / iPad / 桌面三端自适应 -- **导航**:手机端汉堡按钮 + 右滑抽屉导航(核心 / 能耗 / 其他三分组) -- **列表页**:桌面端表格 → 手机端自动切换卡片堆叠(`` 通用组件) -- **表单 / 弹窗**:移动端单列布局 + 底部弹出 Sheet + sticky 操作栏 -- **iOS safe-area**:全面屏适配(`env(safe-area-inset-*)`) -- **PWA**: - - 安装 `vite-plugin-pwa`,workbox 自动生成 Service Worker - - `manifest.webmanifest` + 192/512/maskable/apple-touch 全套图标 - - 离线访问已缓存页面,导航降级到 `/offline` - - 启动屏(`apple-touch-startup-image` 由浏览器自动截屏处理) - - 3 个快捷方式:新建洗车 / 加油 / 保养 - - 4 种运行时 toast(`PwaToasts.vue`):新版本可用 / 离线就绪 / Android&桌面安装引导 / iOS Safari 添加到主屏幕 - -### 移动端使用方式 -1. iOS Safari:底部分享按钮 ⬆ → 添加到主屏幕(首次进入会有 toast 提示,session 内只弹一次) -2. Android Chrome:底栏会出现"安装 CarLog"toast,点"安装"即可 -3. 桌面 Chrome:地址栏右侧 ➕ 安装 CarLog -4. 新版本发布后下拉刷新(或访问站点)会触发"新版本可用"toast,点"刷新"即可更新 - -### Lighthouse 评分(最新一次运行,桌面端) -| 指标 | 分数 | -|---|---| -| Performance | 99 | -| Accessibility | 87 | -| Best Practices | 95 | -| SEO | 91 | - -报告 HTML 在 `.lighthouseci/lhr-*.html`,重新跑分:`npm run lighthouse` - -### PWA 检查 -`npm run lighthouse:pwa` 自动验证 11 项 PWA installability(manifest / icons 192+512+maskable+apple-touch / SW 注册 / theme-color / meta 标签 / 离线 fallback)。**Lighthouse 12 已弃用 PWA 类别**,本项目用 puppeteer 自检代替,结果稳定可重复。 - -## 🔌 API 速查 - -> 完整路径以 `/api` 为前缀,所有写操作需 `requireAuth` 中间件。返回 `{ ok, data, error }` 三段式 JSON。 - -| 模块 | 方法 | 路径 | 说明 | -|---|---|---|---| -| 认证 | POST | `/auth/login` | 登录,返回 csrfToken + Set-Cookie | -| 认证 | POST | `/auth/logout` | 登出 | -| 认证 | GET | `/auth/me` | 当前用户 | -| 认证 | POST | `/auth/change-password` | 改密码(CSRF) | -| 车辆 | GET | `/vehicles` | 列表(支持 `?q=&page=&limit=`) | -| 车辆 | POST | `/vehicles` | 新建(CSRF) | -| 车辆 | PUT | `/vehicles/:id` | 更新(CSRF) | -| 车辆 | DELETE | `/vehicles/:id` | 软删(CSRF) | -| 车辆 | GET | `/vehicles/:id/stats` | 单车统计(总里程/能耗/花费) | -| 洗车 | GET/POST/PUT/DELETE | `/washes[/:id]` | 同上 | -| 洗车 | POST | `/washes/:id/photos` | 上传对比照(multipart) | -| 加油 | GET/POST/PUT/DELETE | `/refuels[/:id]` | 油耗自动计算 | -| 充电 | GET/POST/PUT/DELETE | `/chargings[/:id]` | 电耗自动计算 | -| 保养 | GET/POST/PUT/DELETE | `/maintenances[/:id]` | 动态 items[] | -| 保险 | GET/POST/PUT/DELETE | `/insurances[/:id]` | 附件上传 | -| 保险 | GET | `/insurances/expiring?days=30` | 即将到期 | -| 车品 | GET | `/chemicals` | 库存列表 | -| 车品 | GET/POST/PUT/DELETE | `/chemicals/:id` | 详情 / 编辑 | -| 车品 | GET | `/chemicals/categories` | Grocy 分类 | -| 车品 | POST | `/chemicals/sync` | 拉取 Grocy 库存 | -| 车品 | GET | `/chemicals/low-stock` | 低库存预警 | -| 车品 | POST | `/chemicals/batch-purchase` | 批量采购(事务) | -| 日志 | GET | `/operation-logs?page=&action=&user=` | 操作日志 | -| 日志 | POST | `/operation-logs/:id/recover` | 一键恢复软删数据 | -| AI | POST | `/ai/recognize` | 图片识别(multipart:field=file) | -| 统计 | GET | `/stats/summary` | 总览 KPI | -| 统计 | GET | `/stats/cost-by-type?from=&to=` | 按类型花费 | -| 统计 | GET | `/stats/odometer` | 里程折线图 | -| 统计 | GET | `/stats/efficiency?vehicle_id=` | 能耗 | -| 导出 | GET | `/export/insurance.csv` | CSV 导出 | -| 导出 | GET | `/export/insurance.pdf` | PDF 导出 | -| 同步 | GET | `/sync/snapshot` | 全量 JSON 快照 | -| 同步 | POST | `/sync/restore` | 还原(确认 token) | -| 系统 | GET | `/health` | 健康检查 | - -## 💡 建议增加的功能(按 ROI 排序) - -### 🟢 高 ROI(detailer 日常痛点,必加) - -1. **🚗 车辆里程表(odometer)录入 + 自动同步** - 现在加油 / 充电 / 保养 / 洗车都只能手动填里程或留空。加一个 `vehicles.current_km` 字段,每次加油时自动建议上次里程 + 这次的差值(典型 400-600km/周)。**直接解决油耗计算准确度问题**(现在是基于「相邻 is_full=1」算,没数据就 fallback 0)。 - -2. **⛽ 加油提醒 / 保养提醒推送** - 现在保险有到期提醒(30 天),但加油 / 保养只能靠用户自己记得。加一个「提醒中心」页: - - 距上次加油 > 30 天 - - 距上次保养 > 6 个月 / 5000km - - 距上次洗车 > 14 天(你喜欢保持的车身干净程度) - 可以「标记已处理」或 snooze。**每天打开应用就能看到该干啥**。 - -3. **💰 成本分类分析** - 按月 / 按年 / 按车显示:洗车 : 加油 : 充电 : 保养 : 保险 的成本占比饼图。**能看出钱花在哪**,决定要不要砍掉某些开销(比如发现保险比加油贵)。 - -4. **🔍 全局搜索(v2.0+ 已经在做但没暴露 UI)** - `/api/chemicals/grocy-search` 只搜化学品。做一个顶栏 search box,能跨所有领域搜(车牌 / 商家名 / 保养项目 / 保单号),点击跳详情。**找历史记录速度 × 10**。 - -### 🟡 中等 ROI(增强体验,做不做都行) - -5. **📸 拍照快捷录入(手机 PWA)** - 现在 OCR 要先点「AI 识别」按钮 → 选图 → 识别。手机原生 PWA 加「桌面快捷方式」直接打开「拍照录入」页面,扫一眼小票自动填表。**洗车店门口就能记**。 - -6. **🏷️ 标签系统** - 给洗车 / 加油记录打标签:#自驾游 #通勤 #雨季 #精洗 #打蜡。一年后查「#打蜡 多少次」能判断打蜡频率。**纯前端 + 简单 SQL 即可**。 - -7. **📊 同比 / 环比对比** - Stats 页加:「本月 vs 上月」「今年 vs 去年同月」自动算增减百分比。**一眼看出趋势变化**。 - -8. **🔔 系统通知中心** - 应用内通知(不是 push,是站内消息):OCR 完成 / 同步失败 / 备份成功 / 新版本可用。比 toast 更持久,用户能回看。 - -### 🟢 长期 / 玩法(看你个人兴趣) - -9. **🏆 成就系统**(纯前端) - - 「连续 30 天洗车」「1 年累计 100 次洗车」「单次最贵的精洗」 - - 解锁后给个 badge 分享到社交媒体(生成图片卡片) - - **detailer 圈子的「show off」属性** - -10. **🛣️ 路线 / 行程记录** - 加 `trips` 表:起点 / 终点 / 里程 / 油耗 / 备注。 - - 加油时关联 trip_id,自动算 trip 油耗 - - 月底看「本月去过哪些地方」 - - **自驾游爱好者会很喜欢** - -11. **📦 备份到 OSS / 七牛 / 阿里云盘(自动化)** - 现在 `bin/backup.js` 只能本地。加 cron + 上传到对象存储: - - `bin/backup-upload.js` 调 S3 兼容 API - - 宝塔 cron `0 3 * * *` 每天凌晨跑 - - 保留 30 天滚动 - **你的数据安全网,必须做** - -12. **🔍 OCR 文本预览 + 高亮** - 现在 OCR 完直接填表,看不到识别到的原始文本。加一个「识别原始」tab: - - 显示 AI 返的 raw 文本 - - 高亮置信度低的字段(让用户重点核对这些) - - **提升用户对 OCR 结果的信任** - -13. **🌐 i18n 多语言** - 现在 hardcode 中文。如果要跟车友分享 / 出国玩可能需要 EN。vue-i18n 一加就够。 - -### 🔴 别做(性价比低) - -- **多用户 / 多租户**:你一个人玩,加这个复杂度 × 10,价值 = 0 -- **TS 重构**:除非你强迫症,JSDoc 已经覆盖了 -- **复杂 BI / 自定义仪表盘**:你 8 辆车 + 5 年数据,PostgreSQL + Superset 都嫌重 -- **区块链溯源 / NFT 车辆档案**:开玩笑的 😂 - -### ❓ 想问你几个开放问题 - -1. **里程表录入**:你加油时会主动记里程吗?(很多人靠加油站小票 + 全程导航算) -2. **保养预测**:你 4S 店 / 修理厂会提前打电话提醒,还是你 app 自己管? -3. **跟车友分享**:你想不想把数据导出发给车友 / 二手车买家看? -4. **车机 / OBD 集成**:你车有 OBD 接口吗?要不要读实时数据? - -回答这几个我能给你更精准的优先级建议。 - ---- - -## 📋 License - -MIT +MIT \ No newline at end of file