docs: clarify /api/carlog/* prefix + drop W3+ fitness plan
按用户反馈调整: 1. 路由前缀改成 /api/carlog/* (不再是 /api/vehicles 等) - DEV-PLAN.md Task 2.2 mount 全部加前缀 - 新增 Task 2.3.5: 前端 carlogApi helper - ARCHITECTURE.md 架构图 + 6.1/6.2 代码结构更新 - README.md 同步 2. 删除 W3+ 健身子系统计划 (用户还没决定下一子系统) - ARCHITECTURE.md W3+ 章节改成 "暂不规划" - README.md 实施路线只保留当前阶段 - DEV-PLAN.md "不做的事" 显式列出来 3. 加 Mavis review checklist (6.2/6.3/6.4) - 10 项检查清单 (DB/后端/前端/API baseURL/E2E/数据完整性) - 10 个常见坑 (JSON parse / TINYINT boolean / mount 顺序 ...) - review 通过标准
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
# i — 个人生活操作系统
|
||||
|
||||
一个**单 Vue + 单 Express + 单 MySQL** 的生活操作系统平台,按表前缀分多子系统,永远单用户。
|
||||
一个**单 Vue + 单 Express + 单 MySQL** 的生活操作系统平台,按子系统分代码目录和 API 路径,永远单用户。
|
||||
|
||||
第一个子系统是 [CarLog](https://gitea.img2img.com/wsh5485/CarLog)(洗车管理系统),后续要加:健身、阅读、健康等。
|
||||
第一个子系统是 [CarLog](https://gitea.img2img.com/wsh5485/CarLog)(洗车管理系统),将来可能加:健身、阅读、健康等(**用户还没决定下一子系统,本阶段不做**)。
|
||||
|
||||
## 架构核心
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
│ └──────────────────────────────────────────────────────┘│
|
||||
│ │
|
||||
│ ┌──────────────────────────────────────────────────────┐│
|
||||
│ │ 🚗 CarLog | 💪 Fitness | 📚 Reading ││
|
||||
│ │ 🚗 CarLog | 未来子系统 ││
|
||||
│ └──────────────────────────────────────────────────────┘│
|
||||
│ │
|
||||
└────────────────────────────────────────────────────────┘
|
||||
@@ -23,8 +23,9 @@
|
||||
▼
|
||||
┌────────────────────────────────────────────────────────┐
|
||||
│ Express (一个进程) │
|
||||
│ ├── /api/platform/* (总设置 / 子系统管理) │
|
||||
│ ├── /api/{resource} (业务路由, 按子系统分组) │
|
||||
│ ├── /api/platform/* (总设置 / 子系统管理 / Dashboard) │
|
||||
│ ├── /api/carlog/* (CarLog 子系统, 全部带前缀) │
|
||||
│ └── /api/{future}/* (将来加的子系统) │
|
||||
└────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
@@ -36,6 +37,8 @@
|
||||
│ │
|
||||
│ 子系统表 (有前缀): │
|
||||
│ carlog_vehicles / carlog_wash_records / ... │
|
||||
│ │
|
||||
│ (将来加): │
|
||||
│ fitness_workouts / fitness_plans / ... │
|
||||
│ reading_books / reading_notes / ... │
|
||||
└────────────────────────────────────────────────────────┘
|
||||
@@ -45,8 +48,8 @@
|
||||
|
||||
| 维度 | 做法 |
|
||||
|---|---|
|
||||
| 数据 | 表前缀 `{subsystem}_*`(同一 DB 内) |
|
||||
| 路由 | 子系统自己的路径空间(`/api/{resource}`) |
|
||||
| 数据 | 表前缀 `{subsystem}_*`(同一 DB 内;CarLog 还没加前缀,留给将来加第二个子系统时) |
|
||||
| 路由 | 子系统自己的路径空间(`/api/carlog/*`) |
|
||||
| 代码 | 子系统独立目录(`server/src/subsystems/{name}/`、`client/src/views/subsystems/{name}/`) |
|
||||
| 设置 | 每个子系统有自己的 settings schema(JSON Schema,存 `platform_settings` 表,key 前缀 `{name}.*`) |
|
||||
| 菜单 | 每个子系统在 `subsystems` 表注册,平台层根据 `category` 分组渲染左侧导航 |
|
||||
@@ -55,24 +58,24 @@
|
||||
|
||||
## 实施路线
|
||||
|
||||
### W1: 平台骨架
|
||||
### 当前阶段(W1 + W2 一并做)
|
||||
|
||||
- [ ] `server/migrations/001_platform.sql`(subsystems + platform_settings 表 + seed CarLog)
|
||||
- [ ] `server/src/routes/platform/settings.js`
|
||||
- [ ] `server/src/routes/platform/subsystems.js`
|
||||
- [ ] `server/src/views/Platform/GlobalSettings.vue`
|
||||
- [ ] `client/src/views/Platform/SubsystemSettings.vue`(通用 JSON Schema 渲染器)
|
||||
- [ ] `client/src/AppLayout.vue` 改左侧菜单(按 category 分组)
|
||||
- [ ] `server/migrations/019_platform.sql`(subsystems + platform_settings 表 + seed CarLog)
|
||||
- [ ] `server/src/routes/platform/{subsystems,settings,dashboard}.js`
|
||||
- [ ] CarLog 代码移到 `server/src/subsystems/carlog/`(13 个路由文件)
|
||||
- [ ] CarLog 路由全部 mount 到 `/api/carlog/*`(不再是 `/api/vehicles`)
|
||||
- [ ] CarLog 前端 view 移到 `client/src/views/subsystems/carlog/`
|
||||
- [ ] 前端 API 改用 `carlogApi` helper(自动加 `/api/carlog/` 前缀)
|
||||
- [ ] `client/src/views/Platform/{GlobalSettings,SubsystemSettings,Subsystems}.vue`
|
||||
- [ ] `client/src/stores/platform.js` + `AppLayout.vue` 元数据驱动菜单
|
||||
- [ ] 测试(12-20 个平台测试)+ 手动 E2E 验证
|
||||
|
||||
### W2: CarLog 表前缀迁移
|
||||
### 暂不做(用户没决定)
|
||||
|
||||
- [ ] 把 CarLog 现有 14 张表加 `carlog_` 前缀(migration)
|
||||
- [ ] 改 server 端所有 SQL
|
||||
- [ ] 跑全测试套件
|
||||
|
||||
### W3+: 加第二个真子系统
|
||||
|
||||
- [ ] 写一个 Fitness 子系统(健身记录),验证端到端流程
|
||||
- ❌ 加第二个子系统(健身 / 阅读 / 任何)
|
||||
- ❌ CarLog 表前缀迁移(`carlog_*`)
|
||||
- ❌ 跨子系统 dashboard widget
|
||||
- ❌ 多用户 / RBAC / 多租户
|
||||
|
||||
## 路线原则
|
||||
|
||||
@@ -82,10 +85,11 @@
|
||||
- **物理目录隔离**:subsystem 代码独立目录,加新子系统不会乱碰现有代码
|
||||
- **永远向后兼容**:CarLog 的现有功能不破
|
||||
|
||||
详细见 [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)。
|
||||
详细见 [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) 和 [docs/DEV-PLAN.md](docs/DEV-PLAN.md)。
|
||||
|
||||
## Git 仓库
|
||||
|
||||
- 仓库:https://gitea.img2img.com/wsh5485/i.git
|
||||
- 平台:Gitea
|
||||
- 推送:osxkeychain 自动记住 token
|
||||
- 推送:osxkeychain 自动记住 token
|
||||
+25
-16
@@ -32,10 +32,8 @@ i 平台是一个「生活操作系统」:单 Vue SPA + 单 Express 进程 +
|
||||
┌────────────────────────────────────────────────────────┐
|
||||
│ Express (一个进程) │
|
||||
│ ├── /api/platform/* (总设置 / 子系统管理 / Dashboard) │
|
||||
│ ├── /api/vehicles (CarLog 路由, 保持现状) │
|
||||
│ ├── /api/washes (CarLog) │
|
||||
│ ├── /api/fitness/* (将来加的 Fitness 子系统) │
|
||||
│ └── /api/reading/* (将来加的 Reading 子系统) │
|
||||
│ ├── /api/carlog/* (CarLog 子系统) │
|
||||
│ └── /api/{future}/* (将来加的子系统) │
|
||||
└────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
@@ -65,8 +63,8 @@ i 平台是一个「生活操作系统」:单 Vue SPA + 单 Express 进程 +
|
||||
|
||||
| 隔离维度 | 做法 |
|
||||
|---|---|
|
||||
| 数据 | 表前缀 `{subsystem}_*`(同一 DB 内) |
|
||||
| 路由 | 子系统自己的路径空间(`/api/{resource}` 现有;将来 `/api/fitness/*`) |
|
||||
| 数据 | 表前缀 `{subsystem}_*`(同一 DB 内;本阶段 CarLog 还没加前缀,留给将来加第二个子系统时) |
|
||||
| 路由 | 子系统自己的路径空间(`/api/carlog/*` 现有;将来 `/api/fitness/*` 等) |
|
||||
| 代码 | 子系统独立目录(`server/src/subsystems/{name}/`、`client/src/views/subsystems/{name}/`) |
|
||||
| 设置 | 每个子系统有自己的 settings schema(JSON Schema,存 `platform_settings` 表,key 前缀 `{name}.*`) |
|
||||
| 菜单 | 每个子系统在 `subsystems` 表注册,平台层根据 `category` 分组渲染左侧导航 |
|
||||
@@ -168,9 +166,10 @@ server/src/
|
||||
│ │ ├── settings.js # GET/PUT /api/platform/settings
|
||||
│ │ ├── subsystems.js # GET /api/platform/subsystems
|
||||
│ │ └── dashboard.js # GET /api/platform/dashboard (跨子系统聚合)
|
||||
│ ├── vehicles.js # CarLog
|
||||
│ ├── washes.js # CarLog
|
||||
│ └── ...
|
||||
│ └── subsystems/
|
||||
│ └── carlog/
|
||||
│ ├── index.js # 聚合导出 13 个 CarLog router
|
||||
│ └── routes/ # 13 个 CarLog 路由文件
|
||||
└── ...
|
||||
```
|
||||
|
||||
@@ -184,9 +183,13 @@ client/src/
|
||||
│ │ ├── GlobalSettings.vue
|
||||
│ │ ├── SubsystemSettings.vue # 通用渲染器
|
||||
│ │ └── Subsystems.vue # 启停 + 注册
|
||||
│ ├── Login.vue # i 平台统一登录
|
||||
│ ├── Home.vue # Dashboard, 读 /api/platform/dashboard
|
||||
│ ├── WashesList.vue
|
||||
│ └── ...
|
||||
│ └── subsystems/
|
||||
│ └── carlog/ # 20 个 CarLog view
|
||||
├── api/
|
||||
│ ├── client.js # 底层 axios 实例(auth interceptor + 解包)
|
||||
│ └── subsystems.js # carlogApi helper(baseURL = /api/carlog)
|
||||
├── router/index.js
|
||||
└── stores/
|
||||
├── auth.js
|
||||
@@ -257,13 +260,19 @@ export function tableName(name) {
|
||||
}
|
||||
```
|
||||
|
||||
### W3+: 加第二个子系统(健身)
|
||||
### W3+(暂不规划)
|
||||
|
||||
1. 写 `subsystems.fitness`:3-5 张表 + 3-5 个路由 + 5-10 个前端 view
|
||||
用户还没决定下一子系统做什么。本阶段只做:
|
||||
|
||||
1. ✅ 平台基座(subsystems 表 + platform_settings + 3 个平台路由 + 3 个平台前端 + 元数据驱动菜单)
|
||||
2. ✅ CarLog 子系统化(目录迁移 + 加 `/api/carlog/` 前缀 + 注册到 subsystems 表)
|
||||
|
||||
将来加新子系统时按这个流程:
|
||||
1. 写子系统的表(带 `{subsystem}_` 前缀)+ 路由 + 前端
|
||||
2. 注册到 `subsystems` 表
|
||||
3. 平台菜单自动出现 Fitness 入口
|
||||
4. 通用设置渲染器自动支持 Fitness 的 settings
|
||||
5. 验证:能从平台菜单点进 Fitness,操作后回平台
|
||||
3. 平台菜单自动出现新子系统入口
|
||||
4. 通用设置渲染器自动支持新子系统的 settings
|
||||
5. 验证端到端流程
|
||||
|
||||
## 8. 不做的事
|
||||
|
||||
|
||||
+250
-37
@@ -31,11 +31,13 @@ i 是一个生活操作系统平台,**单 Vue SPA + 单 Express 进程 + 单 M
|
||||
|---|---|---|
|
||||
| 子系统隔离 | 物理目录 + 表前缀 | 单用户场景,分进程/分库是过度工程 |
|
||||
| 平台层和子系统共享一个进程 | 是 | 一个 Express server |
|
||||
| 路由前缀 | **保持现状** `/api/*` | 改 URL 路径影响前端所有 link/api call,工作量大且无收益 |
|
||||
| **子系统 API 路径前缀** | **`/api/{subsystem}/*`**(如 `/api/carlog/vehicles`) | 子系统隔离 + 以后加 fitness 直接 `/api/fitness/*` 不冲突 |
|
||||
| CarLog 代码目录 | 移到 `server/src/subsystems/carlog/` | 物理隔离,加子系统不会乱碰 |
|
||||
| CarLog 前端目录 | 移到 `client/src/views/subsystems/carlog/` | 同上 |
|
||||
| CarLog 路由 path | 不变(`/washes`、`/vehicles`) | 用户体验一致 |
|
||||
| CarLog 表前缀 | **Phase 2 不做**(留给 Phase 3) | 当前阶段数据库里只有 CarLog 的表,没必要急着加前缀;加第二个子系统前再做 |
|
||||
| **CarLog 后端路由 path** | `/api/carlog/*`(不再是 `/api/vehicles`) | 跟平台层 `/api/platform/*` 对齐;前端调用要改 |
|
||||
| **CarLog 前端 API 调用** | 所有 CarLog API 加 `/api/carlog/` 前缀 | 跟后端路由对齐 |
|
||||
| CarLog 前端路由 path | 不变(`/washes`、`/vehicles`) | 用户体验一致;router path 还是前端路径,不是 API 路径 |
|
||||
| CarLog 表前缀 | **本阶段不做**(留给将来加第二个子系统时) | 当前阶段数据库里只有 CarLog 的表,没必要急着加前缀 |
|
||||
| 平台层路由前缀 | `/api/platform/*` | 平台层独立路径空间 |
|
||||
| 平台层 UI 路径 | `/settings/global`, `/settings/:subsystem`, `/admin/subsystems` | 用户能直接 URL 进入 |
|
||||
| 元数据驱动 | `subsystems` 表的 `settings_schema` + `nav_items` JSON 字段 | 加新子系统不用改平台前端代码 |
|
||||
@@ -427,8 +429,7 @@ router.get('/', requireAuth, async (req, res, next) => {
|
||||
refuel_liters: Number(refuelsThisMonth[0].liters),
|
||||
},
|
||||
},
|
||||
// fitness: {...}, // Phase 3 加
|
||||
// reading: {...}, // Phase 3 加
|
||||
// 将来加第二个子系统时扩: fitness: {...}, reading: {...}
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
@@ -440,7 +441,7 @@ export default router;
|
||||
```
|
||||
|
||||
**注意**:
|
||||
- 现在只聚 CarLog;Fitness / Reading 子系统加进来后再扩
|
||||
- 现在只聚 CarLog;将来加新子系统时再扩字段
|
||||
- 用 UTC_DATE()(mysql2 timezone='Z' 配置已经设为 UTC)
|
||||
- 总数 / 总额用 `Number()` 转 JS number(mysql2 返回 DECIMAL 是字符串)
|
||||
- 后续扩字段时按需加
|
||||
@@ -594,11 +595,11 @@ cd server && node -e "import('./src/subsystems/carlog/index.js').then(m => conso
|
||||
|
||||
---
|
||||
|
||||
### Task 2.2: 后端路由挂载从新目录
|
||||
### Task 2.2: 后端路由挂载从新目录(加 /api/carlog/ 前缀)
|
||||
|
||||
**修改文件**: `server/src/index.js`
|
||||
|
||||
**改动**: 把现有 mount 改用新 index.js 导出的 router
|
||||
**改动**: 把现有 mount 改用新 index.js 导出的 router, **所有 CarLog 路由加 `/api/carlog/` 前缀**
|
||||
```js
|
||||
// 原来的:
|
||||
// import vehiclesRouter from './routes/vehicles.js';
|
||||
@@ -613,25 +614,25 @@ import {
|
||||
notificationsRouter, achievementsRouter,
|
||||
} from './subsystems/carlog/index.js';
|
||||
|
||||
app.use('/api/vehicles', vehiclesRouter);
|
||||
app.use('/api/washes', washesRouter);
|
||||
app.use('/api/refuels', refuelsRouter); // 注意: 如果原文件名是 refuels.js 就这样, 看实际
|
||||
// ... 其他 11 个
|
||||
app.use('/api/insurance', insuranceRouter);
|
||||
app.use('/api/chemicals', chemicalsRouter);
|
||||
app.use('/api/ai', aiRouter);
|
||||
app.use('/api/auth', authRouter);
|
||||
app.use('/api/settings', settingsRouter);
|
||||
app.use('/api/logs', logsRouter);
|
||||
app.use('/api/operation-logs', operationLogsRouter);
|
||||
app.use('/api/extra', extraRouter);
|
||||
app.use('/api/tags', tagsRouter);
|
||||
app.use('/api/notifications', notificationsRouter);
|
||||
app.use('/api/achievements', achievementsRouter);
|
||||
// ⚠️ 全部加 /api/carlog/ 前缀(不再是 /api/vehicles)
|
||||
app.use('/api/carlog/vehicles', vehiclesRouter);
|
||||
app.use('/api/carlog/washes', washesRouter);
|
||||
app.use('/api/carlog/refuels', refuelsRouter); // 看实际文件名, 可能是 refuel
|
||||
app.use('/api/carlog/insurance', insuranceRouter);
|
||||
app.use('/api/carlog/chemicals', chemicalsRouter);
|
||||
app.use('/api/carlog/ai', aiRouter);
|
||||
app.use('/api/carlog/auth', authRouter); // 登录路由也加前缀
|
||||
app.use('/api/carlog/settings', settingsRouter);
|
||||
app.use('/api/carlog/logs', logsRouter);
|
||||
app.use('/api/carlog/operation-logs', operationLogsRouter);
|
||||
app.use('/api/carlog/extra', extraRouter);
|
||||
app.use('/api/carlog/tags', tagsRouter);
|
||||
app.use('/api/carlog/notifications', notificationsRouter);
|
||||
app.use('/api/carlog/achievements', achievementsRouter);
|
||||
```
|
||||
|
||||
**重要**:
|
||||
- 路由 path 不要变(保持 `/api/vehicles` 而不是 `/api/carlog/vehicles`)
|
||||
**重要**:
|
||||
- **路由 path 加 `/api/carlog/` 前缀**(用户决定 — 跟 `/api/platform/*` 对齐,方便以后加 fitness/reading 不冲突)
|
||||
- 文件名要按 `ls server/src/subsystems/carlog/routes/` 实际看到的写
|
||||
- 一定要先 cd 到 server/src/subsystems/carlog/routes 看看实际文件名, 常见陷阱:
|
||||
- `refuels.js` vs `refuel.js`
|
||||
@@ -643,10 +644,10 @@ app.use('/api/achievements', achievementsRouter);
|
||||
```bash
|
||||
cd server && npm run dev
|
||||
# 登录 + 列车辆 + 列洗车 + 列加油 + 列成就 + 列通知 + 全 OK
|
||||
TOKEN=$(curl -s -X POST http://localhost:8787/api/auth/login -H 'Content-Type: application/json' -d '{"username":"admin","password":"carwash2026"}' | python3 -c "import json,sys; print(json.load(sys.stdin)['data']['token'])")
|
||||
TOKEN=$(curl -s -X POST http://localhost:8787/api/carlog/auth/login -H 'Content-Type: application/json' -d '{"username":"admin","password":"carwash2026"}' | python3 -c "import json,sys; print(json.load(sys.stdin)['data']['token'])")
|
||||
for ep in vehicles washes refuels insurance chemicals tags notifications achievements; do
|
||||
echo "Testing /api/$ep ..."
|
||||
curl -s -H "Authorization: Bearer $TOKEN" "http://localhost:8787/api/$ep" | head -1
|
||||
echo "Testing /api/carlog/$ep ..."
|
||||
curl -s -H "Authorization: Bearer $TOKEN" "http://localhost:8787/api/carlog/$ep" | head -1
|
||||
done
|
||||
# 期望: 每行返回 JSON 不报错
|
||||
```
|
||||
@@ -699,6 +700,84 @@ cd client && npm run dev
|
||||
|
||||
---
|
||||
|
||||
### Task 2.3.5: 前端 API 调用加 `/api/carlog/` 前缀(重要!)
|
||||
|
||||
**为什么**:后端路由全部从 `/api/vehicles` 改到 `/api/carlog/vehicles`,前端所有 API 调用必须同步改,否则登录后所有页面都 404。
|
||||
|
||||
**方案:每个子系统一个 API helper(baseURL)**
|
||||
|
||||
不要在每个 view 里手写 `/api/carlog/...`。在 `client/src/api/subsystems.js` 加子系统 baseURL helper:
|
||||
|
||||
```js
|
||||
// client/src/api/subsystems.js
|
||||
import { api } from './client.js';
|
||||
|
||||
// 子系统 API helper —— 一个 helper 一个 baseURL
|
||||
export const carlogApi = {
|
||||
get: (path, config) => api.get(`/api/carlog${path}`, config),
|
||||
post: (path, data, config) => api.post(`/api/carlog${path}`, data, config),
|
||||
put: (path, data, config) => api.put(`/api/carlog${path}`, data, config),
|
||||
patch: (path, data, config) => api.patch(`/api/carlog${path}`, data, config),
|
||||
delete: (path, config) => api.delete(`/api/carlog${path}`, config),
|
||||
};
|
||||
|
||||
// 将来加 fitness 子系统直接加一行:
|
||||
// export const fitnessApi = { ... 同结构, baseURL: '/api/fitness' };
|
||||
|
||||
// 平台层 API(不走子系统前缀)
|
||||
export const platformApi = api; // 平台路由已经是 /api/platform/*, 用原始 api
|
||||
```
|
||||
|
||||
**改造 client/src/api/ 下 9 个 CarLog 文件**:每个文件把 `import { api } from './client.js'` 改成 `import { carlogApi } from './subsystems.js'`,所有 `api.get('xxx')` 改成 `carlogApi.get('xxx')` —— **path 里不要带 `/api/carlog/` 前缀,helper 自动加**。
|
||||
|
||||
```js
|
||||
// 原来 (client/src/api/vehicles.js):
|
||||
import { api } from './client.js';
|
||||
export const listVehicles = () => api.get('/vehicles');
|
||||
|
||||
// 改成:
|
||||
import { carlogApi } from './subsystems.js';
|
||||
export const listVehicles = () => carlogApi.get('/vehicles');
|
||||
// ^^^^^^^^^^ 注意是 carlogApi, path 不带 /api/carlog/
|
||||
```
|
||||
|
||||
**改造 20 个 view 里直接调 api 的地方**(如果有 inline api 调用):
|
||||
```js
|
||||
// 原来: api.get('/vehicles')
|
||||
// 改成: carlogApi.get('/vehicles')
|
||||
```
|
||||
|
||||
**9 个 CarLog api 文件清单**(要看 `ls client/src/api/` 实际):
|
||||
```
|
||||
client/src/api/ai.js
|
||||
client/src/api/auth.js
|
||||
client/src/api/chemicals.js
|
||||
client/src/api/client.js ← 这个不动!它是底层 axios 实例
|
||||
client/src/api/insurance.js
|
||||
client/src/api/logs.js
|
||||
client/src/api/operationLogs.js
|
||||
client/src/api/settings.js
|
||||
```
|
||||
|
||||
**注意**:
|
||||
- `client/src/api/client.js` 是底层 axios 实例(带 auth interceptor + 解包 `{ok,data}`),**不要动**
|
||||
- 其他 8 个 api 文件全部 import `carlogApi` 而不是 `api`
|
||||
- 平台层 view(GlobalSettings / SubsystemSettings / Subsystems)继续用 `api`(因为是 `/api/platform/*`)
|
||||
|
||||
**验证**:
|
||||
```bash
|
||||
# 跑 grep 确认没有残留的 /api/vehicles /api/washes 字符串
|
||||
grep -rn "/api/vehicles\|/api/washes\|/api/refuels\|/api/insurance\|/api/chemicals\|/api/auth" client/src/ --include="*.js" --include="*.vue"
|
||||
# 期望: 没有结果(除了 api/subsystems.js 里写 baseURL 那行)
|
||||
|
||||
# 跑 server 起来 + 浏览器登录测试
|
||||
cd server && npm run dev
|
||||
cd client && npm run dev
|
||||
# 浏览器登录 → 进所有页面 → 都正常加载
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2.4: 前端平台层 — 总设置 UI
|
||||
|
||||
**新增文件**: `client/src/views/Platform/GlobalSettings.vue`
|
||||
@@ -1062,12 +1141,13 @@ SELECT COUNT(*) AS refuels FROM refuel_records;
|
||||
|
||||
## 4. 不做的事(明确边界)
|
||||
|
||||
- ❌ **表前缀迁移**:本 Phase 不加 `carlog_` 前缀,留到 Phase 3+ 加第二个子系统时再做
|
||||
- ❌ **表前缀迁移**:本 Phase 不加 `carlog_` 前缀,留到将来加第二个子系统时再做
|
||||
- ❌ **JWT / SSO**:单用户 cookie session 已经够
|
||||
- ❌ **iframe 嵌入**:同 SPA,不需要 iframe
|
||||
- ❌ **独立子系统部署**:所有子系统一个进程
|
||||
- ❌ **manifest 协议**:subsystem 直接走 SQL 注册,不走 HTTP manifest 拉取
|
||||
- ❌ **子系统 dashboard widget 跨子系统聚合**:Phase 4 加第二子系统再做
|
||||
- ❌ **加第二个子系统**(健身 / 阅读 / 任何):用户还没决定下一子系统做什么,本阶段**只做底座 + CarLog 子系统化**
|
||||
- ❌ **子系统 dashboard widget 跨子系统聚合**:等 2+ 子系统再做
|
||||
- ❌ **现有 CarLog 业务逻辑修改**:迁移目录不改 SQL、不改业务、不改 UI
|
||||
- ❌ **现有 CarLog 测试代码修改**:除非路径变了(深层 import)
|
||||
|
||||
@@ -1124,11 +1204,144 @@ Phase 2: CarLog 子系统化
|
||||
|
||||
## 6. 提交给 Mavis(我)做 code review 时
|
||||
|
||||
请把以下附上:
|
||||
1. `git diff main --stat`(看了多少行)
|
||||
2. `cd server && npm test` 输出
|
||||
3. `cd client && npm run lint` 输出
|
||||
4. MySQL `SHOW TABLES;` 输出
|
||||
5. 手动 E2E 截图(关键 5 个页面: Dashboard / 总设置 / CarLog 设置 / 子系统管理 / 任意业务页面)
|
||||
### 6.1 必须附上的材料
|
||||
|
||||
我会逐文件 review + 跑测试 + 给改进意见。
|
||||
```bash
|
||||
# 1. diff stat
|
||||
cd /Users/yabozi/wzpstudio/i
|
||||
git diff main --stat > review-diff-stat.txt
|
||||
|
||||
# 2. server 测试
|
||||
cd server && npm install && npm test > review-server-test.txt 2>&1
|
||||
|
||||
# 3. client lint + build
|
||||
cd ../client && npm install && npm run lint > review-client-lint.txt 2>&1
|
||||
|
||||
# 4. DB 表清单
|
||||
mysql -h 162.14.110.130 -P 33306 -u carlog -pZeMRBwXP8JC6B3rF carlog \
|
||||
-e "SHOW TABLES; SELECT COUNT(*) AS subsystems FROM subsystems; SELECT COUNT(*) AS settings FROM platform_settings;" > review-db.txt 2>&1
|
||||
|
||||
# 5. 关键 5 个页面截图(手动)
|
||||
# - Dashboard (登录后首页)
|
||||
# - 总设置 (/settings/global)
|
||||
# - CarLog 设置 (/settings/carlog)
|
||||
# - 子系统管理 (/admin/subsystems)
|
||||
# - 任一业务页面 (/washes 或 /vehicles)
|
||||
# 命名: review-screen-{1..5}-{name}.png
|
||||
```
|
||||
|
||||
### 6.2 我会逐项检查的内容
|
||||
|
||||
#### 6.2.1 数据库(migration 019)
|
||||
|
||||
- [ ] `019_platform.sql` 是否幂等(再跑一次不报错)
|
||||
- [ ] `subsystems` 表字段齐全:`id, name, description, icon, color, category, version, enabled, sort_order, settings_schema (JSON), nav_items (JSON), created_at, updated_at`
|
||||
- [ ] `platform_settings` 表字段齐全:`key (PK), value (JSON), type, description, updated_at`
|
||||
- [ ] CarLog seed 数据:id='carlog', category='vehicle', nav_items 至少含 `/, /vehicles, /washes, /refuels, /stats, /settings` 6 项
|
||||
- [ ] settings_schema 至少 5 个字段(string/number/boolean/select/password 各覆盖)
|
||||
|
||||
#### 6.2.2 平台后端路由
|
||||
|
||||
- [ ] `routes/platform/subsystems.js`: GET 列表、GET 单个、PATCH 启停三件齐
|
||||
- [ ] `routes/platform/settings.js`: GET list、GET single、PUT single、POST batch 四件齐
|
||||
- [ ] `routes/platform/dashboard.js`: 聚合 CarLog 4-6 个 stats 字段
|
||||
- [ ] 所有路由都走 `requireAuth` middleware
|
||||
- [ ] 响应统一 `{ok: true, data: ...}` 包装
|
||||
- [ ] JSON 字段(settings_schema / nav_items / value)正确 parse
|
||||
- [ ] mount 到 `/api/platform/*` 三条线(subsystems / settings / dashboard)
|
||||
|
||||
#### 6.2.3 CarLog 后端目录迁移
|
||||
|
||||
- [ ] `server/src/routes/` 只剩 `.gitkeep`(13 个文件全移走)
|
||||
- [ ] `server/src/subsystems/carlog/routes/` 13 个文件齐全
|
||||
- [ ] 每个文件 import 路径:`'../../db.js'` → `'../../../db.js'`, middleware 同理
|
||||
- [ ] `subsystems/carlog/index.js` 正确聚合导出(**实际 ls 一下 routes/ 名字再 import,别凭印象**)
|
||||
- [ ] `server/src/index.js` mount 全部加 `/api/carlog/` 前缀(13 条 mount line)
|
||||
- [ ] **没有任何残留的 `/api/vehicles` 等老路径 mount**
|
||||
|
||||
#### 6.2.4 CarLog 前端目录迁移
|
||||
|
||||
- [ ] `client/src/views/subsystems/carlog/` 20 个 view 齐全
|
||||
- [ ] `client/src/views/` 只剩 `.gitkeep + Login.vue + Offline.vue` 等非子系统
|
||||
- [ ] `client/src/router/index.js` 所有 CarLog view import 改成新路径
|
||||
- [ ] **前端 router path 不变**(`/washes` 仍是 `/washes`,不是 `/carlog/washes`)
|
||||
|
||||
#### 6.2.5 前端 API baseURL(最易出 bug 的地方)
|
||||
|
||||
- [ ] `client/src/api/subsystems.js` 创建 `carlogApi` helper(get/post/put/patch/delete 5 个方法 + `/api/carlog` 前缀)
|
||||
- [ ] 8 个 CarLog api 文件(ai / auth / chemicals / insurance / logs / operationLogs / settings / ... 实际 ls 一下)**全部 import carlogApi 而不是 api**
|
||||
- [ ] **grep 验证无残留**:
|
||||
```bash
|
||||
grep -rn "/api/vehicles\|/api/washes\|/api/refuels\|/api/insurance\|/api/chemicals\|/api/auth" \
|
||||
client/src/ --include="*.js" --include="*.vue" \
|
||||
| grep -v "client/src/api/subsystems.js"
|
||||
# 期望: 没有输出
|
||||
```
|
||||
- [ ] 20 个 view 里如果有 inline `api.get('/xxx')` 调用也改成 `carlogApi.get('/xxx')`
|
||||
|
||||
#### 6.2.6 平台前端
|
||||
|
||||
- [ ] `GlobalSettings.vue`: 5 个总设置字段 + 加载/保存 + alert 提示
|
||||
- [ ] `SubsystemSettings.vue`: 通用渲染器支持 string/number/boolean/select/password/textarea 6 种类型
|
||||
- [ ] `Subsystems.vue`: 列表 + 启停开关
|
||||
- [ ] `stores/platform.js`: `subsystems` ref + `loadSubsystems()` + `groupedCategories` computed
|
||||
- [ ] `AppLayout.vue`: 菜单读 `platform.groupedCategories` 而不是硬编码
|
||||
- [ ] 启动时调 `platform.loadSubsystems()`(mounted / onMounted)
|
||||
|
||||
#### 6.2.7 路由
|
||||
|
||||
- [ ] `client/src/router/index.js` 加 3 条平台路由:`/settings/global`, `/settings/:subsystem`, `/admin/subsystems`
|
||||
- [ ] 3 个平台 view 都在 `meta.requiresAuth = true`
|
||||
- [ ] path **不重复**现有 router
|
||||
|
||||
#### 6.2.8 测试
|
||||
|
||||
- [ ] `server/test/routes/platform.*.test.js` 至少 12 个新测试
|
||||
- [ ] **旧 101 个测试一个不破**(任何破测试 = Phase 2 失败)
|
||||
- [ ] 测试用 supertest + mock db,参考 `server/test/setup.js`
|
||||
|
||||
#### 6.2.9 E2E 手动验证
|
||||
|
||||
我会跑这 14 步(Dev Plan Phase 3.3 清单),任何一步失败都要返工:
|
||||
|
||||
1. 登录 admin/carwash2026
|
||||
2. 左侧菜单按 🚗 车辆 / ⚙️ 系统 分组
|
||||
3. `/washes` 加载列表
|
||||
4. `/vehicles` 加载列表
|
||||
5. `/refuels` 加载列表
|
||||
6. `/stats` 加载图表
|
||||
7. `/settings/global` 加载 + 5 字段显示
|
||||
8. 改主题 → 保存 → 刷新保持
|
||||
9. `/settings/carlog` 加载 + 5 字段显示
|
||||
10. 改 `ai.provider` → 保存 → DB 里有 `carlog.ai.provider`
|
||||
11. `/admin/subsystems` 看到 CarLog 一条
|
||||
12. 取消启用 → 刷新 → 菜单无 CarLog
|
||||
13. 重新启用 → 菜单恢复
|
||||
14. 重新登录 → 设置还在
|
||||
|
||||
#### 6.2.10 数据完整性
|
||||
|
||||
- [ ] migration 019 **没动**现有 14 张 CarLog 表
|
||||
- [ ] `SELECT COUNT(*) FROM vehicles;` 等数字跟改造前一致
|
||||
|
||||
### 6.3 我会特别盯的几个常见坑
|
||||
|
||||
1. **mysql2 JSON 字段 parse 漏** — 直接返回字符串给前端,前端 `data.settings_schema` 变 `[object Object]` 或 undefined
|
||||
2. **TINYINT(1) 不转 boolean** — 前端 `data.enabled` 可能是 0/1 而不是 true/false
|
||||
3. **mount 顺序错** — auth middleware 在业务 route 之后 mount 导致 token 检查失败
|
||||
4. **carwash 路由文件 import 路径没改全** — `from '../../db.js'` 还是老的,跑起来才报错
|
||||
5. **前端 API 没改全** — 有些 view 里 inline `axios.get('/vehicles')` 漏改,所有页面 404
|
||||
6. **router path 改了** — 用户 URL 从 `/washes` 变 `/carlog/washes`,用户书签/链接全失效(**不要改 router path**)
|
||||
7. **platform store 没在 AppLayout mounted 加载** — 菜单永远空
|
||||
8. **subsystem 启停后没 reload store** — 切启用状态菜单没更新
|
||||
9. **nav_items JSON 没 parse** — 菜单变 `[object Object]`
|
||||
10. **测试 mock 没覆盖新路由** — 测试覆盖率掉了
|
||||
|
||||
### 6.4 review 通过标准
|
||||
|
||||
- 所有 6.2 子项勾完
|
||||
- 6.3 10 个常见坑都不存在
|
||||
- 6.1 五份材料齐全
|
||||
- 我跑完测试 + E2E 后给「OK」反馈
|
||||
|
||||
任何 review 反馈都要修完再合并下一阶段。
|
||||
Reference in New Issue
Block a user