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

7.7 KiB
Raw Permalink Blame History

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,新旧代码可共存。

  • 安装依赖:typescripttsxdrizzle-ormdrizzle-kit@types/express@types/cors
  • 移除依赖:sequelizePhase 4 最后移除)
  • 创建 tsconfig.json
{
  "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
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
{
  "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.tsDrizzle 连接)
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
// 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 查询:
// 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}%`));
  1. 添加请求/响应类型(配合 Zod validator
  2. 验证接口行为不变

Phase 4:迁移 Services + 清理

目标:全部代码 TS 化,移除 Sequelize。

  • 迁移 6 个 service 文件(主要是加类型标注,逻辑不变)
  • 迁移 middlewareauth.ts, bodyLimit.ts
  • 迁移 configenv.ts, logger.ts, redis.ts
  • 迁移 utils
  • 迁移入口 app.jsapp.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 5CMS 模块 另计(属于新功能开发)
合计(不含 Phase 5 2.5~3 天