Files
admin/docs/dashboard/backend-migration-plan.md
T
2026-07-30 11:23:50 +08:00

262 lines
7.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Dashboard 后端迁移计划:JS + Sequelize → TypeScript + Drizzle
## 概述
将 dashboard 后端从 JavaScript + SequelizeCommonJS)迁移至 TypeScript + Drizzle ORMESM),提升类型安全性和开发体验,同时为官网 CMS 模块(`/api/www/*`)奠定技术基础。
**迁移策略**:渐进式,逐模块替换,确保每个阶段可独立验证。
---
## 现有代码清单
### Models9 个表)
| 文件 | 表名 | 说明 |
|------|------|------|
| Brand.js | brand | 耳机品牌 |
| Model.js | model | 耳机型号 |
| Ota.js | ota | OTA 固件 |
| OtaTargetDevice.js | ota_target_device | OTA 目标设备 |
| BlackList.js | black_list | 黑名单 |
| DashboardUser.js | dashboard_user | 后台用户 |
| ShareCodeLog.js | share_code_log | 分享码日志 |
| UserActive.js | user_active | 用户活跃记录 |
| UserDevice.js | user_device | 用户设备 |
### Routes9 个路由模块)
| 文件 | 体量 | 复杂度 | 迁移优先级 |
|------|------|--------|-----------|
| auth.js | 3.6KB | ⭐ | P1(先迁) |
| shareCodeLogs.js | 2.7KB | ⭐ | P1 |
| brands.js | 4.8KB | ⭐ | P1 |
| blacklist.js | 6.6KB | ⭐⭐ | P2 |
| users.js | 6.2KB | ⭐⭐ | P2 |
| otaTargetDevice.js | 7.8KB | ⭐⭐ | P2 |
| dashboard.js | 9.5KB | ⭐⭐ | P2 |
| ota.js | 9.9KB | ⭐⭐⭐ | P3 |
| models.js | 26.1KB | ⭐⭐⭐ | P3(最后) |
### Services6 个)
| 文件 | 说明 |
|------|------|
| curveClient.js | 频响曲线 API 客户端 |
| eqCacheStorage.js | Redis EQ 缓存 |
| measurementStorage.js | S3 测量文件存储 |
| otaStorage.js | OTA 固件存储 |
| squiglink.js | Squiglink 数据服务 |
| userBootstrap.js | 超级管理员初始化 |
### 其他
| 模块 | 文件 | 说明 |
|------|------|------|
| validators/ | 5 个 | 已使用 Zod,迁移成本极低 |
| middleware/ | auth.js, bodyLimit.js | JWT 鉴权 + 请求体限制 |
| config/ | database.js, env.js, loadEnv.js, logger.js, redis.js | 基础配置 |
| utils/ | 3 个 | 工具函数 |
---
## 迁移阶段
### Phase 1:搭建 TypeScript 基础设施
**目标**:项目能运行 TS,新旧代码可共存。
- [ ] 安装依赖:`typescript``tsx``drizzle-orm``drizzle-kit``@types/express``@types/cors`
- [ ] 移除依赖:`sequelize`Phase 4 最后移除)
- [ ] 创建 `tsconfig.json`
```jsonc
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"esModuleInterop": true,
"strict": true,
"outDir": "dist",
"rootDir": "src",
"skipLibCheck": true,
"resolveJsonModule": true,
"declaration": true,
"paths": { "@/*": ["./src/*"] }
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules", "dist"]
}
```
- [ ] 创建 `drizzle.config.ts`
```ts
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
schema: './src/schemas/index.ts',
out: './drizzle',
dialect: 'mysql',
dbCredentials: {
host: process.env.DATABASE_HOST || 'localhost',
port: Number(process.env.DATABASE_PORT || 3306),
user: process.env.DATABASE_USER || 'root',
password: process.env.DATABASE_PASSWORD || '',
database: process.env.DATABASE_NAME || 'audio',
},
});
```
- [ ] 更新 `package.json` scripts
```json
{
"type": "module",
"scripts": {
"dev": "tsx watch src/app.ts",
"build": "tsc",
"start": "node dist/app.js",
"db:generate": "drizzle-kit generate",
"db:migrate": "drizzle-kit migrate",
"db:push": "drizzle-kit push",
"db:studio": "drizzle-kit studio"
}
}
```
- [ ] 创建 `src/config/database.ts`Drizzle 连接)
```ts
import { drizzle } from 'drizzle-orm/mysql2';
import mysql from 'mysql2/promise';
import * as schema from '../schemas';
const pool = mysql.createPool({
host: process.env.DATABASE_HOST || 'localhost',
port: Number(process.env.DATABASE_PORT || 3306),
user: process.env.DATABASE_USER || 'root',
password: process.env.DATABASE_PASSWORD || '',
database: process.env.DATABASE_NAME || 'audio',
charset: 'utf8mb4',
waitForConnections: true,
connectionLimit: 10,
});
export const db = drizzle(pool, { schema, mode: 'default' });
```
**验证**`tsx src/app.ts` 能正常启动(此时仍用旧 JS 代码)。
---
### Phase 2:迁移 Models → Drizzle Schema
**目标**:所有表定义用 Drizzle schema 表达,生成初始迁移。
- [ ] 创建 `src/schemas/` 目录
- [ ] 逐表编写 Drizzle schema
```ts
// src/schemas/brand.ts
import { mysqlTable, int, varchar, datetime } from 'drizzle-orm/mysql-core';
import { sql } from 'drizzle-orm';
export const brands = mysqlTable('brand', {
id: int('id').primaryKey().autoincrement(),
name: varchar('name', { length: 100 }).notNull(),
// ...
});
```
- [ ] 创建 `src/schemas/index.ts` 统一导出
- [ ] 运行 `drizzle-kit generate` 生成初始迁移(与现有表结构对齐)
- [ ] 验证:`drizzle-kit studio` 能正确读取现有数据
**注意**:现有表已存在,首次迁移使用 `drizzle-kit push`(同步 schema 到数据库而不执行 SQL),或生成迁移后标记为已执行。
---
### Phase 3:逐个迁移 Routes
**目标**:将路由处理函数中的 Sequelize 查询替换为 Drizzle。
**迁移顺序**(从简到繁):
| 批次 | 路由 | 原因 |
|------|------|------|
| 第 1 批 | auth.ts, shareCodeLogs.ts, brands.ts | 逻辑简单,CRUD 为主 |
| 第 2 批 | blacklist.ts, users.ts, otaTargetDevice.ts, dashboard.ts | 中等复杂度 |
| 第 3 批 | ota.ts, models.ts | 最复杂,涉及文件上传/外部服务 |
**每个路由的迁移步骤**
1. 重命名 `.js``.ts`
2. `require()``import`
3. Sequelize 查询 → Drizzle 查询:
```ts
// Before (Sequelize)
const brands = await Brand.findAll({ where: { name: { [Op.like]: `%${keyword}%` } } });
// After (Drizzle)
const result = await db.select().from(brands).where(like(brands.name, `%${keyword}%`));
```
4. 添加请求/响应类型(配合 Zod validator
5. 验证接口行为不变
---
### Phase 4:迁移 Services + 清理
**目标**:全部代码 TS 化,移除 Sequelize。
- [ ] 迁移 6 个 service 文件(主要是加类型标注,逻辑不变)
- [ ] 迁移 middlewareauth.ts, bodyLimit.ts
- [ ] 迁移 configenv.ts, logger.ts, redis.ts
- [ ] 迁移 utils
- [ ] 迁移入口 `app.js``app.ts`
- [ ] 删除所有旧 `.js` 文件
- [ ] 卸载 `sequelize` 依赖
- [ ] 全量 `tsc --noEmit` 通过
- [ ] 更新 Dockerfile(构建步骤改为 `tsc && node dist/app.js`
---
### Phase 5:新增官网 CMS 模块
**目标**:在迁移完成的基础上,开发 `/api/www/*` 接口。
- [ ] 设计 CMS 数据表(pages、sections、banners、products、news、faq、site_settings 等)
- [ ] 编写 Drizzle schema + 迁移
- [ ] 实现前台接口 `/api/www/*`(公开)
- [ ] 实现管理接口 `/api/admin/www/*`JWT 鉴权)
- [ ] dashboard 前端新增「官网管理」菜单
---
## 风险与注意事项
| 风险 | 应对 |
|------|------|
| 迁移期间线上服务中断 | 渐进式迁移,新旧代码共存,逐模块切换 |
| Sequelize 隐式行为(如自动时间戳) | 现有配置已关闭 timestamps,影响小 |
| `sequelize.sync()` 自动建表 → 手动迁移 | Phase 2 完成后用 drizzle-kit 管理,不再自动 sync |
| CommonJS → ESM 兼容问题 | 部分依赖(如 multer)可能需要 `esModuleInterop` 或动态 import |
| models.js 路由 26KB 过大 | 迁移时拆分为 models + measurements 两个路由文件 |
---
## 预计工时
| 阶段 | 预计时间 |
|------|----------|
| Phase 1TS 基础设施 | 0.5 天 |
| Phase 2Schema 迁移 | 0.5 天 |
| Phase 3Routes 迁移 | 1~1.5 天 |
| Phase 4Services + 清理 | 0.5 天 |
| Phase 5:CMS 模块 | 另计(属于新功能开发) |
| **合计(不含 Phase 5** | **2.5~3 天** |