87 lines
6.2 KiB
Markdown
87 lines
6.2 KiB
Markdown
---
|
||||
|
|
title: 数据模型
|
|||
|
|
description: 14 张表的职责、跨库通用约定、迁移历史与启动时自动迁移。
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 数据模型
|
|||
|
|
|
|||
|
|
模型定义只有一份:`packages/server/prisma/schema.prisma`。
|
|||
|
|
|
|||
|
|
## 跨库通用约定
|
|||
|
|
|
|||
|
|
- 默认是 **SQLite**(Prisma 7 的 libSQL driver adapter,数据文件 `packages/server/prisma/dev.db`);
|
|||
|
|
- 字段**只用** `String / Int / Float / Boolean / DateTime`,**不用 Prisma enum、不用 `@db.*` 原生类型**;
|
|||
|
|
角色与优先级存字符串,在 API 层用 zod 校验 —— 这样 SQLite 与 MySQL 行为完全一致;
|
|||
|
|
- 因此切库只需改 `provider` 与连接串(`pnpm db:switch:mysql` 就地改写它,没有第二份 schema);
|
|||
|
|
- 主键统一用 cuid 字符串,便于多端离线生成与合并。
|
|||
|
|
|
|||
|
|
## 14 张表
|
|||
|
|
|
|||
|
|
| 表 | 职责 | 值得注意的字段 |
|
|||
|
|
| ------------------- | ---------------------------------------------- | --------------------------------------------------------------------------------- |
|
|||
|
|
| `User` | 教师 / 管理员 / 学生统一存储 | `role`、`classId`;学生 `passwordHash` 是空串占位,不能登录 |
|
|||
|
|
| `Class` | 班级 | `code` + `passwordHash`(班级账号)、`termWeeks`、`notificationChannel` |
|
|||
|
|
| `ClassTeacher` | 教师-班级 分配关系(协作教师) | `@@unique([classId, teacherId])` |
|
|||
|
|
| `Enrollment` | 学生-班级 归属记录 | 与 `User.classId` 同步,为将来多班预留 |
|
|||
|
|
| `Course` | 课程(班级维度) | `@@unique([classId, name])` |
|
|||
|
|
| `Schedule` | 课表条目 | `weekStart` / `weekEnd` / `weekParity`(单双周)/ `source`(manual|classisland) |
|
|||
|
|
| `Homework` | 作业 | `assignDate`(所属日期)、`attachmentUrl`;`dueAt` 是已下线的遗留列 |
|
|||
|
|
| `HomeworkStatus` | 学生作业完成状态 | `@@unique([homeworkId, userId])` |
|
|||
|
|
| `Notification` | 通知 | `priority` |
|
|||
|
|
| `NotificationRead` | 已读记录 | `@@unique([notificationId, userId])` |
|
|||
|
|
| `TimeLayout` | 节次时间配置(ClassIsland 同款 JSON 导入得到) | `items` 是 JSON 字符串:`[{ index, name, startTime, endTime, type, skipped }]` |
|
|||
|
|
| `Grade` | 成绩 | `score` / `totalScore` / `examName` |
|
|||
|
|
| `IntegrationDevice` | ClassIsland 联动设备 | `tokenHash`(只存 sha256)、`tokenHint`、两个开关、最后一次上报的快照字段 |
|
|||
|
|
| `ClassIslandPush` | 下发给 ClassIsland 的提醒 | `kind`(notification|call)、`ackedAt`、`expiresAt` |
|
|||
|
|
|
|||
|
|
### 三个容易忽略的设计
|
|||
|
|
|
|||
|
|
**`ClassIslandPush` 与 `IntegrationDevice`** —— 提醒是**落库**的,不是"发出去就算"。
|
|||
|
|
设备离线时通知不丢:插件重连后拉取未确认的那条立即弹出,弹完回执(`ackedAt`)。
|
|||
|
|
`expiresAt` 防止学生几天后突然弹出旧提醒。
|
|||
|
|
|
|||
|
|
**`Schedule.source`** —— 决定插件全量上报时能清理哪些行:只清 `classisland` 来源且本次未再上报的行。
|
|||
|
|
**`source` 只在 create 时确定,update 一律保持原值**(否则手排的课会被"收养"后又被清掉)。
|
|||
|
|
|
|||
|
|
**`Homework.dueAt`** —— 截止时间功能已下线,UI 与接口都不再读写它。
|
|||
|
|
保留列是为了**不对已安装的库做破坏性迁移**;新库里它恒为 `null`。
|
|||
|
|
|
|||
|
|
## 迁移历史(10 个)
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
init → add_time_layout → add_class_account → add_schedule_week_parity →
|
|||
|
|
add_class_term_weeks → add_classisland_integration → add_class_notification_channel →
|
|||
|
|
add_homework_assign_date → add_push_kind → add_schedule_source
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 启动时自动迁移
|
|||
|
|
|
|||
|
|
`src/lib/db-bootstrap.ts` 用一张账本表 `_ch_migrations` 记录已执行的迁移目录名:
|
|||
|
|
|
|||
|
|
| 情况 | 行为 |
|
|||
|
|
| -------- | --------------------------------------------------- |
|
|||
|
|
| 全新安装 | 依次执行全部迁移并创建初始管理员 |
|
|||
|
|
| 覆盖安装 | 只补跑账本里没有的迁移(已存在的表 / 索引自动跳过) |
|
|||
|
|
| 已是最新 | **不做任何写操作** |
|
|||
|
|
|
|||
|
|
日志会写明「全新安装 / 升级安装」、应用了几个迁移、跳过了几个已存在对象。
|
|||
|
|
用户因此**不需要手动执行 `prisma migrate deploy`**。
|
|||
|
|
|
|||
|
|
## 备份格式就是这份模型的拓扑序导出
|
|||
|
|
|
|||
|
|
「数据库管理」里的备份 / 导出导入 / 跨库迁移共用一种 JSON 快照:14 张表按拓扑序导出,
|
|||
|
|
恢复时**临时关闭外键检查** —— 因为 `User.classId → Class` 与 `Class.teacherId → User`
|
|||
|
|
**互相引用**,任何排序都会被其中一侧卡死。见[数据库管理](../management/database.md)。
|
|||
|
|
|
|||
|
|
## 开发时的模型改动
|
|||
|
|
|
|||
|
|
1. 改 `schema.prisma`;
|
|||
|
|
2. 在 `prisma/migrations/` 下加一个迁移目录(本机用
|
|||
|
|
`pnpm --filter @classhelper/server db:deploy` 应用,**别用 `pnpm db:migrate`** ——
|
|||
|
|
它在 Windows 上不可靠);
|
|||
|
|
3. 改 schema 时先看**哪些外键指向 `User` 且是 Cascade**,再回来核对
|
|||
|
|
`deleteTeacher` 的护栏清单(它必须覆盖 `Class.teacherId` / `Course.teacherId` /
|
|||
|
|
`ClassTeacher.teacherId` / `Homework.createdBy` / `Notification.createdBy` 五项)。
|
|||
|
|
|
|||
|
|
模型总数与迁移个数会随版本增长,以 `schema.prisma` 与 `prisma/migrations/` 的实际内容为准;
|
|||
|
|
本文里的数字标注的是当前版本(1.1.2)的状态。
|