From d7dab31f196e5e3f09afada2646374479214b21f Mon Sep 17 00:00:00 2001 From: wsh5485 Date: Sat, 20 Jun 2026 23:06:12 +0800 Subject: [PATCH] docs: add table prefix migration task (21 CarLog tables) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 按用户决定「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 张) --- README.md | 23 +++-- docs/ARCHITECTURE.md | 46 ++++++---- docs/DEV-PLAN.md | 201 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 245 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index c6edefb..e243384 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ | 维度 | 做法 | |---|---| -| 数据 | 表前缀 `{subsystem}_*`(同一 DB 内;CarLog 还没加前缀,留给将来加第二个子系统时) | +| 数据 | 表前缀 `{subsystem}_*`(同一 DB 内;CarLog 已加 `carlog_` 前缀,将来加 fitness/reading 直接走 `fitness_*` / `reading_*`) | | 路由 | 子系统自己的路径空间(`/api/carlog/*`) | | 代码 | 子系统独立目录(`server/src/subsystems/{name}/`、`client/src/views/subsystems/{name}/`) | | 设置 | 每个子系统有自己的 settings schema(JSON 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/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 路由全部 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 验证 + +**Phase 2.8 表前缀迁移**(用户决定) +- [ ] `server/migrations/020_carlog_prefix.sql` — 21 张 CarLog 表加 `carlog_` 前缀 +- [ ] **改前先 mysqldump 备份** +- [ ] 13 个路由文件 + reset-all.js SQL 加前缀 +- [ ] 数据完整性验证(行数不变) + +**Phase 3 验证** +- [ ] 手动 E2E 14 步 + DB 数据完整 + 备份清理 ### 暂不做(用户没决定) - ❌ 加第二个子系统(健身 / 阅读 / 任何) -- ❌ CarLog 表前缀迁移(`carlog_*`) - ❌ 跨子系统 dashboard widget - ❌ 多用户 / RBAC / 多租户 diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index fdb1b49..ddce6a1 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -63,7 +63,7 @@ i 平台是一个「生活操作系统」:单 Vue SPA + 单 Express 进程 + | 隔离维度 | 做法 | |---|---| -| 数据 | 表前缀 `{subsystem}_*`(同一 DB 内;本阶段 CarLog 还没加前缀,留给将来加第二个子系统时) | +| 数据 | 表前缀 `{subsystem}_*`(同一 DB 内;CarLog 已加 `carlog_` 前缀,将来加 fitness/reading 直接走 `fitness_*` / `reading_*`) | | 路由 | 子系统自己的路径空间(`/api/carlog/*` 现有;将来 `/api/fitness/*` 等) | | 代码 | 子系统独立目录(`server/src/subsystems/{name}/`、`client/src/views/subsystems/{name}/`) | | 设置 | 每个子系统有自己的 settings schema(JSON Schema,存 `platform_settings` 表,key 前缀 `{name}.*`) | @@ -238,26 +238,40 @@ const groupedCategories = computed(() => { ### W2: 表前缀迁移(CarLog) -把所有 CarLog 表加 `carlog_` 前缀: +把所有 21 张 CarLog 业务表加 `carlog_` 前缀(共享表 `users / login_attempts / auth_locks / schema_migrations` 不动): ```sql -RENAME TABLE vehicles TO carlog_vehicles; -RENAME TABLE wash_records TO carlog_wash_records; -RENAME TABLE refuel_records TO carlog_refuels; -RENAME TABLE charging_records TO carlog_chargings; --- ... 其他表 +-- migration 020_carlog_prefix.sql +DROP TABLE IF EXISTS _weather_snapshots_new; -- 清理 0013 临时表 + +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 -const TABLE_PREFIX = { - vehicles: 'carlog_vehicles', - wash_records: 'carlog_wash_records', -}; -export function tableName(name) { - return TABLE_PREFIX[name] || name; -} +> ⚠️ **改前先 mysqldump 备份**!RENAME 是 DDL 不能事务回滚。 + +**注意**: 直接改 SQL,**不加 helper 函数**(helper 让代码难读,20 张表工作量可控)。 ``` ### W3+(暂不规划) diff --git a/docs/DEV-PLAN.md b/docs/DEV-PLAN.md index 9b6be6f..107905a 100644 --- a/docs/DEV-PLAN.md +++ b/docs/DEV-PLAN.md @@ -2,7 +2,7 @@ > **目标读者**: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 前端 API 调用** | 所有 CarLog API 加 `/api/carlog/` 前缀 | 跟后端路由对齐 | | CarLog 前端路由 path | 不变(`/washes`、`/vehicles`) | 用户体验一致;router path 还是前端路径,不是 API 路径 | -| CarLog 表前缀 | **本阶段不做**(留给将来加第二个子系统时) | 当前阶段数据库里只有 CarLog 的表,没必要急着加前缀 | +| **CarLog 表前缀** | **本阶段做**(加 `carlog_` 前缀) | 用户决定 — 表前缀现在就做,将来加新子系统不需要再做迁移 | | 平台层路由前缀 | `/api/platform/*` | 平台层独立路径空间 | | 平台层 UI 路径 | `/settings/global`, `/settings/:subsystem`, `/admin/subsystems` | 用户能直接 URL 进入 | | 元数据驱动 | `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 端 SQL(13 个路由文件 + 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: 验证 ### Task 3.1: 跑 server 测试 @@ -1180,7 +1362,7 @@ SELECT COUNT(*) AS refuels FROM refuel_records; ## 4. 不做的事(明确边界) -- ❌ **表前缀迁移**:本 Phase 不加 `carlog_` 前缀,留到将来加第二个子系统时再做 +- ❌ ~~**表前缀迁移**~~ → 改为 ✅ Task 2.8 做(用户决定) - ❌ **JWT / SSO**:单用户 cookie session 已经够 - ❌ **iframe 嵌入**:同 SPA,不需要 iframe - ❌ **独立子系统部署**:所有子系统一个进程 @@ -1396,6 +1578,19 @@ done - [ ] migration 019 **没动**现有 14 张 CarLog 表 - [ ] `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 我会特别盯的几个常见坑 1. **mysql2 JSON 字段 parse 漏** — 直接返回字符串给前端,前端 `data.settings_schema` 变 `[object Object]` 或 undefined