init: 整合项目(dashboard + www + docs)

This commit is contained in:
eafonyang
2026-07-30 11:23:50 +08:00
commit 99ab6cc4f4
435 changed files with 61037 additions and 0 deletions
+261
View File
@@ -0,0 +1,261 @@
# 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 天** |