Files
ClassHelper/docs/mysql.md
T
LoveChengke 572295c297 feat(server,web,desktop,shared,deploy,plugin): 数据库管理模块 + 客户端深色模式/设置拆子路由/首启引导,版本统一 1.0.0
- 服务端新增 database 模块(/api/database/*,仅 ADMIN):全库 JSON 快照(备份 / 恢复 / 导入导出 / 定时备份
  共用同一格式,14 张表按拓扑序导出、恢复时关外键检查)、连接测试与目标库校验、一键 SQLite ⇄ MySQL 切换
  (备份 → 导出 → 测试连接 → 校验空库 → 改 schema → 子进程 generate / tsc / db push → 写数据,全部成功才改写
  .env,中途失败回滚 schema);XML 快照与 SQLite 数据文件均可下载;打包形态随包带上 prisma CLI 与 typescript
  (prisma-client 生成物是 .ts,需再编进 dist/generated,安装包因之变大)
- Web 管理端新增「数据库管理」页(仅管理员),并加首次登录新手引导(el-tour,可跳过、可随时重看,版本号写
  localStorage,改版后老用户会再收到一次)
- 客户端设置改版:侧边栏改为分组折叠菜单,设置拆成 6 个子路由
  (/settings/{general,appearance,island,reminder,account,about}),新增深色模式(主题持久化到主进程配置,
  主窗口底色跟主题走,避免"先白后黑"闪一下),新增「关于」页(应用信息 / 诊断信息)
- 客户端新增首启六步引导(OnboardingWelcome,状态存主进程配置 onboardingDone)
- 客户端主窗口初始尺寸与 ClassIsland 对齐(1242x582 / minHeight 540 / 居中)
- 版本号统一提升到 1.0.0:5 个 package.json、部署配置与镜像标签(.env.example / docker-compose / Dockerfile)、
  服务端 /health 与首页、插件 manifest.yml 与 BridgeService.PluginVersion、文档里的安装包名
- 文档与验收同步:AGENTS.md 补第 37~51 条约定(深色主题实现边界、设置子路由、数据库管理三件事、侧栏与窗口
  尺寸、引导在自动化里的判据等),README 补「数据库管理 / 新手引导 / 设置改版」三节;verify:e2e 增补 13 条
  数据库管理用例,客户端冒烟与 Web UI 冒烟跟进子路由、深色主题、侧栏与引导

验证:pnpm lint 0 error;pnpm typecheck 全过;verify:e2e 186/186;verify:classisland-plugin 68/68;
build:desktop 通过(含产物凭据门禁);verify:desktop 99/102(3 项为上课时段下的"真实通知链路"用例,
客户端按产品规则暂存普通通知所致,见下);插件 dotnet 编译 0 错误。

注:verify:desktop 的「真实通知链路 / 标记已读」三项要求当前不在上课时段——本次运行时种子课表恰有
「周五 10:55-11:40 生物」正在进行,客户端正确地暂存了普通通知(AGENTS.md 第 18 条),属时间相关用例,
与本次改动无关。
2026-10-02 11:18:47 +08:00

7.0 KiB
Raw Permalink Blame History

MySQL 切换指南

本项目默认使用 SQLite(libSQL 内嵌模式) 跑本地开发,数据模型与访问层已按 MySQL 兼容方式设计,可平滑切换到云端 MySQL / MariaDB 统一存储。

为什么默认是 SQLite

  • 本地零依赖即可跑通全部功能(无需安装数据库服务)
  • schema.prisma 刻意只使用跨库通用类型 (String / Int / Float / Boolean / DateTime,无 enum、无 @db.* 原生类型)

设计上的兼容约定

约定 说明
不使用 Prisma enum 角色(ADMIN/TEACHER/STUDENT)与优先级(LOW/NORMAL/HIGH/URGENT)以 String 存储,由 zod 在 API 层校验,两种数据库行为完全一致
主键为 String @default(cuid()) 不依赖自增,便于多端离线生成与合并
时间统一 DateTime 由应用层与驱动适配器负责时区,不使用数据库原生时间函数
唯一约束仅使用 @@unique / @unique SQLite 与 MySQL 语义一致
级联删除使用 Prisma 的 onDelete 由 Prisma 生成对应外键动作

切换到 MySQL

方式一:Web 管理端「数据库管理 → 一键切换」(推荐,v1.0.0 新增)

管理员在 Web 管理端打开 数据库管理 页(/database,仅 ADMIN 可见),在「一键切换数据库」卡片里:

  1. 选目标类型(SQLite ⇄ MySQL)并填空库连接串(有表的库会被拒绝,防误覆盖);
  2. 输入「确认迁移」→ 点「开始迁移」。

界面会自动依次执行(带步骤进度条):

  1. 备份当前数据库(gzip JSON 快照存 <数据目录>/backups/,失败可回滚);
  2. 导出当前数据为快照;
  3. 测试目标库连接;
  4. 检查目标库为空;
  5. 改写 schema.prisma 的 provider;
  6. 子进程 prisma generate 生成新方言的 Prisma 客户端;
  7. (打包形态)把生成的 .ts 客户端编译进 dist/generated;
  8. 子进程 prisma db push 在目标库建表;
  9. 子进程 dist/tools/apply-snapshot.js 把快照写入目标库;
  10. 全部成功后改写 .env 的 DATABASE_PROVIDER / DATABASE_URL。

完成后必须重启服务端(安装版运行 restart.cmd,开发模式重跑 pnpm dev:server)才会连接新库。 中途任一步失败会回滚 schema.prisma,.env 不被改写 —— 现有数据库与配置保持可用。

同一页还提供:连接状态检测(版本 / 体积 / 各表行数)、连接测试、手动与定时备份、 备份恢复、快照导出导入、SQLite 数据库文件下载。备份与导入导出共用同一种 JSON 快照格式, 因此任意备份都能恢复回任意一种受支持的数据库。

只支持 SQLite 与 MySQL 两种主库:Redis 等键值库不是 Prisma 支持的主数据库(无法建表/迁移), 接口层直接拒绝。

方式二:命令行手工切换

1. 修改 provider

# 自动把 prisma/schema.prisma 的 provider 改成 mysql
node scripts/use-database.mjs mysql

2. MySQL 驱动适配器(自 v1.0.0 起已随包内置)

Prisma 7 要求所有数据库都通过 driver adapter 接入,MySQL 对应 @prisma/adapter-mariadb (该适配器基于 mariadb 驱动,兼容 MySQL 8)。它已在 packages/server/package.json 的 依赖里(数据库管理模块的连接测试/迁移需要),因此无需再手动安装:

# 仅当从旧版本升级、依赖里还没有时才需要
pnpm --filter @classhelper/server add @prisma/adapter-mariadb

packages/server/src/lib/db.ts 内置分支:当 DATABASE_PROVIDER=mysql 时会动态加载该适配器, 服务端代码无需改动。

3. 配置环境变量

编辑 packages/server/.env:

DATABASE_PROVIDER=mysql
DATABASE_URL="mysql://classhelper:your-password@127.0.0.1:3306/classhelper"

云端部署时把 127.0.0.1 换成云数据库内网/公网地址; 生产环境请同时修改 JWT_SECRET 与 CORS_ORIGIN。

4. 重建迁移并生成客户端

SQLite 与 MySQL 的 DDL 方言不同,必须重新生成迁移文件:

# 删除 SQLite 迁移历史(迁移文件是方言相关的)
rm -rf packages/server/prisma/migrations
# 重新生成并应用(需要目标库可连接,且账号有建表权限)
pnpm db:migrate
# 初始化演示数据
pnpm db:seed

也可以先只生成 SQL 供 DBA 审核:

pnpm --filter @classhelper/server exec prisma migrate dev --create-only --name init_mysql

只搬数据、不要演示数据时,用界面的「下载快照 / 导入快照」更省事: 旧库导出 JSON → 切库建表 → 新库导入,一份快照也可用 CLI 迁移: node server/dist/tools/apply-snapshot.js --provider mysql --url "mysql://..." --file snapshot.json

5. 回到 SQLite

node scripts/use-database.mjs sqlite
# 同步 .env:DATABASE_PROVIDER=sqlite、DATABASE_URL="file:./prisma/dev.db"
rm -rf packages/server/prisma/migrations   # 若之前已切换成 MySQL 迁移
pnpm db:migrate

用界面「一键切换」回 SQLite 时,目标库同样必须是空文件:删除旧 .db 或指定一个新路径。

生产部署注意事项

  1. 连接池:Prisma 7 的 driver adapter 使用底层驱动(mariadb)的连接池设置, 与 Prisma 6 默认值不同(例如默认无连接超时)。建议通过适配器参数显式设置连接上限与时区。
  2. 时区:Grade.publishedAt、Homework.dueAt 等字段建议在数据库会话中统一为 UTC, 三端展示时再按本地时区格式化(前端使用 @classhelper/shared 的 formatDate)。
  3. 迁移部署:CI/CD 中使用 prisma migrate deploy(对应 pnpm db:deploy)而不是 migrate dev。
  4. 备份:切换云数据库后,packages/server/prisma/dev.db 不再被使用,可从 .gitignore 之外移除。 界面「定时备份」把快照写进 <数据目录>/backups/(MySQL 模式下即安装根 data/backups/), 服务端进程不运行时不会补跑;关键环境建议同时用数据库自带的备份策略。
  5. 安装包体积:切换链路需要随包内置 prisma CLI 与 typescript 编译器(Prisma 7 生成器产出 .ts,切换后需在目标机编译进 dist/generated),服务端安装包因此约 34MB → 71MB。