feat(teachers,classes,schedules,web): 教师录入(仅管理员)+ 班主任可设置/更改 + 授课科目全校统一 + 登录页去示例

① 支持录入教师(与学生录入对齐),权限只有管理员
- 新增 Web「教师管理」页(`Views/TeachersView.vue`):单个录入(姓名/用户名/初始密码/角色)、
  导入名单(xlsx/csv:用户名、姓名、初始密码、角色)、编辑(改名/改角色)、重置密码、删除
- 服务端 `teachers` 模块补齐:GET / POST / PATCH /:id / POST /:id/reset-password / DELETE /:id,
  全部 `requireRole('ADMIN')`;表格导入新增 kind=teachers(`IMPORT_FIELDS.teachers`、`commitTeachers`、
  教师名单模板),教师调用预览/提交一律 403
- 删除护栏:还是班主任(`Class.teacherId`)或持有课程/班级分配的账号拒绝删除(409,提示先更换班主任)——
  教师是数据创建者,级联删除会连带删掉班级数据
- 前端路由加 `meta.roles`:`/classes`、`/students`、`/teachers` 仅 ADMIN,教师直接输网址会被挡回仪表盘
  (菜单隐藏只是"看不见",这次补上真正的守卫);新增环境变量 `DEFAULT_TEACHER_PASSWORD`

② 创建班级仅管理员 + 设置 / 更改班主任
- 新建班级时可直接指定班主任(不填则创建者本人),班级详情顶部「更换班主任」下拉随时改:
  新增 `PATCH /api/classes/:id/head-teacher`(仅管理员,校验目标账号是教师/管理员,广播 class:updated)
- 原班主任保留在协作教师名单里,避免换人后丢掉历史协作关系

③ 授课科目全校统一(用户澄清:"统一"指的是授课科目固定,不是各班课表相同)
- `SUBJECT_CATALOG` 改为用户给定的 18 科:语文/数学/英语/政治/历史/物理/化学/地理/生物/信息技术/
  体育/美术/音乐/班会/早读/晚读/通用技术/听力
- 课表「新增课表」与成绩「单条/批量录入」的科目下拉直接列出统一目录(该班还没有的进
  「统一科目(选中后自动建课)」分组),选中后自动为该班建同名课程再落库 —— 不必按班级一个个录科目
- 各班课表仍然各自独立(各班的课表本来就不一样)

④ 登录页去掉所有示例内容
- Web 登录页删除「演示账号(点击填充)」标签列表与"种子数据见 README"提示;客户端班级码占位符
  由「例如 G101(由老师在班级管理中提供)」改为「请输入班级码」
- `verify:web` 与 `verify:desktop` 各自断言登录页正文/占位符不含 `演示/示例/admin123/teacher123/G101`

⑤ 验证脚本不再依赖演示数据(并修掉自身残留)
- 清空服务端作业/通知后,联网自检改为**自建**一条作业/通知并在收尾删除(`seedSmokeContent`);
  权限用例需要"其它教师的班级/科任班级"时现建临时班级并在收尾删除
- e2e 新增清理:实时推送用例造的通知/作业、上课时段用例造的两条通知、教师名单/临时班级
  (此前这几条会留在库里);班级详情相关断言不再依赖种子班级布局
- ui-smoke 新增 7 项:登录页无示例、教师端访问 /teachers 被挡回、统一科目下拉 + 自动建课 + 收尾删除
  课表与课程、管理员真人录入教师 → 列表出现 → 删除
- 冒烟里"岛就绪"与"截图留档"改为轮询等待(偶发丢一张留档图会误判"形态尺寸递增")

实测:verify:e2e 163/163(安装实例)、verify:web 27/27、verify:desktop 77/77(离线复跑 69/69)、
verify:packaged 对 release/win-unpacked 与 D:\class\@classhelperdesktop-client 均 77/77 且 packaged=true;
typecheck / lint / format:check 全绿。服务端安装实例与客户端安装目录已就地更新。
This commit is contained in:
LoveChengke committed 2026-09-13 10:34:46 +08:00
1 parent 0ff2dc228b
commit 845fbdd863
27 files changed
+1655 -200

