docs: add table prefix migration task (21 CarLog tables)

按用户决定「i系统内的carlog数据库表要加carlog_前缀」:
- 之前方案是「留给将来加第二个子系统时」, 现在提前做
- 新增 Task 2.8 表前缀迁移 (含 5 个子节 + review checklist)
- DEV-PLAN.md:
  - 关键决策表「本阶段不做」→「本阶段做」
  - 加 Task 2.8: 备份 + migration 020 + 改 server SQL + reset-all.js + 验证
  - 加 §6.2.11 表前缀迁移 review checklist (10 项)
  - 「不做的事」: 删掉表前缀
  - 「完成定义」: 加 Task 2.8
- ARCHITECTURE.md:
  - §3 决策表同步
  - §7 W2 改成完整 RENAME TABLE 21 张 + 备份警告
  - 删掉过时的 db.js tableName helper 建议
- README.md:
  - 决策表同步
  - 实施路线按 Phase 1 / 2 / 2.8 / 3 重新分组
  - 删掉「暂不做: CarLog 表前缀」

迁移策略:
- 21 张 CarLog 业务表加 carlog_ 前缀 (含 carlog_settings)
- 5 张共享表不动 (users / login_attempts / auth_locks / schema_migrations / 平台表)
- 改前必做 mysqldump 备份 (RENAME 是 DDL 不能事务回滚)
- 直接改 SQL (不加 helper 函数, 20 张表工作量可控)
- sed \\b word boundary 安全替换 (列了完整 21 张表清单, 不动共享表 4 张)
This commit is contained in:
2026-06-20 23:06:12 +08:00
parent 60b7df9015
commit d7dab31f19
3 changed files with 245 additions and 25 deletions
+17 -6
View File
@@ -48,7 +48,7 @@
| 维度 | 做法 | | 维度 | 做法 |
|---|---| |---|---|
| 数据 | 表前缀 `{subsystem}_*`(同一 DB 内;CarLog 还没加前缀,留给将来加第二个子系统时 | | 数据 | 表前缀 `{subsystem}_*`(同一 DB 内;CarLog 已加 `carlog_` 前缀,将来加 fitness/reading 直接走 `fitness_*` / `reading_*` |
| 路由 | 子系统自己的路径空间(`/api/carlog/*` | | 路由 | 子系统自己的路径空间(`/api/carlog/*` |
| 代码 | 子系统独立目录(`server/src/subsystems/{name}/``client/src/views/subsystems/{name}/` | | 代码 | 子系统独立目录(`server/src/subsystems/{name}/``client/src/views/subsystems/{name}/` |
| 设置 | 每个子系统有自己的 settings schemaJSON Schema,存 `platform_settings` 表,key 前缀 `{name}.*` | | 设置 | 每个子系统有自己的 settings schemaJSON Schema,存 `platform_settings` 表,key 前缀 `{name}.*` |
@@ -58,22 +58,33 @@
## 实施路线 ## 实施路线
### 当前阶段(W1 + W2 一并做 ### 当前阶段(Phase 1 平台基座 + Phase 2 CarLog 子系统化 + 表前缀迁移
**Phase 1 平台基座**
- [ ] `server/migrations/019_platform.sql`subsystems + platform_settings 表 + seed CarLog - [ ] `server/migrations/019_platform.sql`subsystems + platform_settings 表 + seed CarLog
- [ ] `server/src/routes/platform/{subsystems,settings,dashboard}.js` - [ ] `server/src/routes/platform/{subsystems,settings,dashboard}.js`
- [ ] `client/src/views/Platform/{GlobalSettings,SubsystemSettings,Subsystems}.vue`
- [ ] `client/src/stores/platform.js` + `AppLayout.vue` 元数据驱动菜单
- [ ] 测试(12-20 个平台测试)
**Phase 2 CarLog 子系统化**
- [ ] CarLog 代码移到 `server/src/subsystems/carlog/`13 个路由文件) - [ ] CarLog 代码移到 `server/src/subsystems/carlog/`13 个路由文件)
- [ ] CarLog 路由全部 mount 到 `/api/carlog/*`(不再是 `/api/vehicles` - [ ] CarLog 路由全部 mount 到 `/api/carlog/*`(不再是 `/api/vehicles`
- [ ] CarLog 前端 view 移到 `client/src/views/subsystems/carlog/` - [ ] CarLog 前端 view 移到 `client/src/views/subsystems/carlog/`
- [ ] 前端 API 改用 `carlogApi` helper(自动加 `/api/carlog/` 前缀) - [ ] 前端 API 改用 `carlogApi` helper(自动加 `/api/carlog/` 前缀)
- [ ] `client/src/views/Platform/{GlobalSettings,SubsystemSettings,Subsystems}.vue`
- [ ] `client/src/stores/platform.js` + `AppLayout.vue` 元数据驱动菜单 **Phase 2.8 表前缀迁移**(用户决定)
- [ ] 测试(12-20 个平台测试)+ 手动 E2E 验证 - [ ] `server/migrations/020_carlog_prefix.sql` — 21 张 CarLog 表加 `carlog_` 前缀
- [ ] **改前先 mysqldump 备份**
- [ ] 13 个路由文件 + reset-all.js SQL 加前缀
- [ ] 数据完整性验证(行数不变)
**Phase 3 验证**
- [ ] 手动 E2E 14 步 + DB 数据完整 + 备份清理
### 暂不做(用户没决定) ### 暂不做(用户没决定)
- ❌ 加第二个子系统(健身 / 阅读 / 任何) - ❌ 加第二个子系统(健身 / 阅读 / 任何)
- ❌ CarLog 表前缀迁移(`carlog_*`
- ❌ 跨子系统 dashboard widget - ❌ 跨子系统 dashboard widget
- ❌ 多用户 / RBAC / 多租户 - ❌ 多用户 / RBAC / 多租户
+30 -16
View File
@@ -63,7 +63,7 @@ i 平台是一个「生活操作系统」:单 Vue SPA + 单 Express 进程 +
| 隔离维度 | 做法 | | 隔离维度 | 做法 |
|---|---| |---|---|
| 数据 | 表前缀 `{subsystem}_*`(同一 DB 内;本阶段 CarLog 还没加前缀,留给将来加第二个子系统时 | | 数据 | 表前缀 `{subsystem}_*`(同一 DB 内;CarLog 已加 `carlog_` 前缀,将来加 fitness/reading 直接走 `fitness_*` / `reading_*` |
| 路由 | 子系统自己的路径空间(`/api/carlog/*` 现有;将来 `/api/fitness/*` 等) | | 路由 | 子系统自己的路径空间(`/api/carlog/*` 现有;将来 `/api/fitness/*` 等) |
| 代码 | 子系统独立目录(`server/src/subsystems/{name}/``client/src/views/subsystems/{name}/` | | 代码 | 子系统独立目录(`server/src/subsystems/{name}/``client/src/views/subsystems/{name}/` |
| 设置 | 每个子系统有自己的 settings schemaJSON Schema,存 `platform_settings` 表,key 前缀 `{name}.*` | | 设置 | 每个子系统有自己的 settings schemaJSON Schema,存 `platform_settings` 表,key 前缀 `{name}.*` |
@@ -238,26 +238,40 @@ const groupedCategories = computed(() => {
### W2: 表前缀迁移(CarLog ### W2: 表前缀迁移(CarLog
把所有 CarLog 表加 `carlog_` 前缀: 把所有 21 张 CarLog 业务表加 `carlog_` 前缀(共享表 `users / login_attempts / auth_locks / schema_migrations` 不动)
```sql ```sql
RENAME TABLE vehicles TO carlog_vehicles; -- migration 020_carlog_prefix.sql
RENAME TABLE wash_records TO carlog_wash_records; DROP TABLE IF EXISTS _weather_snapshots_new; -- 清理 0013 临时表
RENAME TABLE refuel_records TO carlog_refuels;
RENAME TABLE charging_records TO carlog_chargings; RENAME TABLE
-- ... 其他表 vehicles TO carlog_vehicles,
wash_records TO carlog_wash_records,
chemicals TO carlog_chemicals,
insurance_records TO carlog_insurance_records,
chemical_usage TO carlog_chemical_usage,
refuel_records TO carlog_refuel_records,
settings TO carlog_settings,
maintenance_records TO carlog_maintenance_records,
charging_records TO carlog_charging_records,
weather_snapshots TO carlog_weather_snapshots,
operation_logs TO carlog_operation_logs,
record_tags TO carlog_record_tags,
notifications TO carlog_notifications,
wash_photos TO carlog_wash_photos,
tags TO carlog_tags,
user_achievements TO carlog_user_achievements,
notification_prefs TO carlog_notification_prefs,
grocy_sync_logs TO carlog_grocy_sync_logs,
category_mappings TO carlog_category_mappings,
chemical_inventory_log TO carlog_chemical_inventory_log;
``` ```
`db.js` 加 helper: **改 server SQL**13 个路由文件 + reset-all.js 全部加前缀(用 sed word boundary `\b` 替换,**`users`/`login_attempts`/`auth_locks`/`schema_migrations` 不要替换**)。
```js > ⚠️ **改前先 mysqldump 备份**RENAME 是 DDL 不能事务回滚。
const TABLE_PREFIX = {
vehicles: 'carlog_vehicles', **注意**: 直接改 SQL**不加 helper 函数**helper 让代码难读,20 张表工作量可控)。
wash_records: 'carlog_wash_records',
};
export function tableName(name) {
return TABLE_PREFIX[name] || name;
}
``` ```
### W3+(暂不规划) ### W3+(暂不规划)
+198 -3
View File
@@ -2,7 +2,7 @@
> **目标读者**Trae(另一个 AI IDE 工具),按本文档逐步实施,最终提交代码由 Mavis(我)做 code review + 跑测试。 > **目标读者**Trae(另一个 AI IDE 工具),按本文档逐步实施,最终提交代码由 Mavis(我)做 code review + 跑测试。
> >
> **完成定义**:所有 Phase 1 + Phase 2 任务通过验收测试,端到端流程跑通。 > **完成定义**:所有 Phase 1 + Phase 2 (含 Task 2.8 表前缀迁移) 任务通过验收测试,端到端流程跑通。
--- ---
@@ -37,7 +37,7 @@ i 是一个生活操作系统平台,**单 Vue SPA + 单 Express 进程 + 单 M
| **CarLog 后端路由 path** | `/api/carlog/*`(不再是 `/api/vehicles` | 跟平台层 `/api/platform/*` 对齐;前端调用要改 | | **CarLog 后端路由 path** | `/api/carlog/*`(不再是 `/api/vehicles` | 跟平台层 `/api/platform/*` 对齐;前端调用要改 |
| **CarLog 前端 API 调用** | 所有 CarLog API 加 `/api/carlog/` 前缀 | 跟后端路由对齐 | | **CarLog 前端 API 调用** | 所有 CarLog API 加 `/api/carlog/` 前缀 | 跟后端路由对齐 |
| CarLog 前端路由 path | 不变(`/washes``/vehicles` | 用户体验一致;router path 还是前端路径,不是 API 路径 | | CarLog 前端路由 path | 不变(`/washes``/vehicles` | 用户体验一致;router path 还是前端路径,不是 API 路径 |
| CarLog 表前缀 | **本阶段**留给将来加第二个子系统时) | 当前阶段数据库里只有 CarLog 的表,没必要急着加前缀 | | **CarLog 表前缀** | **本阶段做**`carlog_` 前缀) | 用户决定 — 表前缀现在就做,将来加新子系统不需要再做迁移 |
| 平台层路由前缀 | `/api/platform/*` | 平台层独立路径空间 | | 平台层路由前缀 | `/api/platform/*` | 平台层独立路径空间 |
| 平台层 UI 路径 | `/settings/global`, `/settings/:subsystem`, `/admin/subsystems` | 用户能直接 URL 进入 | | 平台层 UI 路径 | `/settings/global`, `/settings/:subsystem`, `/admin/subsystems` | 用户能直接 URL 进入 |
| 元数据驱动 | `subsystems` 表的 `settings_schema` + `nav_items` JSON 字段 | 加新子系统不用改平台前端代码 | | 元数据驱动 | `subsystems` 表的 `settings_schema` + `nav_items` JSON 字段 | 加新子系统不用改平台前端代码 |
@@ -1110,6 +1110,188 @@ onMounted(load);
--- ---
### Task 2.8: 表前缀迁移(21 张 CarLog 表加 `carlog_` 前缀)
**目标**:把 CarLog 业务表全部加 `carlog_` 前缀,将来加新子系统(fitness / reading 等)时不需要再做表前缀迁移。
**为什么放最后**:表前缀迁移涉及 DB 改动 + 大量 SQL 文件修改,独立于前端 UI / 路由改动,便于出问题回滚。Phase 2.1-2.7 全部完成后再做。
#### Task 2.8.1: 备份 DB
**⚠️ 必做:迁移前先备份**(防数据丢失)
```bash
mysqldump -h 162.14.110.130 -P 33306 -u carlog -pZeMRBwXP8JC6B3rF carlog > backup-before-prefix-$(date +%Y%m%d-%H%M%S).sql
ls -lh backup-before-prefix-*.sql # 确认备份文件大小合理(>100KB = 有数据)
```
#### Task 2.8.2: 写 migration `020_carlog_prefix.sql`
**新增文件**: `server/migrations/020_carlog_prefix.sql`
**内容**:
```sql
-- ============================================================
-- 020_carlog_prefix.sql — i 平台基座 (CarLog 表加 carlog_ 前缀)
-- ============================================================
-- 21 张 CarLog 业务表加 carlog_ 前缀。共享表 (users/login_attempts/auth_locks/schema_migrations) 不动。
-- ⚠️ 跑前必须 mysqldump 备份!RENAME 是 DDL 不能事务回滚。
-- 清理 0013 临时表(如果有)
DROP TABLE IF EXISTS _weather_snapshots_new;
RENAME TABLE
vehicles TO carlog_vehicles,
wash_records TO carlog_wash_records,
chemicals TO carlog_chemicals,
insurance_records TO carlog_insurance_records,
chemical_usage TO carlog_chemical_usage,
refuel_records TO carlog_refuel_records,
settings TO carlog_settings,
maintenance_records TO carlog_maintenance_records,
charging_records TO carlog_charging_records,
weather_snapshots TO carlog_weather_snapshots,
operation_logs TO carlog_operation_logs,
record_tags TO carlog_record_tags,
notifications TO carlog_notifications,
wash_photos TO carlog_wash_photos,
tags TO carlog_tags,
user_achievements TO carlog_user_achievements,
notification_prefs TO carlog_notification_prefs,
grocy_sync_logs TO carlog_grocy_sync_logs,
category_mappings TO carlog_category_mappings,
chemical_inventory_log TO carlog_chemical_inventory_log;
-- 不动(共享表):
-- users, login_attempts, auth_locks, schema_migrations
-- subsystems, platform_settings (019 migration 新建)
```
**注意**:
- MySQL RENAME TABLE 自动 update FK 引用(如果有外键),无需手动改 FK
- carlog_settings 是 CarLog 自己的配置表(ai / grocy / 密码等),**不是平台共享表**
- migration 跑完记录在 schema_migrations 表里,重启 server 自动加载
**验证**:
```bash
mysql -h 162.14.110.130 -P 33306 -u carlog -pZeMRBwXP8JC6B3rF carlog -e "SHOW TABLES;"
# 期望: 看到 21 张 carlog_* 表 + 6 张共享表 (users/login_attempts/auth_locks/schema_migrations/subsystems/platform_settings)
# 期望: 没有不带前缀的 vehicles / wash_records 等
mysql -h 162.14.110.130 -P 33306 -u carlog -pZeMRBwXP8JC6B3rF carlog -e "SELECT COUNT(*) FROM carlog_vehicles;"
# 期望: 数字跟改前 vehicles 表一致
```
#### Task 2.8.3: 改 server 端 SQL13 个路由文件 + reset-all.js
**目标**:所有 server/src/ 里引用 CarLog 表名的 SQL 全部加 `carlog_` 前缀。
**CarLog 业务表清单**21 张 — 改这些):
```
vehicles, wash_records, chemicals, insurance_records, chemical_usage,
refuel_records, settings, maintenance_records, charging_records,
weather_snapshots, operation_logs, record_tags, notifications,
wash_photos, tags, user_achievements, notification_prefs,
grocy_sync_logs, category_mappings, chemical_inventory_log
```
**共享表清单**5 张 — **不动**):
```
users, login_attempts, auth_locks, schema_migrations
subsystems + platform_settings 是 019 新建,已经是带 platform 前缀的)
```
**操作方法**:
1. 全局 grep 找所有 CarLog 表名引用
2. 一个个 .js 文件改 SQL
3. 跑 server 测试验证
**必须改的文件**(按引用频次排序,看 `ls server/src/subsystems/carlog/routes/`):
```
vehicles.js (53 次 vehicles)
washes.js (52 次 wash_records)
chemicals.js (30 次 chemicals + 22 次 chemical_usage)
insurance.js (22 次 insurance_records)
extra.js (charging_records + maintenance_records + refuel_records 等)
ai.js (settings 表)
settings.js (settings 表 16 次)
auth.js (users/login_attempts/auth_locks — 共享表,不动)
notifications.js (6 次 notifications + 3 次 notification_prefs)
tags.js (4 次 tags + 7 次 record_tags)
achievements.js (3 次 user_achievements)
operationLogs.js (9 次 operation_logs)
weather.js (14 次 weather_snapshots)
grocy.js (3 次 grocy_sync_logs + 2 次 category_mappings)
```
**安全替换脚本**(按表名替换,**先备份**:
```bash
cd server/src/subsystems/carlog/
# 复制原文件做对照(可选)
for f in $(grep -rl "FROM \`\?vehicles\`\?\|JOIN \`\?vehicles\`\?\|INTO \`\?vehicles\`\?\|UPDATE \`\?vehicles\`\?" routes/); do
sed -i '' 's/\bvehicles\b/carlog_vehicles/g; s/\bwash_records\b/carlog_wash_records/g; s/\bchemicals\b/carlog_chemicals/g; s/\binsurance_records\b/carlog_insurance_records/g; s/\bchemical_usage\b/carlog_chemical_usage/g; s/\brefuel_records\b/carlog_refuel_records/g; s/\bsettings\b/carlog_settings/g; s/\bmaintenance_records\b/carlog_maintenance_records/g; s/\bcharging_records\b/carlog_charging_records/g; s/\bweather_snapshots\b/carlog_weather_snapshots/g; s/\boperation_logs\b/carlog_operation_logs/g; s/\brecord_tags\b/carlog_record_tags/g; s/\bnotifications\b/carlog_notifications/g; s/\bwash_photos\b/carlog_wash_photos/g; s/\btags\b/carlog_tags/g; s/\buser_achievements\b/carlog_user_achievements/g; s/\bnotification_prefs\b/carlog_notification_prefs/g; s/\bgrocy_sync_logs\b/carlog_grocy_sync_logs/g; s/\bcategory_mappings\b/carlog_category_mappings/g; s/\bchemical_inventory_log\b/carlog_chemical_inventory_log/g' "$f"
done
```
**⚠️ 警告**:
- sed 必须用 `\b` word boundary,否则 `notifications` 会匹配 `notification_prefs` 之类
- **`users / login_attempts / auth_locks / schema_migrations` 不要替换**(共享表)— sed 上面没列这 4 个,安全
- 改完跑一次 `grep -E "FROM|JOIN|INTO|UPDATE" server/src/` 检查所有引用都加前缀了
-`cd server && npm test` 看测试是否破
**reset-all.js 必须改**:
```bash
# 在 server/src/bin/reset-all.js 改:
# KEEP_TABLES = ['users', 'carlog_settings', 'schema_migrations', 'auth_locks', 'login_attempts',
# 'carlog_category_mappings'];
# TABLES_IN_ORDER = ['carlog_operation_logs', 'carlog_chemical_usage', ...] 全部加前缀
```
**Trae 自检命令**:
```bash
# 1. 任何 SQL 引用未加前缀的 CarLog 表
grep -rE "(FROM|JOIN|INTO|UPDATE) \`?(vehicles|wash_records|chemicals|refuel_records|maintenance_records|charging_records|insurance_records|chemical_usage|weather_snapshots|operation_logs|tags|record_tags|achievements|user_achievements|notifications|notification_prefs|wash_photos|grocy_sync_logs|category_mappings|chemical_inventory_log|settings)\`?" server/src/ \
| grep -v "carlog_" \
| head -10
# 期望: 没有输出(除了 users/login_attempts/auth_locks/schema_migrations 共享表)
# 2. carlog_ 前缀引用数量应该跟原引用数量一致
echo "原 vehicles 引用: $(grep -r 'carlog_vehicles' server/src/ | wc -l)"
# 期望: 53 (跟 Phase 0 grep 数一致)
```
#### Task 2.8.4: 跑测试 + 手动验证
```bash
# 1. 跑 server 测试
cd server && npm test
# 期望: 旧 101 测试 + 新平台测试全过
# 2. 数据完整性
mysql -h 162.14.110.130 -P 33306 -u carlog -pZeMRBwXP8JC6B3rF carlog -e "
SELECT
(SELECT COUNT(*) FROM carlog_vehicles) AS vehicles,
(SELECT COUNT(*) FROM carlog_wash_records) AS washes,
(SELECT COUNT(*) FROM carlog_refuel_records) AS refuels,
(SELECT COUNT(*) FROM carlog_settings) AS settings_rows
"
# 期望: 数字跟 Phase 0 备份前一致(数据没动)
# 3. 启动 server 跑完整 E2E(同 Phase 3.3 的 14 步)
cd server && npm run dev
cd ../client && npm run dev
# 浏览器登录 → 所有页面 → 都正常加载(说明 SQL 没漏)
```
#### Task 2.8.5: 删除备份(验证完后再删)
```bash
# 跑完所有验证,确认没问题后
rm backup-before-prefix-*.sql
```
---
## Phase 3: 验证 ## Phase 3: 验证
### Task 3.1: 跑 server 测试 ### Task 3.1: 跑 server 测试
@@ -1180,7 +1362,7 @@ SELECT COUNT(*) AS refuels FROM refuel_records;
## 4. 不做的事(明确边界) ## 4. 不做的事(明确边界)
-**表前缀迁移**:本 Phase 不加 `carlog_` 前缀,留到将来加第二个子系统时再做 -~~**表前缀迁移**~~ → 改为 ✅ Task 2.8 做(用户决定)
-**JWT / SSO**:单用户 cookie session 已经够 -**JWT / SSO**:单用户 cookie session 已经够
-**iframe 嵌入**:同 SPA,不需要 iframe -**iframe 嵌入**:同 SPA,不需要 iframe
-**独立子系统部署**:所有子系统一个进程 -**独立子系统部署**:所有子系统一个进程
@@ -1396,6 +1578,19 @@ done
- [ ] migration 019 **没动**现有 14 张 CarLog 表 - [ ] migration 019 **没动**现有 14 张 CarLog 表
- [ ] `SELECT COUNT(*) FROM vehicles;` 等数字跟改造前一致 - [ ] `SELECT COUNT(*) FROM vehicles;` 等数字跟改造前一致
#### 6.2.11 表前缀迁移(Task 2.8 — 必查)
- [ ] **备份存在**: `backup-before-prefix-*.sql` 大小合理(>100KB
- [ ] **migration 020** 跑了,`SHOW TABLES;` 有 21 张 `carlog_*` 表
- [ ] **共享表没动**: `users / login_attempts / auth_locks / schema_migrations / subsystems / platform_settings` 名字还是老的
- [ ] **数据完整**: `SELECT COUNT(*) FROM carlog_vehicles;` 等跟改前 `vehicles` 一致
- [ ] **server SQL 改全**: grep 验证无残留未加前缀的 CarLog 表名
- [ ] **reset-all.js 改了**: `KEEP_TABLES` 含 `'carlog_settings'`, `TABLES_IN_ORDER` 全部加前缀
- [ ] **旧 101 测试不破**(任何破测试 = SQL 漏改)
- [ ] **新增平台测试不破**
- [ ] **手动 E2E 全过**: 登录 → 进所有页面 → 增删改查 → 通知/成就/提醒/统计都正常
- [ ] **备份可以删**(验证完没问题后 `rm backup-before-prefix-*.sql`
### 6.3 我会特别盯的几个常见坑 ### 6.3 我会特别盯的几个常见坑
1. **mysql2 JSON 字段 parse 漏** — 直接返回字符串给前端,前端 `data.settings_schema` 变 `[object Object]` 或 undefined 1. **mysql2 JSON 字段 parse 漏** — 直接返回字符串给前端,前端 `data.settings_schema` 变 `[object Object]` 或 undefined