No files matched your search

+79 -47
View File
@@ -20,7 +20,7 @@
| 1 | pnpm monorepo 脚手架、TypeScript / ESLint / Prettier / 环境变量 | ✅ 已完成 |
| 2 | 后端:Prisma schema + 迁移 + 种子数据 + JWT 认证 + RBAC + 模块化 REST API + Socket.IO | ✅ 已完成 |
| 3 | Web 管理端:登录、主布局、仪表盘、班级/学生/课表/作业/通知/成绩页面、Axios 封装、实时提示 | ✅ 已完成 |
| 4 | EXE 客户端:Electron 主进程/preload/渲染进程、登录、课表/作业/通知/成绩/设置、实时推送、IndexedDB 离线缓存 | ✅ 已完成(冒烟验证 76/76) |
| 4 | EXE 客户端:Electron 主进程/preload/渲染进程、登录、课表/作业/通知/成绩/设置、实时推送、IndexedDB 离线缓存 | ✅ 已完成(冒烟验证 77/77) |
| 5 | 三端联调脚本、打包命令、完整 README | ✅ 已完成(三套安装包 + 两套 UI 回归测试) |
| 6 | 测试账号与种子数据说明 | ✅ 已完成(见下文) |
| 7 | 生产化:服务端安装程序(内置 Node)、Web 端 PWA 可安装、Docker + Nginx 部署、生产加固与运维文档 | ✅ 已完成(见 [`docs/production.md`](docs/production.md)) |
@@ -75,7 +75,7 @@ class-helper/
│ ├── prisma/migrations/ # 迁移历史(已生成并应用)
│ ├── prisma/seed.ts # 种子数据
│ ├── prisma.config.ts # Prisma 7 配置(迁移/种子/连接串)
│ ├── scripts/verify-e2e.mjs # 后端端到端验收脚本(149 项)
│ ├── scripts/verify-e2e.mjs # 后端端到端验收脚本(163 项)
│ └── src/
│ ├── app.ts # Express 装配(静态托管 + 探针 + 限流 + 模块挂载)
│ ├── index.ts # 启动入口(自检/初始化 + HTTP + Socket.IO + 优雅退出)
@@ -171,16 +171,16 @@ pnpm verify:desktop
```bash
# 另开一个终端先启动后端:pnpm dev:server
pnpm verify:e2e # 后端 + REST + Socket.IO + RBAC + 上课时段拦截 + 紧急/普通叫人 + 单双周课表/ClassIsland 课程表导入 + 班级账号(个人学生登录停用):149 项
pnpm verify:web # Web 管理端真实点击(含权限入口隐藏、手机适配、叫人入口 + 紧急/普通级别、多班批量发布、未交名单、课程表导入弹窗、手机端课表横向滚动):20 项
pnpm verify:desktop # Electron 客户端(含灵动岛收起常驻/命中兜底/展开收起再展开/联合通知回归/胶囊类型汇总、未读红点位置、圆角四角一致、开合不震动、紧急与普通叫人、真实链路、课表时间轴、作业看板全屏自适应、个性化与设置页链路、托盘/退出无残留):76 项
pnpm verify:e2e # 后端 + REST + Socket.IO + RBAC + 上课时段拦截 + 紧急/普通叫人 + 单双周课表/ClassIsland 课程表导入 + 班级账号(个人学生登录停用):163 项
pnpm verify:web # Web 管理端真实点击(含权限入口隐藏、手机适配、叫人入口 + 紧急/普通级别、多班批量发布、未交名单、课程表导入弹窗、手机端课表横向滚动、教师录入、统一科目):27 项
pnpm verify:desktop # Electron 客户端(含灵动岛收起常驻/命中兜底/展开收起再展开/联合通知回归/胶囊类型汇总、未读红点位置、圆角四角一致、开合不震动、紧急与普通叫人、真实链路、课表时间轴、作业看板全屏自适应、个性化与设置页链路、托盘/退出无残留):77 项
pnpm verify:packaged # 对**打包后/已安装**的客户端 EXE 跑同一套冒烟(证明 packaged=true,默认 release/win-unpacked)
```
后端脚本验证**实时推送时延、作业完成、成绩下发、权限隔离、上课时段紧急通知拦截、导入与失败回滚、班级账号代全班操作、老师端导入不越权、单双周课表与 ClassIsland 课程表导入、个人学生登录停用**等 149 项,
后端脚本验证**实时推送时延、作业完成、成绩下发、权限隔离、上课时段紧急通知拦截、导入与失败回滚、班级账号代全班操作、老师端导入不越权、单双周课表与 ClassIsland 课程表导入、个人学生登录停用**等 163 项,
实测通知 37ms、作业 26ms、成绩 25ms 到达(要求 < 5 秒),紧急通知 409 拦截与二次确认后发布均通过;
客户端脚本验证 preload 桥接、渲染进程、IndexedDB 读写、断网回退、**灵动岛四种状态切换**与联网集成
(**班级账号登录** + 四类数据 + Socket.IO),实测 76/76 通过;退出后再启动一次校验单实例锁与文件锁已释放(离线复跑 68/68)。
(**班级账号登录** + 四类数据 + Socket.IO),实测 77/77 通过;退出后再启动一次校验单实例锁与文件锁已释放(离线复跑 69/69)。
`pnpm verify:packaged --exe "<客户端路径>"` 对**打包后/已安装**的 EXE 跑同一套用例(`packaged: true`),
用于确认交付给学生的那个副本真的带上了本次修复 —— 开发产物通过**不等于**用户机器上的副本通过。
@@ -223,9 +223,9 @@ pnpm verify:packaged # 对**打包后/已安装**的客户端 EXE 跑同一
| `pnpm dist:dir` | 打包客户端免安装目录 `release/win-unpacked`(含可执行文件,最快) |
| `pnpm dist:win` | 打包客户端 nsis 安装包 + portable 单文件 EXE |
| `pnpm dist:all` | 服务端安装程序 + 客户端安装程序一起打 |
| `pnpm verify:e2e` | 后端端到端验收(149 项,含角色权限矩阵、单双周课表与 ClassIsland 课程表导入、导入与班级账号,需服务端已启动) |
| `pnpm verify:web` | Web 管理端 UI 真实点击测试(Electron 驱动,20 项,含权限入口隐藏、叫人紧急/普通与导入弹窗) |
| `pnpm verify:desktop` | EXE 客户端冒烟验证(76 项,含灵动岛圆角/收起常驻/命中兜底/展开收起再展开/开合不震动/已读/作业/紧急与普通叫人、课表时间轴、作业看板全屏自适应、个性化全参数) |
| `pnpm verify:e2e` | 后端端到端验收(163 项,含角色权限矩阵、单双周课表与 ClassIsland 课程表导入、导入与班级账号,需服务端已启动) |
| `pnpm verify:web` | Web 管理端 UI 真实点击测试(Electron 驱动,27 项,含权限入口隐藏、叫人紧急/普通与导入弹窗) |
| `pnpm verify:desktop` | EXE 客户端冒烟验证(77 项,含灵动岛圆角/收起常驻/命中兜底/展开收起再展开/开合不震动/已读/作业/紧急与普通叫人、课表时间轴、作业看板全屏自适应、个性化全参数) |
| `pnpm verify:packaged` | 对打包后/已安装的客户端 EXE 跑同一套冒烟(`--exe` 指定路径,实测 `packaged: true`) |
| `pnpm typecheck` | 全仓库类型检查(含 `vue-tsc`) |
| `pnpm lint` / `pnpm lint:fix` | ESLint 检查 / 自动修复 |
@@ -289,7 +289,34 @@ pnpm verify:packaged # 对**打包后/已安装**的客户端 EXE 跑同一
实现位置:`src/composables/useResponsive.ts`(断点 + resize 监听)、`src/layouts/AdminLayout.vue`(抽屉/顶栏)、
`src/styles/index.css`(全局响应式规则)、各视图表格卡片加 `.table-card`。
`pnpm verify:web` 的 20 项里,后半段专门验证权限入口隐藏、手机端、叫人入口(含紧急/普通两级)、成绩导入入口与 ClassIsland 时间配置导入弹窗(Electron 真实点击):
### 人员与班级(仅管理员)
| 能力 | 说明 |
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **教师管理**(新页面) | 侧边栏「教师管理」:**单个录入**(姓名 / 用户名 / 初始密码 / 角色)+**导入名单**(xlsx/csv,列:用户名、姓名、初始密码、角色)+编辑(改名/改角色)+重置密码+删除 |
| 录入教师仅管理员 | 页面只对 `ADMIN` 显示,路由 `meta.roles=['ADMIN']` 直接输网址也会被挡回仪表盘;服务端 `/api/teachers` 全量接口 `requireRole('ADMIN')`(教师调用 403,e2e 有断言) |
| 删除教师有护栏 | 还是**班主任**(`Class.teacherId`)或持有课程/班级分配的账号拒绝删除(409,提示先更换班主任)——教师是数据创建者,级联删除会连带删掉班级数据 |
| 创建班级仅管理员 | `POST /api/classes` = `requireRole('ADMIN')`,教师(含班主任)403;前端「新建班级」也只在管理员可见 |
| **设置 / 更改班主任** | 新建班级时可直接选班主任(默认自己);班级详情顶部「更换班主任」下拉随时改(`PATCH /api/classes/:id/head-teacher`,仅管理员),原班主任仍保留在协作教师名单 |
| 学生录入(原有) | 「学生管理」:单个新建(登录名自动生成)+导入名单;学生端主体是班级账号 |
### 授课科目统一(不再按班级一个个录)
全校固定科目目录定义在 `@classhelper/shared` 的 `SUBJECT_CATALOG`:
**语文 / 数学 / 英语 / 政治 / 历史 / 物理 / 化学 / 地理 / 生物 / 信息技术 / 体育 / 美术 / 音乐 / 班会 / 早读 / 晚读 / 通用技术 / 听力**。
- 课表「新增课表」与成绩「单条/批量录入」的**科目下拉直接列出这个统一目录**(该班还没有的科目显示在「统一科目(选中后自动建课)」分组里);
- 选中后由前端自动为该班 `POST /api/courses` 建同名课程(已存在则复用),再写入课表/成绩 —— 科任与班主任都不需要先去"班级 → 课程"里把科目建一遍;
- ClassIsland 课程表导入本来就会按导入内容自动补建课程,两者口径一致;
- `pnpm verify:web` 会在真实浏览器里打开「新增课表」,断言统一科目分组存在、选中后课表写入成功,并在收尾删掉这条课表(不留副作用)。
### 登录页
管理端与客户端登录页都**不含任何示例/演示内容**(没有"演示账号点击填充"、没有示例班级码占位符、
不提示种子账号);`pnpm verify:web` 与 `pnpm verify:desktop` 各自断言登录页正文与占位符里不出现
`演示 / 示例 / admin123 / teacher123 / G101` 等字样,防止以后又被加回去。
`pnpm verify:web` 的 27 项里,后半段专门验证权限入口隐藏、手机端、叫人入口(含紧急/普通两级)、成绩导入入口与 ClassIsland 时间配置导入弹窗(Electron 真实点击):
| 校验项 | 实测结果 |
| -------------------------------------- | -------------------------------------------------------------------- |
@@ -620,7 +647,7 @@ pnpm dist:win # nsis 安装包 + portable 单文件(需联网下载 NSIS
| **退出后无残留(文件锁 + 二次启动)** | ✅ 配置目录可删除(句柄已释放)→ 用同一配置目录二次启动成功并再次全绿 |
该验证对**开发产物与打包后的 EXE 都适用**(打包后用 `ELECTRON_SMOKE_RESULT=<file>` 写出 JSON 结果,
已实测 `packaged: true`、76/76 通过、退出码 0)。灵动岛各状态的窗口截图会写到
已实测 `packaged: true`、77/77 通过、退出码 0)。灵动岛各状态的窗口截图会写到
`ISLAND_SHOTS_DIR`(默认 `.cache/island-shots/`,仓库内留档目录 `docs/screenshots/island/`),
并对尺寸、宽高比、绘制内容(不透明像素/颜色种类)、紧急态红色像素与外溢红光做断言。
冒烟进程使用独立 userData(`ELECTRON_SMOKE_PROFILE`),因此**用户开着客户端也能跑验证**。
@@ -990,37 +1017,42 @@ pnpm db:migrate && pnpm db:seed
## 验收标准对照
| 验收项 | 结果 | 证据 |
| ------------------------------------------------ | ---- | -------------------------------------------------------------------------------------------------------------------------- |
| 教师 Web 端发布通知,学生端 5 秒内收到 | ✅ | `verify:e2e`:`notification:new` **37–44ms** |
| 教师发布作业,学生能查看并标记完成 | ✅ | `homework:new` **26–40ms**,`PATCH /homeworks/:id/status` 200 且列表回显 `completed=true` |
| 教师录入成绩,学生能查看个人成绩 | ✅ | `grade:updated` **21–25ms**,`/grades/my` 返回记录;批量录入与统计接口通过 |
| 学生能查看课表,支持按周切换 | ✅ | `/schedules/grid?week=1` 返回 30 节,`week` 过滤 `weekStart ≤ week ≤ weekEnd` |
| 断网后客户端可查看缓存数据 | ✅ | 客户端冒烟:`断网时回退到本地缓存 → fromCache=true items=1`;离线横幅 + 缓存统计页可用 |
| 权限隔离:学生不能访问其他班级数据 | ✅ | 10 项越权断言全部 403/401(学生跨班/跨班作业/跨班课表、教师跨班发布、未登录访问…) |
| 上课时段发布紧急通知必须二次确认 | ✅ | 服务端 409 `URGENT_DURING_CLASS`(`confirmDuringClass` 后 201);Web 端全屏警告 + 3 秒倒计时 |
| 客户端灵动岛:上课隐藏 / 下课弹出 / 紧急立即展开 | ✅ | `verify:desktop` 状态断言 + 像素级截图(`docs/screenshots/island/`) |
| 灵动岛:收回无"方框"闪烁 / 点击屏幕任意处收回 | ✅ | 采样卡片尺寸恒为固定值(396×308、260×38)+ 失焦自动收回 |
| 灵动岛"标为已读"同步通知中心 | ✅ | 真实链路:点击后 `read=false → true`、未读数 `1 → 0` |
| 作业发布也上岛("新作业"胶囊) | ✅ | `kind=homework` + 展开显示作业要求;**截止时间功能已下线**(接口不再返回 `dueAt`,e2e 断言 `dueAt === undefined`) |
| **灵动岛收起后始终可再次打开** | ✅ | `verify:desktop`:「收起后胶囊常驻(超时不再消失)」「收起态点击可再次展开(指针未移动也算命中)」「空闲细缝态保持可交互」 |
| 叫人(老师点名,分紧急/普通两级) | ✅ | `verify:e2e` 8 项(普通 HIGH / 紧急 URGENT)+ `verify:desktop`「紧急叫人上课也立即展开」「普通叫人上课只进队列、下课弹出」 |
| 成绩 / 名单表格导入(xlsx·xls·csv) | ✅ | 模板下载 + 预览映射 + 重复处理 + 行号级错误:`verify:e2e` 导入 22 项、`verify:web` 弹窗实测 |
| ClassIsland 时间配置导入(覆盖 / 合并 / 回滚) | ✅ | 合法 200、非法 400 `IMPORT_INVALID` 且原配置仍为 3 节、merge 覆盖 1 新增 1 共 4 节 |
| 安装版覆盖升级自动补迁移 | ✅ | 真实旧库升级日志:`升级安装:已应用 2 个迁移文件,跳过 34 个已存在对象`,`TimeLayout` 自动建表 |
| 学生端主体 = 班级(班级码 + 班级密码登录) | ✅ | `verify:e2e` 班级账号 19 项:登录 / 错误密码 401 / 跨班 403 / 发布 403 / 班级码重复 400 / 密码重置 / 班级码用后还原 | `n | 成绩与表格导入:老师端可用且不越权 | ✅ | 班主任预览/导入本班成绩 200、跨班 403、科任 403、名单导入与模板 403;Web 端班主任可见导入按钮并打开弹窗 |
| 班级设备代全班操作(已读 · 完成 · 成绩总览) | ✅ | 标记已读写入 6 条(全班 6 人)、教师端 `readCount=6`、`completedCount=6`、`/grades/my` 全班成绩 |
| 客户端只保留班级登录入口 | ✅ | `verify:desktop`:`login=高一(1)班 classSession=true`,导航「成绩」标题变为「本班成绩」 |
| 能成功打包 Windows EXE | ✅ | `班级小助手-0.1.0-x64-setup.exe` / `-portable.exe` / `win-unpacked/*.exe`(见下表) |
| 提供完整 README(启动、构建、打包、默认账号) | ✅ | 本文档含快速开始、命令表、API、WebSocket、RBAC、模块化、MySQL 切换、打包与常见问题 |
| 验收项 | 结果 | 证据 |
| ------------------------------------------------ | ---- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 教师 Web 端发布通知,学生端 5 秒内收到 | ✅ | `verify:e2e`:`notification:new` **37–44ms** |
| 教师发布作业,学生能查看并标记完成 | ✅ | `homework:new` **26–40ms**,`PATCH /homeworks/:id/status` 200 且列表回显 `completed=true` |
| 教师录入成绩,学生能查看个人成绩 | ✅ | `grade:updated` **21–25ms**,`/grades/my` 返回记录;批量录入与统计接口通过 |
| 学生能查看课表,支持按周切换 | ✅ | `/schedules/grid?week=1` 返回 30 节,`week` 过滤 `weekStart ≤ week ≤ weekEnd` |
| 断网后客户端可查看缓存数据 | ✅ | 客户端冒烟:`断网时回退到本地缓存 → fromCache=true items=1`;离线横幅 + 缓存统计页可用 |
| 权限隔离:学生不能访问其他班级数据 | ✅ | 10 项越权断言全部 403/401(学生跨班/跨班作业/跨班课表、教师跨班发布、未登录访问…) |
| 上课时段发布紧急通知必须二次确认 | ✅ | 服务端 409 `URGENT_DURING_CLASS`(`confirmDuringClass` 后 201);Web 端全屏警告 + 3 秒倒计时 |
| 客户端灵动岛:上课隐藏 / 下课弹出 / 紧急立即展开 | ✅ | `verify:desktop` 状态断言 + 像素级截图(`docs/screenshots/island/`) |
| 灵动岛:收回无"方框"闪烁 / 点击屏幕任意处收回 | ✅ | 采样卡片尺寸恒为固定值(396×308、260×38)+ 失焦自动收回 |
| 灵动岛"标为已读"同步通知中心 | ✅ | 真实链路:点击后 `read=false → true`、未读数 `1 → 0` |
| 作业发布也上岛("新作业"胶囊) | ✅ | `kind=homework` + 展开显示作业要求;**截止时间功能已下线**(接口不再返回 `dueAt`,e2e 断言 `dueAt === undefined`) |
| **灵动岛收起后始终可再次打开** | ✅ | `verify:desktop`:「收起后胶囊常驻(超时不再消失)」「收起态点击可再次展开(指针未移动也算命中)」「空闲细缝态保持可交互」 |
| 叫人(老师点名,分紧急/普通两级) | ✅ | `verify:e2e` 8 项(普通 HIGH / 紧急 URGENT)+ `verify:desktop`「紧急叫人上课也立即展开」「普通叫人上课只进队列、下课弹出」 |
| 成绩 / 名单表格导入(xlsx·xls·csv) | ✅ | 模板下载 + 预览映射 + 重复处理 + 行号级错误:`verify:e2e` 导入 22 项、`verify:web` 弹窗实测 |
| ClassIsland 时间配置导入(覆盖 / 合并 / 回滚) | ✅ | 合法 200、非法 400 `IMPORT_INVALID` 且原配置仍为 3 节、merge 覆盖 1 新增 1 共 4 节 |
| 安装版覆盖升级自动补迁移 | ✅ | 真实旧库升级日志:`升级安装:已应用 2 个迁移文件,跳过 34 个已存在对象`,`TimeLayout` 自动建表 |
| 学生端主体 = 班级(班级码 + 班级密码登录) | ✅ | `verify:e2e` 班级账号 19 项:登录 / 错误密码 401 / 跨班 403 / 发布 403 / 班级码重复 400 / 密码重置 / 班级码用后还原 | `n | 成绩与表格导入:老师端可用且不越权 | ✅ | 班主任预览/导入本班成绩 200、跨班 403、科任 403、名单导入与模板 403;Web 端班主任可见导入按钮并打开弹窗 |
| 班级设备代全班操作(已读 · 完成 · 成绩总览) | ✅ | 标记已读写入 6 条(全班 6 人)、教师端 `readCount=6`、`completedCount=6`、`/grades/my` 全班成绩 |
| 客户端只保留班级登录入口 | ✅ | `verify:desktop`:`login=高一(1)班 classSession=true`,导航「成绩」标题变为「本班成绩」 |
| **教师录入(仅管理员)** | ✅ | `verify:e2e`:教师读/建教师 403、管理员建 201 → 编辑 → 重置密码(新密码 200/旧密码 401)→ 删除(有职责 409 / 无职责 200);`verify:web`:管理员真人点「教师管理 → 新建教师 → 保存 → 列表出现 → 删除」 |
| **教师名单表格导入(仅管理员)** | ✅ | `verify:e2e`:教师预览 403、管理员预览/提交 200(`inserted=1`)、导入账号可登录;模板列=用户名/姓名/初始密码/角色 |
| **创建班级仅管理员 + 设置/更改班主任** | ✅ | `verify:e2e`:教师建班 403;管理员建班可带 `teacherId`、`PATCH /classes/:id/head-teacher` 落库生效;`verify:web`:教师直接访问 `/teachers` 被挡回仪表盘 |
| **授课科目统一(18 科固定目录)** | ✅ | `verify:web`:新增课表的科目下拉出现「统一科目(选中后自动建课)」,选中 → 自动建课 → 写入课表成功(收尾删除该条课表) |
| **登录页无示例内容** | ✅ | `verify:web`(管理端)与 `verify:desktop`(客户端)都断言登录页正文/占位符不含 `演示 / 示例 / admin123 / teacher123 / G101` |
| 能成功打包 Windows EXE | ✅ | `班级小助手-0.1.0-x64-setup.exe` / `-portable.exe` / `win-unpacked/*.exe`(见下表) |
| 提供完整 README(启动、构建、打包、默认账号) | ✅ | 本文档含快速开始、命令表、API、WebSocket、RBAC、模块化、MySQL 切换、打包与常见问题 |
打包产物验证(对最终 EXE 实测,非仅开发产物):
| 产物 | 大小 | 冒烟结果 |
| -------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------- |
| 服务端安装程序(内置 Node + Web 端) | 34MB | ✅ 静默安装(升级保留 `.env` 与数据库)→ 自动建库建号 → 服务就绪 → **对安装实例跑 149/149 e2e + 20/20 UI** |
| `release/win-unpacked/班级小助手.exe` | 234.7MB | ✅ 打包版冒烟 **76/76**(离线复跑 68/68),`packaged: true`,退出码 0(含灵动岛动画/作业看板全屏自适应/叫人/时间轴) |
| `release/…-x64-portable.exe`(单文件) | 107.1MB | ✅ 打包后冒烟 **76/76**,`packaged: true`,退出码 0(自解压单文件同样通过) |
| 服务端安装程序(内置 Node + Web 端) | 34MB | ✅ 静默安装(升级保留 `.env` 与数据库)→ 自动建库建号 → 服务就绪 → **对安装实例跑 163/163 e2e + 27/27 UI** |
| `release/win-unpacked/班级小助手.exe` | 234.7MB | ✅ 打包版冒烟 **77/77**(离线复跑 69/69),`packaged: true`,退出码 0(含灵动岛动画/作业看板全屏自适应/叫人/时间轴) |
| `release/…-x64-portable.exe`(单文件) | 107.1MB | ✅ 打包后冒烟 **77/77**,`packaged: true`,退出码 0(自解压单文件同样通过) |
| `release/…-x64-setup.exe`(客户端) | 107.4MB | ✅ 构建成功,已嵌入自定义图标;含灵动岛与本次行为修正 |
> **把修复交付到"已安装的客户端"**:给学生的 EXE 安装在 `D:\class\@classhelperdesktop-client`(桌面/开始菜单快捷方式指向它),
@@ -1038,15 +1070,15 @@ pnpm db:migrate && pnpm db:seed
当前实测:
| 验证 | 结果 |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- |
| `pnpm verify:e2e`(开发环境与**安装后的生产实例**各跑一次) | **149/149** ✅ |
| `pnpm verify:web`(Web 管理端真实点击 + 权限入口隐藏 + 手机适配 + 叫人 + 成绩/时间配置导入 + PWA) | **20/20** ✅ |
| `pnpm verify:desktop`(客户端冒烟 + WinIsland 架构/连续圆角/收起常驻 + 命中兜底与"展开收起再展开" + 作业看板全屏自适应 + 个性化全参数 + 托盘与退出无残留) | **76/76** ✅(离线复跑 68/68) |
| `pnpm verify:packaged`(对 `release/win-unpacked` 与 `D:\class\@classhelperdesktop-client` 实测) | **76/76**,`packaged: true` ✅ |
| `pnpm typecheck` / `pnpm lint` / `pnpm format:check` | 全部通过 ✅ |
| 安装程序完整生命周期(静默安装 → 启停脚本 → 卸载) | 通过 ✅ |
| Docker / Nginx 部署样例 | 文件已提供,本机无 Docker 未实测 ⚠️ |
| 验证 | 结果 |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- |
| `pnpm verify:e2e`(开发环境与**安装后的生产实例**各跑一次) | **163/163** ✅ |
| `pnpm verify:web`(Web 管理端真实点击 + 权限入口隐藏 + 手机适配 + 叫人 + 成绩/时间配置导入 + 教师录入 + 统一科目 + PWA) | **27/27** ✅ |
| `pnpm verify:desktop`(客户端冒烟 + WinIsland 架构/连续圆角/收起常驻 + 命中兜底与"展开收起再展开" + 作业看板全屏自适应 + 登录页无示例 + 个性化全参数 + 托盘与退出无残留) | **77/77** ✅(离线复跑 69/69) |
| `pnpm verify:packaged`(对 `release/win-unpacked` 与 `D:\class\@classhelperdesktop-client` 实测) | **77/77**,`packaged: true` ✅ |
| `pnpm typecheck` / `pnpm lint` / `pnpm format:check` | 全部通过 ✅ |
| 安装程序完整生命周期(静默安装 → 启停脚本 → 卸载) | 通过 ✅ |
| Docker / Nginx 部署样例 | 文件已提供,本机无 Docker 未实测 ⚠️ |
## 常见问题(本机环境已知坑)