From 24e8dd902b4aa15e1f6c97df910176295dfe6f42 Mon Sep 17 00:00:00 2001 From: LoveChengke Date: Fri, 23 Oct 2026 22:55:21 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E5=96=84=E5=AD=A6=E7=94=9F?= =?UTF-8?q?=E7=94=9F=E5=91=BD=E5=91=A8=E6=9C=9F=E7=AE=A1=E7=90=86=E4=B8=8E?= =?UTF-8?q?=E6=9D=83=E9=99=90=E8=8C=83=E5=9B=B4=E5=B9=B6=E5=8F=91=E5=B8=83?= =?UTF-8?q?=201.4.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/docker-compose.sqlite.yml | 2 +- deploy/docker-compose.yml | 2 +- deploy/package.runtime.json | 2 +- docs/app/classes.md | 29 +- docs/app/schedules.md | 8 +- docs/dev/api.md | 134 +- docs/dev/data-model.md | 84 +- docs/dev/implementation.md | 14 +- docs/dev/permissions.md | 46 +- docs/dev/testing.md | 41 +- docs/get-started/web-admin.md | 22 +- docs/screenshots/plan-editor-empty.png | Bin 0 -> 57138 bytes docs/screenshots/plan-editor-live-api.png | Bin 0 -> 97691 bytes docs/screenshots/plan-editor-mobile.png | Bin 0 -> 40519 bytes docs/screenshots/plan-editor-reference.png | Bin 0 -> 110534 bytes docs/screenshots/plan-editor-time-layout.png | Bin 0 -> 88340 bytes docs/screenshots/plan-editor-workspace.png | Bin 110265 -> 68879 bytes package.json | 3 +- packages/desktop-client/package.json | 2 +- packages/server/package.json | 2 +- .../migration.sql | 6 + .../migration.sql | 23 + packages/server/prisma/schema.prisma | 32 +- packages/server/prisma/seed.ts | 4 +- packages/server/scripts/verify-e2e.mjs | 119 +- packages/server/scripts/verify-lifecycle.ts | 416 +++ packages/server/scripts/verify-v14.ts | 2 + packages/server/src/lib/access.ts | 88 +- packages/server/src/lib/mappers.ts | 20 +- packages/server/src/lib/permission-scope.ts | 44 +- packages/server/src/lib/permissions.ts | 31 + packages/server/src/lib/settings.ts | 1 + packages/server/src/lib/snapshot.ts | 16 +- packages/server/src/lib/student-lifecycle.ts | 166 + packages/server/src/lib/students.ts | 6 +- packages/server/src/middleware/auth.ts | 10 +- .../src/modules/archives/archives.module.ts | 12 +- .../src/modules/archives/archives.schemas.ts | 8 - .../src/modules/archives/archives.service.ts | 121 +- .../modules/archives/transferred.service.ts | 39 +- .../server/src/modules/calls/calls.module.ts | 15 +- .../server/src/modules/calls/calls.service.ts | 5 +- .../modules/class-plans/class-plans.module.ts | 88 +- .../class-plans/profile-import.service.ts | 4 +- .../src/modules/classes/classes.module.ts | 90 +- .../src/modules/classes/classes.schemas.ts | 10 +- .../src/modules/classes/classes.service.ts | 190 +- .../src/modules/courses/courses.module.ts | 22 +- .../src/modules/homeworks/homeworks.module.ts | 87 +- .../modules/homeworks/homeworks.service.ts | 15 +- .../src/modules/imports/class-plan.service.ts | 4 +- .../src/modules/imports/imports.module.ts | 150 +- .../modules/imports/table-import.service.ts | 67 +- .../modules/imports/time-layout.service.ts | 4 +- .../notifications/notifications.module.ts | 101 +- .../notifications/notifications.service.ts | 13 +- .../modules/permissions/permissions.module.ts | 35 +- .../permissions/permissions.service.ts | 478 +-- .../src/modules/schedules/schedules.module.ts | 113 +- .../src/modules/students/students.module.ts | 48 +- .../src/modules/students/students.schemas.ts | 56 +- .../src/modules/students/students.service.ts | 348 +- .../src/modules/term/promotion.service.ts | 510 ++- .../server/src/modules/term/term.module.ts | 41 +- .../server/src/modules/term/term.service.ts | 13 +- packages/shared/package.json | 2 +- packages/shared/src/index.ts | 1 + packages/shared/src/lifecycle.ts | 52 + packages/shared/src/permissions.ts | 11 +- packages/shared/src/types.ts | 30 +- packages/web-admin/package.json | 2 +- packages/web-admin/src/api/index.ts | 20 +- .../web-admin/src/components/ArchiveIcon.vue | 52 + .../src/components/PromotionSettings.vue | 326 +- .../src/components/TransferredStudents.vue | 61 +- packages/web-admin/src/config/navigation.ts | 2 +- packages/web-admin/src/router/index.ts | 14 +- packages/web-admin/src/views/ArchivesView.vue | 14 +- packages/web-admin/src/views/ClassesView.vue | 110 +- .../web-admin/src/views/PermissionsView.vue | 206 +- .../web-admin/src/views/PlanEditorView.vue | 2888 +++++++++++++---- packages/web-admin/src/views/StudentsView.vue | 281 +- .../web-admin/src/views/TermWeeksView.vue | 34 +- ...permission-and-student-lifecycle.prompt.md | 467 +++ ...permission-and-student-lifecycle.result.md | 145 + scripts/ui-smoke/main.cjs | 140 +- scripts/verify-client-migrations.mjs | 85 +- scripts/verify-e2e-isolated.mjs | 2 + scripts/verify-lifecycle.mjs | 39 + scripts/verify-v14.mjs | 2 + website/download.html | 2 +- website/index.html | 6 +- 92 files changed, 6801 insertions(+), 2255 deletions(-) create mode 100644 docs/screenshots/plan-editor-empty.png create mode 100644 docs/screenshots/plan-editor-live-api.png create mode 100644 docs/screenshots/plan-editor-mobile.png create mode 100644 docs/screenshots/plan-editor-reference.png create mode 100644 docs/screenshots/plan-editor-time-layout.png create mode 100644 packages/server/prisma/migrations/20261023000000_promotion_permission/migration.sql create mode 100644 packages/server/prisma/migrations/20261023010000_student_lifecycle_audit/migration.sql create mode 100644 packages/server/scripts/verify-lifecycle.ts create mode 100644 packages/server/src/lib/student-lifecycle.ts create mode 100644 packages/shared/src/lifecycle.ts create mode 100644 packages/web-admin/src/components/ArchiveIcon.vue create mode 100644 prompts/permission-and-student-lifecycle.prompt.md create mode 100644 prompts/permission-and-student-lifecycle.result.md create mode 100644 scripts/verify-lifecycle.mjs diff --git a/deploy/docker-compose.sqlite.yml b/deploy/docker-compose.sqlite.yml index ee9956e..47ffcde 100644 --- a/deploy/docker-compose.sqlite.yml +++ b/deploy/docker-compose.sqlite.yml @@ -23,7 +23,7 @@ services: context: .. dockerfile: deploy/Dockerfile target: runtime - image: classhelper-server:${APP_VERSION:-1.4.1} + image: classhelper-server:${APP_VERSION:-1.4.2} restart: unless-stopped environment: NODE_ENV: production diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index 4aaaed0..893812d 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -60,7 +60,7 @@ services: context: .. dockerfile: deploy/Dockerfile target: runtime - image: classhelper-server:${APP_VERSION:-1.4.1} + image: classhelper-server:${APP_VERSION:-1.4.2} restart: unless-stopped environment: NODE_ENV: production diff --git a/deploy/package.runtime.json b/deploy/package.runtime.json index 13e4a30..693b93d 100644 --- a/deploy/package.runtime.json +++ b/deploy/package.runtime.json @@ -1,6 +1,6 @@ { "name": "classhelper-server-runtime", - "version": "1.4.1", + "version": "1.4.2", "private": true, "type": "module", "description": "ClassHelper 服务端运行时(安装包 / 容器内置)", diff --git a/docs/app/classes.md b/docs/app/classes.md index bc85bbd..c001b49 100644 --- a/docs/app/classes.md +++ b/docs/app/classes.md @@ -19,8 +19,8 @@ description: 名单、班级会话、任课关系与年度升级。 Student 存学号、姓名、班级、性别、家长电话和状态,没有密码或角色字段。 学号为必填唯一标识,格式为 1–32 位字母、数字或 . _ -;不会自动生成登录名。 -管理员或本班班主任在具备名单管理权限后管理学生,教师也可按授权范围只读名单。 -名单表格导入仍要求管理员及相应导入权限;学号查重在 Student 表完成,不当成 User 用户名。 +具有名单管理键的管理员、本班班主任或全校名单范围的授权员工管理学生;只读账号按名单读取键和范围查看历史。 +名单表格导入要求 import:students、roster:manage 和对应班级范围;学号查重在 Student 表完成,不当成 User 用户名。 调班保留历史内容原归属;转出独立记账并从现行业务名单过滤,学号不可复用。 ## 教师与科目 @@ -39,5 +39,26 @@ Student 存学号、姓名、班级、性别、家长电话和状态,没有密 范围不代替科目写权限,详见[角色与权限](../dev/permissions.md)。 学期周数受全校可配置上限约束,默认上限 30、允许 1–60,见[课表](schedules.md)。 学年升级按计划时间运行,执行前保存快照,以年份幂等;启动发现漏跑需管理员预览补跑。 -高三归档、高一/高二升一级,新高一不复制任课关系。归档页查看届别历史与转出记录, -不再通过手动创建归档来代替年度升级。撤回转出等操作仍受归档与管理员业务检查约束。 +默认高中轨道按高一、高二、高三排列,毕业学段归档,其余升一级;也可配置其他轨道。新班不复制任课关系。归档页查看届别历史与转出记录, +不再通过手动创建归档来代替年度升级。撤回转出由 roster:manage 控制,原班归档后可指定其他在读班。 + +## 学生生命周期 + +资料编辑仅修改学号、姓名、性别与家长电话,不接受状态或班级字段;旧客户端发送这两项会收到 400 和专用接口提示。 +默认名单只显示未归档的 active;状态筛选可查看 inactive、transferred、graduated,“包含归档”另行控制归档标记。 + +调班、转出、转入、复学、撤回与移出班级均在一个事务内修改学生并写历史,包含批次、学年、原新班级与年级、来源去向学校、操作人和备注。 +调班需同时覆盖源班与目标班;跨年级须管理员勾选允许跨年级并填写备注。目标班必须在读。 +转出保留原班及业务历史,只接受在读或停用学生。转入支持新建学号或选择转出/停用学生,毕业或归档学生不能通过名单入口复活。 +撤回转出可选择另一个在读班,原历史保留。默认删除动作改为停用;硬删除另须管理员确认且无成绩、作业、已读或历史。 + +## 升级预览与执行 + +“学年升级”页面独立要求 promotion:manage,全校范围。先保存轨道、有序年级、起始班数、执行时间、开学日和周次重置策略,再预览逐班影响。 +预览包含在读/停用/转出人数、新班码、周次删除行数、阻断和警告、计划哈希。未知年级、缺失届别、重复班号、已有届别档案或新班码冲突会阻断执行。 +执行需确认学年和预览哈希;业务或配置变化后必须重新预览。未来学年禁止执行,过去学年仅 ADMIN 明确补跑。 + +毕业班所有学生挂届别:在读转毕业,转出和停用保留状态;非毕业班只升到下一学段并清本班开学日覆盖。 +新班只有骨架,开学日取计划值(未配置时为执行学年 9 月 1 日)。保留周次时,旧逐周区间会继续覆盖新学年,页面会警告。 +同年执行幂等,执行记录显示操作人、快照路径、SHA256、明细与错误。快照失败不升级,失败事务回滚后可修正重试。 +撤销归档只清标记并将毕业生恢复在读,转出/停用不变;不回滚年级、不删除新班、不恢复已清空周次,整学年恢复需管理员手工使用快照。 diff --git a/docs/app/schedules.md b/docs/app/schedules.md index 7890ac4..50047ef 100644 --- a/docs/app/schedules.md +++ b/docs/app/schedules.md @@ -8,9 +8,13 @@ description: 日课表、共享作息、轮换、临时换课和教学周。 Web 管理端统一从「教学事务 → 课表档案」查看周课表并编辑时间表与日课表,客户端提供“今天”时间轴和“本周”视图。 上课状态、灵动岛课程提醒和紧急通知确认共用服务端的课表判定。 -编辑工作区参照 [CsesWebEditor](https://github.com/SmartTeachCN/CsesWebEditor) 的资源列表、设置卡片、课时列表与快速选科布局实现。左侧集中切换课程表、时间表、科目、群组与预定、整周预览和文件预览。旧管理页地址 `/schedules` 自动进入同一工作区。 +编辑工作区参照 [ClassIsland 档案编辑器](https://github.com/ClassIsland/ClassIsland/blob/master/ClassIsland/Views/ProfileSettingsWindow.axaml) 的布局与交互实现,使用独立的深色全屏工作区。顶部切换课表、时间表、科目和调课;默认以周网格展示课程,右侧提供科目简称面板,底部可切换列表、周和月视图。旧管理页地址 `/schedules` 自动进入同一工作区,点击「返回管理」回到管理端。 -点击课时后可用右侧科目按钮快速填充;拖动课时交换课程,拖动时间点调整时间表顺序,完成后保存。时间表支持任意分钟输入;设置的课程/课间分钟数会用于新建时间点的默认起止时间。完整档案可在页头导入或导出 JSON,保留轮换、时间点类型、空课、停用、群组和预定。 +点击每天列头的「新课表」选择时间表并创建日课表。点击网格中的课时后可用右侧科目按钮快速填充,支持选择是否自动移动到下一节;方向键可移动焦点,Delete 可清空选中课程。课表名称、时间表、启用星期、轮换规则、生效周次与群组在「详细信息」中维护。 + +列表视图保留批量排课、复制粘贴、拖动交换课程等操作;时间表页支持拖动时间点排序、任意分钟输入,以及上课、课间、分割线、行动四种时间点。设置的课程/课间分钟数用于新建时间点的默认起止时间。编辑完成点击「保存」或按 Ctrl+S;切周与实时刷新保留草稿,切换班级和离开页面时提醒未保存的修改。 + +完整档案可在页头「导入/导出」中导入或导出 JSON,保留轮换、时间点类型、空课、停用、群组和预定。月视图提供日期导航,点击日期后由服务端解析教学周并进入周视图。 ## 日课表与时间表 diff --git a/docs/dev/api.md b/docs/dev/api.md index 0adeafb..5408042 100644 --- a/docs/dev/api.md +++ b/docs/dev/api.md @@ -12,66 +12,96 @@ API 前缀 /api,JWT 使用 Authorization: Bearer。成功/失败结构以 `lib ## 身份与班级 -| 方法与路径(省略 /api) | 行为及边界 | -| ----------------------------------------------------------------------- | -------------------------------------------------------------- | -| POST /auth/login、/auth/class-login | 员工/班级登录,共享登录限流;学生无账号 | -| GET /auth/me;POST /auth/logout;PATCH /auth/password | 当前身份、退出、员工本人改密码 | -| GET/POST /classes;GET/PATCH/DELETE /classes/:id | 班级查询/管理,写入需 class:manage 与管理员业务断言 | -| PATCH /classes/:id/class-account、/classes/:id/head-teacher | 班级凭据/班主任维护 | -| PUT /classes/:id/subject-teachers | 任课关系设置,Course 为唯一来源 | -| GET/PATCH /classes/:id/student-grade-query | 查看/修改班级个人成绩查询开关,班级端不能修改 | -| GET/POST /classes/:id/students;DELETE /classes/:id/students/:studentId | 本班名单及维护,受名单功能权限和班级关系约束 | -| /students、/teachers | 名单与账号 CRUD;读/写使用不同权限键,具体角色限制读取对应模块 | -| POST /teachers/:id/reset-password | 指定新密码或默认重置;学生无对应接口 | -| /permissions/me、/catalog、/groups、/users | 当前有效权限、目录、组与账号分组 | +| 方法与路径(省略 /api) | 行为及边界 | +| ----------------------------------------------------------------------- | --------------------------------------------------------------------------------- | +| POST /auth/login、/auth/class-login | 员工/班级登录,共享登录限流;学生无账号 | +| GET /auth/me;POST /auth/logout;PATCH /auth/password | 当前身份、退出、员工本人改密码 | +| GET/POST /classes;GET/PATCH/DELETE /classes/:id | 班级查询/管理,读取 class:read;写入 class:manage 与目标范围,创建/删除需全校范围 | +| PATCH /classes/:id/class-account、/classes/:id/head-teacher | 班级凭据/班主任维护 | +| PUT /classes/:id/subject-teachers | 任课关系设置,Course 为唯一来源 | +| GET/PATCH /classes/:id/student-grade-query | 查看/修改班级个人成绩查询开关,班级端不能修改 | +| GET/POST /classes/:id/students;DELETE /classes/:id/students/:studentId | 本班名单及维护,受名单功能权限和班级关系约束 | +| /students、/teachers | 名单与账号 CRUD;读/写使用不同权限键,具体角色限制读取对应模块 | +| POST /teachers/:id/reset-password | 指定新密码或默认重置;学生无对应接口 | +| /permissions/me、/catalog、/groups、/users | 当前有效权限、目录、组与账号分组 | ## 教学与文件 -| 路径/动作 | 行为及边界 | -| ---------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | -| /courses | 班级科目与任课关系 | -| GET /schedules、/grid、/current、/active-week | 新关系模型的兼容投影,按班级范围读取 | -| POST/PATCH/DELETE /schedules[/:id] | 兼容 CRUD,写新模型,不双写旧 Schedule | -| POST /schedules/swap、/overlay、/scheduled;GET /schedules/scheduled;DELETE /schedules/scheduled/:id | 换课、临时层、预定 | -| /time-layouts;POST /time-layouts/:id/clone、/:id/import | 时间表 CRUD/克隆/导入,写入 timetable:manage | -| /class-plans;PUT /class-plans/:id/entries;PATCH /class-plans/:id/enabled | 日课表 CRUD、整表保存、启用,写入 schedule:write + 班主任/管理员 | -| POST /class-plans/:id/clone、/class-plans/import、/class-plans/import/profile;GET /class-plans/:id/export | 复制、导入导出及完整档案导入 | -| /class-plan-groups;GET /class-plan-groups/selection;POST /class-plan-groups/select | 群组及当前/临时选择 | -| /homeworks、/homeworks/bulk、/:id/status、/:id/submissions | 作业、批量、完成与未交;员工功能键+科目检查,班级端可录作业 | -| /notifications、/notifications/bulk、/days、/creators、/unread-count、/:id/read、/read-all | 通知、归档和已读;发布需 notification:publish,课堂紧急须确认 | -| POST /calls | 叫人,call:create + 范围检查 | -| /grades、/grades/bulk、/stats、/levels、/my、/overview、/collective、/student/:studentNo、/exams、/ranking | [成绩与隐私](../app/grades.md),查询列表参数用 studentId,写入仍按科目 | -| /imports/template、/table/preview、/table/commit | 表格导入,按 kind 再检查角色、导入权限、班级和科目 | -| /imports/class-plan/preview、/imports/class-plan | 旧预览与完整档案提交;提交走关系化导入 | -| /imports/time-layout[/:id]、/time-layout/preview | 时间配置兼容入口,写入/删除需 timetable:manage | -| GET /class-plans/profile?classId= | 完整档案导出,按班级范围读取,保留时间点与课表关联 | -| GET /courseware、/courseware/:id、/:id/download | 课件目录/详情/下载;员工需 courseware:read,班级端按可见范围 | -| POST /courseware/upload、/:id/push;PATCH /courseware/:id/visibility;DELETE /courseware/:id | 写入 courseware:write + 员工身份;本人文件、任课班级与可见范围限制 | -| GET/POST /courseware/folders;PATCH /courseware/:id/folder | 员工 courseware:write;目录与课件均限本人 | -| GET /courseware/transfers;POST /courseware/transfers/:id/ack | 班级端补领、逐设备回执 | +| 路径/动作 | 行为及边界 | +| ---------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | +| /courses | 班级科目与任课关系 | +| GET /schedules、/grid、/current、/active-week | 新关系模型的兼容投影,按班级范围读取 | +| POST/PATCH/DELETE /schedules[/:id] | 兼容 CRUD,写新模型,不双写旧 Schedule | +| POST /schedules/swap、/overlay、/scheduled;GET /schedules/scheduled;DELETE /schedules/scheduled/:id | 换课、临时层、预定 | +| /time-layouts;POST /time-layouts/:id/clone、/:id/import | 时间表 CRUD/克隆/导入,写入 timetable:manage | +| /class-plans;PUT /class-plans/:id/entries;PATCH /class-plans/:id/enabled | 日课表 CRUD、整表保存、启用,写入 schedule:write + 班主任/管理员 | +| POST /class-plans/:id/clone、/class-plans/import、/class-plans/import/profile;GET /class-plans/:id/export | 复制、导入导出及完整档案导入;导入另需 import:schedule | +| /class-plan-groups;GET /class-plan-groups/selection;POST /class-plan-groups/select | 群组及当前/临时选择 | +| /homeworks、/homeworks/bulk、/:id/status、/:id/submissions | 作业、批量、完成与未交;员工功能键+科目检查,班级端可录作业 | +| /notifications、/notifications/bulk、/days、/creators、/unread-count、/:id/read、/read-all | 通知、归档和已读;发布需 notification:publish,课堂紧急须确认 | +| POST /calls | 叫人,call:create + 范围检查 | +| /grades、/grades/bulk、/stats、/levels、/my、/overview、/collective、/student/:studentNo、/exams、/ranking | [成绩与隐私](../app/grades.md),查询列表参数用 studentId,写入仍按科目 | +| /imports/template、/table/preview、/table/commit | 表格导入,按 kind 再检查对应导入键、管理键、班级和科目 | +| /imports/class-plan/preview、/imports/class-plan | 预览与提交均需 import:schedule + schedule:write;提交走关系化导入 | +| /imports/time-layout[/:id]、/time-layout/preview | 时间配置兼容入口,导入需 import:schedule + timetable:manage,删除需 timetable:manage | +| GET /class-plans/profile?classId= | 完整档案导出,按班级范围读取,保留时间点与课表关联 | +| GET /courseware、/courseware/:id、/:id/download | 课件目录/详情/下载;员工需 courseware:read,班级端按可见范围 | +| POST /courseware/upload、/:id/push;PATCH /courseware/:id/visibility;DELETE /courseware/:id | 写入 courseware:write + 员工身份;本人文件、任课班级与可见范围限制 | +| GET/POST /courseware/folders;PATCH /courseware/:id/folder | 员工 courseware:write;目录与课件均限本人 | +| GET /courseware/transfers;POST /courseware/transfers/:id/ack | 班级端补领、逐设备回执 | ## 设备、系统与运维 -| 路径/动作 | 行为及边界 | -| ------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | -| POST /clients/heartbeat、/clients/commands/:id/ack | 登录设备心跳/回执,校验 deviceKey 和归属 | -| POST /clients/update-progress | 设备上传下载、校验、等待、安装和失败状态,校验设备班级归属 | -| GET /clients/devices;DELETE /clients/devices/:id | 设备读取/维护,分别检查功能键和范围 | -| POST /clients/commands | remote:control + 对应范围,白名单动作 | -| POST /clients/updates | client:update,安装策略 immediate/after-school/idle | -| GET /update/check | 员工需 update:check;强制刷新另检查 update:force | -| GET /update/client/releases | 缓存版本读取,按路由允许的读取权限 | -| POST /update/client/fetch、/update/client/releases;DELETE /update/client/releases/:version | 安装包写入口统一 client:update | -| GET /update/client/download/:version | 已登录下载,不挂员工权限键,供班级端更新 | -| /term;POST /term/auto;GET /term/holidays | 学期读取/编辑/假日建议,term:read/term:write | -| GET/PUT /term/promotion;POST /term/promotion/run | 升级计划、预览和执行,system:manage,执行还做业务校验 | -| /archives、/archives/transferred、/archives/transferred/:id/restore | 历史归档与撤回转出,archive:manage;不提供创建归档代替年度升级的入口 | -| /database/status、/test-connection、/backups、/export、/import、/sqlite-file、/backup-schedule、/switch | database:manage;目标空库、快照及换库子进程保护 | -| /system/settings、/autostart、/diagnostics | system:manage,非安装形态自启动返回不可用原因 | -| /ai/settings、/interpret、/audit | ai:manage/ai:use;只解析待确认动作,密钥只进不出 | -| /dashboard/summary、/dashboard/term | 按范围读取看板与周次 | +| 路径/动作 | 行为及边界 | +| ------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | +| POST /clients/heartbeat、/clients/commands/:id/ack | 登录设备心跳/回执,校验 deviceKey 和归属 | +| POST /clients/update-progress | 设备上传下载、校验、等待、安装和失败状态,校验设备班级归属 | +| GET /clients/devices;DELETE /clients/devices/:id | 设备读取/维护,分别检查功能键和范围 | +| POST /clients/commands | remote:control + 对应范围,白名单动作 | +| POST /clients/updates | client:update,安装策略 immediate/after-school/idle | +| GET /update/check | 员工需 update:check;强制刷新另检查 update:force | +| GET /update/client/releases | 缓存版本读取,按路由允许的读取权限 | +| POST /update/client/fetch、/update/client/releases;DELETE /update/client/releases/:version | 安装包写入口统一 client:update | +| GET /update/client/download/:version | 已登录下载,不挂员工权限键,供班级端更新 | +| /term;POST /term/auto;GET /term/holidays | 学期读取/编辑/假日建议,term:read/term:write | +| GET/PUT /term/promotion;POST /term/promotion/run | 升级计划与执行,promotion:manage + 全校范围,执行要求确认与计划哈希 | +| /archives、/archives/transferred、/archives/transferred/:id/restore | 历史归档需 archive:manage 全校范围;撤回转出需 roster:manage;不提供手动创建归档 | +| /database/status、/test-connection、/backups、/export、/import、/sqlite-file、/backup-schedule、/switch | database:manage;目标空库、快照及换库子进程保护 | +| /system/settings、/autostart、/diagnostics | system:manage,非安装形态自启动返回不可用原因 | +| /ai/settings、/interpret、/audit | ai:manage/ai:use;只解析待确认动作,密钥只进不出 | +| /dashboard/summary、/dashboard/term | 按范围读取看板与周次 | 探针 /healthz 和 /readyz 不带 API 前缀;/api/health 提供版本与模块详情。 课件上传是原始文件流,512 MB 限制独立于表格 JSON 的 12 MB 限制;metadata 走上传查询 schema。 心跳 agentProtocol:2 使用统一响应 data,旧协议兼容字段只用于升级过渡。 更多说明见[权限](permissions.md)、[实时事件](realtime.md)、[数据模型](data-model.md)。 + +## 权限治理与学生年度流程 + +| 方法与路径(省略 /api) | 参数与权限边界 | +| ---------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | +| GET /permissions/scopes | permission:manage;返回可选年级、部建议和计划年级 | +| GET /permissions/groups/:id/members | permission:manage;包含显式分组及角色默认回落的生效成员 | +| GET /permissions/audit?page=&pageSize= | permission:manage;分页操作人、目标、前后 JSON,pageSize 最大 100 | +| POST/PATCH/DELETE /permissions/groups[/:id] | permission:manage;范围校验与防提权;默认组仅 ADMIN 可改,内置组不可删 | +| PUT /permissions/users/:id/groups | groupIds 整体替换;默认组角色须匹配,空数组恢复角色默认模板;禁止越键越范围 | +| GET /students?status=&includeArchived= | roster:read;默认 active,归档单独筛选;历史状态不会再被在读条件覆盖 | +| POST /students | roster:manage;仅 active/inactive,学号重复 409,班级不得归档 | +| PATCH /students/:id | roster:manage;仅资料字段;classId/status 明确返回 400 | +| POST /students/transfer | roster:manage;studentIds、toClassId(null 表示移出)、note;跨年级需 ADMIN、allowGradeChange=true 与备注 | +| POST /students/transfer-out | roster:manage;studentIds、note、toSchool;仅 active/inactive,毕业/归档/重复转出 409 | +| POST /students/transfer-in | roster:manage;指定 toClassId,新建用 studentNo/name,重入用 studentId;可传 fromSchool/note | +| POST /students/:id/restore | roster:manage;可传 toClassId/note;转出或停用恢复,原班归档时应另选在读班 | +| POST /archives/transferred/:id/restore | 兼容同上,权限改为 roster:manage | +| DELETE /students/:id | roster:manage;默认停用;hard=true&confirm=true 仅 ADMIN,存在业务或历史则 409 | +| GET /students/transfers;GET /students/:id/transfers | roster:read;不排除转出状态;支持 studentId/classId/batchId/academicYear 与分页 | +| GET/PUT /term/promotion | promotion:manage 全校范围;读取计划、执行时间、应执行学年与记录;保存兼容旧 month/day/hour/classCount,也接受 plan | +| POST /term/promotion/preview | promotion:manage 全校范围;year;只读返回逐轨道逐班统计、阻断项、警告、新班码、周次影响、planHash | +| POST /term/promotion/run | promotion:manage 全校范围;year、planHash、confirm:true;过去学年还须 ADMIN + catchUp:true;未来年份 400;阻断或哈希变化 409 带 details | +| POST /classes/promote | promotion:manage 与各目标范围;仅 classIds,不收自由 grade;从计划推导下一学段,毕业学段 409 | +| PATCH /classes/:id | 不再接受 grade,旧请求返回 400;改年级须走升级入口 | +| DELETE /archives/:id | archive:manage 全校范围;仅撤销归档标记,保留转出与停用状态,不回滚年级、新班及周次 | + +生命周期历史以 kind 为事实源,mode 为派生兼容字段;新响应另含 batchId、academicYear、fromGrade/toGrade、fromSchool/toSchool。 +归档详情含 inactive 列表、inactiveCount 和按学生去重的 transferInCount;升级记录含 operatorId/operatorName、detailsJson、snapshotSha256、error。 +所有学生与历史写入在同一事务,提交后再广播旧班与新班。班级详情只有持 roster:read 且在对应范围内的员工才能获得学生明细。 diff --git a/docs/dev/data-model.md b/docs/dev/data-model.md index 76abaf9..17ff418 100644 --- a/docs/dev/data-model.md +++ b/docs/dev/data-model.md @@ -10,42 +10,43 @@ description: 现行模型职责、迁移与快照边界。 ## 模型职责 -| 模型 | 职责 | -| ---------------------- | -------------------------------------------------- | -| `User` | 教师与管理员账号;学生不在此表,班级凭据在 Class | -| `Student` | 学生名单、学号、在读/转出/毕业状态,无密码和角色 | -| `StudentClassTransfer` | 调班与转出历史,保留班级/姓名快照 | -| `ArchivedYear` | 毕业届别与统计快照 | -| `TermWeek` | 逐周日期区间,classId 空串为全校默认 | -| `Class` | 班级、班级码/密码、班主任、周数与成绩查询开关 | -| `Course` | 班级+科目+教师任课关系,含简称和室外标记 | -| `Schedule` | 旧课表迁移审计来源,现行接口不再以它为业务真值 | -| `Homework` | 作业与本地 assignDate;dueAt 为遗留列 | -| `HomeworkStatus` | 按 studentId 记录作业完成状态 | -| `Notification` | 通知/叫人记录、优先级与本地 assignDate | -| `NotificationRead` | 按 studentId 记录已读 | -| `TimeLayout` | 共享或班级自有时间表;旧 items JSON 保留迁移审计 | -| `Grade` | studentId、课程、考试、分数和等级 | -| `PermissionGroup` | 默认/自定义/岗位组及 scope/scopeValue | -| `PermissionGroupItem` | 组内权限键 | -| `UserPermissionGroup` | 账号与组的多对多分配 | -| `AppSetting` | 周次、升级、AI 和系统设置 | -| `ClientDevice` | deviceKey、心跳、更新目标与状态 | -| `ClientCommand` | 白名单远控、有效期与执行回执 | -| `ClientRelease` | 服务端缓存安装包、SHA256 与大小 | -| `AiAudit` | 助手解析审计,不保存 API Key | -| `Exam` | 考试名称与是否计入总分 | -| `TimeLayoutItem` | 有序时间点及类型,被课程显式引用 | -| `ClassPlanGroup` | 班级课表群组、全局/默认标记 | -| `ClassPlan` | 日课表、时间表、轮换、启用与临时层规则 | -| `ClassPlanEntry` | 日课表与时间点、课程的外键关联、空课/停用/换课标记 | -| `ScheduledClassPlan` | 按本地日期预定日课表 | -| `PromotionRun` | 年度升级幂等与审计 | -| `Courseware` | 文件元数据、上传教师、存储路径与 SHA256 | -| `CoursewareFolder` | 教师自建分类目录,按上传者隔离 | -| `CoursewareVisibility` | 课件可见班级 | -| `CoursewarePush` | 按班级的推送任务 | -| `CoursewareReceipt` | 按设备的课件接收回执 | +| 模型 | 职责 | +| ---------------------- | --------------------------------------------------- | +| `User` | 教师与管理员账号;学生不在此表,班级凭据在 Class | +| `Student` | 学生名单、学号、在读/转出/毕业状态,无密码和角色 | +| `StudentClassTransfer` | 生命周期历史,kind/batchId/学年/班级年级及学校快照 | +| `ArchivedYear` | 毕业届别与统计快照 | +| `TermWeek` | 逐周日期区间,classId 空串为全校默认 | +| `Class` | 班级、班级码/密码、班主任、周数与成绩查询开关 | +| `Course` | 班级+科目+教师任课关系,含简称和室外标记 | +| `Schedule` | 旧课表迁移审计来源,现行接口不再以它为业务真值 | +| `Homework` | 作业与本地 assignDate;dueAt 为遗留列 | +| `HomeworkStatus` | 按 studentId 记录作业完成状态 | +| `Notification` | 通知/叫人记录、优先级与本地 assignDate | +| `NotificationRead` | 按 studentId 记录已读 | +| `TimeLayout` | 共享或班级自有时间表;旧 items JSON 保留迁移审计 | +| `Grade` | studentId、课程、考试、分数和等级 | +| `PermissionAudit` | 权限与人工升级审计,操作人和前后 JSON,不建账号外键 | +| `PermissionGroup` | 默认/自定义/岗位组及 scope/scopeValue | +| `PermissionGroupItem` | 组内权限键 | +| `UserPermissionGroup` | 账号与组的多对多分配 | +| `AppSetting` | 周次、升级、AI 和系统设置 | +| `ClientDevice` | deviceKey、心跳、更新目标与状态 | +| `ClientCommand` | 白名单远控、有效期与执行回执 | +| `ClientRelease` | 服务端缓存安装包、SHA256 与大小 | +| `AiAudit` | 助手解析审计,不保存 API Key | +| `Exam` | 考试名称与是否计入总分 | +| `TimeLayoutItem` | 有序时间点及类型,被课程显式引用 | +| `ClassPlanGroup` | 班级课表群组、全局/默认标记 | +| `ClassPlan` | 日课表、时间表、轮换、启用与临时层规则 | +| `ClassPlanEntry` | 日课表与时间点、课程的外键关联、空课/停用/换课标记 | +| `ScheduledClassPlan` | 按本地日期预定日课表 | +| `PromotionRun` | 年度升级幂等与审计 | +| `Courseware` | 文件元数据、上传教师、存储路径与 SHA256 | +| `CoursewareFolder` | 教师自建分类目录,按上传者隔离 | +| `CoursewareVisibility` | 课件可见班级 | +| `CoursewarePush` | 按班级的推送任务 | +| `CoursewareReceipt` | 按设备的课件接收回执 | 学生和账号分离:Student 没有密码/角色;教师/管理员存 User,CLASS_DEVICE 只是 Class 登录形成的会话。 任课关系只在 Course;班主任关系在 Class.teacherId,可以与任课关系指向同一账号。 @@ -73,3 +74,14 @@ User.careMode 是预留列;当前 Web 偏好在 localStorage,客户端偏好 2. 核对外键级联与删除护栏,特别是教师的班级、课程、作业、通知和课件。 3. 在临时库验证新建和旧库升级;不对真实业务库运行 seed/reset。 4. 重新生成 Prisma Client,并按修改范围执行[验收](testing.md)。 + +## 权限与生命周期迁移 + +`20261023000000_promotion_permission` 为已初始化的 ADMIN 默认组补 promotion:manage;默认组尚未创建时允许不插入,由运行时模板创建。 +`20261023010000_student_lifecycle_audit` 增加 PermissionAudit、StudentClassTransfer 的批次/学年/班级年级/学校快照、ArchivedYear.inactiveCount、PromotionRun 的操作人/明细/摘要/错误。 +旧 mode 按 single/batch → class-change、transfer-out → transfer-out、restore-transfer → restore 迁移后删除该列,避免双事实源。 +旧行的新字符串为空,学年为空,统计默认零;不臆造无法恢复的历史年级或批次。 + +SNAPSHOT_TABLES 已加入 PermissionAudit,新增字段随模型导出。旧快照没有审计表可恢复为空,旧历史 mode 在恢复时转为 kind; +没有新增列的旧行使用 schema 默认值。执行记录与升级快照不自动清理。 +升级计划存 AppSetting.term.promotionPlan,时间存 term.promotionAt,无需新增学校或学生账号模型。 diff --git a/docs/dev/implementation.md b/docs/dev/implementation.md index 64d39ca..6ccf4a3 100644 --- a/docs/dev/implementation.md +++ b/docs/dev/implementation.md @@ -21,7 +21,7 @@ description: 课表、周次、学年、岛、设备与课件的现行实现入 默认权限组、岗位范围、科目写权限分别由 permissions/permission-scope/access 维护,范围并集不授予任课权。 全校周数上限在 `term.maxWeeks`,默认 30、允许 1–60;开学日和逐周区间支持全校与班级设置。 个人成绩查询关闭后只返回服务端集体聚合,个人明细、原始全班成绩与排名拒绝读取;样本不足或稀疏分段抑制。 -年度升级由 `term.promotionAt` 和 `PromotionRun.year` 管理,先存快照;运行期到点执行,启动漏跑需管理员预览补跑。 +年度升级由 `term.promotionPlan`、`term.promotionAt` 和 `PromotionRun.year` 管理,先存快照;运行期到点执行,启动漏跑需管理员预览补跑。 毕业班归档,其余升一级,新班不复制任课关系。转出学生由 `lib/students.ts` 统一过滤。 ## 岛与本机配置 @@ -38,3 +38,15 @@ description: 课表、周次、学年、岛、设备与课件的现行实现入 更新从服务端缓存下载,客户端验 SHA256,按下发策略安装;远控白名单动作执行后回执,测试只用假执行器。 课件上限 512 MB,服务端流式落盘后创建元数据和推送,客户端逐设备验摘要并回执,不自动运行文件。 JSON 快照含课件表,完整恢复还必须带文件实体目录。细节见[课件](../app/courseware.md)。 + +## 学生与权限写入契约 + +学生状态/班级写入口集中在 `lib/student-lifecycle.ts`,班级名单添加/移出、名单导入、转出和转入均复用它。 +资料 PATCH 不承担状态或班级迁移。学生与历史在事务内提交,UUID 标识批次,事务后广播两端班级,历史读取不附带在读过滤。 +权限、功能对应范围、业务关系依次断言;权限只在当前请求对象上复用,下一请求重新计算。 +范围解析在 shared,默认组或分组均按当前功能键过滤,不能借另一功能的范围扩大当前操作。 +权限组或成员分配与 PermissionAudit 同事务,成员权限一次预取计算。 + +年度升级服务由计划推导所有轨道的下一学段。预览哈希覆盖配置、班级、学生和周次,执行事务内重新计算; +先保存全库快照并计算 SHA256,再以年份唯一键提交升级。毕业班所有状态挂届别,非毕业班仅升级及清班级开学日覆盖。 +周次是否清空由计划决定,预览公开影响。服务保留 runPromotion(year, dryRun) 内部兼容签名;HTTP 执行必须提供哈希、确认并检查年份。 diff --git a/docs/dev/permissions.md b/docs/dev/permissions.md index dd1babb..35f98d3 100644 --- a/docs/dev/permissions.md +++ b/docs/dev/permissions.md @@ -35,22 +35,23 @@ HEAD/SUBJECT 是班级关系,级长/部长是岗位组,均不新增 User.rol | 部长 | 配置的一组年级,scopeValue 用逗号分隔 | | 班级端 | 登录绑定班级 | -空年级/部配置不放行。设备写操作只合并含对应功能键的分组范围,不能借只读组扩权。 +空年级/部配置不放行。范围按当前功能键独立合并,不能借其他只读组扩大名单、班级或设备写权限。 科目编辑再由 `assertCanManageSubjectContent` 检查:管理员可管理,教师仅自己任课科目,无科目内容归班主任。 班主任不任教某科就不能改该科的作业或成绩;班级端只允许作业写入,不能写成绩。 -| 业务 | 主要功能键与额外边界 | -| ---------------- | ---------------------------------------------------------------------------- | -| 名单读取/管理 | roster:read / roster:manage;管理仍须管理员或本班班主任断言 | -| 教师列表/维护 | teacher:read / teacher:manage;导入名单另检查导入权限和角色 | -| 日课表写入 | schedule:write + 管理员/本班班主任 | -| 全校时间表写入 | timetable:manage;默认给 ADMIN | -| 作业/成绩 | homework:write / grade:write 或 grade:import,再按班级科目收口 | -| 通知/叫人 | notification:publish / call:create,再检查班级范围 | -| 课件 | courseware:read / courseware:write,上传和推送自己的文件还受本人授课班级约束 | -| 客户端维护/远控 | client:read、client:manage、remote:control,写操作按对应键范围 | -| 版本与安装包写入 | client:update;旧 client:update-push 不再作为写入口要求 | -| 数据库/系统/权限 | database:manage / system:manage / permission:manage | +| 业务 | 主要功能键与额外边界 | +| ---------------- | ----------------------------------------------------------------------------------------------------- | +| 名单读取/管理 | roster:read / roster:manage;管理需名单键与目标班范围;关联班级仍须班主任关系,全校名单范围可跨班维护 | +| 教师列表/维护 | teacher:read / teacher:manage;导入教师另检查 import:teachers 与 teacher:manage | +| 日课表写入 | schedule:write + 管理员/本班班主任 | +| 全校时间表写入 | timetable:manage;默认给 ADMIN | +| 作业/成绩 | homework:write / grade:write 或 grade:import,再按班级科目收口 | +| 通知/叫人 | notification:publish / call:create,再检查班级范围 | +| 课件 | courseware:read / courseware:write,上传和推送自己的文件还受本人授课班级约束 | +| 客户端维护/远控 | client:read、client:manage、remote:control,写操作按对应键范围 | +| 版本与安装包写入 | client:update;旧 client:update-push 不再作为写入口要求 | +| 学年升级 | promotion:manage;计划、预览与执行独立授权,全校范围,默认给 ADMIN | +| 数据库/系统/权限 | database:manage / system:manage / permission:manage | 完整键目录读取 `PERMISSION_KEYS`,不在文档维护固定键数。 @@ -67,3 +68,22 @@ Web 菜单属性来自路由,`meta.roles` 粗筛、`meta.permission` 细筛; `/api/clients/heartbeat` 和命令回执供已登录设备使用,不依赖班级端的空权限集合;服务端校验 deviceKey 与班级归属。 远控从管理端确认下发,客户端执行后回传真实结果。更新文件由服务端缓存,教室机验 SHA256, 按立即、放学后、空闲策略安装;关闭自动更新不会强制安装。详见[更新与升级](../management/upgrade.md)。 + +## 范围配置与权限治理 + +范围由 shared 的 `parsePermissionScope` 统一解析。关联班级只接受空值(班主任与任课班)或 HEAD(仅班主任班); +GRADE 必选一个年级,DIVISION 必选多个年级组成的集合;全角逗号、顿号和空白统一分隔、去重排序。 +ALL 存空范围值。年级选项来自数据库已有年级和升级计划,空范围或未知年级返回 400;旧库未配置的岗位组不放行。 +`GET /permissions/scopes` 提供选择器选项,组 DTO 的 scopeLabel 供页面展示。 + +默认组只能分配给同角色;非 ADMIN 的权限管理员只能管理关联班级范围的自定义组, +组原有键和新键均不得超过自己当前权限。角色默认组只有 ADMIN 可改;删除组可能触发默认回落,仅 ADMIN 可执行。 +分配空数组恢复数据库角色默认组,分配任何组则仅取所选组并集;需要保留默认时显式同时选择同角色默认组。 + +权限页保存前展示成员与权限差异,支持复制组、查看生效成员和分页审计。 +PermissionAudit 在组或分配事务中保存操作人、目标、前后 JSON;`GET /permissions/audit` 受 permission:manage 控制。 +成员权限由一次预取组和账号后计算,不逐账号重复加载。 + +新增迁移为旧库已有 ADMIN 默认组补 promotion:manage;新库由默认模板创建。 +自定义组和已分配其他组的管理员不会自动获得该键,需要在权限页显式授予。 +班级端权限仍为空,本班名单、课表、通知、叫人和作业代录走显式员工守卫例外;班级端发布内容归属本班班主任。 diff --git a/docs/dev/testing.md b/docs/dev/testing.md index a554af4..9c1d86b 100644 --- a/docs/dev/testing.md +++ b/docs/dev/testing.md @@ -10,23 +10,24 @@ description: 隔离 API、隐藏 UI、后台客户端与显式视觉验收。 ## 命令与前置条件 -| 命令 | 覆盖/前置条件 | -| ------------------------------------------------ | ----------------------------------------------------------------- | -| pnpm lint / pnpm typecheck / pnpm check:icons | 静态检查;typecheck 会先构建 shared | -| pnpm docs:check | docfx 严格构建,需 .NET SDK 与 docfx | -| pnpm verify:e2e | 自动临时 SQLite、种子和带前缀账号,启动随机端口服务;无需真实服务 | -| pnpm verify:web:isolated | 同上隔离服务,隐藏 Electron UI 回归;先构建 Web 产物 | -| pnpm verify:web | 连接已准备测试服务,需 Web 产物和测试管理员;不应指向业务服务 | -| pnpm verify:v14 | 临时库验证关系课表、周次、范围、隐私与升级等 | -| pnpm verify:courseware | 临时库和文件目录的课件专项,不向真实班级推送 | -| pnpm verify:client-agent | 假系统适配器验证命令、回执和更新策略 | -| pnpm verify:client-migrations | 临时旧库验证迁移、联动退役与默认组持久化 | -| pnpm verify:review-client | 无窗口逻辑回归 | -| pnpm verify:review-api / pnpm verify:clients-api | 需明确准备的隔离测试服务,使用 VERIFY_BASE_URL 等脚本参数 | -| pnpm verify:desktop | 先 pnpm build:desktop;隐藏快速检查,独立 userData | -| pnpm verify:desktop:online | 显式联网,需要测试管理员并创建临时教师/班级 | -| pnpm verify:desktop:visual | 显式使用桌面,完整岛几何/动画/透明度/截图检查 | -| pnpm verify:packaged --exe <客户端EXE> | 默认快速离线,两次启动、packaged=true;--visual 才使用桌面 | +| 命令 | 覆盖/前置条件 | +| ------------------------------------------------ | ---------------------------------------------------------------------------- | +| pnpm lint / pnpm typecheck / pnpm check:icons | 静态检查;typecheck 会先构建 shared | +| pnpm docs:check | docfx 严格构建,需 .NET SDK 与 docfx | +| pnpm verify:e2e | 自动临时 SQLite、种子和带前缀账号,启动随机端口服务;无需真实服务 | +| pnpm verify:web:isolated | 同上隔离服务,隐藏 Electron UI 回归;先构建 Web 产物 | +| pnpm verify:web | 连接已准备测试服务,需 Web 产物和测试管理员;不应指向业务服务 | +| pnpm verify:lifecycle | 临时 SQLite 验证权限治理、事务故障回滚、状态机、历史、多轨道、计划哈希和快照 | +| pnpm verify:v14 | 临时库验证关系课表、周次、范围、隐私与升级等 | +| pnpm verify:courseware | 临时库和文件目录的课件专项,不向真实班级推送 | +| pnpm verify:client-agent | 假系统适配器验证命令、回执和更新策略 | +| pnpm verify:client-migrations | 临时旧库验证迁移、联动退役与默认组持久化 | +| pnpm verify:review-client | 无窗口逻辑回归 | +| pnpm verify:review-api / pnpm verify:clients-api | 需明确准备的隔离测试服务,使用 VERIFY_BASE_URL 等脚本参数 | +| pnpm verify:desktop | 先 pnpm build:desktop;隐藏快速检查,独立 userData | +| pnpm verify:desktop:online | 显式联网,需要测试管理员并创建临时教师/班级 | +| pnpm verify:desktop:visual | 显式使用桌面,完整岛几何/动画/透明度/截图检查 | +| pnpm verify:packaged --exe <客户端EXE> | 默认快速离线,两次启动、packaged=true;--visual 才使用桌面 | 联网 packaged 使用 --online;调用方须预先准备隔离服务、临时教师和班级并显式注入凭据,该启动器不会自动创建它们。 开发版 verify:desktop:online 则通过 provisionSmokeClass 创建和清理临时身份。--offline 保持离线。 @@ -42,6 +43,8 @@ pnpm check:icons pnpm docs:check pnpm verify:e2e pnpm verify:v14 +pnpm verify:lifecycle +pnpm verify:client-migrations pnpm verify:courseware pnpm build:desktop pnpm verify:desktop @@ -67,3 +70,7 @@ docs/images 为 README/文档展示图;docs/screenshots 为回归留档;webs Web 采集入口 `website/tools/capture-shots.mjs`,先准备隔离测试服务和数据。 岛/客户端采集使用 smoke.mjs --visual,并按需 --online;ISLAND_SHOTS_DIR 与 ELECTRON_CLIENT_SHOTS_DIR 指向临时目录。 日常 quick 模式不会生成完整视觉截图,不能把 quick 通过表述为动画/透明度已验证。 + +权限与年度流程回归入口为 `scripts/verify-lifecycle.mjs`,为子进程指定临时库与测试密钥,不使用业务 .env 的登录凭据。 +旧库升级脚本动态读取最新迁移,另建重构前夹具校验 ADMIN 默认组补键、历史 kind 映射、空组保留及重复启动。 +事务回滚用 SQLite 触发器模拟历史写入失败,不能通过删除断言或放宽状态规则通过测试。 diff --git a/docs/get-started/web-admin.md b/docs/get-started/web-admin.md index 9caf689..10597b3 100644 --- a/docs/get-started/web-admin.md +++ b/docs/get-started/web-admin.md @@ -12,16 +12,16 @@ description: 登录、菜单分组、权限与移动布局。 侧栏按以下分组组织,具体页面由路由权限筛选,并支持浅色/深色和关怀模式。 -| 分组 | 主要页面 | -| ---------- | -------------------------------------------------- | -| 工作台 | 仪表盘、AI 助手 | -| 教学事务 | 课表、时间表与日课表编辑、作业、通知、课件、成绩 | -| 班级与人员 | 班级、学生、教师、只读学生名单、学期周次、毕业归档 | -| 设备运维 | 客户端在线状态、命令、更新与安装策略 | -| 系统管理 | 权限组、系统设置、大模型设置、数据库管理 | +| 分组 | 主要页面 | +| ---------- | ------------------------------------------------------------ | +| 工作台 | 仪表盘、AI 助手 | +| 教学事务 | 课表、时间表与日课表编辑、作业、通知、课件、成绩 | +| 班级与人员 | 班级、学生、教师、只读学生名单、学期周次、学年升级、毕业归档 | +| 设备运维 | 客户端在线状态、命令、更新与安装策略 | +| 系统管理 | 权限组、系统设置、大模型设置、数据库管理 | -班级/学生/教师管理、学期周次、归档和数据库页面还有管理员角色门槛; -教师只读名单、教学页与设备页按权限键和数据范围开放。 +班级、学生、学期周次、学年升级和归档页面按各自生效权限开放;教师管理和数据库页面仍保留角色门槛。 +名单、教学页与设备页还按对应数据范围开放,写按钮需额外管理键。 每次操作服务端继续校验,不是显示菜单就能改所有班和科目。详见[角色与权限](../dev/permissions.md)。 顶栏提供本人密码、使用引导、检查更新与退出等入口。 @@ -32,3 +32,7 @@ description: 登录、菜单分组、权限与移动布局。 同局域网手机填写服务器局域网地址,不用 localhost;公网反代见[生产部署](../management/production.md)。 下一步:[建班级、排课表](setup-class.md)。 + +权限页选组会替代角色默认模板,保存前可查看新增/移除权限与影响成员;保留默认能力时同时选择同角色默认组。 +名单页默认在读,历史状态与包含归档分开筛选;资料编辑、调班、转出、转入/复学各有专用入口。 +学年升级先保存计划、预览阻断项和周次影响,再输入学年确认执行;执行记录保留快照与 SHA256。 diff --git a/docs/screenshots/plan-editor-empty.png b/docs/screenshots/plan-editor-empty.png new file mode 100644 index 0000000000000000000000000000000000000000..b2f47171ac17ab271051d3b6429f141e63e64d8a GIT binary patch literal 57138 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 l_HDjA00006NkliHJD)GG&~KK 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¬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 literal 0 HcmV?d00001 diff --git a/docs/screenshots/plan-editor-mobile.png b/docs/screenshots/plan-editor-mobile.png new file mode 100644 index 0000000000000000000000000000000000000000..c04371aa757568972026b743c22f74324c57aa90 GIT binary patch literal 40519 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 AzyJUM literal 0 HcmV?d00001 diff --git a/docs/screenshots/plan-editor-reference.png b/docs/screenshots/plan-editor-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..009622a85e79882a5628e6cf68780c61515130eb GIT binary patch literal 110534 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 literal 0 HcmV?d00001 diff --git a/docs/screenshots/plan-editor-time-layout.png b/docs/screenshots/plan-editor-time-layout.png new file mode 100644 index 0000000000000000000000000000000000000000..027a935f88b459f644a1592bf26d807d69677e63 GIT binary patch literal 88340 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 literal 110265 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 c2PGmz+bL$R6k-oB4k00g)tTduEiT6V4M?M4 { toClassName: classes[1]!.name, operatorId: admin.id, operatorName: admin.name, - mode: 'batch', + kind: 'class-change', note: '军训后按分班考试成绩重新均衡', createdAt: new Date(now - 30 * day), }, @@ -492,7 +492,7 @@ async function main(): Promise { toClassName: '转出本校', operatorId: admin.id, operatorName: admin.name, - mode: 'transfer-out', + kind: 'transfer-out', note: '随家长工作调动转学至外地', createdAt: new Date(now - 12 * day), }, diff --git a/packages/server/scripts/verify-e2e.mjs b/packages/server/scripts/verify-e2e.mjs index 48d107f..c7587e8 100644 --- a/packages/server/scripts/verify-e2e.mjs +++ b/packages/server/scripts/verify-e2e.mjs @@ -1,4 +1,5 @@ #!/usr/bin/env node +import { readFileSync } from 'node:fs'; /** * 端到端联调验证(对应验收标准): * 1. 教师发布通知 -> ClassHelper 班级端 Socket.IO 5 秒内收到 @@ -1056,9 +1057,13 @@ async function main() { const studentCalls = await api('/calls', { method: 'POST', token: studentToken, - body: { classId, studentId: targetStudent?.id, quickPhrase: '学生不能叫人' }, + body: { classId, studentId: targetStudent?.id, quickPhrase: '班级端代叫人' }, }); - record('学生调用叫人接口被拒绝(403)', studentCalls.status === 403, `status=${studentCalls.status}`); + record( + '班级端可为本班叫人(201)', + studentCalls.status === 201 && Boolean(studentCalls.payload?.data?.id), + `status=${studentCalls.status}`, + ); const studentListAfterCall = await api('/notifications', { token: studentToken }); const receivedCall = (studentListAfterCall.payload?.data ?? []).find( @@ -1566,7 +1571,11 @@ async function main() { }); record( '管理员可以分配权限组(PUT /users/:id/groups → 200)', - Boolean(adminMember) && assignAdmin.status === 200 && assignAdmin.payload?.data?.fixed === false, + Boolean(adminMember) && + assignAdmin.status === 200 && + Array.isArray(assignAdmin.payload?.data?.permissions) && + assignAdmin.payload.data.permissions.includes('permission:manage') && + assignAdmin.payload.data.permissions.includes('system:manage'), `status=${assignAdmin.status} 响应=${assignAdmin.payload?.message ?? ''}`, ); @@ -2035,9 +2044,13 @@ async function main() { const studentPublish = await api('/notifications', { method: 'POST', token: studentToken, - body: { classId, title: '学生越权发布', content: '不应成功' }, + body: { classId, title: '班级端本班通知', content: '允许本班发布' }, }); - record('学生调用教师接口被拒绝(403)', studentPublish.status === 403, `status=${studentPublish.status}`); + record( + '班级端可发布本班通知(201)', + studentPublish.status === 201 && Boolean(studentPublish.payload?.data?.id), + `status=${studentPublish.status}`, + ); const anonymous = await api('/classes'); record('未登录访问被拒绝(401)', anonymous.status === 401, `status=${anonymous.status}`); @@ -2098,8 +2111,8 @@ async function main() { token: teacherToken, }); record( - '班主任下载名单模板被拒绝(403,名单仅管理员)', - teacherRosterTemplate.status === 403, + '有导入键的班主任可下载名单模板(200)', + teacherRosterTemplate.status === 200 && Boolean(teacherRosterTemplate.payload?.data?.content), `status=${teacherRosterTemplate.status}`, ); @@ -2581,11 +2594,12 @@ async function main() { }, }); record( - '班主任预览学生名单模板被拒绝(403,名单仅管理员)', - teacherRosterPreview.status === 403, + '有导入键的班主任可预览名单模板(200)', + teacherRosterPreview.status === 200 && Boolean(teacherRosterPreview.payload?.data), `status=${teacherRosterPreview.status}`, ); + const teacherImportedNo = `T${Date.now()}`; const studentsByTeacher = await api('/imports/table/commit', { method: 'POST', token: teacherToken, @@ -2593,17 +2607,43 @@ async function main() { kind: 'students', classId, fileName: 'students.csv', - contentBase64: Buffer.from(`\ufeff${studentsCsv}${NL}`, 'utf8').toString('base64'), + contentBase64: Buffer.from( + `\ufeff学号,姓名${NL}${teacherImportedNo},班主任导入学生${NL}`, + 'utf8', + ).toString('base64'), mapping: { studentNo: '学号', name: '姓名' }, mode: 'append', }, }); record( - '教师导入学生名单被拒绝(403)', - studentsByTeacher.status === 403, + '有导入键的班主任按范围导入本班名单(200)', + studentsByTeacher.status === 200 && + studentsByTeacher.payload?.data?.failed === 0 && + studentsByTeacher.payload?.data?.inserted === 1, `status=${studentsByTeacher.status}`, ); + const teacherImportedStudents = await api(`/students?classId=${classId}&keyword=${teacherImportedNo}`, { + token: adminToken, + }); + const teacherImportedStudent = teacherImportedStudents.payload?.data?.find( + (item) => item.studentNo === teacherImportedNo, + ); + if (teacherImportedStudent) + await api(`/students/${teacherImportedStudent.id}`, { method: 'DELETE', token: adminToken }); + const teacherImportAfter = await api( + `/students?classId=${classId}&status=inactive&keyword=${teacherImportedNo}`, + { token: adminToken }, + ); + record( + '班主任导入夹具收尾:停用并保留历史,恢复原在读名单', + Boolean( + teacherImportAfter.payload?.data?.some( + (item) => item.studentNo === teacherImportedNo && item.status === 'inactive', + ), + ), + ); + // 清理:删除导入产生的成绩、时间配置与学生,保持环境可重复验证 const importedGrades = await api( `/grades?classId=${classId}&examName=${encodeURIComponent('期中导入考试')}`, @@ -2725,9 +2765,13 @@ async function main() { const classPublish = await api('/notifications', { method: 'POST', token: classToken, - body: { classId, title: '班级账号越权发布', content: '不应成功' }, + body: { classId, title: '班级账号本班发布', content: '允许本班发布' }, }); - record('班级账号发布通知被拒绝(403)', classPublish.status === 403, `status=${classPublish.status}`); + record( + '班级账号发布本班通知成功(201)', + classPublish.status === 201 && Boolean(classPublish.payload?.data?.id), + `status=${classPublish.status}`, + ); const classGradesWrite = await api('/grades', { method: 'POST', @@ -3201,44 +3245,11 @@ async function main() { // ---------------------------------------------------------------- 数据库管理(仅管理员) // 覆盖:状态 / 越权 403 / 连接测试 / 备份→导入导出→恢复 roundtrip / 定时配置校验 / 切换校验分支。 // 注意:**不真跑切换**(会改写 .env 与 schema.prisma),只验证拒绝分支;真实切换需人工复验。 - // 数据表清单随模型演进增长(2026-10-10 加了权限组 / 客户端管理 8 张表后共 23 张)。 - // **不写死总数**:断言"服务端快照枚举 == 这份清单",加模型时只改这一处。 - const REQUIRED_DB_TABLES = [ - 'User', - 'ArchivedYear', - 'Class', - 'Student', - 'Course', - 'StudentClassTransfer', - 'Schedule', - 'TimeLayout', - 'Homework', - 'HomeworkStatus', - 'Notification', - 'NotificationRead', - 'Grade', - 'PermissionGroup', - 'PermissionGroupItem', - 'UserPermissionGroup', - 'AppSetting', - 'ClientDevice', - 'Courseware', - 'CoursewareFolder', - 'CoursewareVisibility', - 'CoursewarePush', - 'CoursewareReceipt', - 'ClientCommand', - 'ClientRelease', - 'AiAudit', - 'TimeLayoutItem', - 'ClassPlanGroup', - 'ClassPlan', - 'ClassPlanEntry', - 'ScheduledClassPlan', - 'PromotionRun', - 'TermWeek', - 'Exam', - ]; + // 直接读取快照注册表,避免新增模型后测试清单遗漏。 + const snapshotSource = readFileSync(new URL('../src/lib/snapshot.ts', import.meta.url), 'utf8'); + const registry = snapshotSource.match(/export const SNAPSHOT_TABLES = \[([\s\S]*?)\] as const/); + if (!registry) throw new Error('无法解析快照表注册表'); + const REQUIRED_DB_TABLES = [...registry[1].matchAll(/'([^']+)'/g)].map((match) => match[1]); const dbStatus = await api('/database/status', { token: adminToken }); const dbStatusData = dbStatus.payload?.data ?? {}; const dbStatusTableNames = Array.isArray(dbStatusData.tables) @@ -3278,14 +3289,14 @@ async function main() { `status=${dbStatusTeacher.status} 备份=${dbBackupTeacher.status} 定时=${dbScheduleTeacher.status} 切换=${dbSwitchTeacher.status}`, ); - // 连接测试:当前库必须可达(SQLite 用相对路径,MySQL 用一个不可达地址测失败分支) + // 连接测试:当前库必须可达(SQLite 取服务端当前临时库地址,MySQL 用一个不可达地址测失败分支) let testOkResult; let testOkName; if (dbStatusData.provider === 'sqlite') { testOkResult = await api('/database/test-connection', { method: 'POST', token: adminToken, - body: { provider: 'sqlite', url: 'file:./prisma/dev.db' }, + body: { provider: 'sqlite', url: dbStatusData.databaseUrlMasked }, }); testOkName = '连接测试(当前 SQLite 库可达)'; } else { diff --git a/packages/server/scripts/verify-lifecycle.ts b/packages/server/scripts/verify-lifecycle.ts new file mode 100644 index 0000000..b421fcc --- /dev/null +++ b/packages/server/scripts/verify-lifecycle.ts @@ -0,0 +1,416 @@ +import assert from 'node:assert/strict'; +import fs from 'node:fs/promises'; +import { createHash } from 'node:crypto'; +import { createServer } from 'node:http'; +import { PERMISSION_KEYS, defaultPromotionPlan, parsePermissionScope } from '@classhelper/shared'; +import { prisma } from '../src/lib/db.js'; +import { createApp } from '../src/app.js'; +import { signToken, type TokenPayload } from '../src/lib/jwt.js'; +import { ensureDefaultGroups, loadEffectivePermissions } from '../src/lib/permissions.js'; +import { registerEmitter } from '../src/realtime/bus.js'; +import * as students from '../src/modules/students/students.service.js'; +import * as classes from '../src/modules/classes/classes.service.js'; +import * as permissions from '../src/modules/permissions/permissions.service.js'; +import { + configurePromotion, + previewPromotion, + runPromotion, + promotionStatus, +} from '../src/modules/term/promotion.service.js'; +import { deleteArchive, getArchivedYearDetail } from '../src/modules/archives/archives.service.js'; +import { updateStudentSchema } from '../src/modules/students/students.schemas.js'; +import { exportSnapshot, restoreSnapshot } from '../src/lib/snapshot.js'; + +if (!process.env.DATABASE_URL?.includes('lifecycle-verification')) + throw new Error('只能对生命周期专用临时数据库运行'); +let count = 0; +function check(name: string, fn: () => unknown) { + fn(); + console.log(`通过 ${++count}:${name}`); +} +async function denied(action: Promise, status: number) { + await assert.rejects(action, (error: unknown) => (error as { status?: number }).status === status); +} +const account = async (name: string, role: string) => { + const row = await prisma.user.create({ + data: { username: name, name, role, passwordHash: '隔离测试,不登录' }, + }); + return { sub: row.id, username: row.username, name, role, classId: null } as TokenPayload; +}; +const admin = await account('临时管理员', 'ADMIN'), + teacher = await account('临时班主任', 'TEACHER'), + manager = await account('临时权限协助', 'TEACHER'); +await ensureDefaultGroups(); +const cls = async ( + name: string, + grade: string, + enrollmentYear: number, + classIndex: number, + teacherId = teacher.sub, +) => prisma.class.create({ data: { name, grade, enrollmentYear, classIndex, code: name, teacherId } }); +const a = await cls('TESTA', '高一', 2025, 1), + b = await cls('TESTB', '高一', 2025, 2), + c = await cls('TESTC', '高二', 2024, 1, admin.sub); +const events: { classId: string; event: string }[] = []; +registerEmitter((rooms, event) => { + for (const room of rooms) events.push({ classId: room.replace('class:', ''), event }); +}); +const server = createServer(createApp()); +await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve)); +const base = `http://127.0.0.1:${(server.address() as { port: number }).port}/api`; +async function request(url: string, actor: TokenPayload, method = 'GET', body?: unknown, expected = 200) { + const response = await fetch(base + url, { + method, + headers: { authorization: `Bearer ${signToken(actor)}`, 'content-type': 'application/json' }, + ...(body === undefined ? {} : { body: JSON.stringify(body) }), + }); + const data = await response.json(); + assert.equal(response.status, expected, `${method} ${url}: ${JSON.stringify(data)}`); + return data.data; +} +try { + check('资料 schema 拒绝班级和状态字段', () => { + assert.equal(updateStudentSchema.safeParse({ status: 'graduated' }).success, false); + assert.equal(updateStudentSchema.safeParse({ classId: b.id }).success, false); + }); + const empty = await permissions.createGroup(admin, { name: '空组', keys: [] }); + await permissions.assignUserGroups(admin, teacher.sub, { groupIds: [empty.id] }); + for (const url of [ + '/classes', + `/classes/${a.id}`, + `/classes/${a.id}/students`, + `/schedules?classId=${a.id}`, + `/homeworks?classId=${a.id}`, + `/notifications?classId=${a.id}`, + '/students', + '/term', + ]) + await request(url, teacher, 'GET', undefined, 403); + await request('/homeworks', teacher, 'POST', {}, 403); + check('取消读写键立即拒绝所有对应入口', () => + assert.equal((PERMISSION_KEYS as readonly string[]).includes('promotion:manage'), true), + ); + const delegate = await account('临时教务授权', 'TEACHER'); + const governance = await permissions.createGroup(admin, { + name: '教务授权', + scope: 'ALL', + keys: ['class:read', 'class:manage', 'archive:manage', 'promotion:manage'], + }); + await permissions.assignUserGroups(admin, delegate.sub, { groupIds: [governance.id] }); + const delegatedClass = await request( + '/classes', + delegate, + 'POST', + { grade: '高一', enrollmentYear: 2028, classIndex: 99 }, + 201, + ); + await request(`/classes/${delegatedClass.id}`, delegate, 'PATCH', { termWeeks: 10 }); + await request(`/classes/${delegatedClass.id}`, delegate, 'DELETE'); + await request('/archives', delegate); + await request('/term/promotion', delegate); + check('教师持对应全校功能键可管理班级、读取归档与升级配置', () => {}); + const allRoster = await permissions.createGroup(admin, { + name: '全校名单', + scope: 'ALL', + keys: ['roster:read', 'roster:manage'], + }); + await permissions.assignUserGroups(admin, delegate.sub, { groupIds: [allRoster.id] }); + const unassigned = await students.createStudent(delegate, { + studentNo: 'UNASSIGNED', + name: '未分班测试学生', + }); + assert( + (await students.listStudents(delegate, { includeArchived: false })).some( + (student) => student.id === unassigned.id, + ), + ); + assert((await students.listTransfers(delegate, { studentId: unassigned.id })).items.length > 0); + check('全校名单授权包含未分班学生及其历史', () => {}); + + const readAll = await permissions.createGroup(admin, { + name: '全校班级只读', + scope: 'ALL', + keys: ['class:read'], + }); + const ownWrite = await permissions.createGroup(admin, { name: '关联班级维护', keys: ['class:manage'] }); + await permissions.assignUserGroups(admin, delegate.sub, { groupIds: [readAll.id, ownWrite.id] }); + const foreignDetail = await request(`/classes/${a.id}`, delegate); + assert.equal(foreignDetail.students.length, 0, '班级读取键不能代替名单读取键'); + await request(`/classes/${a.id}`, delegate, 'PATCH', { termWeeks: 11 }, 403); + check('全校读取组不能借给写操作范围,班级详情不泄漏未授权名单', () => {}); + const defaults = await prisma.permissionGroup.findUniqueOrThrow({ where: { defaultRole: 'TEACHER' } }); + await permissions.assignUserGroups(admin, teacher.sub, { groupIds: [] }); + assert((await loadEffectivePermissions(teacher)).has('roster:manage')); + const additional = await permissions.createGroup(admin, { + name: '追加升级权限', + scope: 'ALL', + keys: ['promotion:manage'], + }); + await permissions.assignUserGroups(admin, teacher.sub, { groupIds: [defaults.id, additional.id] }); + check('无组回落、显式默认组与多组并集', () => {}); + assert((await loadEffectivePermissions(teacher)).has('promotion:manage')); + await denied( + permissions.assignUserGroups(admin, teacher.sub, { groupIds: ['default-permission-admin'] }), + 403, + ); + const assistant = await permissions.createGroup(admin, { + name: '权限协助', + keys: ['permission:manage', 'roster:read'], + }); + await permissions.assignUserGroups(admin, manager.sub, { groupIds: [assistant.id] }); + await denied(permissions.createGroup(manager, { name: '越权', keys: ['system:manage'] }), 403); + await denied( + permissions.createGroup(manager, { name: '越范围', scope: 'ALL', keys: ['roster:read'] }), + 403, + ); + await denied(permissions.updateGroup(manager, defaults.id, { keys: [] }), 403); + await denied(permissions.assignUserGroups(manager, manager.sub, { groupIds: [additional.id] }), 403); + check('跨角色默认组、越键、越范围和默认组编辑均拒绝', () => {}); + await denied(permissions.createGroup(admin, { name: '空范围', scope: 'GRADE' }), 400); + await denied(permissions.createGroup(admin, { name: '未知范围', scope: 'GRADE', scopeValue: '未知' }), 400); + check('范围统一归一化并拒绝空值和未知年级', () => + assert.equal(parsePermissionScope('DIVISION', '高二,高一、 高一').scopeValue, '高一,高二'), + ); + const memberList = await permissions.listMembers(); + check('成员含真实权限与范围,组成员包含默认回落账号', () => + assert(memberList.find((m) => m.id === teacher.sub)?.permissions.includes('promotion:manage')), + ); + const audits = await permissions.listAudits(); + check('审计记录操作人与变更前后快照', () => + assert(audits.items.every((audit) => audit.actorId && audit.actorName && audit.afterJson)), + ); + let queryCount = 0; + const restoreMethods: (() => void)[] = []; + for (const model of [prisma.permissionGroup, prisma.userPermissionGroup, prisma.user]) { + for (const method of ['findMany', 'findUnique', 'upsert']) { + const original = Reflect.get(model, method); + Object.defineProperty(model, method, { + configurable: true, + value: (...args: unknown[]) => { + queryCount++; + return Reflect.apply(original, model, args); + }, + }); + restoreMethods.push(() => + Object.defineProperty(model, method, { configurable: true, value: original }), + ); + } + } + try { + await permissions.listMembers(); + const small = queryCount; + queryCount = 0; + await prisma.user.createMany({ + data: Array.from({ length: 30 }, (_, i) => ({ + username: `批量账号${i}`, + name: `成员${i}`, + role: 'TEACHER', + passwordHash: '临时', + })), + }); + await permissions.listMembers(); + check('成员增加后权限查询次数保持不变', () => assert.equal(queryCount, small)); + } finally { + restoreMethods.forEach((restore) => restore()); + } + const scheduleOnly = await permissions.createGroup(admin, { name: '仅课表编辑', keys: ['schedule:write'] }); + await permissions.assignUserGroups(admin, teacher.sub, { groupIds: [scheduleOnly.id] }); + const layoutPreview = { + classId: a.id, + payload: { TimeLayouts: [{ Name: '第一节', StartTime: '08:00', EndTime: '08:45', TimeType: 0 }] }, + }; + await request('/imports/time-layout/preview', teacher, 'POST', layoutPreview, 403); + await permissions.assignUserGroups(admin, teacher.sub, { groupIds: [] }); + const parsedLayout = await request('/imports/time-layout/preview', teacher, 'POST', layoutPreview); + check('课表导入键取消后真实预览入口 403,有键可解析', () => assert.equal(parsedLayout.items.length, 1)); + const student = await students.createStudent(admin, { studentNo: 'S1', name: '学生一', classId: a.id }); + await request(`/students/${student.id}`, admin, 'PATCH', { status: 'graduated' }, 400); + await request(`/students/${student.id}`, admin, 'PATCH', { classId: b.id }, 400); + check('HTTP 旧编辑请求明确返回 400', () => {}); + await prisma.$executeRawUnsafe( + `CREATE TRIGGER lifecycle_fail BEFORE INSERT ON StudentClassTransfer BEGIN SELECT RAISE(ABORT, '历史写入故障'); END`, + ); + await assert.rejects(students.transferStudents(admin, { studentIds: [student.id], toClassId: b.id })); + assert.equal((await prisma.student.findUniqueOrThrow({ where: { id: student.id } })).classId, a.id); + await prisma.$executeRawUnsafe('DROP TRIGGER lifecycle_fail'); + check('注入历史写入故障后学生班级事务回滚', () => {}); + const moved = await students.transferStudents(teacher, { + studentIds: [student.id], + toClassId: b.id, + note: '同年级调班', + }); + check('班主任调班按名单键授权、批次精确返回、两班广播', () => { + assert.equal(moved.transfers[0].fromGrade, '高一'); + assert(moved.batchId); + assert(events.some((e) => e.classId === a.id) && events.some((e) => e.classId === b.id)); + }); + await denied(students.transferStudents(teacher, { studentIds: [student.id], toClassId: c.id }), 403); + await denied(students.transferStudents(admin, { studentIds: [student.id], toClassId: c.id }), 409); + check('源目标范围与跨年级调班限制生效', () => {}); + await students.transferOutStudents(admin, { studentIds: [student.id], note: '迁居', toSchool: '另一学校' }); + const history = await students.listTransfers(admin, { studentId: student.id }); + check('转出状态筛选与转出历史均可见', () => { + assert(history.items.some((h) => h.kind === 'transfer-out' && h.toSchool === '另一学校')); + }); + assert.equal( + (await students.listStudents(admin, { status: 'transferred', includeArchived: false })).length, + 1, + ); + await request(`/students/${student.id}/transfers`, teacher); + await denied(students.transferOutStudents(admin, { studentIds: [student.id] }), 409); + await students.transferInStudent(admin, { studentId: student.id, toClassId: a.id, fromSchool: '另一学校' }); + await students.transferInStudent(admin, { + studentNo: 'S2', + name: '学生二', + toClassId: a.id, + fromSchool: '其他学校', + }); + check('重复转出拒绝,新建转入与历史学生重入有完整记录', () => {}); + await students.deleteStudent(admin, student.id); + assert.equal( + (await students.listStudents(admin, { status: 'inactive', includeArchived: false })).length, + 1, + ); + await students.restoreStudent(admin, student.id, { toClassId: a.id }); + await denied(students.deleteStudent(admin, student.id, { hard: true, confirm: true }), 409); + check('停用、复学和有历史学生的硬删除保护', () => {}); + const device: TokenPayload = { + sub: a.id, + role: 'CLASS_DEVICE', + classId: a.id, + name: a.name, + username: a.code, + classSession: true, + }; + for (const url of [ + `/classes/${a.id}/students`, + `/schedules?classId=${a.id}`, + `/notifications?classId=${a.id}`, + ]) + await request(url, device); + await request( + '/homeworks', + device, + 'POST', + { classId: a.id, title: '临时作业', content: '完成练习', assignDate: '2026-10-23' }, + 201, + ); + await request( + '/notifications', + device, + 'POST', + { classId: a.id, title: '临时通知', content: '隔离验收' }, + 201, + ); + await request('/grades', device, 'POST', {}, 403); + await request( + '/calls', + device, + 'POST', + { classId: a.id, studentId: student.id, quickPhrase: '请到办公室' }, + 201, + ); + check('班级端空权限保留名单课表通知和代录作业,禁止写成绩', () => {}); + const g = await cls('TESTG', '高三', 2023, 1); + for (const [i, status] of ['active', 'transferred', 'inactive'].entries()) + await prisma.student.create({ + data: { studentNo: `G${i}`, name: `毕业班学生${i}`, classId: g.id, status }, + }); + await prisma.termWeek.create({ + data: { classId: a.id, weekNumber: 1, startDate: '2026-09-01', endDate: '2026-09-07' }, + }); + const plan = defaultPromotionPlan(1); + plan.resetTermWeeks = false; + plan.termStartDate = '2026-09-08'; + await configurePromotion({ month: 8, day: 1, hour: 0, plan }, admin); + const before = await prisma.promotionRun.count(); + const preview = await previewPromotion(2026); + check('预览逐班逐状态、不写库、保留周次有警告', () => { + assert.equal(preview.blockers.length, 0); + assert.equal(preview.termWeekDeleteCount, 0); + assert(preview.tracks[0].classes.find((c) => c.id === g.id)?.transferred === 1); + }); + assert.equal(await prisma.promotionRun.count(), before); + await denied(runPromotion(2026, { actor: admin, planHash: '0'.repeat(64), confirm: true }), 409); + const due = (await promotionStatus()).year; + await denied(runPromotion(due + 1, { actor: admin, planHash: preview.planHash, confirm: true }), 400); + await permissions.assignUserGroups(admin, teacher.sub, { groupIds: [defaults.id, additional.id] }); + await denied(runPromotion(due - 1, { actor: teacher, planHash: preview.planHash, confirm: true }), 403); + check('过期哈希、未来年份与教师补跑过去学年均拒绝', () => {}); + const result = await runPromotion(2026, { actor: admin, planHash: preview.planHash, confirm: true }); + assert.equal(result.status, 'done'); + if (!('snapshotPath' in result)) throw new Error('执行未返回快照'); + const awaitedContent = await fs.readFile(result.snapshotPath); + check('快照 SHA256 与磁盘内容一致、执行记录有操作人明细', () => + assert.equal(result.snapshotSha256, createHash('sha256').update(awaitedContent).digest('hex')), + ); + const archived = await prisma.archivedYear.findUniqueOrThrow({ where: { enrollmentYear: 2023 } }); + const detail = await getArchivedYearDetail(admin, archived.id); + check('毕业转出停用全部挂届别、状态保持且统计完整', () => { + assert.equal(detail.graduates.length, 1); + assert.equal(detail.transferred.length, 1); + assert.equal(detail.inactive.length, 1); + assert.equal(detail.inactiveCount, 1); + }); + assert.equal(await prisma.termWeek.count(), 1); + const skeleton = await prisma.class.findFirstOrThrow({ where: { enrollmentYear: 2026, grade: '高一' } }); + assert.equal(skeleton.teacherId, null); + assert.equal(await prisma.course.count({ where: { classId: skeleton.id } }), 0); + assert.equal( + (await prisma.appSetting.findUniqueOrThrow({ where: { key: 'term.startDate' } })).value, + '2026-09-08', + ); + await runPromotion(2026, { actor: admin, planHash: preview.planHash, confirm: true }); + assert.equal(await prisma.archivedYear.count(), 1); + check('升级日期按计划、周次保留、新班无任课、重复运行幂等', () => {}); + await denied(classes.addStudent(admin, a.id, { studentNo: 'G0', name: '不能复活' }), 409); + await denied(students.transferOutStudents(admin, { studentIds: [detail.graduates[0].id] }), 409); + await students.restoreStudent(admin, detail.transferred[0].id, { toClassId: a.id }); + check('归档学生不可复活或转出,归档转出学生可指定在读班撤回', () => {}); + await deleteArchive(admin, archived.id); + assert.equal((await prisma.student.findUniqueOrThrow({ where: { studentNo: 'G2' } })).status, 'inactive'); + check('撤销归档保留停用状态,不回滚升级周次', () => assert.equal(detail.inactive[0].status, 'inactive')); + const unknown = await cls('TESTUNKNOWN', '未知学段', 2022, 10); + assert((await previewPromotion(2027)).blockers.some((b) => b.code === 'UNKNOWN_GRADE')); + await prisma.class.delete({ where: { id: unknown.id } }); + await prisma.class.create({ + data: { name: 'CODECONFLICT', code: 'N202701', grade: '高一', enrollmentYear: 2027, classIndex: 2 }, + }); + check('未知年级与新班码冲突在预览阶段阻断', () => {}); + assert((await previewPromotion(2027)).blockers.some((b) => b.code === 'CODE_CONFLICT')); + await prisma.class.deleteMany({ where: { name: 'CODECONFLICT' } }); + const duplicate = await cls('DUPLICATE', '高二', 2025, 1); + assert((await previewPromotion(2027)).blockers.some((b) => b.code === 'DUPLICATE_INDEX')); + await prisma.class.delete({ where: { id: duplicate.id } }); + check('同轨道同届重复班号被阻断', () => {}); + await request(`/classes/${a.id}`, admin, 'PATCH', { grade: '任意年级' }, 400); + await denied(classes.promoteClasses(admin, { classIds: [c.id] }), 409); + await classes.promoteClasses(admin, { classIds: [a.id] }); + assert.equal((await prisma.class.findUniqueOrThrow({ where: { id: a.id } })).grade, '高三'); + check('手动升级只到下一学段,毕业班与直接修改年级被拒绝', () => {}); + const junior = await cls('JUNIOR', '初一', 2025, 1); + const multi = defaultPromotionPlan(0); + multi.tracks.push({ + id: 'junior', + name: '初中', + entryClassCount: 0, + stages: [{ grade: '初一' }, { grade: '初二' }, { grade: '初三', terminal: true }], + }); + await configurePromotion({ month: 8, day: 1, hour: 0, plan: multi }, admin); + const multiPreview = await previewPromotion(2099); + assert.equal(multiPreview.blockers.length, 0); + assert.equal(multiPreview.termWeekDeleteCount, 1); + await runPromotion(2099); + check('多轨道分别升级,确认重置后仅清除预览中的周次', () => {}); + assert.equal((await prisma.class.findUniqueOrThrow({ where: { id: junior.id } })).grade, '初二'); + assert.equal(await prisma.termWeek.count(), 0); + const snapshot = await exportSnapshot(prisma, 'sqlite'); + const auditCount = snapshot.counts.PermissionAudit; + await restoreSnapshot(prisma, 'sqlite', snapshot); + check('全库快照恢复保留审计、生命周期和执行明细', () => assert(auditCount > 0)); + assert.equal(await prisma.permissionAudit.count(), auditCount); + console.log(`=== 权限与生命周期:${count}/${count} 项通过 ===`); +} finally { + await new Promise((resolve) => server.close(() => resolve())); + await prisma.$disconnect(); +} diff --git a/packages/server/scripts/verify-v14.ts b/packages/server/scripts/verify-v14.ts index f48704d..ac4a0e1 100644 --- a/packages/server/scripts/verify-v14.ts +++ b/packages/server/scripts/verify-v14.ts @@ -476,6 +476,8 @@ try { assert.equal(value.supported, false); assert(value.detail.length > 10); }); + // 范围测试的初中班不属于默认高中计划,结束该测试夹具后再升级高中;未知年级阻断另由 lifecycle 验收。 + await prisma.class.delete({ where: { id: third.id } }); // 此文件开头已强制临时库,实际升级不会触及演示或业务数据。 await prisma.class.create({ data: { diff --git a/packages/server/src/lib/access.ts b/packages/server/src/lib/access.ts index 40fd118..f03c995 100644 --- a/packages/server/src/lib/access.ts +++ b/packages/server/src/lib/access.ts @@ -1,7 +1,5 @@ import type { PermissionKey as ContractPermissionKey } from '@classhelper/shared'; import { - canAssignTeachers, - canManageClasses, canManageGrades, canManageRoster, canManageSchedule, @@ -13,6 +11,8 @@ import { import { teacherScopeWhere } from './permission-scope.js'; import { prisma } from './db.js'; import { ApiError } from './http.js'; +import { assertPermission, currentRequestPermission } from './permissions.js'; +import { hasAllScope } from './permission-scope.js'; import type { TokenPayload } from './jwt.js'; /** @@ -55,6 +55,14 @@ export function isClassDevice(user: TokenPayload): boolean { return user.role === 'CLASS_DEVICE'; } +/** 班级端代录内容归属班主任账号,班级主体不能写入 User 外键。 */ +export async function resolveContentCreator(user: TokenPayload, classId: string) { + if (!isClassDevice(user)) return user.sub; + const record = await prisma.class.findUnique({ where: { id: classId }, select: { teacherId: true } }); + if (!record?.teacherId) throw ApiError.conflict('请先为班级配置班主任,再在班级端发布内容'); + return record.teacherId; +} + /** ClassHelper 班级端绑定的班级 id;不是班级端会话时返回 null */ export function classDeviceClassId(user: TokenPayload): string | null { return isClassDevice(user) ? (user.classId ?? user.sub) : null; @@ -88,11 +96,15 @@ export async function getAccessibleClassIds( return classId ? [classId] : []; } -export async function canAccessClass(user: TokenPayload, classId: string): Promise { +export async function canAccessClass( + user: TokenPayload, + classId: string, + permission?: ContractPermissionKey, +): Promise { if (isAdmin(user)) return true; if (isTeacher(user)) { const count = await prisma.class.count({ - where: { AND: [{ id: classId }, await teacherScopeWhere(user)] }, + where: { AND: [{ id: classId }, await teacherScopeWhere(user, permission)] }, }); return count > 0; } @@ -100,10 +112,14 @@ export async function canAccessClass(user: TokenPayload, classId: string): Promi } /** 读权限断言 */ -export async function assertClassAccess(user: TokenPayload, classId: string): Promise { +export async function assertClassAccess( + user: TokenPayload, + classId: string, + permission?: ContractPermissionKey, +): Promise { const exists = await prisma.class.findUnique({ where: { id: classId }, select: { id: true } }); if (!exists) throw ApiError.notFound('班级不存在'); - if (!(await canAccessClass(user, classId))) throw ApiError.forbidden('无权访问该班级的数据'); + if (!(await canAccessClass(user, classId, permission))) throw ApiError.forbidden('无权访问该班级的数据'); } /** 写权限断言:无账号的学生不在会话里,因此这里只放行 staff 与 ClassHelper 班级端 */ @@ -135,17 +151,38 @@ export async function resolveUserClassRole(user: TokenPayload, classId: string): } /** 班级本身(增删改)与人员分配:仅管理员 */ -export function assertCanManageClasses(user: TokenPayload): void { - if (!canManageClasses(user.role)) { - throw ApiError.forbidden('只有管理员可以创建、修改或删除班级'); - } +export async function assertCanManageClasses(user: TokenPayload, classId?: string): Promise { + await assertPermission(user, 'class:manage'); + if (classId) await assertClassInScope(user, classId, 'class:manage'); + else if (!(await hasAllScope(user, 'class:manage'))) throw ApiError.forbidden('创建或删除班级需要全校范围'); + if ( + classId && + (await prisma.class.findUnique({ where: { id: classId }, select: { archivedYearId: true } })) + ?.archivedYearId + ) + throw ApiError.conflict('该班级已归档,请先撤销归档'); +} + +export async function assertGlobalPermission(user: TokenPayload, key: ContractPermissionKey) { + await assertPermission(user, key); + if (!(await hasAllScope(user, key))) throw ApiError.forbidden('此操作影响全校,需要对应功能的全校范围'); } /** 班主任 / 各科科任老师的分配:仅管理员 */ -export function assertCanAssignTeachers(user: TokenPayload): void { - if (!canAssignTeachers(user.role)) { - throw ApiError.forbidden('只有管理员可以分配班主任与各科科任老师'); - } +export async function assertCanAssignTeachers(user: TokenPayload, classId?: string): Promise { + await assertCanManageClasses(user, classId); +} + +export async function assertClassInScope(user: TokenPayload, classId: string, key: ContractPermissionKey) { + await assertPermission(user, key); + if (isAdmin(user)) return; + const count = await prisma.class.count({ + where: { AND: [{ id: classId }, await teacherScopeWhere(user, key)] }, + }); + if (!count) throw ApiError.forbidden('目标班级不在此功能的授权范围内'); +} +export async function assertClassesInScope(user: TokenPayload, ids: string[], key: ContractPermissionKey) { + for (const id of new Set(ids)) await assertClassInScope(user, id, key); } /** @@ -154,11 +191,14 @@ export function assertCanAssignTeachers(user: TokenPayload): void { * @param classId 目标班级;不传表示"全局名单操作"(例如未分班的学生),此时仅管理员 */ export async function assertCanManageRoster(user: TokenPayload, classId?: string): Promise { + await assertPermission(user, 'roster:manage'); if (!classId) { - if (!isAdmin(user)) throw ApiError.forbidden('该操作需要指定班级,只有管理员可以全局执行'); + if (!(await hasAllScope(user, 'roster:manage'))) + throw ApiError.forbidden('未分班学生操作需要全校名单范围'); return; } - await assertClassAccess(user, classId); + await assertClassInScope(user, classId, 'roster:manage'); + if (await hasAllScope(user, 'roster:manage')) return; const classRole = await resolveUserClassRole(user, classId); if (!canManageRoster(classRole)) { throw ApiError.forbidden( @@ -182,6 +222,17 @@ export async function assertCanManageSchedule(user: TokenPayload, classId: strin } } +/** 导入需同时覆盖导入能力和对应写能力,范围不能从另一项能力借用。 */ +export async function assertCanImportSchedule( + user: TokenPayload, + classId: string, + writeKey: ContractPermissionKey = 'schedule:write', +): Promise { + await assertClassInScope(user, classId, 'import:schedule'); + await assertClassInScope(user, classId, writeKey); + await assertCanManageSchedule(user, classId); +} + /** 通知 / 叫人:管理员、班主任、科任老师、ClassHelper 班级端(面向全班,不分科目) */ export async function assertCanPublishNotification(user: TokenPayload, classId: string): Promise { await assertClassAccess(user, classId); @@ -298,10 +349,13 @@ export async function resolveClassScope( permission?: ContractPermissionKey, ): Promise { if (requestedClassId) { - await assertClassAccess(user, requestedClassId); + await assertClassAccess(user, requestedClassId, permission); return { mode: 'in', classIds: [requestedClassId] }; } if (isAdmin(user)) return { mode: 'all' }; + const scopePermission = permission ?? currentRequestPermission(user); + if (isTeacher(user) && scopePermission && (await hasAllScope(user, scopePermission))) + return { mode: 'all' }; const classIds = await getAccessibleClassIds(user, permission); // 没有任何班级时不抛错,返回空集合让列表接口给出空数组,避免新教师首次登录报错 diff --git a/packages/server/src/lib/mappers.ts b/packages/server/src/lib/mappers.ts index 9b0d52f..4b440fd 100644 --- a/packages/server/src/lib/mappers.ts +++ b/packages/server/src/lib/mappers.ts @@ -458,7 +458,13 @@ export interface StudentTransferLike { toClassName: string; operatorId: string | null; operatorName: string; - mode: string; + kind: string; + batchId: string; + academicYear: number | null; + fromGrade: string; + toGrade: string; + fromSchool: string; + toSchool: string; note?: string | null; createdAt: Date; } @@ -475,7 +481,15 @@ export function toStudentTransferDto(item: StudentTransferLike): StudentTransfer toClassName: item.toClassName, operatorId: item.operatorId, operatorName: item.operatorName, - mode: item.mode as StudentTransferDto['mode'], + kind: item.kind as StudentTransferDto['kind'], + batchId: item.batchId, + academicYear: item.academicYear, + fromGrade: item.fromGrade, + toGrade: item.toGrade, + fromSchool: item.fromSchool, + toSchool: item.toSchool, + mode: + item.kind === 'transfer-out' ? 'transfer-out' : item.kind === 'restore' ? 'restore-transfer' : 'single', note: item.note ?? '', createdAt: toIso(item.createdAt), }; @@ -492,6 +506,7 @@ export interface ArchivedYearLike { classCount: number; studentCount: number; transferredCount: number; + inactiveCount: number; homeworkCount: number; notificationCount: number; archivedAt: Date; @@ -509,6 +524,7 @@ export function toArchivedYearDto(item: ArchivedYearLike): ArchivedYearDto { classCount: item.classCount, studentCount: item.studentCount, transferredCount: item.transferredCount, + inactiveCount: item.inactiveCount, homeworkCount: item.homeworkCount, notificationCount: item.notificationCount, archivedAt: toIso(item.archivedAt), diff --git a/packages/server/src/lib/permission-scope.ts b/packages/server/src/lib/permission-scope.ts index e75a68e..f3c7845 100644 --- a/packages/server/src/lib/permission-scope.ts +++ b/packages/server/src/lib/permission-scope.ts @@ -1,32 +1,52 @@ import { prisma } from './db.js'; import type { TokenPayload } from './jwt.js'; import type { Prisma } from '../generated/prisma/client.js'; -import type { PermissionKey } from '@classhelper/shared'; +import { parsePermissionScope, type PermissionScope, type PermissionKey } from '@classhelper/shared'; +import { currentRequestPermission } from './permissions.js'; /** 范围与功能键正交;显式分组覆盖角色默认,不在范围内一律不放行。 */ export async function teacherScopeWhere( user: TokenPayload, permission?: PermissionKey, ): Promise { + permission ??= currentRequestPermission(user); const assignments = await prisma.userPermissionGroup.findMany({ where: { userId: user.sub }, include: { group: { include: { items: true } } }, }); const own = { OR: [{ teacherId: user.sub }, { courses: { some: { teacherId: user.sub } } }] }; - if (!assignments.length) return own; + const groups = assignments.length + ? assignments.map(({ group }) => group) + : await prisma.permissionGroup.findMany({ where: { defaultRole: user.role }, include: { items: true } }); const alternatives: Prisma.ClassWhereInput[] = []; - for (const { group } of assignments) { + for (const group of groups) { if (permission && !group.items.some((item) => item.key === permission)) continue; - if (group.scope === 'ALL') return {}; - if (group.scope === 'SELF_CLASS') - alternatives.push(group.scopeValue === 'HEAD' ? { teacherId: user.sub } : own); - if (group.scope === 'GRADE' || group.scope === 'DIVISION') { - const grades = group.scopeValue - .split(',') - .map((s) => s.trim()) - .filter(Boolean); - if (grades.length) alternatives.push({ grade: { in: grades } }); + try { + const parsed = parsePermissionScope(group.scope as PermissionScope, group.scopeValue); + if (parsed.scope === 'ALL') return {}; + if (parsed.scope === 'SELF_CLASS') + alternatives.push(parsed.scopeValue === 'HEAD' ? { teacherId: user.sub } : own); + else alternatives.push({ grade: { in: parsed.grades } }); + } catch { + /* 旧岗位组未配置或范围非法时不授予任何班级。 */ } } return { OR: alternatives }; } + +export async function hasAllScope(user: TokenPayload, permission: PermissionKey) { + if (user.role === 'ADMIN') return true; + const links = await prisma.userPermissionGroup.findMany({ + where: { userId: user.sub }, + select: { groupId: true }, + }); + return ( + (await prisma.permissionGroup.count({ + where: { + ...(links.length ? { id: { in: links.map((link) => link.groupId) } } : { defaultRole: user.role }), + scope: 'ALL', + items: { some: { key: permission } }, + }, + })) > 0 + ); +} diff --git a/packages/server/src/lib/permissions.ts b/packages/server/src/lib/permissions.ts index f0d8c1f..5ac2406 100644 --- a/packages/server/src/lib/permissions.ts +++ b/packages/server/src/lib/permissions.ts @@ -8,6 +8,37 @@ import { } from '@classhelper/shared'; import { prisma } from './db.js'; import type { TokenPayload } from './jwt.js'; +import { ApiError } from './http.js'; + +// 仅复用当前请求对象上的权限,绝不跨请求缓存。 +const requestPermissions = new WeakMap>(); +const requestPermission = new WeakMap(); +export function bindRequestPermission(user: TokenPayload, key: PermissionKey) { + requestPermission.set(user, key); +} +export function currentRequestPermission(user: TokenPayload) { + return requestPermission.get(user); +} +export function bindRequestPermissions(user: TokenPayload, permissions: ReadonlySet) { + requestPermissions.set(user, permissions); +} +export async function hasEffectivePermission(user: TokenPayload, key: PermissionKey) { + return (requestPermissions.get(user) ?? (await loadEffectivePermissions(user))).has(key); +} +export async function assertPermission(user: TokenPayload, key: PermissionKey): Promise { + if (!(await hasEffectivePermission(user, key))) + throw ApiError.forbidden(`缺少「${PERMISSION_LABELS[key]}」权限`); + bindRequestPermission(user, key); +} +export async function assertAnyPermission(user: TokenPayload, keys: PermissionKey[]): Promise { + const permissions = requestPermissions.get(user) ?? (await loadEffectivePermissions(user)); + if (!keys.some((key) => permissions.has(key))) throw ApiError.forbidden('缺少该操作所需权限'); + const matched = keys.find((key) => permissions.has(key)); + if (matched) bindRequestPermission(user, matched); +} +export async function assertStaffPermission(user: TokenPayload, key: PermissionKey): Promise { + if (user.role !== 'CLASS_DEVICE') await assertPermission(user, key); +} /** * 生效权限集合的**唯一加载入口**(权限组功能的全部运行时逻辑都收在这里)。 diff --git a/packages/server/src/lib/settings.ts b/packages/server/src/lib/settings.ts index e80299e..eca8575 100644 --- a/packages/server/src/lib/settings.ts +++ b/packages/server/src/lib/settings.ts @@ -30,6 +30,7 @@ export const SETTING_KEYS = { termStartDate: 'term.startDate', termMaxWeeks: 'term.maxWeeks', promotionAt: 'term.promotionAt', + promotionPlan: 'term.promotionPlan', } as const; export type SettingKey = (typeof SETTING_KEYS)[keyof typeof SETTING_KEYS]; diff --git a/packages/server/src/lib/snapshot.ts b/packages/server/src/lib/snapshot.ts index da2d5ad..4c5adf1 100644 --- a/packages/server/src/lib/snapshot.ts +++ b/packages/server/src/lib/snapshot.ts @@ -34,6 +34,7 @@ export const SNAPSHOT_VERSION = 1; */ export const SNAPSHOT_TABLES = [ 'User', + 'PermissionAudit', 'PermissionGroup', 'PermissionGroupItem', 'UserPermissionGroup', @@ -138,7 +139,7 @@ export function assertValidSnapshot(value: unknown): asserts value is DatabaseSn for (const table of SNAPSHOT_TABLES) { const rows = (snapshot.data as Record)[table]; // 旧版本快照没有课件表;允许恢复,文件实体仍需单独备份。 - if (rows === undefined && table.startsWith('Courseware')) continue; + if (rows === undefined && (table.startsWith('Courseware') || table === 'PermissionAudit')) continue; if (!Array.isArray(rows)) { throw new Error(`快照缺少表 ${table} 的数据`); } @@ -182,6 +183,19 @@ export async function restoreSnapshot( } for (const table of SNAPSHOT_TABLES) { const rows = (snapshot.data[table] ?? []).map((row) => { + if (table === 'StudentClassTransfer') { + const { mode, ...rest } = row; + return { + ...rest, + kind: + rest.kind ?? + (mode === 'transfer-out' + ? 'transfer-out' + : mode === 'restore-transfer' + ? 'restore' + : 'class-change'), + }; + } if (table !== 'Class' || typeof row !== 'object' || row === null) return row; const copy = { ...(row as Record) }; delete copy.notificationChannel; diff --git a/packages/server/src/lib/student-lifecycle.ts b/packages/server/src/lib/student-lifecycle.ts new file mode 100644 index 0000000..1466515 --- /dev/null +++ b/packages/server/src/lib/student-lifecycle.ts @@ -0,0 +1,166 @@ +import { randomUUID } from 'node:crypto'; +import { currentAcademicYearStart, SOCKET_EVENTS, type StudentLifecycleKind } from '@classhelper/shared'; +import { prisma } from './db.js'; +import { ApiError } from './http.js'; +import { assertCanManageRoster } from './access.js'; +import type { TokenPayload } from './jwt.js'; +import { toStudentTransferDto } from './mappers.js'; +import { emitToClass } from '../realtime/bus.js'; + +export function notifyStudentClasses(ids: (string | null)[]) { + for (const classId of new Set(ids.filter((id): id is string => Boolean(id)))) + emitToClass(classId, SOCKET_EVENTS.classUpdated, { classId, action: 'updated' }); +} + +/** 生命周期写入只有此入口;数据、班级快照与历史在同一事务提交。 */ +export async function changeStudentLifecycle( + user: TokenPayload, + input: { + studentIds: string[]; + kind: StudentLifecycleKind; + toClassId?: string | null; + note?: string; + fromSchool?: string; + toSchool?: string; + allowGradeChange?: boolean; + newStudent?: { + studentNo: string; + name: string; + gender?: string; + guardianPhone?: string; + status?: 'active' | 'inactive'; + }; + }, +) { + const batchId = randomUUID(); + const ids = [...new Set(input.studentIds)]; + const existing = ids.length ? await prisma.student.findMany({ where: { id: { in: ids } } }) : []; + if (existing.length !== ids.length) throw ApiError.notFound('部分学生不存在,请刷新后重试'); + for (const cls of new Set(existing.map((student) => student.classId))) + await assertCanManageRoster(user, cls ?? undefined); + if (input.toClassId) await assertCanManageRoster(user, input.toClassId); + else if (!existing.length) await assertCanManageRoster(user); + const changedClasses: (string | null)[] = []; + const result = await prisma.$transaction(async (tx) => { + const target = input.toClassId ? await tx.class.findUnique({ where: { id: input.toClassId } }) : null; + if (input.toClassId && !target) throw ApiError.badRequest('目标班级不存在'); + if (target?.archivedYearId) throw ApiError.conflict('目标班级已归档,不能调入'); + if (['transfer-in', 'restore'].includes(input.kind) && !target) + throw ApiError.badRequest('请指定在读班级'); + const students = ids.length + ? await tx.student.findMany({ where: { id: { in: ids } }, include: { class: true } }) + : []; + if (input.newStudent) { + if (await tx.student.findUnique({ where: { studentNo: input.newStudent.studentNo } })) + throw ApiError.conflict('学号已被占用,请使用转入或复学入口'); + const created = await tx.student.create({ + data: { ...input.newStudent, classId: target?.id ?? null }, + include: { class: true }, + }); + students.push(created); + } + if (!students.length) throw ApiError.badRequest('请至少选择一名学生'); + const records = []; + for (const student of students) { + const isNew = Boolean(input.newStudent); + const prior = existing.find((item) => item.id === student.id); + if ( + prior && + (prior.classId !== student.classId || + prior.status !== student.status || + prior.archivedYearId !== student.archivedYearId) + ) + throw ApiError.conflict('学生状态已变化,请刷新后重试'); + // 撤回转出是唯一允许将归档转出记录迁入其他在读班的路径。 + if ( + student.status === 'graduated' || + (!isNew && + (student.archivedYearId || student.class?.archivedYearId) && + !(student.status === 'transferred' && input.kind === 'restore')) + ) + throw ApiError.conflict('该生已毕业或已归档,请先在归档页撤销归档'); + if (!isNew) { + const allowed: Record = { + 'class-change': ['active'], + unassign: ['active'], + 'transfer-out': ['active', 'inactive'], + 'transfer-in': ['transferred', 'inactive'], + restore: ['transferred', 'inactive'], + deactivate: ['active'], + }; + if (!allowed[input.kind].includes(student.status)) + throw ApiError.conflict('当前学生状态不允许此操作,请刷新名单并使用对应生命周期入口'); + } + const from = isNew ? null : student.class; + if (!isNew && input.kind === 'class-change' && student.classId === target?.id) continue; + if ( + input.kind === 'class-change' && + from && + target && + from.grade !== target.grade && + !(user.role === 'ADMIN' && input.allowGradeChange && input.note?.trim()) + ) + throw ApiError.conflict('跨年级调班需要管理员勾选允许跨年级并填写备注'); + const keepClass = ['transfer-out', 'deactivate'].includes(input.kind); + const nextClassId = keepClass ? student.classId : (target?.id ?? null); + const nextStatus = + input.kind === 'transfer-out' + ? 'transferred' + : input.kind === 'deactivate' + ? 'inactive' + : isNew + ? student.status + : 'active'; + await tx.student.update({ + where: { id: student.id }, + data: { + classId: nextClassId, + status: nextStatus, + ...(input.kind === 'transfer-out' + ? { transferredAt: new Date(), transferNote: input.note ?? '' } + : {}), + ...(['transfer-in', 'restore'].includes(input.kind) + ? { transferredAt: null, archivedYearId: null, archivedAt: null } + : {}), + }, + }); + records.push( + await tx.studentClassTransfer.create({ + data: { + studentId: student.id, + studentNo: student.studentNo, + studentName: student.name, + kind: input.kind, + batchId, + academicYear: currentAcademicYearStart(), + fromClassId: from?.id ?? null, + fromClassName: from?.name ?? '', + fromGrade: from?.grade ?? '', + toClassId: keepClass ? null : (target?.id ?? null), + toClassName: + input.kind === 'transfer-out' + ? '转出本校' + : input.kind === 'deactivate' + ? '停用' + : (target?.name ?? '未分班'), + toGrade: keepClass ? '' : (target?.grade ?? ''), + fromSchool: input.fromSchool ?? '', + toSchool: input.toSchool ?? '', + operatorId: user.sub, + operatorName: user.name, + note: input.note ?? '', + }, + }), + ); + changedClasses.push(from?.id ?? null, nextClassId); + } + return { + batchId, + count: records.length, + transfers: records.map(toStudentTransferDto), + studentId: students[0].id, + }; + }); + notifyStudentClasses(changedClasses); + return result; +} diff --git a/packages/server/src/lib/students.ts b/packages/server/src/lib/students.ts index f1a6fea..1d83faa 100644 --- a/packages/server/src/lib/students.ts +++ b/packages/server/src/lib/students.ts @@ -1,7 +1,7 @@ /** 所有在读入口共用;归档/转出专页显式读取历史状态。 */ export function activeStudentWhere() { - return { status: { notIn: ['transferred', 'inactive'] } }; + return { status: 'active', archivedYearId: null }; } -export function isActiveStudent(student: { status: string }) { - return !activeStudentWhere().status.notIn.includes(student.status); +export function isActiveStudent(student: { status: string; archivedYearId?: string | null }) { + return student.status === 'active' && !student.archivedYearId; } diff --git a/packages/server/src/middleware/auth.ts b/packages/server/src/middleware/auth.ts index 7ef5528..3af5fd1 100644 --- a/packages/server/src/middleware/auth.ts +++ b/packages/server/src/middleware/auth.ts @@ -3,7 +3,11 @@ import type { PermissionKey, UserRole } from '@classhelper/shared'; import { PERMISSION_LABELS } from '@classhelper/shared'; import { prisma } from '../lib/db.js'; import { ApiError } from '../lib/http.js'; -import { loadEffectivePermissions } from '../lib/permissions.js'; +import { + bindRequestPermission, + bindRequestPermissions, + loadEffectivePermissions, +} from '../lib/permissions.js'; import { extractBearerToken, verifyToken, type TokenPayload } from '../lib/jwt.js'; /** @@ -58,6 +62,7 @@ export function authenticate(): RequestHandler { classId: null, }; req.auth = { token, user: authUser, permissions: await loadEffectivePermissions(authUser) }; + bindRequestPermissions(authUser, req.auth.permissions!); next(); } catch (error) { next(error); @@ -100,6 +105,7 @@ export function requirePermission(...keys: PermissionKey[]): RequestHandler { next(ApiError.forbidden(`当前账号缺少${labels}权限,请联系管理员在「权限组」里分配`)); return; } + if (keys[0]) bindRequestPermission(req.auth.user, keys[0]); next(); }; } @@ -133,6 +139,8 @@ export function requireAnyPermission(...keys: PermissionKey[]): RequestHandler { ); return; } + const matched = keys.find((key) => permissions.has(key)); + if (matched) bindRequestPermission(req.auth.user, matched); next(); }; } diff --git a/packages/server/src/modules/archives/archives.module.ts b/packages/server/src/modules/archives/archives.module.ts index 0c7e14c..6f691e3 100644 --- a/packages/server/src/modules/archives/archives.module.ts +++ b/packages/server/src/modules/archives/archives.module.ts @@ -1,3 +1,4 @@ +import { restoreStudentSchema } from '../students/students.schemas.js'; import { listTransferred, restoreTransferred } from './transferred.service.js'; import { Router } from 'express'; import { sendOk } from '../../lib/http.js'; @@ -20,13 +21,14 @@ router.get('/transferred', requirePermission('archive:manage'), async (req, res) ); router.post( '/transferred/:id/restore', - requirePermission('archive:manage'), - validate({ params: idParamSchema }), - async (req, res) => sendOk(res, await restoreTransferred(getAuthUser(req), String(req.params.id))), + requirePermission('roster:manage'), + validate({ params: idParamSchema, body: restoreStudentSchema }), + async (req, res) => + sendOk(res, await restoreTransferred(getAuthUser(req), String(req.params.id), req.body)), ); /** - * 毕业归档(**全部接口仅管理员**)。 + * 毕业归档(**归档操作按 archive:manage 与全校范围校验**)。 * * 归档是**打标记 + 只读**:毕业班级的作业与通知原样留在库里, * 通过 `GET /:id/classes/:classId/content` 只读查看。 @@ -49,7 +51,7 @@ router.get('/summary', requirePermission('archive:manage'), async (req, res) => sendOk( res, { - ...(await archiveService.getArchiveSummary()), + ...(await archiveService.getArchiveSummary(getAuthUser(req))), suggestedEnrollmentYears: suggestedEnrollmentYears(), }, '获取归档汇总成功', diff --git a/packages/server/src/modules/archives/archives.schemas.ts b/packages/server/src/modules/archives/archives.schemas.ts index f486017..ce17817 100644 --- a/packages/server/src/modules/archives/archives.schemas.ts +++ b/packages/server/src/modules/archives/archives.schemas.ts @@ -2,14 +2,6 @@ import { z } from 'zod'; import { currentAcademicYearStart, graduationYearOf } from '@classhelper/shared'; import { enrollmentYearSchema } from '../../lib/schemas.js'; -/** 执行毕业归档:按**届别**(入学年份)把整届班级与学生归档 */ -export const createArchiveSchema = z.object({ - enrollmentYear: enrollmentYearSchema, - /** 毕业年份;留空 = 入学年份 + 默认学制(3 年) */ - graduationYear: z.coerce.number().int().min(2000).max(2200).optional(), - note: z.string().trim().max(200).optional(), -}); - /** 届别列表的查询参数 */ export const listArchivesQuerySchema = z.object({ /** 只看某一届 */ diff --git a/packages/server/src/modules/archives/archives.service.ts b/packages/server/src/modules/archives/archives.service.ts index 4b0bbc0..3945d9e 100644 --- a/packages/server/src/modules/archives/archives.service.ts +++ b/packages/server/src/modules/archives/archives.service.ts @@ -1,12 +1,10 @@ import { SOCKET_EVENTS, - formatYearLabel, - graduationYearOf, type ArchivedClassContentDto, type ArchivedYearDetailDto, type ArchivedYearDto, } from '@classhelper/shared'; -import { assertCanManageClasses } from '../../lib/access.js'; +import { assertGlobalPermission } from '../../lib/access.js'; import { prisma } from '../../lib/db.js'; import { ApiError } from '../../lib/http.js'; import type { TokenPayload } from '../../lib/jwt.js'; @@ -19,10 +17,6 @@ import { } from '../../lib/mappers.js'; import { emitToClass } from '../../realtime/bus.js'; import { logger } from '../../lib/logger.js'; -import type { createArchiveSchema } from './archives.schemas.js'; -import type { z } from 'zod'; - -type CreateArchiveInput = z.infer; /** * 毕业归档。 @@ -36,12 +30,12 @@ type CreateArchiveInput = z.infer; * **未毕业而升级的班级不归档**:升级只改 `Class.grade`(高一 → 高二), * 班级记录、学生名单、学号、作业成绩全部沿用(见 `classes.service.promoteClasses`)。 * - * 全部接口**仅管理员**。 + * 全部接口受 archive:manage 与全校范围保护。 */ /** 届别列表(按入学年份倒序) */ export async function listArchivedYears(user: TokenPayload): Promise { - assertCanManageClasses(user); + await assertGlobalPermission(user, 'archive:manage'); const records = await prisma.archivedYear.findMany({ orderBy: { enrollmentYear: 'desc' } }); return records.map(toArchivedYearDto); } @@ -51,7 +45,7 @@ export async function getArchivedYearDetail( user: TokenPayload, archiveId: string, ): Promise { - assertCanManageClasses(user); + await assertGlobalPermission(user, 'archive:manage'); const record = await prisma.archivedYear.findUnique({ where: { id: archiveId } }); if (!record) throw ApiError.notFound('该届别档案不存在'); @@ -72,11 +66,18 @@ export async function getArchivedYearDetail( ]); const dtos = students.map(toStudentDto); + const transferredIn = await prisma.studentClassTransfer.findMany({ + where: { kind: 'transfer-in', student: { archivedYearId: archiveId } }, + distinct: ['studentId'], + select: { studentId: true }, + }); return { ...toArchivedYearDto(record), + transferInCount: transferredIn.length, classes: classes.map(toArchivedClassDto), graduates: dtos.filter((item) => item.status === 'graduated'), transferred: dtos.filter((item) => item.status === 'transferred'), + inactive: dtos.filter((item) => item.status === 'inactive'), }; } @@ -92,7 +93,7 @@ export async function getArchivedClassContent( classId: string, limit = 200, ): Promise { - assertCanManageClasses(user); + await assertGlobalPermission(user, 'archive:manage'); const record = await prisma.class.findUnique({ where: { id: classId }, select: { id: true, name: true, archivedYearId: true }, @@ -132,99 +133,6 @@ export async function getArchivedClassContent( }; } -/** - * 执行归档:把某一届的全部班级与学生标记为已归档。 - * - * 步骤: - * 1. 找到该届**尚未归档**的班级(`enrollmentYear` 匹配且 `archivedYearId` 为空); - * 2. 把这些班的学生标记为 `graduated`;把其中已经 `transferred` 的保持原状态; - * 3. 统计班级数 / 学生数 / 转出数 / 作业数 / 通知数写进档案快照; - * 4. 全部在**一个事务**里完成,中途失败不会留下"班归档了、学生没归档"的半截状态。 - */ -export async function createArchive( - user: TokenPayload, - input: CreateArchiveInput, -): Promise { - assertCanManageClasses(user); - - const existing = await prisma.archivedYear.findUnique({ - where: { enrollmentYear: input.enrollmentYear }, - select: { id: true }, - }); - if (existing) throw ApiError.conflict(`${formatYearLabel(input.enrollmentYear)} 已经归档过了`); - - const classes = await prisma.class.findMany({ - where: { enrollmentYear: input.enrollmentYear, archivedYearId: null }, - select: { id: true }, - }); - if (classes.length === 0) { - throw ApiError.badRequest( - `没有找到 ${formatYearLabel(input.enrollmentYear)} 的在读班级(可能已归档,或入学年份没填对)`, - ); - } - - const classIds = classes.map((item) => item.id); - const [students, homeworkCount, notificationCount] = await Promise.all([ - prisma.student.findMany({ - where: { classId: { in: classIds }, archivedYearId: null }, - select: { id: true, status: true }, - }), - prisma.homework.count({ where: { classId: { in: classIds } } }), - prisma.notification.count({ where: { classId: { in: classIds } } }), - ]); - - const transferredCount = students.filter((item) => item.status === 'transferred').length; - const graduateCount = students.length - transferredCount; - - const created = await prisma.$transaction(async (tx) => { - const archive = await tx.archivedYear.create({ - data: { - enrollmentYear: input.enrollmentYear, - graduationYear: input.graduationYear ?? graduationYearOf(input.enrollmentYear), - name: formatYearLabel(input.enrollmentYear), - note: input.note ?? '', - operatorId: user.sub, - operatorName: user.name, - classCount: classes.length, - studentCount: graduateCount, - transferredCount, - homeworkCount, - notificationCount, - }, - select: { id: true }, - }); - - const now = new Date(); - await tx.class.updateMany({ - where: { id: { in: classIds } }, - data: { archivedYearId: archive.id, archivedAt: now }, - }); - - // 在读的 → 已毕业;已经转出的保持 transferred(他们的归档记录也一并挂到这一届) - if (students.length > 0) { - await tx.student.updateMany({ - where: { id: { in: students.filter((s) => s.status !== 'transferred').map((s) => s.id) } }, - data: { status: 'graduated', archivedYearId: archive.id, archivedAt: now }, - }); - await tx.student.updateMany({ - where: { id: { in: students.filter((s) => s.status === 'transferred').map((s) => s.id) } }, - data: { archivedYearId: archive.id, archivedAt: now }, - }); - } - - return archive; - }); - - for (const classId of classIds) { - emitToClass(classId, SOCKET_EVENTS.classUpdated, { classId, action: 'deleted' }); - } - logger.info( - `毕业归档:${formatYearLabel(input.enrollmentYear)} 班级=${classes.length} 毕业生=${graduateCount} 转出=${transferredCount} 由 ${user.name} 执行`, - ); - - return getArchivedYearDetail(user, created.id); -} - /** * 撤销归档(管理员误操作的退路)。 * @@ -232,7 +140,7 @@ export async function createArchive( * **不会删除任何作业或通知** —— 它们本来就没被删过。 */ export async function deleteArchive(user: TokenPayload, archiveId: string): Promise { - assertCanManageClasses(user); + await assertGlobalPermission(user, 'archive:manage'); const record = await prisma.archivedYear.findUnique({ where: { id: archiveId } }); if (!record) throw ApiError.notFound('该届别档案不存在'); @@ -265,12 +173,13 @@ export async function deleteArchive(user: TokenPayload, archiveId: string): Prom } /** 归档页顶部的汇总(一届一行之外的全局数字) */ -export async function getArchiveSummary(): Promise<{ +export async function getArchiveSummary(user: TokenPayload): Promise<{ archiveCount: number; classCount: number; graduateCount: number; transferredCount: number; }> { + await assertGlobalPermission(user, 'archive:manage'); const [archiveCount, classCount, graduateCount, transferredCount] = await Promise.all([ prisma.archivedYear.count(), prisma.class.count({ where: { NOT: { archivedYearId: null } } }), diff --git a/packages/server/src/modules/archives/transferred.service.ts b/packages/server/src/modules/archives/transferred.service.ts index c22571e..e6f2168 100644 --- a/packages/server/src/modules/archives/transferred.service.ts +++ b/packages/server/src/modules/archives/transferred.service.ts @@ -1,10 +1,9 @@ import { prisma } from '../../lib/db.js'; -import { assertCanManageClasses } from '../../lib/access.js'; +import { assertGlobalPermission } from '../../lib/access.js'; +import { restoreStudent } from '../students/students.service.js'; import type { TokenPayload } from '../../lib/jwt.js'; -import { ApiError } from '../../lib/http.js'; -import { notifyScheduleChanged } from '../class-plans/class-plans.service.js'; export async function listTransferred(user: TokenPayload) { - assertCanManageClasses(user); + await assertGlobalPermission(user, 'archive:manage'); return prisma.student.findMany({ where: { status: 'transferred' }, select: { @@ -19,31 +18,11 @@ export async function listTransferred(user: TokenPayload) { orderBy: { transferredAt: 'desc' }, }); } -export async function restoreTransferred(user: TokenPayload, id: string) { - assertCanManageClasses(user); - const student = await prisma.student.findUnique({ where: { id }, include: { class: true } }); - if (student?.status !== 'transferred' || !student.classId || student.class?.archivedYearId) - throw ApiError.badRequest('只能撤回原班仍在读的转出记录'); - await prisma.$transaction([ - prisma.student.update({ - where: { id }, - data: { status: 'active', transferredAt: null, archivedAt: null, archivedYearId: null }, - }), - prisma.studentClassTransfer.create({ - data: { - studentId: id, - studentNo: student.studentNo, - studentName: student.name, - fromClassId: null, - toClassId: student.classId, - toClassName: student.class?.name ?? '', - operatorId: user.sub, - operatorName: user.name, - mode: 'restore-transfer', - note: '撤回转出,保留原转出记录', - }, - }), - ]); - await notifyScheduleChanged(student.classId); +export async function restoreTransferred( + user: TokenPayload, + id: string, + input: { toClassId?: string; note?: string } = {}, +) { + await restoreStudent(user, id, input); return { id, restored: true }; } diff --git a/packages/server/src/modules/calls/calls.module.ts b/packages/server/src/modules/calls/calls.module.ts index 6366373..9cb57f5 100644 --- a/packages/server/src/modules/calls/calls.module.ts +++ b/packages/server/src/modules/calls/calls.module.ts @@ -1,6 +1,6 @@ import { Router } from 'express'; import { sendCreated } from '../../lib/http.js'; -import { authenticate, getAuthUser, requirePermission } from '../../middleware/auth.js'; +import { authenticate, getAuthUser, requireStaffPermission } from '../../middleware/auth.js'; import { validate } from '../../middleware/validate.js'; import { defineModule } from '../module.types.js'; import { createCallSchema, type CreateCallInput } from './calls.schemas.js'; @@ -13,10 +13,15 @@ router.use(authenticate()); * POST /api/calls - 叫人(老师点名让学生过来) * ClassHelper 班级端会在灵动岛上立即弹出"请 XXX 同学找 XXX 老师",无论是否在上课。 */ -router.post('/', requirePermission('call:create'), validate({ body: createCallSchema }), async (req, res) => { - const user = getAuthUser(req); - sendCreated(res, await callService.createCall(user, req.body as CreateCallInput), '已通知该学生'); -}); +router.post( + '/', + requireStaffPermission('call:create'), + validate({ body: createCallSchema }), + async (req, res) => { + const user = getAuthUser(req); + sendCreated(res, await callService.createCall(user, req.body as CreateCallInput), '已通知该学生'); + }, +); export const callsModule = defineModule({ name: 'calls', diff --git a/packages/server/src/modules/calls/calls.service.ts b/packages/server/src/modules/calls/calls.service.ts index 03838c3..3135346 100644 --- a/packages/server/src/modules/calls/calls.service.ts +++ b/packages/server/src/modules/calls/calls.service.ts @@ -1,3 +1,5 @@ +import { assertStaffPermission } from '../../lib/permissions.js'; +import { resolveContentCreator } from '../../lib/access.js'; import { SOCKET_EVENTS, buildCallTitle, dayKeyLocal, type NotificationDto } from '@classhelper/shared'; import { assertCanPublishNotification } from '../../lib/access.js'; import { isActiveStudent } from '../../lib/students.js'; @@ -27,6 +29,7 @@ const creatorSelect = { select: { id: true, name: true, username: true } } as co * 全班都看得到在叫谁,这与需求「教室机器代全班」一致。 */ export async function createCall(user: TokenPayload, input: CreateCallInput): Promise { + await assertStaffPermission(user, 'call:create'); await assertCanPublishNotification(user, input.classId); const student = await prisma.student.findUnique({ @@ -54,7 +57,7 @@ export async function createCall(user: TokenPayload, input: CreateCallInput): Pr // 紧急叫人 → URGENT(客户端无视上课时段立即展开);普通叫人 → HIGH // (不会触发上课时段 409 拦截,但客户端按普通通知排队处理) priority: input.urgent ? 'URGENT' : 'HIGH', - createdBy: user.sub, + createdBy: await resolveContentCreator(user, input.classId), assignDate: dayKeyLocal(new Date()), }, include: { creator: creatorSelect, reads: true }, diff --git a/packages/server/src/modules/class-plans/class-plans.module.ts b/packages/server/src/modules/class-plans/class-plans.module.ts index 25c74a5..4a39961 100644 --- a/packages/server/src/modules/class-plans/class-plans.module.ts +++ b/packages/server/src/modules/class-plans/class-plans.module.ts @@ -6,7 +6,12 @@ import { prisma } from '../../lib/db.js'; import { assertCanManageSchedule, assertClassAccess } from '../../lib/access.js'; import { sendOk, sendCreated, ApiError } from '../../lib/http.js'; import { idParamSchema } from '../../lib/schemas.js'; -import { authenticate, getAuthUser, requirePermission } from '../../middleware/auth.js'; +import { + authenticate, + getAuthUser, + requireStaffPermission, + requirePermission, +} from '../../middleware/auth.js'; import { validate, validatedQuery } from '../../middleware/validate.js'; import { defineModule } from '../module.types.js'; import * as schemas from './class-plans.schemas.js'; @@ -14,8 +19,15 @@ import * as service from './class-plans.service.js'; const layouts = Router(); layouts.use(authenticate()); -layouts.get('/', validate({ query: schemas.scopeQuery }), async (req, res) => - sendOk(res, await service.listLayouts(getAuthUser(req), validatedQuery<{ classId?: string }>(req).classId)), +layouts.get( + '/', + requireStaffPermission('schedule:read'), + validate({ query: schemas.scopeQuery }), + async (req, res) => + sendOk( + res, + await service.listLayouts(getAuthUser(req), validatedQuery<{ classId?: string }>(req).classId), + ), ); layouts.post( '/', @@ -83,12 +95,16 @@ layouts.delete( const plans = Router(); plans.use(authenticate()); -plans.get('/profile', validate({ query: z.object({ classId: z.string().min(1) }) }), async (req, res) => - sendOk(res, await exportProfile(getAuthUser(req), validatedQuery<{ classId: string }>(req).classId)), +plans.get( + '/profile', + requireStaffPermission('schedule:read'), + validate({ query: z.object({ classId: z.string().min(1) }) }), + async (req, res) => + sendOk(res, await exportProfile(getAuthUser(req), validatedQuery<{ classId: string }>(req).classId)), ); plans.post( '/import/profile', - requirePermission('schedule:write'), + requirePermission('schedule:write', 'import:schedule'), validate({ body: z.object({ classId: z.string().min(1), @@ -102,10 +118,15 @@ plans.post( await importProfile(getAuthUser(req), req.body.classId, req.body.payload, req.body.mode), ), ); -plans.get('/', validate({ query: schemas.scopeQuery }), async (req, res) => { - const q = validatedQuery<{ classId?: string; week?: number }>(req); - sendOk(res, await service.listPlans(getAuthUser(req), q.classId, q.week)); -}); +plans.get( + '/', + requireStaffPermission('schedule:read'), + validate({ query: schemas.scopeQuery }), + async (req, res) => { + const q = validatedQuery<{ classId?: string; week?: number }>(req); + sendOk(res, await service.listPlans(getAuthUser(req), q.classId, q.week)); + }, +); plans.post( '/', requirePermission('schedule:write'), @@ -145,13 +166,18 @@ plans.post( async (req, res) => sendCreated(res, await service.clonePlan(getAuthUser(req), String(req.params.id), req.body.name)), ); -plans.get('/:id/export', validate({ params: idParamSchema }), async (req, res) => { - const p = await service.requirePlan(getAuthUser(req), String(req.params.id), false); - sendOk(res, { format: 'classhelper-class-plan', plan: p, timeLayout: p.layout }); -}); +plans.get( + '/:id/export', + requireStaffPermission('schedule:read'), + validate({ params: idParamSchema }), + async (req, res) => { + const p = await service.requirePlan(getAuthUser(req), String(req.params.id), false); + sendOk(res, { format: 'classhelper-class-plan', plan: p, timeLayout: p.layout }); + }, +); plans.post( '/import', - requirePermission('schedule:write'), + requirePermission('schedule:write', 'import:schedule'), validate({ body: z.object({ plan: schemas.planSchema, entries: schemas.entriesSchema.shape.entries }) }), async (req, res) => { const p = await service.savePlan(getAuthUser(req), req.body.plan); @@ -172,17 +198,27 @@ plans.delete( const groups = Router(); groups.use(authenticate()); -groups.get('/selection', validate({ query: z.object({ classId: z.string().min(1) }) }), async (req, res) => { - const { classId } = validatedQuery<{ classId: string }>(req); - await assertClassAccess(getAuthUser(req), classId); - const value = await prisma.appSetting.findUnique({ where: { key: `schedule.group.${classId}` } }); - sendOk(res, value ? JSON.parse(value.value) : {}); -}); -groups.get('/', validate({ query: z.object({ classId: z.string().min(1) }) }), async (req, res) => { - const { classId } = validatedQuery<{ classId: string }>(req); - await assertClassAccess(getAuthUser(req), classId); - sendOk(res, await prisma.classPlanGroup.findMany({ where: { classId } })); -}); +groups.get( + '/selection', + requireStaffPermission('schedule:read'), + validate({ query: z.object({ classId: z.string().min(1) }) }), + async (req, res) => { + const { classId } = validatedQuery<{ classId: string }>(req); + await assertClassAccess(getAuthUser(req), classId); + const value = await prisma.appSetting.findUnique({ where: { key: `schedule.group.${classId}` } }); + sendOk(res, value ? JSON.parse(value.value) : {}); + }, +); +groups.get( + '/', + requireStaffPermission('schedule:read'), + validate({ query: z.object({ classId: z.string().min(1) }) }), + async (req, res) => { + const { classId } = validatedQuery<{ classId: string }>(req); + await assertClassAccess(getAuthUser(req), classId); + sendOk(res, await prisma.classPlanGroup.findMany({ where: { classId } })); + }, +); groups.post( '/', requirePermission('schedule:write'), diff --git a/packages/server/src/modules/class-plans/profile-import.service.ts b/packages/server/src/modules/class-plans/profile-import.service.ts index c558946..0cf2aa0 100644 --- a/packages/server/src/modules/class-plans/profile-import.service.ts +++ b/packages/server/src/modules/class-plans/profile-import.service.ts @@ -2,7 +2,7 @@ import { createHash } from 'node:crypto'; import { z } from 'zod'; import { isDayKey, dayKeyLocal } from '@classhelper/shared'; import { prisma } from '../../lib/db.js'; -import { assertCanManageSchedule } from '../../lib/access.js'; +import { assertCanImportSchedule } from '../../lib/access.js'; import { ApiError } from '../../lib/http.js'; import type { TokenPayload } from '../../lib/jwt.js'; import { notifyScheduleChanged } from './class-plans.service.js'; @@ -21,7 +21,7 @@ function time(value: unknown) { } /** 直接保留上游三元组/四种时间点/显式课程位置,绝不经过单双周的有损中间格式。 */ export async function importProfile(user: TokenPayload, classId: string, payload: unknown, mode = 'merge') { - await assertCanManageSchedule(user, classId); + await assertCanImportSchedule(user, classId); let parsed = payload; if (typeof payload === 'string') { try { diff --git a/packages/server/src/modules/classes/classes.module.ts b/packages/server/src/modules/classes/classes.module.ts index 35e3e03..c774914 100644 --- a/packages/server/src/modules/classes/classes.module.ts +++ b/packages/server/src/modules/classes/classes.module.ts @@ -1,7 +1,12 @@ import { Router } from 'express'; -import { sendCreated, sendOk } from '../../lib/http.js'; +import { ApiError, sendCreated, sendOk } from '../../lib/http.js'; import { idParamSchema } from '../../lib/schemas.js'; -import { authenticate, getAuthUser, requirePermission } from '../../middleware/auth.js'; +import { + authenticate, + getAuthUser, + requireStaffPermission, + requirePermission, +} from '../../middleware/auth.js'; import { validate, validatedBody, validatedParams, validatedQuery } from '../../middleware/validate.js'; import { defineModule } from '../module.types.js'; import { @@ -35,35 +40,45 @@ router.use(authenticate()); * 只列**在读**班级(毕业归档的班级属于「归档管理」页); * 教师/管理员看自己可访问的班级(班主任 ∪ 任课),ClassHelper 班级端看自己绑定的那个。 */ -router.get('/', validate({ query: listClassesQuerySchema }), async (req, res) => { - const user = getAuthUser(req); - const { keyword } = validatedQuery<{ keyword?: string }>(req); - sendOk(res, await classService.listClasses(user, keyword), '获取班级列表成功'); -}); +router.get( + '/', + requireStaffPermission('class:read'), + validate({ query: listClassesQuerySchema }), + async (req, res) => { + const user = getAuthUser(req); + const { keyword } = validatedQuery<{ keyword?: string }>(req); + sendOk(res, await classService.listClasses(user, keyword), '获取班级列表成功'); + }, +); /** - * POST /api/classes/promote - 学年升级(批量改年级,仅管理员) + * POST /api/classes/promote - 学年升级(批量改年级,按功能权限及范围校验) * * **升级不归档、数据沿用**:只改 `grade`(高一 → 高二),班级记录、学生名单、学号、 - * 作业与成绩全部原样。归档只在毕业时由「归档管理」页显式执行。 + * 作业与成绩全部原样。毕业归档只由年度升级执行。 */ router.post( '/promote', - requirePermission('class:manage'), + requirePermission('promotion:manage'), validate({ body: promoteClassesSchema }), async (req, res) => { const user = getAuthUser(req); - const body = validatedBody<{ classIds: string[]; grade: string }>(req); + const body = validatedBody<{ classIds: string[] }>(req); sendOk(res, await classService.promoteClasses(user, body), '班级已升级,数据继续沿用'); }, ); /** GET /api/classes/:id - 班级详情(学生名单 + 课程 + 班主任 + 各科任课老师) */ -router.get('/:id', validate({ params: idParamSchema }), async (req, res) => { - const user = getAuthUser(req); - const { id } = validatedParams<{ id: string }>(req); - sendOk(res, await classService.getClassDetail(user, id), '获取班级详情成功'); -}); +router.get( + '/:id', + requireStaffPermission('class:read'), + validate({ params: idParamSchema }), + async (req, res) => { + const user = getAuthUser(req); + const { id } = validatedParams<{ id: string }>(req); + sendOk(res, await classService.getClassDetail(user, id), '获取班级详情成功'); + }, +); /** POST /api/classes - 创建班级(称呼由入学年份 + 班号生成,如「2026级1班」) */ router.post( @@ -76,10 +91,14 @@ router.post( }, ); -/** PATCH /api/classes/:id - 编辑班级(年级 / 届别 / 教学周数),仅管理员 */ +/** PATCH /api/classes/:id - 编辑班级(年级 / 届别 / 教学周数),按功能权限及范围校验 */ router.patch( '/:id', requirePermission('class:manage'), + (req, _res, next) => { + if (req.body && 'grade' in req.body) return next(ApiError.badRequest('年级请通过学年升级入口修改')); + next(); + }, validate({ params: idParamSchema, body: updateClassSchema }), async (req, res) => { const user = getAuthUser(req); @@ -103,7 +122,7 @@ router.delete( /** * PATCH /api/classes/:id/class-account - 设置 / 重置班级账号(班级码 + 班级密码) - * 仅管理员:教室机器「ClassHelper 班级端」登录用的就是这个班级码 + * 按功能权限及范围校验:教室机器「ClassHelper 班级端」登录用的就是这个班级码 */ router.patch( '/:id/class-account', @@ -122,7 +141,7 @@ router.patch( /** * PATCH /api/classes/:id/head-teacher - 设置 / 更改班主任(每班 1 人) - * 仅管理员:班主任决定谁能管这个班的学生名单与课表,属于人员分配权限 + * 按功能权限及范围校验:班主任决定谁能管这个班的学生名单与课表,属于人员分配权限 */ router.patch( '/:id/head-teacher', @@ -139,7 +158,7 @@ router.patch( /** * PUT /api/classes/:id/subject-teachers - 设置各科任课老师(班级 + 科目 + 教师) * - * 仅管理员。这是「科任老师」这条关系的唯一写入口 —— 作业与成绩能不能被某位老师改动, + * 按功能权限及范围校验。这是「科任老师」这条关系的唯一写入口 —— 作业与成绩能不能被某位老师改动, * 查的就是这里写下的 `Course.teacherId`。 * 班主任同时是某科科任老师时,两条记录指向同一个账号:不重复建号、权限自动合并。 */ @@ -162,14 +181,20 @@ router.put( * GET /api/classes/:id/student-grade-query - 本班是否允许班级端按学号查成绩明细 * PATCH 同路径 - 教师/管理员按班级管理权限设置,班级端只读 */ -router.get('/:id/student-grade-query', validate({ params: idParamSchema }), async (req, res) => { - const user = getAuthUser(req); - const { id } = validatedParams<{ id: string }>(req); - sendOk(res, await classService.getStudentGradeQuery(user, id), '获取成绩查询开关成功'); -}); +router.get( + '/:id/student-grade-query', + requireStaffPermission('class:read'), + validate({ params: idParamSchema }), + async (req, res) => { + const user = getAuthUser(req); + const { id } = validatedParams<{ id: string }>(req); + sendOk(res, await classService.getStudentGradeQuery(user, id), '获取成绩查询开关成功'); + }, +); router.patch( '/:id/student-grade-query', + requirePermission('grade:write'), validate({ params: idParamSchema, body: updateStudentGradeQuerySchema }), async (req, res) => { const user = getAuthUser(req); @@ -183,11 +208,16 @@ router.patch( ); /** GET /api/classes/:id/students - 班级学生名单 */ -router.get('/:id/students', validate({ params: idParamSchema }), async (req, res) => { - const user = getAuthUser(req); - const { id } = validatedParams<{ id: string }>(req); - sendOk(res, await classService.listClassStudents(user, id), '获取学生名单成功'); -}); +router.get( + '/:id/students', + requireStaffPermission('roster:read'), + validate({ params: idParamSchema }), + async (req, res) => { + const user = getAuthUser(req); + const { id } = validatedParams<{ id: string }>(req); + sendOk(res, await classService.listClassStudents(user, id), '获取学生名单成功'); + }, +); /** POST /api/classes/:id/students - 把学生加入班级(按学号,已存在则转入并留调班痕迹) */ router.post( diff --git a/packages/server/src/modules/classes/classes.schemas.ts b/packages/server/src/modules/classes/classes.schemas.ts index 7c18be0..f5a1920 100644 --- a/packages/server/src/modules/classes/classes.schemas.ts +++ b/packages/server/src/modules/classes/classes.schemas.ts @@ -18,7 +18,7 @@ export const createClassSchema = z.object({ enrollmentYear: enrollmentYearSchema, /** 班号(同一届内的序号) */ classIndex: classIndexSchema, - /** 仅管理员可指定班主任,教师创建时固定为自己 */ + /** 班主任可空;具备班级管理权限的员工显式指定 */ teacherId: z.string().min(1).optional(), /** 可选:自定义班级码(ClassHelper 班级端登录用),留空自动生成 */ code: z @@ -37,11 +37,10 @@ export const createClassSchema = z.object({ /** * 编辑班级。 * - * `grade` 是**学年升级**用的:高一 → 高二 只改它,班级记录与学生数据全部沿用,**不归档**。 + * 年级只能通过升级计划修改,本接口维护届别、班号和作息覆盖。 * `enrollmentYear` / `classIndex` 是给"有名无实的旧数据"补届别用的,改了会重算班级称呼。 */ -export const updateClassSchema = z.object({ - grade: z.string().trim().min(1).max(32).optional(), +export const updateClassSchema = z.strictObject({ /** 本班学期开始日期(第 1 教学周的周一);空串 = 回落到全局配置 */ termStartDate: z .union([z.string().trim().refine(isDayKey, { message: '日期格式应为 YYYY-MM-DD' }), z.literal('')]) @@ -52,9 +51,8 @@ export const updateClassSchema = z.object({ }); /** 学年升级(批量改年级,不归档) */ -export const promoteClassesSchema = z.object({ +export const promoteClassesSchema = z.strictObject({ classIds: z.array(z.string().min(1)).min(1, '请至少选择一个班级').max(200), - grade: z.string().trim().min(1, '请输入升级后的年级').max(32), }); /** 设置 / 重置班级账号(班级码 + 班级密码):仅管理员 */ diff --git a/packages/server/src/modules/classes/classes.service.ts b/packages/server/src/modules/classes/classes.service.ts index d9781de..55174e7 100644 --- a/packages/server/src/modules/classes/classes.service.ts +++ b/packages/server/src/modules/classes/classes.service.ts @@ -4,7 +4,6 @@ import { env } from '../../config/env.js'; import { assertCanAssignTeachers, assertCanManageClasses, - assertCanManageRoster, assertClassAccess, classScopeIdFilter, isStaff, @@ -26,7 +25,11 @@ import type { UpdateClassInput, UpdateStudentGradeQueryInput, } from './classes.schemas.js'; -import { activeStudentWhere, isActiveStudent } from '../../lib/students.js'; +import { loadPromotionPlan } from '../term/promotion.service.js'; +import { assertClassInScope, canAccessClass } from '../../lib/access.js'; +import { changeStudentLifecycle } from '../../lib/student-lifecycle.js'; +import { assertStaffPermission, assertPermission, hasEffectivePermission } from '../../lib/permissions.js'; +import { activeStudentWhere } from '../../lib/students.js'; import { getMaxTermWeeks } from '../../lib/term.js'; import { logger } from '../../lib/logger.js'; @@ -76,7 +79,8 @@ function toClassDtoWithAccount( * 混在常规列表里会让老师误以为那届还在上课。需要看归档班级请用 `/api/archives`。 */ export async function listClasses(user: TokenPayload, keyword?: string): Promise { - const scope = await resolveClassScope(user); + await assertStaffPermission(user, 'class:read'); + const scope = await resolveClassScope(user, undefined, 'class:read'); // Class 表按主键 id 过滤(classScopeWhere 针对的是带 classId 字段的子表) const where = { ...classScopeIdFilter(scope), @@ -98,6 +102,7 @@ export async function listClasses(user: TokenPayload, keyword?: string): Promise /** 班级详情:学生名单 + 课程 + 班主任 + 各科任课老师 */ export async function getClassDetail(user: TokenPayload, classId: string): Promise { + await assertStaffPermission(user, 'class:read'); await assertClassAccess(user, classId); const item = await prisma.class.findUnique({ @@ -117,7 +122,12 @@ export async function getClassDetail(user: TokenPayload, classId: string): Promi const base = toClassDtoWithAccount(user, item); return { ...base, - students: item.students.map(toStudentDto), + students: + user.role === 'CLASS_DEVICE' || + ((await hasEffectivePermission(user, 'roster:read')) && + (await canAccessClass(user, classId, 'roster:read'))) + ? item.students.map(toStudentDto) + : [], courses: item.courses.map(toCourseDto), headTeacher: item.teacher ? toTeacherBrief(item.teacher) : null, subjectTeachers: item.courses.map((course) => ({ @@ -159,7 +169,7 @@ export async function listSubjectTeachers( /** 创建班级 */ export async function createClass(user: TokenPayload, input: CreateClassInput): Promise { - assertCanManageClasses(user); + await assertCanManageClasses(user); if ( 'termWeeks' in input && typeof input.termWeeks === 'number' && @@ -167,8 +177,8 @@ export async function createClass(user: TokenPayload, input: CreateClassInput): ) throw ApiError.badRequest('班级周数不能超过全校上限'); - let teacherId = user.sub; - if (input.teacherId && user.role === 'ADMIN') { + let teacherId: string | null = null; + if (input.teacherId) { teacherId = await requireTeacher(input.teacherId, '指定的班主任不存在或角色不是教师'); } @@ -200,7 +210,7 @@ export async function createClass(user: TokenPayload, input: CreateClassInput): } /** - * 设置 / 更改班主任(仅管理员)。 + * 设置 / 更改班主任(按班级管理权限与范围)。 * * 班主任决定了"谁能管这个班的学生名单与课表",所以更换时要保证目标账号确实是教师/管理员。 * 科任关系**不受影响** —— 它在 `Course` 上,是独立的一条条记录; @@ -211,7 +221,7 @@ export async function updateHeadTeacher( classId: string, teacherId: string, ): Promise { - assertCanAssignTeachers(user); + await assertCanAssignTeachers(user, classId); const targetId = await requireTeacher(teacherId, '指定的班主任不存在或角色不是教师'); const updated = await prisma.class.update({ @@ -228,7 +238,7 @@ export async function updateHeadTeacher( } /** - * 批量设置各科科任老师(仅管理员)。 + * 批量设置各科科任老师(按班级管理权限与范围)。 * * 入参里每条是「某班某科的任课老师」: * - 给了 `courseId` 就改那门课; @@ -242,7 +252,7 @@ export async function assignSubjectTeachers( classId: string, input: AssignSubjectTeachersInput, ): Promise { - assertCanAssignTeachers(user); + await assertCanAssignTeachers(user, classId); const targetClass = await prisma.class.findUnique({ where: { id: classId }, select: { id: true } }); if (!targetClass) throw ApiError.notFound('班级不存在'); @@ -288,13 +298,13 @@ export async function assignSubjectTeachers( })); } -/** 设置 / 重置班级账号(班级码 + 班级密码):仅管理员 */ +/** 设置 / 重置班级账号(班级码 + 班级密码):按班级管理权限与范围 */ export async function updateClassAccount( user: TokenPayload, classId: string, input: { code?: string; password?: string }, ): Promise { - assertCanManageClasses(user); + await assertCanManageClasses(user, classId); if ( 'termWeeks' in input && typeof input.termWeeks === 'number' && @@ -315,13 +325,13 @@ export async function updateClassAccount( return toClassDtoWithAccount(user, updated); } -/** 编辑班级(年级 / 教学周数 / 届别) */ +/** 编辑班级资料;年级只能通过升级计划修改。 */ export async function updateClass( user: TokenPayload, classId: string, input: UpdateClassInput, ): Promise { - assertCanManageClasses(user); + await assertCanManageClasses(user, classId); const current = await prisma.class.findUnique({ where: { id: classId }, @@ -329,13 +339,15 @@ export async function updateClass( }); if (!current) throw ApiError.notFound('班级不存在'); + if ('grade' in input) throw ApiError.badRequest('修改年级请使用学年升级入口'); + if (input.termWeeks !== undefined && input.termWeeks > (await getMaxTermWeeks())) + throw ApiError.badRequest('班级周数不能超过全校上限'); const enrollmentYear = input.enrollmentYear ?? current.enrollmentYear; const classIndex = input.classIndex ?? current.classIndex; const updated = await prisma.class.update({ where: { id: classId }, data: { - ...(input.grade ? { grade: input.grade } : {}), ...(input.termWeeks !== undefined ? { termWeeks: input.termWeeks } : {}), // 学期开始日期:空串/null 都表示"回落到全局配置"(如 .env 的 TERM_START_DATE) ...(input.termStartDate !== undefined @@ -357,38 +369,60 @@ export async function updateClass( } /** - * 学年升级(仅管理员):把若干班级的年级改掉,**不归档、数据沿用**。 + * 学年升级(按升级权限与班级范围):把若干班级的年级改掉,**不归档、数据沿用**。 * - * 这是「未毕业而升级的班级不归档」这条例则的操作入口 —— 高一 → 高二 只动 `grade`, - * 班级记录、学生名单、学号、作业与成绩全部原样。归档只在毕业时由归档页显式执行。 + * 从已保存计划推导下一学段;毕业学段必须走年度升级归档。保留班级业务数据。 */ export async function promoteClasses( user: TokenPayload, - input: { classIds: string[]; grade: string }, + input: { classIds: string[] }, ): Promise<{ promoted: number }> { - assertCanManageClasses(user); - const result = await prisma.class.updateMany({ - where: { id: { in: input.classIds }, archivedYearId: null }, - data: { grade: input.grade }, + await assertPermission(user, 'promotion:manage'); + const plan = await loadPromotionPlan(); + const ids = [...new Set(input.classIds)]; + for (const id of ids) await assertClassInScope(user, id, 'promotion:manage'); + await prisma.$transaction(async (tx) => { + const classes = await tx.class.findMany({ where: { id: { in: ids }, archivedYearId: null } }); + if (classes.length !== ids.length) throw ApiError.conflict('部分班级不存在或已归档'); + for (const cls of classes) { + const track = plan.tracks.find((track) => track.stages.some((stage) => stage.grade === cls.grade)); + const index = track?.stages.findIndex((stage) => stage.grade === cls.grade) ?? -1; + if (!track || index < 0) throw ApiError.conflict('班级年级不在升级计划内'); + if (track.stages[index].terminal) throw ApiError.conflict('毕业学段请通过年度升级进行归档'); + const nextGrade = track.stages[index + 1].grade; + await tx.class.update({ where: { id: cls.id }, data: { grade: nextGrade, termStartDate: null } }); + await tx.permissionAudit.create({ + data: { + actorId: user.sub, + actorName: user.name, + action: 'promote-class', + targetType: 'class', + targetId: cls.id, + beforeJson: JSON.stringify({ grade: cls.grade }), + afterJson: JSON.stringify({ grade: nextGrade }), + }, + }); + } }); - for (const classId of input.classIds) { - emitToClass(classId, SOCKET_EVENTS.classUpdated, { classId, action: 'updated' }); - } - return { promoted: result.count }; + for (const classId of ids) emitToClass(classId, SOCKET_EVENTS.classUpdated, { classId, action: 'updated' }); + return { promoted: ids.length }; } /** 删除班级(级联删除课表/作业/通知/成绩;学生记录保留但解除班级归属) */ export async function deleteClass(user: TokenPayload, classId: string): Promise { - assertCanManageClasses(user); + await assertCanManageClasses(user); + if (await prisma.student.count({ where: { classId } })) + throw ApiError.conflict('班级仍有学生,请先通过名单生命周期入口移出或归档,不能级联清除归属'); emitToClass(classId, SOCKET_EVENTS.classUpdated, { classId, action: 'deleted' }); await prisma.class.delete({ where: { id: classId } }); } /** 班级学生名单 */ export async function listClassStudents(user: TokenPayload, classId: string): Promise { + await assertStaffPermission(user, 'roster:read'); await assertClassAccess(user, classId); const students = await prisma.student.findMany({ - where: { classId, archivedYearId: null, ...activeStudentWhere() }, + where: { classId, ...activeStudentWhere() }, include: { class: { select: { name: true, grade: true } } }, orderBy: { studentNo: 'asc' }, }); @@ -406,85 +440,28 @@ export async function addStudent( classId: string, input: AddStudentInput, ): Promise { - await assertCanManageRoster(user, classId); - await assertClassAccess(user, classId); - const existing = await prisma.student.findUnique({ where: { studentNo: input.studentNo } }); - let studentId: string; - - if (existing && !isActiveStudent(existing)) - throw ApiError.conflict('学号已被历史学生占用;请使用新学号或在归档页撤回转出'); - if (existing) { - studentId = existing.id; - if (existing.classId !== classId) { - await prisma.studentClassTransfer.create({ - data: { - studentId: existing.id, - studentNo: existing.studentNo, - studentName: input.name || existing.name, - fromClassId: existing.classId, - fromClassName: await classLabel(existing.classId), - toClassId: classId, - toClassName: await classLabel(classId), - operatorId: user.sub, - operatorName: user.name, - mode: 'single', - note: '在班级名单里直接加入', - }, - }); - } - await prisma.student.update({ - where: { id: existing.id }, - data: { classId, name: input.name, status: 'active' }, - }); - } else { - const created = await prisma.student.create({ - data: { - studentNo: input.studentNo, - name: input.name, - classId, - gender: input.gender ?? '', - guardianPhone: input.guardianPhone ?? '', - }, - }); - studentId = created.id; - } - - const student = await prisma.student.findUniqueOrThrow({ - where: { id: studentId }, - include: { class: { select: { name: true, grade: true } } }, + if (existing && (existing.archivedYearId || existing.status !== 'active')) + throw ApiError.conflict('学号已被历史学生占用,请使用转入、复学或撤销归档入口'); + const result = await changeStudentLifecycle(user, { + studentIds: existing ? [existing.id] : [], + kind: 'class-change', + toClassId: classId, + note: '班级名单添加', + ...(!existing ? { newStudent: input } : {}), }); - - emitToClass(classId, SOCKET_EVENTS.classUpdated, { classId, action: 'updated' }); - return toStudentDto(student); + return toStudentDto( + await prisma.student.findUniqueOrThrow({ + where: { id: result.studentId }, + include: { class: { select: { name: true, grade: true } } }, + }), + ); } -/** 将学生移出班级(保留名单记录,回到「未分班」) */ export async function removeStudent(user: TokenPayload, classId: string, studentId: string): Promise { - await assertCanManageRoster(user, classId); - const student = await prisma.student.findUnique({ where: { id: studentId } }); - if (!student) throw ApiError.notFound('学生不存在'); - if (student.classId !== classId) throw ApiError.badRequest('该学生不属于此班级'); - - await prisma.student.update({ where: { id: studentId }, data: { classId: null } }); - await prisma.studentClassTransfer.create({ - data: { - studentId: student.id, - studentNo: student.studentNo, - studentName: student.name, - fromClassId: classId, - fromClassName: await classLabel(classId), - toClassId: null, - toClassName: '未分班', - operatorId: user.sub, - operatorName: user.name, - mode: 'single', - note: '从班级名单里移出', - }, - }); - - emitToClass(classId, SOCKET_EVENTS.classUpdated, { classId, action: 'updated' }); + if (!student || student.classId !== classId) throw ApiError.badRequest('学生不属于此班级'); + await changeStudentLifecycle(user, { studentIds: [studentId], kind: 'unassign', note: '移出班级' }); } /** @@ -543,10 +520,3 @@ async function requireTeacher(teacherId: string, message: string): Promise { - if (!classId) return ''; - const record = await prisma.class.findUnique({ where: { id: classId }, select: { name: true } }); - return record?.name ?? ''; -} diff --git a/packages/server/src/modules/courses/courses.module.ts b/packages/server/src/modules/courses/courses.module.ts index 1ce173f..92c7349 100644 --- a/packages/server/src/modules/courses/courses.module.ts +++ b/packages/server/src/modules/courses/courses.module.ts @@ -1,7 +1,12 @@ import { Router } from 'express'; import { sendCreated, sendOk } from '../../lib/http.js'; import { idParamSchema } from '../../lib/schemas.js'; -import { authenticate, getAuthUser, requirePermission } from '../../middleware/auth.js'; +import { + authenticate, + getAuthUser, + requireStaffPermission, + requirePermission, +} from '../../middleware/auth.js'; import { validate, validatedParams, validatedQuery } from '../../middleware/validate.js'; import { defineModule } from '../module.types.js'; import { @@ -17,11 +22,16 @@ const router = Router(); router.use(authenticate()); /** GET /api/courses?classId= - 课程列表 */ -router.get('/', validate({ query: listCoursesQuerySchema }), async (req, res) => { - const user = getAuthUser(req); - const { classId } = validatedQuery<{ classId?: string }>(req); - sendOk(res, await courseService.listCourses(user, classId), '获取课程列表成功'); -}); +router.get( + '/', + requireStaffPermission('schedule:read'), + validate({ query: listCoursesQuerySchema }), + async (req, res) => { + const user = getAuthUser(req); + const { classId } = validatedQuery<{ classId?: string }>(req); + sendOk(res, await courseService.listCourses(user, classId), '获取课程列表成功'); + }, +); /** POST /api/courses - 新增课程 */ router.post( diff --git a/packages/server/src/modules/homeworks/homeworks.module.ts b/packages/server/src/modules/homeworks/homeworks.module.ts index 7999a70..322e81c 100644 --- a/packages/server/src/modules/homeworks/homeworks.module.ts +++ b/packages/server/src/modules/homeworks/homeworks.module.ts @@ -1,7 +1,12 @@ import { Router } from 'express'; import { sendCreated, sendOk } from '../../lib/http.js'; import { idParamSchema } from '../../lib/schemas.js'; -import { authenticate, getAuthUser, requirePermission } from '../../middleware/auth.js'; +import { + authenticate, + getAuthUser, + requireStaffPermission, + requirePermission, +} from '../../middleware/auth.js'; import { validate, validatedParams, validatedQuery } from '../../middleware/validate.js'; import { defineModule } from '../module.types.js'; import { @@ -25,16 +30,21 @@ const router = Router(); router.use(authenticate()); /** GET /api/homeworks?classId=&courseId=&pendingOnly=&keyword= - 作业列表 */ -router.get('/', validate({ query: listHomeworksQuerySchema }), async (req, res) => { - const user = getAuthUser(req); - const query = validatedQuery<{ - classId?: string; - courseId?: string; - pendingOnly: boolean; - keyword?: string; - }>(req); - sendOk(res, await homeworkService.listHomeworks(user, query), '获取作业列表成功'); -}); +router.get( + '/', + requireStaffPermission('homework:read'), + validate({ query: listHomeworksQuerySchema }), + async (req, res) => { + const user = getAuthUser(req); + const query = validatedQuery<{ + classId?: string; + courseId?: string; + pendingOnly: boolean; + keyword?: string; + }>(req); + sendOk(res, await homeworkService.listHomeworks(user, query), '获取作业列表成功'); + }, +); /** GET /api/homeworks/:id - 作业详情 */ /** @@ -42,17 +52,27 @@ router.get('/', validate({ query: listHomeworksQuerySchema }), async (req, res) * * 必须注册在 /:id 之前,否则 "days" 会被当成作业 id 命中 /:id 路由。 */ -router.get('/days', validate({ query: listHomeworkDaysQuerySchema }), async (req, res) => { - const user = getAuthUser(req); - const query = validatedQuery(req); - sendOk(res, await homeworkService.listHomeworkDays(user, query), '获取作业日历成功'); -}); +router.get( + '/days', + requireStaffPermission('homework:read'), + validate({ query: listHomeworkDaysQuerySchema }), + async (req, res) => { + const user = getAuthUser(req); + const query = validatedQuery(req); + sendOk(res, await homeworkService.listHomeworkDays(user, query), '获取作业日历成功'); + }, +); -router.get('/:id', validate({ params: idParamSchema }), async (req, res) => { - const user = getAuthUser(req); - const { id } = validatedParams<{ id: string }>(req); - sendOk(res, await homeworkService.getHomework(user, id), '获取作业详情成功'); -}); +router.get( + '/:id', + requireStaffPermission('homework:read'), + validate({ params: idParamSchema }), + async (req, res) => { + const user = getAuthUser(req); + const { id } = validatedParams<{ id: string }>(req); + sendOk(res, await homeworkService.getHomework(user, id), '获取作业详情成功'); + }, +); /** * POST /api/homeworks - 发布作业(发布后广播 homework:new) @@ -62,6 +82,7 @@ router.get('/:id', validate({ params: idParamSchema }), async (req, res) => { */ router.post( '/', + requireStaffPermission('homework:write'), validate({ body: createHomeworkSchema }), async (req, res) => { const user = getAuthUser(req); @@ -87,10 +108,7 @@ router.post( validate({ body: bulkCreateHomeworkSchema }), async (req, res) => { const user = getAuthUser(req); - const result = await homeworkService.bulkCreateHomeworks( - user, - req.body as BulkCreateHomeworkInput, - ); + const result = await homeworkService.bulkCreateHomeworks(user, req.body as BulkCreateHomeworkInput); sendCreated(res, result, `批量发布完成:成功 ${result.succeeded} 个班,失败 ${result.failed} 个班`); }, ); @@ -127,6 +145,7 @@ router.delete( /** PATCH /api/homeworks/:id/status - 标记完成 / 取消完成 */ router.patch( '/:id/status', + requireStaffPermission('homework:write'), validate({ params: idParamSchema, body: updateHomeworkStatusSchema }), async (req, res) => { const user = getAuthUser(req); @@ -141,16 +160,22 @@ router.patch( ); /** GET /api/homeworks/:id/submissions - 提交名单(未交名单功能) */ -router.get('/:id/submissions', validate({ params: idParamSchema }), async (req, res) => { - const user = getAuthUser(req); - const { id } = validatedParams<{ id: string }>(req); - const result = await homeworkService.listHomeworkSubmissions(user, id); - sendOk(res, result, `未交 ${result.notSubmitted.length} / ${result.total} 人`); -}); +router.get( + '/:id/submissions', + requireStaffPermission('homework:read'), + validate({ params: idParamSchema }), + async (req, res) => { + const user = getAuthUser(req); + const { id } = validatedParams<{ id: string }>(req); + const result = await homeworkService.listHomeworkSubmissions(user, id); + sendOk(res, result, `未交 ${result.notSubmitted.length} / ${result.total} 人`); + }, +); /** PATCH /api/homeworks/:id/submissions - 勾选未交名单(其余学生一律视为已交) */ router.patch( '/:id/submissions', + requireStaffPermission('homework:write'), validate({ params: idParamSchema, body: updateHomeworkSubmissionsSchema }), async (req, res) => { const user = getAuthUser(req); diff --git a/packages/server/src/modules/homeworks/homeworks.service.ts b/packages/server/src/modules/homeworks/homeworks.service.ts index 36e74f9..2f1e4a5 100644 --- a/packages/server/src/modules/homeworks/homeworks.service.ts +++ b/packages/server/src/modules/homeworks/homeworks.service.ts @@ -1,3 +1,4 @@ +import { assertStaffPermission } from '../../lib/permissions.js'; import { SOCKET_EVENTS, dayKeyLocal, @@ -55,6 +56,7 @@ export async function listHomeworks( user: TokenPayload, options: ListHomeworkOptions, ): Promise { + await assertStaffPermission(user, 'homework:read'); const scope = await resolveClassScope(user, options.classId); const device = isClassDevice(user); // ClassHelper 班级端以全班学生为范围;教师/管理员没有"自己的完成状态" @@ -95,6 +97,7 @@ export async function listHomeworkDays( user: TokenPayload, options: { classId?: string; from?: string; to?: string; days?: number }, ): Promise { + await assertStaffPermission(user, 'homework:read'); const scope = await resolveClassScope(user, options.classId); const today = dayKeyLocal(new Date()); const to = options.to ?? today; @@ -119,6 +122,7 @@ export async function listHomeworkDays( } export async function getHomework(user: TokenPayload, homeworkId: string): Promise { + await assertStaffPermission(user, 'homework:read'); const device = isClassDevice(user); const personalIds = device ? await resolvePersonalIds(user) : []; const homework = await prisma.homework.findUnique({ @@ -152,6 +156,7 @@ export async function getHomework(user: TokenPayload, homeworkId: string): Promi * 见 AGENTS.md 的现行工程约定),班级端录入的作业归属该班班主任。 */ export async function createHomework(user: TokenPayload, input: CreateHomeworkInput): Promise { + await assertStaffPermission(user, 'homework:write'); await assertCanManageSubjectContent(user, input.classId, input.courseId ?? null, '布置作业', { allowClassDevice: true, }); @@ -207,6 +212,7 @@ export async function bulkCreateHomeworks( user: TokenPayload, input: BulkCreateHomeworkRequest, ): Promise { + await assertStaffPermission(user, 'homework:write'); const classIds = normalizeBulkClassIds(input.classIds); const results: BulkPublishClassResult[] = []; @@ -255,6 +261,7 @@ export async function updateHomework( homeworkId: string, input: UpdateHomeworkInput, ): Promise { + await assertStaffPermission(user, 'homework:write'); const current = await prisma.homework.findUnique({ where: { id: homeworkId } }); if (!current) throw ApiError.notFound('作业不存在'); @@ -287,6 +294,7 @@ export async function updateHomework( } export async function deleteHomework(user: TokenPayload, homeworkId: string): Promise { + await assertStaffPermission(user, 'homework:write'); const current = await prisma.homework.findUnique({ where: { id: homeworkId }, include: { course: courseSelect, creator: creatorSelect }, @@ -315,6 +323,7 @@ export async function updateHomeworkStatus( homeworkId: string, input: UpdateHomeworkStatusInput, ): Promise { + await assertStaffPermission(user, 'homework:write'); const homework = await prisma.homework.findUnique({ where: { id: homeworkId } }); if (!homework) throw ApiError.notFound('作业不存在'); @@ -373,7 +382,7 @@ export async function updateHomeworkStatus( async function buildSubmissions(classId: string, homeworkId: string): Promise { const [students, statuses] = await Promise.all([ prisma.student.findMany({ - where: { classId, archivedYearId: null, ...activeStudentWhere() }, + where: { classId, ...activeStudentWhere() }, select: { id: true, name: true, studentNo: true }, orderBy: { studentNo: 'asc' }, }), @@ -408,6 +417,7 @@ export async function listHomeworkSubmissions( user: TokenPayload, homeworkId: string, ): Promise { + await assertStaffPermission(user, 'homework:read'); const homework = await prisma.homework.findUnique({ where: { id: homeworkId }, select: { id: true, classId: true }, @@ -427,6 +437,7 @@ export async function updateHomeworkSubmissions( homeworkId: string, input: UpdateHomeworkSubmissionsInput, ): Promise { + await assertStaffPermission(user, 'homework:write'); const homework = await prisma.homework.findUnique({ where: { id: homeworkId }, select: { id: true, classId: true }, @@ -436,7 +447,7 @@ export async function updateHomeworkSubmissions( assertCanManageSubmissions(user); const students = await prisma.student.findMany({ - where: { classId: homework.classId, archivedYearId: null, ...activeStudentWhere() }, + where: { classId: homework.classId, ...activeStudentWhere() }, select: { id: true }, }); if (students.length === 0) throw ApiError.badRequest('当前班级还没有学生,请先在名单里添加学生'); diff --git a/packages/server/src/modules/imports/class-plan.service.ts b/packages/server/src/modules/imports/class-plan.service.ts index 532a70c..e226953 100644 --- a/packages/server/src/modules/imports/class-plan.service.ts +++ b/packages/server/src/modules/imports/class-plan.service.ts @@ -1,7 +1,7 @@ import { importProfile } from '../class-plans/profile-import.service.js'; import { prisma } from '../../lib/db.js'; import type { TokenPayload } from '../../lib/jwt.js'; -import { assertCanManageSchedule } from '../../lib/access.js'; +import { assertCanImportSchedule } from '../../lib/access.js'; import { weekParityFromDiv } from '@classhelper/shared'; import type { ClassPlanImportInput } from './imports.schemas.js'; @@ -243,7 +243,7 @@ export async function previewClassPlan( user: TokenPayload, input: ClassPlanImportInput, ): Promise { - await assertCanManageSchedule(user, input.classId); + await assertCanImportSchedule(user, input.classId); const parsed = parseClassIslandClassPlan(input.payload); const courses = await prisma.course.findMany({ where: { classId: input.classId }, diff --git a/packages/server/src/modules/imports/imports.module.ts b/packages/server/src/modules/imports/imports.module.ts index f2c214a..9da905c 100644 --- a/packages/server/src/modules/imports/imports.module.ts +++ b/packages/server/src/modules/imports/imports.module.ts @@ -1,7 +1,15 @@ +import { assertPermission } from '../../lib/permissions.js'; +import type { TokenPayload } from '../../lib/jwt.js'; import { Router } from 'express'; import { ApiError, sendOk } from '../../lib/http.js'; import { idParamSchema } from '../../lib/schemas.js'; -import { authenticate, getAuthUser, requirePermission, requireAnyPermission } from '../../middleware/auth.js'; +import { + authenticate, + getAuthUser, + requireStaffPermission, + requirePermission, + requireAnyPermission, +} from '../../middleware/auth.js'; import { validate, validatedBody, validatedParams, validatedQuery } from '../../middleware/validate.js'; import { defineModule } from '../module.types.js'; import { @@ -28,13 +36,21 @@ import { const router = Router(); router.use(authenticate()); -/** 名单类导入(学生/教师账号)只有管理员能做;成绩类按班级权限判定 */ -function assertImportKindAllowed(kind: (typeof IMPORT_KINDS)[number], role: string): void { - if ((kind === 'students' || kind === 'teachers') && role !== 'ADMIN') { - throw ApiError.forbidden( - kind === 'teachers' ? '只有管理员可以录入或导入教师账号' : '只有管理员可以导入学生名单', - ); - } +/** 导入类型分别校验对应导入键,名单还校验管理键。 */ +async function assertImportKindAllowed( + kind: (typeof IMPORT_KINDS)[number], + user: TokenPayload, +): Promise { + const key = { + students: 'import:students', + teachers: 'import:teachers', + grades: 'grade:import', + classTeachers: 'import:class-teachers', + scheduleTimes: 'import:schedule', + } as const; + await assertPermission(user, key[kind]); + if (kind === 'students') await assertPermission(user, 'roster:manage'); + if (kind === 'teachers') await assertPermission(user, 'teacher:manage'); } /** 查询参数里的 kind 收敛为合法值(默认 grades,避免非法值被当成成绩悄悄写入) */ @@ -46,20 +62,14 @@ function resolveImportKind(value: unknown): (typeof IMPORT_KINDS)[number] { /** * GET /api/imports/template?kind=grades|students|teachers&format=csv|xlsx - 下载导入模板 - * 成绩模板:管理员或班主任(空白模板,不含任何业务数据);学生/教师名单模板:仅管理员 + * 成绩模板:管理员或班主任(空白模板,不含任何业务数据);学生/教师名单模板:对应导入与管理键 */ router.get( '/template', - requireAnyPermission( - 'import:students', - 'grade:import', - 'import:teachers', - 'import:schedule', - 'import:class-teachers', - ), - (req, res) => { + requireAnyPermission('import:students', 'grade:import', 'import:teachers', 'import:class-teachers'), + async (req, res) => { const kind = resolveImportKind(req.query.kind); - assertImportKindAllowed(kind, getAuthUser(req).role); + await assertImportKindAllowed(kind, getAuthUser(req)); const format = req.query.format === 'xlsx' ? 'xlsx' : 'csv'; if (format === 'xlsx') { const base64 = buildTemplateXlsx(kind); @@ -78,21 +88,15 @@ router.get( /** * POST /api/imports/table/preview - 上传表格并预览(解析 + 必填列校验 + 建议字段映射) - * 成绩:管理员或班主任;学生/教师名单:仅管理员(与写入权限保持一致) + * 成绩:管理员或班主任;学生/教师名单按对应导入与管理键校验 */ router.post( '/table/preview', - requireAnyPermission( - 'import:students', - 'grade:import', - 'import:teachers', - 'import:schedule', - 'import:class-teachers', - ), + requireAnyPermission('import:students', 'grade:import', 'import:teachers', 'import:class-teachers'), validate({ body: tableFileSchema }), - (req, res) => { + async (req, res) => { const input = validatedBody(req); - assertImportKindAllowed(input.kind, getAuthUser(req).role); + await assertImportKindAllowed(input.kind, getAuthUser(req)); sendOk(res, previewTable(input), '表格解析成功'); }, ); @@ -101,21 +105,15 @@ router.post( * POST /api/imports/table/commit - 执行导入(字段映射 + 写入模式 + 结果统计) * * 路由放行 ADMIN|TEACHER,真正的权限在 service 内按 kind + 班级二次校验: - * 成绩 → 管理员或本班班主任;学生名单 → 仅管理员;科任老师一律 403(需求 7)。 + * 成绩按班级与科目收口;学生名单按管理键、导入键与班主任/全校范围校验。 */ router.post( '/table/commit', - requireAnyPermission( - 'import:students', - 'grade:import', - 'import:teachers', - 'import:schedule', - 'import:class-teachers', - ), + requireAnyPermission('import:students', 'grade:import', 'import:teachers', 'import:class-teachers'), validate({ body: tableCommitSchema }), async (req, res) => { const input = validatedBody(req); - assertImportKindAllowed(input.kind, getAuthUser(req).role); + await assertImportKindAllowed(input.kind, getAuthUser(req)); // 教师名单与班级无关;成绩/学生名单必须带班级,否则拒绝(避免写到"全局") if (input.kind !== 'teachers' && input.kind !== 'classTeachers' && !input.classId) { throw ApiError.badRequest('请先选择要导入的班级'); @@ -133,19 +131,29 @@ router.post( * POST /api/imports/time-layout/preview - 预览 ClassIsland 课表时间 JSON(只解析不写库) * 权限与"课表管理"一致:管理员或本班班主任 */ -router.post('/time-layout/preview', validate({ body: timeLayoutImportSchema }), async (req, res) => { - const input = validatedBody(req); - // 预览也要校验班级权限(与导入一致),避免越权探测 - const { assertCanManageSchedule } = await import('../../lib/access.js'); - await assertCanManageSchedule(getAuthUser(req), input.classId); - sendOk(res, previewClassIslandTimeLayout(input.payload), '解析完成'); -}); +router.post( + '/time-layout/preview', + requirePermission('import:schedule', 'schedule:write'), + validate({ body: timeLayoutImportSchema }), + async (req, res) => { + const input = validatedBody(req); + // 预览也要校验班级权限(与导入一致),避免越权探测 + const { assertCanImportSchedule } = await import('../../lib/access.js'); + await assertCanImportSchedule(getAuthUser(req), input.classId); + sendOk(res, previewClassIslandTimeLayout(input.payload), '解析完成'); + }, +); /** GET /api/imports/time-layout?classId= - 查看该班已导入的时间配置 */ -router.get('/time-layout', validate({ query: timeLayoutQuerySchema }), async (req, res) => { - const { classId } = validatedQuery<{ classId: string }>(req); - sendOk(res, await listTimeLayouts(getAuthUser(req), classId), '获取时间配置成功'); -}); +router.get( + '/time-layout', + requireStaffPermission('schedule:read'), + validate({ query: timeLayoutQuerySchema }), + async (req, res) => { + const { classId } = validatedQuery<{ classId: string }>(req); + sendOk(res, await listTimeLayouts(getAuthUser(req), classId), '获取时间配置成功'); + }, +); /** * POST /api/imports/time-layout - 导入 ClassIsland 时间配置 @@ -153,7 +161,7 @@ router.get('/time-layout', validate({ query: timeLayoutQuerySchema }), async (re */ router.post( '/time-layout', - requirePermission('timetable:manage'), + requirePermission('timetable:manage', 'import:schedule'), validate({ body: timeLayoutImportSchema }), async (req, res) => { const result = await importTimeLayout(getAuthUser(req), validatedBody(req)); @@ -183,28 +191,38 @@ router.delete( * POST /api/imports/class-plan/preview - 预览 ClassIsland 课程表 JSON(只解析不写库) * 真实档案里单双周由 ClassPlan.TimeRule.WeekCountDiv / WeekCountDivTotal 表达。 */ -router.post('/class-plan/preview', validate({ body: classPlanImportSchema }), async (req, res) => { - const result = await previewClassPlan(getAuthUser(req), validatedBody(req)); - sendOk( - res, - result, - `解析出 ${result.entries.length} 节课(${result.subjects.length} 个科目,缺 ${result.missingSubjects.length} 个待建课程)`, - ); -}); +router.post( + '/class-plan/preview', + requirePermission('import:schedule', 'schedule:write'), + validate({ body: classPlanImportSchema }), + async (req, res) => { + const result = await previewClassPlan(getAuthUser(req), validatedBody(req)); + sendOk( + res, + result, + `解析出 ${result.entries.length} 节课(${result.subjects.length} 个科目,缺 ${result.missingSubjects.length} 个待建课程)`, + ); + }, +); /** * POST /api/imports/class-plan - 导入 ClassIsland 课程表(支持单双周) * replace 清空后写入 / merge 按 星期+开始时间+单双周 去重;解析失败返回 400 且不改动现有课表。 */ -router.post('/class-plan', validate({ body: classPlanImportSchema }), async (req, res) => { - const result = await importClassPlan(getAuthUser(req), validatedBody(req)); - sendOk( - res, - result, - `导入成功:新增 ${result.created} 节 / 更新 ${result.updated} 节` + - `${result.createdCourses.length > 0 ? `(自动补建课程:${result.createdCourses.join('、')})` : ''}`, - ); -}); +router.post( + '/class-plan', + requirePermission('import:schedule', 'schedule:write'), + validate({ body: classPlanImportSchema }), + async (req, res) => { + const result = await importClassPlan(getAuthUser(req), validatedBody(req)); + sendOk( + res, + result, + `导入成功:新增 ${result.created} 节 / 更新 ${result.updated} 节` + + `${result.createdCourses.length > 0 ? `(自动补建课程:${result.createdCourses.join('、')})` : ''}`, + ); + }, +); export const importsModule = defineModule({ name: 'imports', diff --git a/packages/server/src/modules/imports/table-import.service.ts b/packages/server/src/modules/imports/table-import.service.ts index e7759b9..ea9e9f0 100644 --- a/packages/server/src/modules/imports/table-import.service.ts +++ b/packages/server/src/modules/imports/table-import.service.ts @@ -1,3 +1,7 @@ +import { assertPermission } from '../../lib/permissions.js'; +import { assertCanManageRoster, assertCanAssignTeachers, assertClassAccess } from '../../lib/access.js'; +import { addStudent } from '../classes/classes.service.js'; +import { updateStudent } from '../students/students.service.js'; import * as XLSX from 'xlsx'; import { formatClassName, normalizeStudentNo, type TableImportKind } from '@classhelper/shared'; import { activeStudentWhere, isActiveStudent } from '../../lib/students.js'; @@ -566,7 +570,7 @@ export async function commitStudents(user: TokenPayload, input: TableCommitInput const existing = await prisma.student.findUnique({ where: { studentNo }, - select: { id: true, classId: true, status: true }, + select: { id: true, classId: true, status: true, archivedYearId: true }, }); if (existing && !isActiveStudent(existing)) @@ -576,40 +580,16 @@ export async function commitStudents(user: TokenPayload, input: TableCommitInput skipped += 1; continue; } - if (existing.classId !== classId) { - // 换班要留痕,与手动调班同一种历史 - const [from, to] = await Promise.all([classNameOf(existing.classId), classNameOf(classId)]); - await prisma.studentClassTransfer.create({ - data: { - studentId: existing.id, - studentNo, - studentName: name, - fromClassId: existing.classId, - fromClassName: from, - toClassId: classId, - toClassName: to, - operatorId: user.sub, - operatorName: user.name, - mode: 'batch', - note: '学生名单导入', - }, - }); - } - await prisma.student.update({ - where: { id: existing.id }, - data: { - name, - classId, - status: 'active', - ...(gender ? { gender } : {}), - ...(guardianPhone ? { guardianPhone } : {}), - }, + if (existing.classId !== classId) + await addStudent(user, classId, { studentNo, name, gender, guardianPhone }); + await updateStudent(user, existing.id, { + name, + ...(gender ? { gender } : {}), + ...(guardianPhone ? { guardianPhone } : {}), }); updated += 1; } else { - await prisma.student.create({ - data: { studentNo, name, classId, gender, guardianPhone }, - }); + await addStudent(user, classId, { studentNo, name, gender, guardianPhone }); inserted += 1; } } @@ -769,6 +749,7 @@ export async function commitClassTeachers( continue; } + await assertImportAllowed(user, targetClass.id, 'classTeachers'); const role = parseClassTeacherRole(roleCell); if (!role) { errors.push({ row: rowNumber, message: `角色「${roleCell}」无法识别,请填「班主任」或「科任」` }); @@ -816,6 +797,8 @@ export async function commitClassTeachers( }); continue; } + await assertPermission(user, 'teacher:manage'); + await assertPermission(user, 'import:teachers'); const created = await prisma.user.create({ data: { username: teacherNo, @@ -898,9 +881,21 @@ async function assertImportAllowed( classId: string | undefined, kind: 'grades' | 'students' | 'teachers' | 'classTeachers' | 'scheduleTimes', ): Promise { + const key = { + students: 'import:students', + teachers: 'import:teachers', + grades: 'grade:import', + classTeachers: 'import:class-teachers', + scheduleTimes: 'import:schedule', + } as const; + await assertPermission(user, key[kind]); + if (classId) await assertClassAccess(user, classId, key[kind]); + if (kind === 'students') return assertCanManageRoster(user, classId); + if (kind === 'teachers') return assertPermission(user, 'teacher:manage'); + if (kind === 'classTeachers') return assertCanAssignTeachers(user, classId); const classRole = classId ? await resolveUserClassRole(user, classId) : 'NONE'; if (canImport(user.role, classRole, kind)) return; - if (kind === 'students' || kind === 'grades') { + if (kind === 'grades') { throw ApiError.forbidden( classRole === 'SUBJECT' || classRole === 'CLASS_DEVICE' ? '科任老师与 ClassHelper 班级端不能导入名单或成绩,请联系班主任或管理员' @@ -910,12 +905,6 @@ async function assertImportAllowed( throw ApiError.forbidden('该导入仅限管理员使用'); } -async function classNameOf(classId: string | null): Promise { - if (!classId) return ''; - const record = await prisma.class.findUnique({ where: { id: classId }, select: { name: true } }); - return record?.name ?? ''; -} - /** 只接受明确的角色值;空值或未识别值保留原角色,避免意外降级。 */ function parseTeacherRole(cell: string): 'TEACHER' | 'ADMIN' | null { const value = cell.trim().toUpperCase(); diff --git a/packages/server/src/modules/imports/time-layout.service.ts b/packages/server/src/modules/imports/time-layout.service.ts index c6810ec..91e29a9 100644 --- a/packages/server/src/modules/imports/time-layout.service.ts +++ b/packages/server/src/modules/imports/time-layout.service.ts @@ -3,7 +3,7 @@ import { prisma } from '../../lib/db.js'; import { ApiError } from '../../lib/http.js'; import type { TokenPayload } from '../../lib/jwt.js'; import { logger } from '../../lib/logger.js'; -import { assertCanManageSchedule } from '../../lib/access.js'; +import { assertCanManageSchedule, assertCanImportSchedule } from '../../lib/access.js'; import type { TimeLayoutImportInput } from './imports.schemas.js'; /** @@ -324,7 +324,7 @@ export async function importTimeLayout( user: TokenPayload, input: TimeLayoutImportInput, ): Promise<{ layout: TimeLayoutDto; mode: string; warnings: string[]; replaced: number; merged: number }> { - await assertCanManageSchedule(user, input.classId); + await assertCanImportSchedule(user, input.classId, 'timetable:manage'); const parsed = parseClassIslandTimeLayout(input.payload); if (parsed.errors.length > 0) { diff --git a/packages/server/src/modules/notifications/notifications.module.ts b/packages/server/src/modules/notifications/notifications.module.ts index 4c952a4..2674c1e 100644 --- a/packages/server/src/modules/notifications/notifications.module.ts +++ b/packages/server/src/modules/notifications/notifications.module.ts @@ -1,7 +1,12 @@ import { Router } from 'express'; import { sendCreated, sendOk } from '../../lib/http.js'; import { idParamSchema } from '../../lib/schemas.js'; -import { authenticate, getAuthUser, requirePermission } from '../../middleware/auth.js'; +import { + authenticate, + getAuthUser, + requireStaffPermission, + requirePermission, +} from '../../middleware/auth.js'; import { validate, validatedParams, validatedQuery } from '../../middleware/validate.js'; import { defineModule } from '../module.types.js'; import { @@ -21,44 +26,69 @@ const router = Router(); router.use(authenticate()); /** GET /api/notifications?classId=&priority=&unreadOnly=&date=&from=&to=&createdBy= - 通知列表 */ -router.get('/', validate({ query: listNotificationsQuerySchema }), async (req, res) => { - const user = getAuthUser(req); - const query = validatedQuery(req); - sendOk(res, await notificationService.listNotifications(user, query), '获取通知列表成功'); -}); +router.get( + '/', + requireStaffPermission('notification:read'), + validate({ query: listNotificationsQuerySchema }), + async (req, res) => { + const user = getAuthUser(req); + const query = validatedQuery(req); + sendOk(res, await notificationService.listNotifications(user, query), '获取通知列表成功'); + }, +); /** GET /api/notifications/days?classId=&from=&to=&createdBy= - 按天归档(日期 + 条数) */ -router.get('/days', validate({ query: notificationArchiveQuerySchema }), async (req, res) => { - const user = getAuthUser(req); - const query = validatedQuery(req); - sendOk(res, await notificationService.listNotificationDays(user, query), '获取通知日期归档成功'); -}); +router.get( + '/days', + requireStaffPermission('notification:read'), + validate({ query: notificationArchiveQuerySchema }), + async (req, res) => { + const user = getAuthUser(req); + const query = validatedQuery(req); + sendOk(res, await notificationService.listNotificationDays(user, query), '获取通知日期归档成功'); + }, +); /** GET /api/notifications/creators?classId=&from=&to=&date= - 按发布人归档 */ -router.get('/creators', validate({ query: notificationArchiveQuerySchema }), async (req, res) => { - const user = getAuthUser(req); - const query = validatedQuery(req); - sendOk(res, await notificationService.listNotificationCreators(user, query), '获取通知发布人归档成功'); -}); +router.get( + '/creators', + requireStaffPermission('notification:read'), + validate({ query: notificationArchiveQuerySchema }), + async (req, res) => { + const user = getAuthUser(req); + const query = validatedQuery(req); + sendOk(res, await notificationService.listNotificationCreators(user, query), '获取通知发布人归档成功'); + }, +); /** GET /api/notifications/unread-count?classId= - 未读数(客户端红点) */ -router.get('/unread-count', validate({ query: notificationScopeQuerySchema }), async (req, res) => { - const user = getAuthUser(req); - const { classId } = validatedQuery<{ classId?: string }>(req); - sendOk(res, { count: await notificationService.unreadCount(user, classId) }, '获取未读数成功'); -}); +router.get( + '/unread-count', + requireStaffPermission('notification:read'), + validate({ query: notificationScopeQuerySchema }), + async (req, res) => { + const user = getAuthUser(req); + const { classId } = validatedQuery<{ classId?: string }>(req); + sendOk(res, { count: await notificationService.unreadCount(user, classId) }, '获取未读数成功'); + }, +); /** POST /api/notifications/read-all - 全部标为已读 */ -router.post('/read-all', validate({ body: notificationScopeQuerySchema }), async (req, res) => { - const user = getAuthUser(req); - const { classId } = (req.body ?? {}) as { classId?: string }; - sendOk(res, await notificationService.markAllAsRead(user, classId), '已全部标为已读'); -}); +router.post( + '/read-all', + requireStaffPermission('notification:read'), + validate({ body: notificationScopeQuerySchema }), + async (req, res) => { + const user = getAuthUser(req); + const { classId } = (req.body ?? {}) as { classId?: string }; + sendOk(res, await notificationService.markAllAsRead(user, classId), '已全部标为已读'); + }, +); /** POST /api/notifications - 发布通知(发布后广播 notification:new) */ router.post( '/', - requirePermission('notification:publish'), + requireStaffPermission('notification:publish'), validate({ body: createNotificationSchema }), async (req, res) => { const user = getAuthUser(req); @@ -70,7 +100,7 @@ router.post( /** POST /api/notifications/bulk - 一次给多个班级发同一条通知(逐班结果,不整批回滚) */ router.post( '/bulk', - requirePermission('notification:publish'), + requireStaffPermission('notification:publish'), validate({ body: bulkCreateNotificationSchema }), async (req, res) => { const user = getAuthUser(req); @@ -83,11 +113,16 @@ router.post( ); /** POST /api/notifications/:id/read - 标记已读 */ -router.post('/:id/read', validate({ params: idParamSchema }), async (req, res) => { - const user = getAuthUser(req); - const { id } = validatedParams<{ id: string }>(req); - sendOk(res, await notificationService.markAsRead(user, id), '已标记为已读'); -}); +router.post( + '/:id/read', + requireStaffPermission('notification:read'), + validate({ params: idParamSchema }), + async (req, res) => { + const user = getAuthUser(req); + const { id } = validatedParams<{ id: string }>(req); + sendOk(res, await notificationService.markAsRead(user, id), '已标记为已读'); + }, +); /** DELETE /api/notifications/:id - 删除通知 */ router.delete( diff --git a/packages/server/src/modules/notifications/notifications.service.ts b/packages/server/src/modules/notifications/notifications.service.ts index 139e129..050fd32 100644 --- a/packages/server/src/modules/notifications/notifications.service.ts +++ b/packages/server/src/modules/notifications/notifications.service.ts @@ -1,3 +1,5 @@ +import { assertStaffPermission, assertPermission } from '../../lib/permissions.js'; +import { resolveContentCreator } from '../../lib/access.js'; import { SOCKET_EVENTS, dayKeyLocal, @@ -71,6 +73,7 @@ export async function listNotifications( user: TokenPayload, options: ListNotificationOptions, ): Promise { + await assertStaffPermission(user, 'notification:read'); const scope = await resolveClassScope(user, options.classId); const isStaff = user.role === 'ADMIN' || user.role === 'TEACHER'; // 班级账号(班级设备)以全班学生为"个人范围",普通学生就是自己 @@ -109,6 +112,7 @@ export async function listNotificationDays( user: TokenPayload, options: { classId?: string; priority?: string; createdBy?: string; from?: string; to?: string }, ): Promise { + await assertStaffPermission(user, 'notification:read'); const scope = await resolveClassScope(user, options.classId); const grouped = await prisma.notification.groupBy({ by: ['assignDate'], @@ -129,6 +133,7 @@ export async function listNotificationCreators( user: TokenPayload, options: { classId?: string; priority?: string; date?: string; from?: string; to?: string }, ): Promise { + await assertStaffPermission(user, 'notification:read'); const scope = await resolveClassScope(user, options.classId); const grouped = await prisma.notification.groupBy({ by: ['createdBy'], @@ -166,6 +171,7 @@ export async function bulkCreateNotifications( user: TokenPayload, input: BulkCreateNotificationRequest, ): Promise { + await assertStaffPermission(user, 'notification:publish'); const classIds = normalizeBulkClassIds(input.classIds); const results: BulkPublishClassResult[] = []; @@ -202,6 +208,7 @@ export async function createNotification( user: TokenPayload, input: CreateNotificationInput, ): Promise { + await assertStaffPermission(user, 'notification:publish'); await assertCanPublishNotification(user, input.classId); const priority = input.priority ?? 'NORMAL'; @@ -227,7 +234,7 @@ export async function createNotification( title: input.title, content: input.content, priority, - createdBy: user.sub, + createdBy: await resolveContentCreator(user, input.classId), // 按天归档用「本地日期」:晚间发布的通知不会被 UTC 切片算到第二天(与作业同口径) assignDate: dayKeyLocal(new Date()), }, @@ -241,6 +248,7 @@ export async function createNotification( } export async function deleteNotification(user: TokenPayload, notificationId: string): Promise { + await assertPermission(user, 'notification:delete'); const current = await prisma.notification.findUnique({ where: { id: notificationId } }); if (!current) throw ApiError.notFound('通知不存在'); await assertCanPublishNotification(user, current.classId); @@ -259,6 +267,7 @@ export async function markAsRead( user: TokenPayload, notificationId: string, ): Promise<{ notificationId: string; readAt: string; marked: number }> { + await assertStaffPermission(user, 'notification:read'); const notification = await prisma.notification.findUnique({ where: { id: notificationId } }); if (!notification) throw ApiError.notFound('通知不存在'); await assertClassAccess(user, notification.classId); @@ -284,6 +293,7 @@ export async function markAsRead( /** 全部标为已读(可按班级限定) */ export async function markAllAsRead(user: TokenPayload, classId?: string): Promise<{ marked: number }> { + await assertStaffPermission(user, 'notification:read'); const scope = await resolveClassScope(user, classId); const personalIds = await resolvePersonalIds(user); const personalWhere = personalIdWhere(personalIds); @@ -314,6 +324,7 @@ export async function markAllAsRead(user: TokenPayload, classId?: string): Promi /** 未读数量(客户端红点) */ export async function unreadCount(user: TokenPayload, classId?: string): Promise { + await assertStaffPermission(user, 'notification:read'); const scope = await resolveClassScope(user, classId); const personalIds = await resolvePersonalIds(user); return prisma.notification.count({ diff --git a/packages/server/src/modules/permissions/permissions.module.ts b/packages/server/src/modules/permissions/permissions.module.ts index 1b09ef5..d467312 100644 --- a/packages/server/src/modules/permissions/permissions.module.ts +++ b/packages/server/src/modules/permissions/permissions.module.ts @@ -1,8 +1,9 @@ +import { z } from 'zod'; import { Router } from 'express'; import { sendCreated, sendOk } from '../../lib/http.js'; import { idParamSchema } from '../../lib/schemas.js'; import { authenticate, getAuthUser, requirePermission } from '../../middleware/auth.js'; -import { validate, validatedBody, validatedParams } from '../../middleware/validate.js'; +import { validate, validatedBody, validatedParams, validatedQuery } from '../../middleware/validate.js'; import { defineModule } from '../module.types.js'; import { assignUserGroupsSchema, @@ -38,6 +39,30 @@ router.get('/groups', requirePermission('permission:manage'), async (_req, res) sendOk(res, await permissionService.listGroups(), '获取权限组成功'); }); +router.get('/scopes', requirePermission('permission:manage'), async (_req, res) => + sendOk(res, await permissionService.scopeOptions()), +); +router.get( + '/audit', + requirePermission('permission:manage'), + validate({ + query: z.object({ + page: z.coerce.number().int().min(1).default(1), + pageSize: z.coerce.number().int().min(1).max(100).default(30), + }), + }), + async (req, res) => { + const { page, pageSize } = validatedQuery<{ page: number; pageSize: number }>(req); + sendOk(res, await permissionService.listAudits(page, pageSize)); + }, +); +router.get( + '/groups/:id/members', + requirePermission('permission:manage'), + validate({ params: idParamSchema }), + async (req, res) => sendOk(res, await permissionService.groupMembers(String(req.params.id))), +); + /** POST /api/permissions/groups - 新建权限组 */ router.post( '/groups', @@ -45,7 +70,7 @@ router.post( validate({ body: createPermissionGroupSchema }), async (req, res) => { const input = validatedBody(req); - sendCreated(res, await permissionService.createGroup(input), '权限组已创建'); + sendCreated(res, await permissionService.createGroup(getAuthUser(req), input), '权限组已创建'); }, ); @@ -57,7 +82,7 @@ router.patch( async (req, res) => { const { id } = validatedParams<{ id: string }>(req); const input = validatedBody(req); - sendOk(res, await permissionService.updateGroup(id, input), '权限组已更新'); + sendOk(res, await permissionService.updateGroup(getAuthUser(req), id, input), '权限组已更新'); }, ); @@ -68,7 +93,7 @@ router.delete( validate({ params: idParamSchema }), async (req, res) => { const { id } = validatedParams<{ id: string }>(req); - await permissionService.deleteGroup(id); + await permissionService.deleteGroup(getAuthUser(req), id); sendOk(res, { id }, '权限组已删除,原成员回到角色默认权限'); }, ); @@ -86,7 +111,7 @@ router.put( async (req, res) => { const { id } = validatedParams<{ id: string }>(req); const input = validatedBody(req); - sendOk(res, await permissionService.assignUserGroups(id, input), '权限组分配已保存'); + sendOk(res, await permissionService.assignUserGroups(getAuthUser(req), id, input), '权限组分配已保存'); }, ); diff --git a/packages/server/src/modules/permissions/permissions.service.ts b/packages/server/src/modules/permissions/permissions.service.ts index e370300..b314bd6 100644 --- a/packages/server/src/modules/permissions/permissions.service.ts +++ b/packages/server/src/modules/permissions/permissions.service.ts @@ -16,7 +16,14 @@ import { import { prisma } from '../../lib/db.js'; import { ApiError } from '../../lib/http.js'; import type { TokenPayload } from '../../lib/jwt.js'; -import { logger } from '../../lib/logger.js'; +import { + parsePermissionScope, + defaultPromotionPlan, + type PermissionScope, + type PromotionPlan, +} from '@classhelper/shared'; +import { assertPermission } from '../../lib/permissions.js'; +import { getSetting } from '../../lib/settings.js'; import { ADMIN_UNREMOVABLE_PERMISSIONS, ensureDefaultGroups, @@ -66,14 +73,6 @@ const MEMBER_SELECT = { permissionGroups: { select: { groupId: true } }, } as const; -type MemberRow = { - id: string; - username: string; - name: string; - role: string; - permissionGroups: { groupId: string }[]; -}; - /** 目录顺序(key → 序号):组的键与"我的权限"都按它排序,前端渲染才稳定 */ const KEY_ORDER = new Map(PERMISSION_KEYS.map((key, index) => [key, index])); @@ -97,6 +96,7 @@ function toGroupDto(row: GroupRow): PermissionGroupDto { id: row.id, scope: row.scope as PermissionGroupDto['scope'], scopeValue: row.scopeValue, + scopeLabel: scopeLabel(row), name: row.name, description: row.description, builtin: row.builtin, @@ -111,6 +111,252 @@ function toGroupDto(row: GroupRow): PermissionGroupDto { }; } +function scopeLabel(group: { scope: string; scopeValue: string }) { + try { + return parsePermissionScope(group.scope as PermissionScope, group.scopeValue).scopeLabel; + } catch { + return '未配置范围(无班级访问权)'; + } +} + +export async function scopeOptions() { + const classes = await prisma.class.findMany({ distinct: ['grade'], select: { grade: true } }); + const raw = await getSetting('term.promotionPlan'); + const plan: PromotionPlan = raw ? JSON.parse(raw) : defaultPromotionPlan(); + const planGrades = plan.tracks.flatMap((track) => track.stages.map((stage) => stage.grade)); + return { + grades: [...new Set([...classes.map((cls) => cls.grade), ...planGrades])].sort(), + planGrades, + divisions: plan.tracks.map((track) => ({ + name: track.name, + grades: track.stages.map((stage) => stage.grade), + })), + }; +} + +async function validateScope(input: { scope?: PermissionScope; scopeValue?: string }) { + try { + return parsePermissionScope( + input.scope ?? 'SELF_CLASS', + input.scopeValue ?? '', + (await scopeOptions()).grades, + ); + } catch (error) { + throw ApiError.badRequest(String(error instanceof Error ? error.message : error)); + } +} + +async function assertGrant( + actor: TokenPayload, + group: { scope: string; defaultRole?: string | null; keys: readonly string[] }, +) { + await assertPermission(actor, 'permission:manage'); + if (actor.role === 'ADMIN') return; + if (group.defaultRole) throw ApiError.forbidden('只有管理员可以管理角色默认组'); + if (group.scope !== 'SELF_CLASS') throw ApiError.forbidden('只有管理员可以管理年级、部或全校范围组'); + const own = await loadEffectivePermissions(actor); + if (group.keys.some((key) => !own.has(key as PermissionKey))) + throw ApiError.forbidden('不能授予或管理超出自己生效权限的组'); +} + +function auditData( + actor: TokenPayload, + action: string, + targetType: string, + targetId: string, + before: unknown, + after: unknown, +) { + return { + actorId: actor.sub, + actorName: actor.name, + action, + targetType, + targetId, + beforeJson: JSON.stringify(before), + afterJson: JSON.stringify(after), + }; +} + +export async function createGroup( + actor: TokenPayload, + input: CreatePermissionGroupRequest, +): Promise { + const scope = await validateScope(input); + const keys = normalizeKeys(input.keys); + await assertGrant(actor, { ...scope, keys }); + if (await prisma.permissionGroup.findUnique({ where: { name: input.name.trim() } })) + throw ApiError.conflict('权限组名称已存在'); + const created = await prisma.$transaction(async (tx) => { + const row = await tx.permissionGroup.create({ + data: { + name: input.name.trim(), + description: input.description?.trim() ?? '', + scope: scope.scope, + scopeValue: scope.scopeValue, + items: { create: keys.map((key) => ({ key })) }, + }, + select: GROUP_SELECT, + }); + await tx.permissionAudit.create({ + data: auditData(actor, 'create', 'group', row.id, null, toGroupDto(row)), + }); + return row; + }); + return toGroupDto(created); +} + +export async function updateGroup( + actor: TokenPayload, + groupId: string, + input: UpdatePermissionGroupRequest, +): Promise { + const current = await prisma.permissionGroup.findUnique({ where: { id: groupId }, select: GROUP_SELECT }); + if (!current) throw ApiError.notFound('权限组不存在'); + const before = toGroupDto(current); + await assertGrant(actor, { ...current, keys: before.keys }); + const scope = await validateScope({ + scope: input.scope ?? before.scope, + scopeValue: input.scopeValue ?? before.scopeValue, + }); + const keys = normalizeKeys(input.keys ?? before.keys); + await assertGrant(actor, { ...scope, defaultRole: current.defaultRole, keys }); + const updated = await prisma.$transaction(async (tx) => { + await tx.permissionGroupItem.deleteMany({ where: { groupId } }); + const row = await tx.permissionGroup.update({ + where: { id: groupId }, + data: { + name: input.name?.trim(), + description: input.description?.trim(), + scope: scope.scope, + scopeValue: scope.scopeValue, + items: { create: keys.map((key) => ({ key })) }, + }, + select: GROUP_SELECT, + }); + await tx.permissionAudit.create({ + data: auditData( + actor, + current.defaultRole ? 'update-default' : 'update', + 'group', + groupId, + before, + toGroupDto(row), + ), + }); + return row; + }); + return toGroupDto(updated); +} + +export async function deleteGroup(actor: TokenPayload, groupId: string) { + const current = await prisma.permissionGroup.findUnique({ where: { id: groupId }, select: GROUP_SELECT }); + if (!current) throw ApiError.notFound('权限组不存在'); + await assertGrant(actor, { ...current, keys: current.items.map((item) => item.key) }); + if (current.builtin) throw ApiError.badRequest('内置权限组不可删除'); + // 删除最后一组会回落默认组;非管理员不能借删除制造额外授权。 + if (actor.role !== 'ADMIN') throw ApiError.forbidden('删除组会改变成员默认权限,仅管理员可执行'); + await prisma.$transaction(async (tx) => { + await tx.permissionGroup.delete({ where: { id: groupId } }); + await tx.permissionAudit.create({ + data: auditData(actor, 'delete', 'group', groupId, toGroupDto(current), null), + }); + }); + return { id: groupId }; +} + +/** 组和成员各预取一次,账号数增长不会增加权限查询次数。 */ +export async function listMembers(): Promise { + await ensureDefaultGroups(); + const [rows, groups] = await Promise.all([ + prisma.user.findMany({ + where: { role: { in: ['ADMIN', 'TEACHER'] } }, + select: MEMBER_SELECT, + orderBy: [{ role: 'asc' }, { name: 'asc' }], + }), + prisma.permissionGroup.findMany({ include: { items: true } }), + ]); + return rows.map((row) => { + const groupIds = row.permissionGroups.map((link) => link.groupId); + const effective = groups.filter((group) => + groupIds.length ? groupIds.includes(group.id) : group.defaultRole === row.role, + ); + const permissions = normalizeKeys([ + ...effective.flatMap((group) => group.items.map((item) => item.key)), + ...(row.role === 'ADMIN' ? ADMIN_UNREMOVABLE_PERMISSIONS : []), + ]); + return { + id: row.id, + username: row.username, + name: row.name, + role: row.role as UserRole, + groupIds, + permissions, + permissionCount: permissions.length, + scopeLabels: [...new Set(effective.map(scopeLabel))], + }; + }); +} + +export async function groupMembers(groupId: string) { + const group = await prisma.permissionGroup.findUnique({ where: { id: groupId } }); + if (!group) throw ApiError.notFound('权限组不存在'); + return (await listMembers()).filter((member) => + member.groupIds.length ? member.groupIds.includes(groupId) : member.role === group.defaultRole, + ); +} + +export async function assignUserGroups( + actor: TokenPayload, + userId: string, + input: AssignUserGroupsRequest, +): Promise { + await assertPermission(actor, 'permission:manage'); + const user = await prisma.user.findUnique({ where: { id: userId }, select: MEMBER_SELECT }); + if (!user || !['ADMIN', 'TEACHER'].includes(user.role)) throw ApiError.notFound('员工账号不存在'); + const ids = [...new Set(input.groupIds)]; + const groups = await prisma.permissionGroup.findMany({ + where: ids.length ? { id: { in: ids } } : { defaultRole: user.role }, + include: { items: true }, + }); + if (ids.length && groups.length !== ids.length) throw ApiError.badRequest('部分权限组不存在'); + for (const group of groups) { + if (group.defaultRole && group.defaultRole !== user.role) + throw ApiError.forbidden('默认组只能分配给同角色账号'); + // 同角色默认组可显式选中,但仍检查范围与键的授权能力。 + await assertGrant(actor, { ...group, defaultRole: null, keys: group.items.map((item) => item.key) }); + } + if (actor.role !== 'ADMIN' && user.role === 'ADMIN') throw ApiError.forbidden('非管理员不能更改管理员分组'); + const before = (await listMembers()).find((member) => member.id === userId)!; + await prisma.$transaction(async (tx) => { + await tx.userPermissionGroup.deleteMany({ where: { userId } }); + if (ids.length) + await tx.userPermissionGroup.createMany({ data: ids.map((groupId) => ({ userId, groupId })) }); + await tx.permissionAudit.create({ + data: auditData(actor, 'assign', 'user', userId, before, { + groupIds: ids, + permissions: normalizeKeys([ + ...groups.flatMap((group) => group.items.map((item) => item.key)), + ...(user.role === 'ADMIN' ? ADMIN_UNREMOVABLE_PERMISSIONS : []), + ]), + }), + }); + }); + return (await listMembers()).find((member) => member.id === userId)!; +} + +export async function listAudits(page = 1, pageSize = 30) { + const [items, total] = await Promise.all([ + prisma.permissionAudit.findMany({ + orderBy: [{ createdAt: 'desc' }, { id: 'desc' }], + skip: (page - 1) * pageSize, + take: pageSize, + }), + prisma.permissionAudit.count(), + ]); + return { items, total, page, pageSize }; +} + /** GET /api/permissions/catalog - 权限键目录(权限页整棵勾选树都按它渲染) */ export async function permissionCatalog(): Promise { await ensureDefaultGroups(); @@ -150,213 +396,13 @@ export async function listGroups(): Promise { select: GROUP_SELECT, orderBy: [{ builtin: 'desc' }, { createdAt: 'asc' }], }); - return (rows as GroupRow[]).map(toGroupDto); -} - -export async function createGroup(input: CreatePermissionGroupRequest): Promise { - const name = input.name.trim(); - const sameName = await prisma.permissionGroup.findUnique({ where: { name }, select: { id: true } }); - if (sameName) throw ApiError.conflict(`已存在名为「${name}」的权限组`); - - const keys = normalizeKeys(input.keys); - const created = await prisma.permissionGroup.create({ - data: { - name, - scope: input.scope ?? 'SELF_CLASS', - scopeValue: input.scopeValue ?? '', - description: input.description?.trim() ?? '', - items: { create: keys.map((key) => ({ key })) }, - }, - select: GROUP_SELECT, - }); - logger.info(`新建权限组「${name}」(${keys.length} 个权限键)`); - return toGroupDto(created as GroupRow); -} - -export async function updateGroup( - groupId: string, - input: UpdatePermissionGroupRequest, -): Promise { - const current = await prisma.permissionGroup.findUnique({ - where: { id: groupId }, - select: { id: true, name: true }, - }); - if (!current) throw ApiError.notFound('权限组不存在'); - - const name = input.name?.trim(); - if (name && name !== current.name) { - const sameName = await prisma.permissionGroup.findUnique({ where: { name }, select: { id: true } }); - if (sameName) throw ApiError.conflict(`已存在名为「${name}」的权限组`); - } - - const keys = input.keys ? normalizeKeys(input.keys) : null; - - await prisma.$transaction(async (tx) => { - await tx.permissionGroup.update({ - where: { id: groupId }, - data: { - ...(name ? { name } : {}), - ...(input.scope ? { scope: input.scope } : {}), - ...(input.scopeValue !== undefined ? { scopeValue: input.scopeValue } : {}), - ...(input.description !== undefined ? { description: input.description.trim() } : {}), - }, - }); - // 键是整体替换:先清后建。漏传某个键就等于"取消勾选",不会留下幽灵权限。 - if (keys) { - await tx.permissionGroupItem.deleteMany({ where: { groupId } }); - if (keys.length > 0) { - await tx.permissionGroupItem.createMany({ data: keys.map((key) => ({ groupId, key })) }); - } - } - }); - - const updated = await prisma.permissionGroup.findUniqueOrThrow({ - where: { id: groupId }, - select: GROUP_SELECT, - }); - logger.info( - `更新权限组「${(updated as GroupRow).name}」(${keys ? `权限键 ${keys.length} 个` : '未改权限键'})`, - ); - return toGroupDto(updated as GroupRow); -} - -export async function deleteGroup(groupId: string): Promise<{ id: string }> { - const current = await prisma.permissionGroup.findUnique({ - where: { id: groupId }, - select: { id: true, name: true, builtin: true, _count: { select: { users: true } } }, - }); - if (!current) throw ApiError.notFound('权限组不存在'); - if (current.builtin) { - throw ApiError.badRequest(`「${current.name}」是内置权限组,不能删除(可以改名或调整里面的权限)`); - } - - // 键与成员关系都是 onDelete: Cascade,直接删组即可; - // 组内成员随后"没有任何分组",会回落角色默认模板(不会变成空权限)。 - await prisma.permissionGroup.delete({ where: { id: groupId } }); - logger.info(`删除权限组「${current.name}」(原成员 ${current._count.users} 个,已回到角色默认权限)`); - return { id: groupId }; -} - -/** 把一行 User(含分组关联)转成权限页用的成员 DTO */ -function toMemberDto(row: MemberRow, keysOfGroup: Map): PermissionMemberDto { - const groupIds = row.permissionGroups.map((link) => link.groupId); - const union = new Set(); - for (const groupId of groupIds) for (const key of keysOfGroup.get(groupId) ?? []) union.add(key); - - const role = row.role as UserRole; - const permissionCount = - role === 'ADMIN' - ? PERMISSION_KEYS.length - : groupIds.length === 0 - ? ROLE_DEFAULT_PERMISSIONS[role].length - : union.size; - - return { - id: row.id, - username: row.username, - name: row.name, - role, - groupIds, - permissionCount, - fixed: false, - }; -} - -/** GET /api/permissions/users - 成员列表(管理员/教师账号;班级端账号不参与权限组) */ -export async function listMembers(): Promise { - const [rows, groups] = await Promise.all([ - prisma.user.findMany({ - where: { role: { in: ['ADMIN', 'TEACHER'] } }, - select: MEMBER_SELECT, - orderBy: [{ role: 'asc' }, { name: 'asc' }], - }), - prisma.permissionGroup.findMany({ select: { id: true, items: { select: { key: true } } } }), - ]); - - const keysOfGroup = new Map( - groups.map((group) => [ - group.id, - group.items - .map((item) => item.key) - .filter(knownKey) - .sort(byCatalogOrder), - ]), - ); - - return Promise.all( - (rows as MemberRow[]).map(async (row) => ({ - ...toMemberDto(row, keysOfGroup), - permissionCount: ( - await loadEffectivePermissions({ - sub: row.id, - role: row.role as UserRole, - username: row.username, - name: row.name, - classId: null, - }) - ).size, - })), - ); -} - -/** PUT /api/permissions/users/:id/groups - 整体替换某账号的权限组 */ -export async function assignUserGroups( - userId: string, - input: AssignUserGroupsRequest, -): Promise { - const user = await prisma.user.findUnique({ where: { id: userId }, select: MEMBER_SELECT }); - if (!user) throw ApiError.notFound('账号不存在'); - - const row = user as MemberRow; - if (row.role === 'CLASS_DEVICE') { - throw ApiError.badRequest( - 'ClassHelper 班级端账号的能力由"班级内角色"决定(见权限矩阵),不参与权限组分配', - ); - } - - const groupIds = [...new Set(input.groupIds)]; - if (groupIds.length > 0) { - const found = await prisma.permissionGroup.count({ where: { id: { in: groupIds } } }); - if (found !== groupIds.length) throw ApiError.badRequest('有权限组不存在,请刷新后重试'); - } - - await prisma.$transaction(async (tx) => { - await tx.userPermissionGroup.deleteMany({ where: { userId } }); - if (groupIds.length > 0) { - await tx.userPermissionGroup.createMany({ - data: groupIds.map((groupId) => ({ userId, groupId })), - }); - } - }); - - logger.info( - `分配权限组:${row.username} → ${groupIds.length > 0 ? `${groupIds.length} 个组` : '角色默认模板'}`, - ); - - const groups = await prisma.permissionGroup.findMany({ - select: { id: true, items: { select: { key: true } } }, - }); - const keysOfGroup = new Map( - groups.map((group) => [ - group.id, - group.items - .map((item) => item.key) - .filter(knownKey) - .sort(byCatalogOrder), - ]), - ); - return { - ...toMemberDto({ ...row, permissionGroups: groupIds.map((groupId) => ({ groupId })) }, keysOfGroup), - permissionCount: ( - await loadEffectivePermissions({ - sub: row.id, - role: row.role as UserRole, - username: row.username, - name: row.name, - classId: null, - }) - ).size, - }; + const members = await listMembers(); + return (rows as GroupRow[]).map((row) => ({ + ...toGroupDto(row), + memberCount: members.filter((m) => + m.groupIds.length ? m.groupIds.includes(row.id) : row.defaultRole === m.role, + ).length, + })); } /** diff --git a/packages/server/src/modules/schedules/schedules.module.ts b/packages/server/src/modules/schedules/schedules.module.ts index 7de893e..a30d38b 100644 --- a/packages/server/src/modules/schedules/schedules.module.ts +++ b/packages/server/src/modules/schedules/schedules.module.ts @@ -7,7 +7,12 @@ import { ApiError } from '../../lib/http.js'; import { Router } from 'express'; import { sendCreated, sendOk } from '../../lib/http.js'; import { idParamSchema } from '../../lib/schemas.js'; -import { authenticate, getAuthUser, requirePermission } from '../../middleware/auth.js'; +import { + authenticate, + getAuthUser, + requireStaffPermission, + requirePermission, +} from '../../middleware/auth.js'; import { validate, validatedParams, validatedQuery } from '../../middleware/validate.js'; import { defineModule } from '../module.types.js'; import { @@ -23,21 +28,30 @@ import * as scheduleService from './schedules.service.js'; const router = Router(); router.use(authenticate()); -router.get('/active-week', validate({ query: gridQuerySchema }), async (req, res) => - sendOk(res, await scheduleService.getActiveScheduleWeek(getAuthUser(req), validatedQuery(req))), +router.get( + '/active-week', + requireStaffPermission('schedule:read'), + validate({ query: gridQuerySchema }), + async (req, res) => + sendOk(res, await scheduleService.getActiveScheduleWeek(getAuthUser(req), validatedQuery(req))), +); +router.get( + '/scheduled', + requireStaffPermission('schedule:read'), + validate({ query: z.object({ classId: z.string().min(1) }) }), + async (req, res) => { + const { classId } = validatedQuery<{ classId: string }>(req); + await assertClassAccess(getAuthUser(req), classId); + sendOk( + res, + await prisma.scheduledClassPlan.findMany({ + where: { classId }, + include: { plan: { select: { name: true, isOverlay: true } } }, + orderBy: { date: 'asc' }, + }), + ); + }, ); -router.get('/scheduled', validate({ query: z.object({ classId: z.string().min(1) }) }), async (req, res) => { - const { classId } = validatedQuery<{ classId: string }>(req); - await assertClassAccess(getAuthUser(req), classId); - sendOk( - res, - await prisma.scheduledClassPlan.findMany({ - where: { classId }, - include: { plan: { select: { name: true, isOverlay: true } } }, - orderBy: { date: 'asc' }, - }), - ); -}); router.delete( '/scheduled/:id', requirePermission('schedule:write'), @@ -93,41 +107,56 @@ router.post( ); /** GET /api/schedules?classId=&week=&dayOfWeek= - 课表列表 */ -router.get('/', validate({ query: listSchedulesQuerySchema }), async (req, res) => { - const user = getAuthUser(req); - const query = validatedQuery<{ classId?: string; week?: number; dayOfWeek?: number }>(req); - sendOk(res, await scheduleService.listSchedules(user, query), '获取课表成功'); -}); +router.get( + '/', + requireStaffPermission('schedule:read'), + validate({ query: listSchedulesQuerySchema }), + async (req, res) => { + const user = getAuthUser(req); + const query = validatedQuery<{ classId?: string; week?: number; dayOfWeek?: number }>(req); + sendOk(res, await scheduleService.listSchedules(user, query), '获取课表成功'); + }, +); /** GET /api/schedules/grid?classId=&week= - 周视图(7 列结构) */ -router.get('/grid', validate({ query: gridQuerySchema }), async (req, res) => { - const user = getAuthUser(req); - const { classId, week } = validatedQuery<{ classId?: string; week?: number }>(req); - sendOk( - res, - await scheduleService.getScheduleGrid(user, { - classId, - // 不传 week 就取「本班口径」的当前周次(由 service 内按班级配置解析) - week, - }), - '获取周视图成功', - ); -}); +router.get( + '/grid', + requireStaffPermission('schedule:read'), + validate({ query: gridQuerySchema }), + async (req, res) => { + const user = getAuthUser(req); + const { classId, week } = validatedQuery<{ classId?: string; week?: number }>(req); + sendOk( + res, + await scheduleService.getScheduleGrid(user, { + classId, + // 不传 week 就取「本班口径」的当前周次(由 service 内按班级配置解析) + week, + }), + '获取周视图成功', + ); + }, +); /** * GET /api/schedules/current?classId=&at= - 班级当前上课状态。 * 客户端灵动岛(上课期间隐藏、下课后弹出)与 Web 端"上课时段发布紧急通知" * 的全屏二次确认都基于它;`at` 为诊断/联调用时间覆盖。 */ -router.get('/current', validate({ query: classStatusQuerySchema }), async (req, res) => { - const user = getAuthUser(req); - const { classId, at } = validatedQuery<{ classId?: string; at?: string }>(req); - const status = await scheduleService.getClassStatus(user, { - classId, - at: at ? new Date(at) : undefined, - }); - sendOk(res, status, status.inClass ? '当前处于上课时间段' : '当前不在上课时间段'); -}); +router.get( + '/current', + requireStaffPermission('schedule:read'), + validate({ query: classStatusQuerySchema }), + async (req, res) => { + const user = getAuthUser(req); + const { classId, at } = validatedQuery<{ classId?: string; at?: string }>(req); + const status = await scheduleService.getClassStatus(user, { + classId, + at: at ? new Date(at) : undefined, + }); + sendOk(res, status, status.inClass ? '当前处于上课时间段' : '当前不在上课时间段'); + }, +); /** POST /api/schedules - 新增课表 */ router.post( diff --git a/packages/server/src/modules/students/students.module.ts b/packages/server/src/modules/students/students.module.ts index 80fdac4..70eaf4b 100644 --- a/packages/server/src/modules/students/students.module.ts +++ b/packages/server/src/modules/students/students.module.ts @@ -1,11 +1,14 @@ import { Router } from 'express'; -import { sendCreated, sendOk } from '../../lib/http.js'; +import { ApiError, sendCreated, sendOk } from '../../lib/http.js'; import { idParamSchema } from '../../lib/schemas.js'; -import { authenticate, getAuthUser, requirePermission, requireAnyPermission } from '../../middleware/auth.js'; +import { authenticate, getAuthUser, requirePermission } from '../../middleware/auth.js'; import { validate, validatedParams, validatedQuery } from '../../middleware/validate.js'; import { defineModule } from '../module.types.js'; import { createStudentSchema, + transferInSchema, + restoreStudentSchema, + deleteStudentQuerySchema, listStudentsQuerySchema, listTransfersQuerySchema, transferOutStudentsSchema, @@ -29,11 +32,11 @@ router.use(authenticate()); * - 学生**没有账号**:这里是"名单记录"的增删改,没有任何密码/角色字段; * - 读:教师/管理员按自己可访问的班级过滤(服务层 `resolveClassScope`); * 教室机器读本班名单请用 `GET /api/classes/:id/students`; - * - 写:管理员或**本班班主任**(服务层逐班判权限),调班与转出仅管理员。 + * - 写:管理员或**本班班主任**(服务层逐班判权限),调班与转出还需覆盖源班和目标班。 */ router.get( '/', - requireAnyPermission('roster:read', 'roster:manage'), + requirePermission('roster:read'), validate({ query: listStudentsQuerySchema }), async (req, res) => { const user = getAuthUser(req); @@ -45,7 +48,7 @@ router.get( /** GET /api/students/transfers - 调班 / 转出历史(管理员看全部,也可按学生或班级过滤) */ router.get( '/transfers', - requireAnyPermission('roster:read', 'roster:manage'), + requirePermission('roster:read'), validate({ query: listTransfersQuerySchema }), async (req, res) => { const user = getAuthUser(req); @@ -62,7 +65,7 @@ router.get( */ router.post( '/transfer', - requirePermission('class:manage'), + requirePermission('roster:manage'), validate({ body: transferStudentsSchema }), async (req, res) => { const user = getAuthUser(req); @@ -74,7 +77,7 @@ router.post( /** POST /api/students/transfer-out - 学生转出(学籍离开本校,不是调班) */ router.post( '/transfer-out', - requirePermission('class:manage'), + requirePermission('roster:manage'), validate({ body: transferOutStudentsSchema }), async (req, res) => { const user = getAuthUser(req); @@ -83,10 +86,24 @@ router.post( }, ); +router.post( + '/transfer-in', + requirePermission('roster:manage'), + validate({ body: transferInSchema }), + async (req, res) => sendCreated(res, await studentService.transferInStudent(getAuthUser(req), req.body)), +); +router.post( + '/:id/restore', + requirePermission('roster:manage'), + validate({ params: idParamSchema, body: restoreStudentSchema }), + async (req, res) => + sendOk(res, await studentService.restoreStudent(getAuthUser(req), String(req.params.id), req.body)), +); + /** GET /api/students/:id/transfers - 单个学生的调班 / 转出历史 */ router.get( '/:id/transfers', - requirePermission('roster:manage'), + requirePermission('roster:read'), validate({ params: idParamSchema }), async (req, res) => { const user = getAuthUser(req); @@ -115,6 +132,11 @@ router.post( router.patch( '/:id', requirePermission('roster:manage'), + (req, _res, next) => { + if (req.body && ('classId' in req.body || 'status' in req.body)) + return next(ApiError.badRequest('班级和状态请使用调班、转出或复学接口修改')); + next(); + }, validate({ params: idParamSchema, body: updateStudentSchema }), async (req, res) => { const user = getAuthUser(req); @@ -131,12 +153,16 @@ router.patch( router.delete( '/:id', requirePermission('roster:manage'), - validate({ params: idParamSchema }), + validate({ params: idParamSchema, query: deleteStudentQuerySchema }), async (req, res) => { const user = getAuthUser(req); const { id } = validatedParams<{ id: string }>(req); - await studentService.deleteStudent(user, id); - sendOk(res, { id }, '学生记录已删除'); + const query = validatedQuery<{ hard?: string; confirm?: string }>(req); + await studentService.deleteStudent(user, id, { + hard: query.hard === 'true', + confirm: query.confirm === 'true', + }); + sendOk(res, { id }, '学生名单操作已完成'); }, ); diff --git a/packages/server/src/modules/students/students.schemas.ts b/packages/server/src/modules/students/students.schemas.ts index 5ae4f2e..c24fa58 100644 --- a/packages/server/src/modules/students/students.schemas.ts +++ b/packages/server/src/modules/students/students.schemas.ts @@ -23,22 +23,44 @@ export const listStudentsQuerySchema = z.object({ // 学生只有「名单」没有「账号」:没有密码字段、没有角色字段,也不能登录。 // 学号是学生的唯一标识与查询键,`studentNoSchema` 负责归一化与格式校验。 -export const createStudentSchema = z.object({ - studentNo: studentNoSchema, - name: studentNameSchema, - classId: z.string().min(1).nullish(), - gender: studentGenderSchema, - guardianPhone: guardianPhoneSchema, - status: studentStatusSchema, -}); +export const createStudentSchema = z + .object({ + studentNo: studentNoSchema, + name: studentNameSchema, + classId: z.string().min(1).nullish(), + gender: studentGenderSchema, + guardianPhone: guardianPhoneSchema, + status: z.enum(['active', 'inactive']).optional(), + }) + .strict(); -export const updateStudentSchema = z.object({ - studentNo: studentNoSchema.optional(), - name: studentNameSchema.optional(), - classId: z.string().min(1).nullish(), - gender: studentGenderSchema, - guardianPhone: guardianPhoneSchema, - status: studentStatusSchema, +export const updateStudentSchema = z + .object({ + studentNo: studentNoSchema.optional(), + name: studentNameSchema.optional(), + gender: studentGenderSchema, + guardianPhone: guardianPhoneSchema, + }) + .strict(); + +export const transferInSchema = z + .object({ + studentId: z.string().min(1).optional(), + studentNo: studentNoSchema.optional(), + name: studentNameSchema.optional(), + toClassId: z.string().min(1), + gender: studentGenderSchema, + guardianPhone: guardianPhoneSchema, + fromSchool: z.string().trim().max(200).optional(), + note: z.string().trim().max(200).optional(), + }) + .strict(); +export const restoreStudentSchema = z + .object({ toClassId: z.string().min(1).optional(), note: z.string().trim().max(200).optional() }) + .strict(); +export const deleteStudentQuerySchema = z.object({ + hard: z.enum(['true', 'false']).optional(), + confirm: z.enum(['true', 'false']).optional(), }); /** @@ -49,17 +71,21 @@ export const updateStudentSchema = z.object({ export const transferStudentsSchema = z.object({ studentIds: z.array(z.string().min(1)).min(1, '请至少选择一名学生').max(500), toClassId: z.string().min(1).nullable(), + allowGradeChange: z.boolean().optional(), note: z.string().trim().max(200).optional(), }); /** 转出(学籍离开本校,不是调班):班级归属保留作历史 */ export const transferOutStudentsSchema = z.object({ + toSchool: z.string().trim().max(200).optional(), studentIds: z.array(z.string().min(1)).min(1, '请至少选择一名学生').max(500), note: z.string().trim().max(200).optional(), }); /** 调班历史查询 */ export const listTransfersQuerySchema = z.object({ + batchId: z.string().optional(), + academicYear: z.coerce.number().int().optional(), studentId: z.string().min(1).optional(), classId: z.string().min(1).optional(), page: z.coerce.number().int().min(1).optional(), diff --git a/packages/server/src/modules/students/students.service.ts b/packages/server/src/modules/students/students.service.ts index 29be19e..759c211 100644 --- a/packages/server/src/modules/students/students.service.ts +++ b/packages/server/src/modules/students/students.service.ts @@ -5,15 +5,17 @@ import { assertClassAccess, classScopeWhere, isAdmin, + getAccessibleClassIds, resolveClassScope, } from '../../lib/access.js'; -import { activeStudentWhere, isActiveStudent } from '../../lib/students.js'; +import { assertStaffPermission, assertPermission } from '../../lib/permissions.js'; +import { changeStudentLifecycle, notifyStudentClasses } from '../../lib/student-lifecycle.js'; +import { activeStudentWhere } from '../../lib/students.js'; import { prisma } from '../../lib/db.js'; +import { hasAllScope } from '../../lib/permission-scope.js'; import { ApiError } from '../../lib/http.js'; import type { TokenPayload } from '../../lib/jwt.js'; import { toStudentDto, toStudentTransferDto } from '../../lib/mappers.js'; -import { SOCKET_EVENTS } from '@classhelper/shared'; -import { emitToClass } from '../../realtime/bus.js'; import type { CreateStudentInput, ListStudentsQuery, @@ -36,13 +38,13 @@ const withClass = { class: { select: { name: true, grade: true } } } as const; /** 学生名单(按班级权限过滤) */ export async function listStudents(user: TokenPayload, options: ListStudentsQuery): Promise { - const scope = await resolveClassScope(user, options.classId); + await assertStaffPermission(user, 'roster:read'); + const scope = await resolveClassScope(user, options.classId, 'roster:read'); const students = await prisma.student.findMany({ where: { ...classScopeWhere(scope), - ...activeStudentWhere(), - ...(options.status ? { AND: [{ status: options.status }, activeStudentWhere()] } : {}), + status: options.status ?? 'active', // 默认不显示已归档(毕业 / 转出)的学生:他们属于归档页,不该混在在读名单里 ...(options.includeArchived ? {} : { archivedYearId: null }), ...(options.keyword @@ -74,181 +76,141 @@ export async function findStudentByNo(studentNo: string, classId?: string): Prom export async function createStudent(user: TokenPayload, input: CreateStudentInput): Promise { await assertCanManageRoster(user, input.classId ?? undefined); await assertStudentNoFree(input.studentNo); - if (input.classId) await assertClassExists(input.classId); - const created = await prisma.student.create({ - data: { + const result = await changeStudentLifecycle(user, { + studentIds: [], + kind: 'class-change', + toClassId: input.classId, + newStudent: { studentNo: input.studentNo, name: input.name, - classId: input.classId ?? null, - gender: input.gender ?? '', - guardianPhone: input.guardianPhone ?? '', - status: input.status ?? 'active', + gender: input.gender, + guardianPhone: input.guardianPhone, + status: input.status, }, + }); + const created = await prisma.student.findUniqueOrThrow({ + where: { id: result.studentId }, include: withClass, }); - - if (created.classId) { - emitToClass(created.classId, SOCKET_EVENTS.classUpdated, { - classId: created.classId, - action: 'updated', - }); - } return toStudentDto(created); } -/** 修改学生信息(含调班;走本接口的单个调班同样会记入调班历史) */ +/** 资料修改不承担状态或班级迁移;旧请求明确报错。 */ export async function updateStudent( user: TokenPayload, studentId: string, input: UpdateStudentInput, ): Promise { + if ('classId' in input || 'status' in input) + throw ApiError.badRequest('班级和状态请使用调班、转出或复学接口修改'); const student = await prisma.student.findUnique({ where: { id: studentId } }); - if (!student || !isActiveStudent(student)) throw ApiError.notFound('学生不存在'); - + if (!student) throw ApiError.notFound('学生不存在'); + if (student.archivedYearId || !['active', 'inactive'].includes(student.status)) + throw ApiError.conflict('历史学生资料只读,请先办理转入或撤销归档'); await assertCanManageRoster(user, student.classId ?? undefined); - if (input.studentNo && input.studentNo !== student.studentNo) { - await assertStudentNoFree(input.studentNo, studentId); - } - - const nextClassId = input.classId === undefined ? student.classId : (input.classId ?? null); - const classChanged = nextClassId !== student.classId; - if (classChanged) await assertCanManageRoster(user, nextClassId ?? undefined); - - const updated = await prisma.student.update({ - where: { id: studentId }, - data: { - ...(input.studentNo ? { studentNo: input.studentNo } : {}), - ...(input.name ? { name: input.name } : {}), - ...(input.gender !== undefined ? { gender: input.gender } : {}), - ...(input.guardianPhone !== undefined ? { guardianPhone: input.guardianPhone } : {}), - ...(input.status !== undefined ? { status: input.status } : {}), - classId: nextClassId, - }, - include: withClass, - }); - - // 换班要留痕(单个调班与批量调班都写同一种历史) - if (classChanged) { - await recordTransfer(user, [updated], student.classId, nextClassId, 'single', ''); - } - + if (input.studentNo) await assertStudentNoFree(input.studentNo, studentId); + const updated = await prisma.student.update({ where: { id: studentId }, data: input, include: withClass }); + notifyStudentClasses([student.classId]); return toStudentDto(updated); } -/** 删除学生记录(级联删除其作业状态、成绩、已读记录与调班历史) */ -export async function deleteStudent(user: TokenPayload, studentId: string): Promise { +/** 默认停用且保留业务;硬删除须管理员确认并证明没有任何历史。 */ +export async function deleteStudent( + user: TokenPayload, + studentId: string, + options: { hard?: boolean; confirm?: boolean } = {}, +): Promise { const student = await prisma.student.findUnique({ where: { id: studentId } }); if (!student) throw ApiError.notFound('学生不存在'); await assertCanManageRoster(user, student.classId ?? undefined); - - await prisma.student.delete({ where: { id: studentId } }); + if (!options.hard) { + await changeStudentLifecycle(user, { + studentIds: [studentId], + kind: 'deactivate', + note: '名单停用,保留全部业务历史', + }); + return; + } + if (!isAdmin(user) || !options.confirm) throw ApiError.forbidden('硬删除需要管理员二次确认'); + await prisma.$transaction(async (tx) => { + const current = await tx.student.findUniqueOrThrow({ + where: { id: studentId }, + include: { + _count: { + select: { grades: true, homeworkStatuses: true, notificationReads: true, transfers: true }, + }, + }, + }); + if ( + current.archivedYearId || + !['active', 'inactive'].includes(current.status) || + Object.values(current._count).some(Boolean) + ) + throw ApiError.conflict('该生有成绩、作业、已读或生命周期历史,不能硬删除;请停用保留记录'); + await tx.student.delete({ where: { id: studentId } }); + }); + notifyStudentClasses([student.classId]); } -/* ---------------------------------------------------------------- 调班 / 转出 */ - -/** - * 调班:单个与批量共用(传一个 id 就是单个)。 - * - * 规则(需求「管理员调班」): - * - 只改学生**当前班级**,学号不变、不创建任何账号; - * - 原班级、新班级、操作人、时间写入 `StudentClassTransfer`,历史可查; - * - 历史作业与成绩保留原归属(它们挂在各自的 `classId` 上,不随学生走)。 - */ -export async function transferStudents( - user: TokenPayload, - input: TransferStudentsInput, -): Promise<{ moved: number; transfers: StudentTransferDto[] }> { - if (!isAdmin(user)) throw ApiError.forbidden('只有管理员可以调班'); - - const students = await prisma.student.findMany({ - where: { id: { in: input.studentIds }, ...activeStudentWhere() }, +export async function transferStudents(user: TokenPayload, input: TransferStudentsInput) { + const result = await changeStudentLifecycle(user, { + ...input, + kind: input.toClassId ? 'class-change' : 'unassign', }); - if (students.length !== input.studentIds.length) { - throw ApiError.badRequest('部分学生不存在,请刷新后重试'); - } - if (input.toClassId) await assertClassExists(input.toClassId); - - const movable = students.filter((item) => item.classId !== input.toClassId); - if (movable.length === 0) { - throw ApiError.badRequest('所选学生已经全部在这个班级里'); - } - - await prisma.$transaction( - movable.map((item) => - prisma.student.update({ where: { id: item.id }, data: { classId: input.toClassId } }), - ), - ); - - const mode = movable.length > 1 ? 'batch' : 'single'; - // 注意传的是**调班前**的 movable(classId 还是原班级):历史里的"原班级"要按每条自己的来 - const transfers = await recordTransfer(user, movable, null, input.toClassId, mode, input.note ?? ''); - - notifyClassChange( - movable.map((item) => item.classId), - input.toClassId, - ); - return { moved: movable.length, transfers }; + return { ...result, moved: result.count }; } - -/** - * 学生转出:学籍离开本校(**不是调班**)。 - * - * 班级归属保留作历史(成绩/作业/已读都还在),状态置为 `transferred`, - * 并写一条 `mode = 'transfer-out'` 的历史,归档页据此统计"当年转出的学生"。 - */ -export async function transferOutStudents( +export async function transferOutStudents(user: TokenPayload, input: TransferOutStudentsInput) { + const result = await changeStudentLifecycle(user, { ...input, kind: 'transfer-out' }); + return { ...result, transferred: result.count }; +} +export async function transferInStudent( user: TokenPayload, - input: TransferOutStudentsInput, -): Promise<{ transferred: number; transfers: StudentTransferDto[] }> { - if (!isAdmin(user)) throw ApiError.forbidden('只有管理员可以办理学生转出'); - - const students = await prisma.student.findMany({ where: { id: { in: input.studentIds } } }); - if (students.length !== input.studentIds.length) { - throw ApiError.badRequest('部分学生不存在,请刷新后重试'); - } - const pending = students.filter((item) => item.status !== 'transferred'); - if (pending.length === 0) throw ApiError.badRequest('所选学生都已经转出'); - - const now = new Date(); - await prisma.$transaction( - pending.map((item) => - prisma.student.update({ - where: { id: item.id }, - data: { status: 'transferred', transferredAt: now, transferNote: input.note ?? '' }, - }), - ), - ); - - // 班级名快照:转出时把学生"离开的那个班"记下来,之后班级改名也不影响历史 - const classNames = await loadClassNames(pending.map((item) => item.classId)); - await prisma.studentClassTransfer.createMany({ - data: pending.map((item) => ({ - studentId: item.id, - studentNo: item.studentNo, - studentName: item.name, - fromClassId: item.classId, - fromClassName: item.classId ? (classNames.get(item.classId) ?? '') : '', - toClassId: null, - toClassName: '转出本校', - operatorId: user.sub, - operatorName: user.name, - mode: 'transfer-out', - note: input.note ?? '', - })), + input: { + studentId?: string; + studentNo?: string; + name?: string; + toClassId: string; + gender?: string; + guardianPhone?: string; + fromSchool?: string; + note?: string; + }, +) { + if (!input.studentId && (!input.studentNo || !input.name)) + throw ApiError.badRequest('新转入学生必须填写学号与姓名'); + const result = await changeStudentLifecycle(user, { + ...input, + kind: 'transfer-in', + studentIds: input.studentId ? [input.studentId] : [], + ...(!input.studentId + ? { + newStudent: { + studentNo: input.studentNo!, + name: input.name!, + gender: input.gender, + guardianPhone: input.guardianPhone, + }, + } + : {}), }); - - notifyClassChange( - pending.map((item) => item.classId), - null, + return toStudentDto( + await prisma.student.findUniqueOrThrow({ where: { id: result.studentId }, include: withClass }), ); - - const created = await prisma.studentClassTransfer.findMany({ - where: { studentId: { in: pending.map((item) => item.id) }, mode: 'transfer-out' }, - orderBy: { createdAt: 'desc' }, - take: pending.length, +} +export async function restoreStudent( + user: TokenPayload, + id: string, + input: { toClassId?: string; note?: string } = {}, +) { + const student = await prisma.student.findUnique({ where: { id } }); + if (!student) throw ApiError.notFound('学生不存在'); + return changeStudentLifecycle(user, { + studentIds: [id], + kind: 'restore', + toClassId: input.toClassId ?? student.classId, + note: input.note ?? '撤回转出或复学,保留原历史', }); - return { transferred: pending.length, transfers: created.map(toStudentTransferDto) }; } /** 调班 / 转出历史(单个学生或某个班级,管理员看全部) */ @@ -256,9 +218,8 @@ export async function listTransfers( user: TokenPayload, options: ListTransfersQuery, ): Promise> { - if (!isAdmin(user) && !options.classId && !options.studentId) { - throw ApiError.forbidden('查看全部调班历史需要管理员权限'); - } + await assertPermission(user, 'roster:read'); + const hasGlobalRosterScope = isAdmin(user) || (await hasAllScope(user, 'roster:read')); if (options.classId) await assertClassAccess(user, options.classId); if (options.studentId) { @@ -268,11 +229,16 @@ export async function listTransfers( }); if (!student) throw ApiError.notFound('学生不存在'); if (student.classId) await assertClassAccess(user, student.classId); - else if (!isAdmin(user)) throw ApiError.forbidden('无权查看未分班学生的调班历史'); + else if (!hasGlobalRosterScope) throw ApiError.forbidden('无权查看未分班学生的调班历史'); } + const accessible = hasGlobalRosterScope ? null : await getAccessibleClassIds(user, 'roster:read'); const where = { - student: activeStudentWhere(), + ...(options.batchId ? { batchId: options.batchId } : {}), + ...(options.academicYear ? { academicYear: options.academicYear } : {}), + ...(accessible + ? { AND: [{ OR: [{ fromClassId: { in: accessible } }, { toClassId: { in: accessible } }] }] } + : {}), ...(options.studentId ? { studentId: options.studentId } : {}), ...(options.classId ? { OR: [{ fromClassId: options.classId }, { toClassId: options.classId }] } : {}), }; @@ -292,76 +258,6 @@ export async function listTransfers( return { items: items.map(toStudentTransferDto), total, page, pageSize }; } -/* ---------------------------------------------------------------- 内部工具 */ - -/** - * 写入调班历史。 - * - * - `fromOverride` 非空时,所有条目都用它作为"原班级"(单个调班场景); - * - 为空时逐条取**该学生自己的 classId** 作为"原班级"(批量调班场景, - * 各人的来源班可能不同)。 - */ -async function recordTransfer( - user: TokenPayload, - students: { id: string; studentNo: string; name: string; classId: string | null }[], - fromOverride: string | null, - toClassId: string | null, - mode: 'single' | 'batch', - note: string, -): Promise { - const fromIds = students.map((item) => fromOverride ?? item.classId); - const classNames = await loadClassNames([...fromIds, toClassId]); - - await prisma.studentClassTransfer.createMany({ - data: students.map((item, index) => { - const fromClassId = fromIds[index] ?? null; - return { - studentId: item.id, - studentNo: item.studentNo, - studentName: item.name, - fromClassId, - fromClassName: fromClassId ? (classNames.get(fromClassId) ?? '') : '', - toClassId, - toClassName: toClassId ? (classNames.get(toClassId) ?? '') : '未分班', - operatorId: user.sub, - operatorName: user.name, - mode, - note, - }; - }), - }); - - const created = await prisma.studentClassTransfer.findMany({ - where: { studentId: { in: students.map((item) => item.id) } }, - orderBy: { createdAt: 'desc' }, - take: students.length, - }); - return created.map(toStudentTransferDto); -} - -async function loadClassNames(classIds: (string | null)[]): Promise> { - const ids = [...new Set(classIds.filter((id): id is string => Boolean(id)))]; - if (ids.length === 0) return new Map(); - const classes = await prisma.class.findMany({ - where: { id: { in: ids } }, - select: { id: true, name: true }, - }); - return new Map(classes.map((item) => [item.id, item.name])); -} - -/** 调班后让相关班级的前端刷新名单(旧班与新班都要收到) */ -function notifyClassChange(fromClassIds: (string | null)[], toClassId: string | null): void { - const targets = new Set([...fromClassIds, toClassId].filter((id): id is string => Boolean(id))); - for (const classId of targets) { - emitToClass(classId, SOCKET_EVENTS.classUpdated, { classId, action: 'updated' }); - } -} - -async function assertClassExists(classId: string): Promise { - const record = await prisma.class.findUnique({ where: { id: classId }, select: { id: true } }); - if (!record) throw ApiError.badRequest('班级不存在'); -} - /** 学号唯一:`Student.studentNo` 上已有唯一约束,这里给出一句人话的 409 */ async function assertStudentNoFree(studentNo: string, exceptId?: string): Promise { const existing = await prisma.student.findUnique({ diff --git a/packages/server/src/modules/term/promotion.service.ts b/packages/server/src/modules/term/promotion.service.ts index 781390c..283538d 100644 --- a/packages/server/src/modules/term/promotion.service.ts +++ b/packages/server/src/modules/term/promotion.service.ts @@ -1,210 +1,462 @@ -import { randomUUID } from 'node:crypto'; +import { createHash, randomUUID } from 'node:crypto'; import fs from 'node:fs/promises'; import path from 'node:path'; import { z } from 'zod'; -import { dayKeyLocal, formatClassName } from '@classhelper/shared'; +import { + dayKeyLocal, + formatClassName, + isDayKey, + defaultPromotionPlan, + SOCKET_EVENTS, + type PromotionPlan, +} from '@classhelper/shared'; import { prisma } from '../../lib/db.js'; +import type { Prisma } from '../../generated/prisma/client.js'; +import type { TokenPayload } from '../../lib/jwt.js'; import { env } from '../../config/env.js'; import { ApiError } from '../../lib/http.js'; +import { assertGlobalPermission } from '../../lib/access.js'; import { exportSnapshot } from '../../lib/snapshot.js'; -import { getSetting, setSetting, SETTING_KEYS } from '../../lib/settings.js'; +import { SETTING_KEYS } from '../../lib/settings.js'; import { getMaxTermWeeks } from '../../lib/term.js'; import { hashPassword } from '../../lib/password.js'; import { logger } from '../../lib/logger.js'; +import { emitToClass } from '../../realtime/bus.js'; import { notifyScheduleChanged } from '../class-plans/class-plans.service.js'; -export const promotionConfigSchema = z.object({ - month: z.number().int().min(1).max(12), - day: z.number().int().min(1).max(31), - hour: z.number().int().min(0).max(23), - classCount: z.number().int().min(1).max(99).default(1), -}); -export async function promotionStatus(now = new Date()) { - const raw = await getSetting(SETTING_KEYS.promotionAt); - let config = { month: 8, day: 1, hour: 0, classCount: 1 }; - if (raw) { - const parsed = promotionConfigSchema.safeParse(JSON.parse(raw)); - if (parsed.success) config = parsed.data; - } +export const promotionPlanSchema = z + .object({ + tracks: z + .array( + z.object({ + id: z.string().regex(/^[a-z][a-z0-9-]{0,19}$/), + name: z.string().trim().min(1).max(40), + entryClassCount: z.number().int().min(0).max(99), + stages: z + .array(z.object({ grade: z.string().trim().min(1).max(32), terminal: z.boolean().optional() })) + .min(1) + .max(20), + }), + ) + .min(1) + .max(20), + termStartDate: z.string().refine(isDayKey, '学期开始日期无效').nullable().default(null), + resetTermWeeks: z.boolean().default(true), + }) + .superRefine((plan, ctx) => { + const grades = new Set(), + tracks = new Set(); + for (const [index, track] of plan.tracks.entries()) { + if (tracks.has(track.id)) + ctx.addIssue({ code: 'custom', path: ['tracks', index, 'id'], message: '轨道标识不能重复' }); + tracks.add(track.id); + for (const [i, stage] of track.stages.entries()) { + if (grades.has(stage.grade)) + ctx.addIssue({ + code: 'custom', + path: ['tracks', index, 'stages', i], + message: '每个年级只能出现一次', + }); + grades.add(stage.grade); + if (Boolean(stage.terminal) !== (i === track.stages.length - 1)) + ctx.addIssue({ + code: 'custom', + path: ['tracks', index, 'stages', i], + message: '仅最后一个学段必须标记为毕业学段', + }); + } + } + }); +export const promotionConfigSchema = z + .object({ + month: z.number().int().min(1).max(12), + day: z.number().int().min(1).max(31), + hour: z.number().int().min(0).max(23), + classCount: z.number().int().min(0).max(99).optional(), + plan: promotionPlanSchema.optional(), + }) + .refine((config) => config.day <= new Date(2000, config.month, 0).getDate(), '该月份没有所选日期'); + +type Db = Prisma.TransactionClient; +async function loadConfig(db: Db = prisma) { + const settings = await db.appSetting.findMany({ + where: { key: { in: [SETTING_KEYS.promotionAt, SETTING_KEYS.promotionPlan] } }, + }); + const raw = settings.find((setting) => setting.key === SETTING_KEYS.promotionAt)?.value; + const config = raw + ? promotionConfigSchema.parse(JSON.parse(raw)) + : { month: 8, day: 1, hour: 0, classCount: 1 }; + const rawPlan = settings.find((setting) => setting.key === SETTING_KEYS.promotionPlan)?.value; + const plan = rawPlan + ? promotionPlanSchema.parse(JSON.parse(rawPlan)) + : defaultPromotionPlan(config.classCount ?? 1); + return { + config: { + month: config.month, + day: config.day, + hour: config.hour, + classCount: plan.tracks[0].entryClassCount, + }, + plan, + }; +} +export async function loadPromotionPlan(): Promise { + return (await loadConfig()).plan; +} +function dueYear(config: { month: number; day: number; hour: number }, now: Date) { const due = new Date( now.getFullYear(), config.month - 1, Math.min(config.day, new Date(now.getFullYear(), config.month, 0).getDate()), config.hour, ); - const year = now < due ? now.getFullYear() - 1 : now.getFullYear(); + return now < due ? now.getFullYear() - 1 : now.getFullYear(); +} +export async function promotionStatus(now = new Date()) { + const { config, plan } = await loadConfig(); + const year = dueYear(config, now); const run = await prisma.promotionRun.findUnique({ where: { year } }); return { config, + plan, year, - pending: !run || run.status !== 'done', - runs: await prisma.promotionRun.findMany({ orderBy: { year: 'desc' }, take: 20 }), + pending: run?.status !== 'done', + runs: await prisma.promotionRun.findMany({ orderBy: { year: 'desc' }, take: 30 }), today: dayKeyLocal(now), }; } -export async function configurePromotion(input: z.infer) { - await setSetting(SETTING_KEYS.promotionAt, JSON.stringify(input)); +export async function configurePromotion(input: z.infer, actor?: TokenPayload) { + if (actor) await assertGlobalPermission(actor, 'promotion:manage'); + const parsed = promotionConfigSchema.parse(input); + const current = await loadConfig(); + const plan = + parsed.plan ?? + (parsed.classCount === undefined + ? current.plan + : { + ...current.plan, + tracks: current.plan.tracks.map((track, i) => + i ? track : { ...track, entryClassCount: parsed.classCount! }, + ), + }); + await prisma.$transaction(async (tx) => { + for (const [key, value] of [ + [ + SETTING_KEYS.promotionAt, + JSON.stringify({ + month: parsed.month, + day: parsed.day, + hour: parsed.hour, + classCount: plan.tracks[0].entryClassCount, + }), + ], + [SETTING_KEYS.promotionPlan, JSON.stringify(plan)], + ]) + await tx.appSetting.upsert({ where: { key }, create: { key, value }, update: { value } }); + }); return promotionStatus(); } -/** 一年一个事务幂等键;快照失败不得升级。启动遗漏只提示补跑,不擅自改演示库。 */ -export async function runPromotion(year: number, dryRun = false) { - const existing = await prisma.promotionRun.findUnique({ where: { year } }); - if (existing?.status === 'done') return existing; - const classes = await prisma.class.findMany({ - where: { archivedYearId: null, grade: { in: ['高一', '高二', '高三'] } }, +/** 只读预览与执行校验共用同一查询,哈希覆盖配置、学生、班级和周次。 */ +async function buildPreview(year: number, db: Db = prisma) { + const { config, plan } = await loadConfig(db); + const [allClasses, students, weeks, archives] = await Promise.all([ + db.class.findMany({ orderBy: { id: 'asc' } }), + db.student.findMany({ + orderBy: { id: 'asc' }, + select: { id: true, classId: true, status: true, archivedYearId: true, updatedAt: true }, + }), + db.termWeek.findMany({ orderBy: { id: 'asc' } }), + db.archivedYear.findMany({ orderBy: { id: 'asc' } }), + ]); + const classes = allClasses.filter((cls) => !cls.archivedYearId); + const blockers: { code: string; message: string; classId?: string }[] = []; + const warnings = ['新建班级只有骨架,没有班主任或任课关系,请随后分配。']; + const grades = new Set(plan.tracks.flatMap((track) => track.stages.map((stage) => stage.grade))); + for (const cls of classes) { + if (!grades.has(cls.grade)) + blockers.push({ + code: 'UNKNOWN_GRADE', + classId: cls.id, + message: `${cls.name} 的年级「${cls.grade}」不在升级计划内`, + }); + if (!cls.enrollmentYear || !cls.classIndex) + blockers.push({ code: 'MISSING_COHORT', classId: cls.id, message: `${cls.name} 缺少入学年份或班号` }); + if ( + students.some( + (student) => student.classId === cls.id && (student.archivedYearId || student.status === 'graduated'), + ) + ) + blockers.push({ + code: 'INCONSISTENT_STUDENT', + classId: cls.id, + message: `${cls.name} 含已归档或已毕业学生,请先处理数据一致性`, + }); + } + const tracks = plan.tracks.map((track, ti) => { + const relevant = classes.filter((cls) => track.stages.some((stage) => stage.grade === cls.grade)); + const seen = new Set(); + const affected = relevant.map((cls) => { + const key = `${cls.enrollmentYear}:${cls.classIndex}`; + if (seen.has(key)) + blockers.push({ + code: 'DUPLICATE_INDEX', + classId: cls.id, + message: `${track.name} 同届存在重复班号:${cls.name}`, + }); + seen.add(key); + const index = track.stages.findIndex((stage) => stage.grade === cls.grade); + const terminal = Boolean(track.stages[index].terminal); + if (terminal && archives.some((archive) => archive.enrollmentYear === cls.enrollmentYear)) + blockers.push({ + code: 'ARCHIVE_EXISTS', + classId: cls.id, + message: `${cls.enrollmentYear} 届已存在档案`, + }); + const members = students.filter((student) => student.classId === cls.id); + return { + id: cls.id, + name: cls.name, + grade: cls.grade, + enrollmentYear: cls.enrollmentYear, + classIndex: cls.classIndex, + nextGrade: terminal ? '毕业归档' : track.stages[index + 1].grade, + terminal, + active: members.filter((student) => student.status === 'active').length, + inactive: members.filter((student) => student.status === 'inactive').length, + transferred: members.filter((student) => student.status === 'transferred').length, + }; + }); + const newClasses = []; + for (let i = 1; i <= track.entryClassCount; i++) { + if ( + relevant.some( + (cls) => cls.enrollmentYear === year && cls.classIndex === i && cls.grade === track.stages[0].grade, + ) + ) + continue; + const code = + ti === 0 ? `N${year}${String(i).padStart(2, '0')}` : `T${ti}${year}${String(i).padStart(2, '0')}`; + if (allClasses.some((cls) => cls.code === code)) + blockers.push({ code: 'CODE_CONFLICT', message: `新班级码 ${code} 已被占用,请先修改冲突班级码` }); + newClasses.push({ + name: formatClassName(year, i), + grade: track.stages[0].grade, + code, + classIndex: i, + enrollmentYear: year, + }); + } + return { id: track.id, name: track.name, classes: affected, newClasses }; }); - const classIds = classes.map((c) => c.id); - const studentCount = await prisma.student.count({ where: { classId: { in: classIds }, status: 'active' } }); - if (dryRun) - return { year, classCount: classes.length, studentCount, status: 'dry-run', snapshotRequired: true }; + warnings.push( + plan.resetTermWeeks + ? `将删除 ${weeks.length} 条全校及班级周次区间。` + : '保留原有周次区间,它们会继续覆盖新学年日期。', + ); + const planHash = createHash('sha256') + .update(JSON.stringify({ year, config, plan, allClasses, students, weeks, archives })) + .digest('hex'); + return { + year, + classCount: classes.length, + studentCount: students.filter( + (student) => student.status === 'active' && classes.some((cls) => cls.id === student.classId), + ).length, + status: 'dry-run', + snapshotRequired: true, + plan, + tracks, + blockers, + warnings, + termWeekDeleteCount: plan.resetTermWeeks ? weeks.length : 0, + termStartDate: plan.termStartDate ?? `${year}-09-01`, + planHash, + }; +} +export async function previewPromotion(year: number) { + return buildPreview(year); +} +function assertExecutable(preview: Awaited>, hash?: string) { + if (hash && hash !== preview.planHash) + throw new ApiError(409, 'CONFLICT', '预览已过期,配置或业务数据已变化,请重新预览', { + code: 'PLAN_CHANGED', + }); + if (preview.blockers.length) + throw new ApiError(409, 'CONFLICT', '升级存在阻断项,请修正后重新预览', preview.blockers); +} +type RunOptions = { + dryRun?: boolean; + actor?: TokenPayload; + planHash?: string; + confirm?: boolean; + catchUp?: boolean; +}; +const running = new Map>>>(); +export async function runPromotion(year: number, dryRunOrOptions: boolean | RunOptions = false) { + const options = typeof dryRunOrOptions === 'boolean' ? { dryRun: dryRunOrOptions } : dryRunOrOptions; + if (options.actor) { + await assertGlobalPermission(options.actor, 'promotion:manage'); + const status = await promotionStatus(); + if (year > status.year) throw ApiError.badRequest('不能执行尚未到期的未来学年'); + if (year < status.year && !(options.actor.role === 'ADMIN' && options.catchUp)) + throw ApiError.forbidden('过去学年仅管理员明确补跑'); + if (!options.dryRun && (!options.confirm || !options.planHash)) + throw ApiError.badRequest('执行必须确认学年并提供本次预览哈希'); + } + if (options.dryRun) return buildPreview(year); + const prior = running.get(year); + if (prior) { + if (options.planHash && (await prisma.promotionRun.findUnique({ where: { year } }))?.status !== 'done') + assertExecutable(await buildPreview(year), options.planHash); + return prior; + } + const task = executePromotion(year, options); + running.set(year, task); + try { + return await task; + } finally { + running.delete(year); + } +} +async function executePromotion(year: number, options: RunOptions) { + const existing = await prisma.promotionRun.findUnique({ where: { year } }); + if (existing?.status === 'done') return { ...existing, alreadyDone: true }; + const preview = await buildPreview(year); + assertExecutable(preview, options.planHash); const dir = path.resolve( env.sqliteFilePath ? path.dirname(env.sqliteFilePath) : path.resolve(env.serverRoot, '..', 'data'), 'promotion-snapshots', ); const snapshotPath = path.join(dir, `${year}-${Date.now()}-${randomUUID()}.json`); + let snapshotSha256 = ''; + const actor = options.actor; try { await fs.mkdir(dir, { recursive: true }); - await fs.writeFile(snapshotPath, JSON.stringify(await exportSnapshot(prisma, env.databaseProvider)), { - flag: 'wx', - }); - } catch (error) { - await recordPromotionFailure(year, snapshotPath, error, classes.length, studentCount); - throw ApiError.internal(`执行前快照失败,未升级任何班级:${String(error)}`); - } - const { config } = await promotionStatus(); - const passwordHash = await hashPassword(env.defaultClassPassword); - const maxWeeks = await getMaxTermWeeks(); - try { + const content = JSON.stringify(await exportSnapshot(prisma, env.databaseProvider)); + await fs.writeFile(snapshotPath, content, { flag: 'wx' }); + snapshotSha256 = createHash('sha256').update(content).digest('hex'); + const passwordHash = await hashPassword(env.defaultClassPassword); + const maxWeeks = await getMaxTermWeeks(); await prisma.$transaction( async (tx) => { - // 唯一键在事务里争抢,重入/多进程都只能有一个提交成功。 - if (existing) await tx.promotionRun.deleteMany({ where: { year, status: { not: 'done' } } }); + const fresh = await buildPreview(year, tx); + assertExecutable(fresh, preview.planHash); + const current = await tx.promotionRun.findUnique({ where: { year } }); + if (current?.status === 'done') return; + await tx.promotionRun.deleteMany({ where: { year, status: { not: 'done' } } }); await tx.promotionRun.create({ - data: { year, status: 'running', snapshotPath, classCount: classes.length, studentCount }, + data: { + year, + status: 'running', + snapshotPath, + snapshotSha256, + operatorId: actor?.sub, + operatorName: actor?.name ?? '学年自动升级', + detailsJson: JSON.stringify(fresh), + classCount: fresh.classCount, + studentCount: fresh.studentCount, + }, }); - const graduating = classes.filter((c) => c.grade === '高三'); - const cohorts = [...new Set(graduating.map((c) => c.enrollmentYear ?? year - 3))]; - for (const enrollmentYear of cohorts) { - const ids = graduating - .filter((c) => (c.enrollmentYear ?? year - 3) === enrollmentYear) - .map((c) => c.id); - const archived = await tx.archivedYear.create({ + const graduating = fresh.tracks.flatMap((track) => track.classes.filter((cls) => cls.terminal)); + for (const enrollmentYear of new Set(graduating.map((cls) => cls.enrollmentYear!))) { + const ids = graduating.filter((cls) => cls.enrollmentYear === enrollmentYear).map((cls) => cls.id); + const counts = graduating.filter((cls) => ids.includes(cls.id)); + const archive = await tx.archivedYear.create({ data: { enrollmentYear, graduationYear: year, name: `${enrollmentYear} 级`, - operatorName: '学年自动升级', + operatorId: actor?.sub, + operatorName: actor?.name ?? '学年自动升级', classCount: ids.length, - studentCount: await tx.student.count({ where: { classId: { in: ids }, status: 'active' } }), + studentCount: counts.reduce((n, cls) => n + cls.active, 0), + transferredCount: counts.reduce((n, cls) => n + cls.transferred, 0), + inactiveCount: counts.reduce((n, cls) => n + cls.inactive, 0), + homeworkCount: await tx.homework.count({ where: { classId: { in: ids } } }), + notificationCount: await tx.notification.count({ where: { classId: { in: ids } } }), }, }); await tx.class.updateMany({ where: { id: { in: ids } }, - data: { archivedYearId: archived.id, archivedAt: new Date() }, + data: { archivedYearId: archive.id, archivedAt: new Date() }, }); await tx.student.updateMany({ where: { classId: { in: ids }, status: 'active' }, - data: { status: 'graduated', archivedYearId: archived.id, archivedAt: new Date() }, + data: { status: 'graduated' }, + }); + await tx.student.updateMany({ + where: { classId: { in: ids } }, + data: { archivedYearId: archive.id, archivedAt: new Date() }, }); } - for (const cls of classes.filter((c) => c.grade !== '高三')) - await tx.class.update({ - where: { id: cls.id }, - data: { - grade: cls.grade === '高一' ? '高二' : '高三', - termStartDate: null, - termWeeks: Math.min(cls.termWeeks, maxWeeks), - }, - }); - for (let i = 1; i <= config.classCount; i++) { - if ( - await tx.class.findFirst({ where: { enrollmentYear: year, classIndex: i, archivedYearId: null } }) - ) - continue; - await tx.class.create({ - data: { - name: formatClassName(year, i), - enrollmentYear: year, - classIndex: i, - grade: '高一', - teacherId: null, - code: `N${year}${String(i).padStart(2, '0')}`, - passwordHash, - termWeeks: Math.min(20, maxWeeks), - }, - }); + for (const track of fresh.tracks) { + for (const cls of track.classes.filter((cls) => !cls.terminal)) + await tx.class.update({ + where: { id: cls.id }, + data: { grade: cls.nextGrade, termStartDate: null }, + }); + for (const cls of track.newClasses) + await tx.class.create({ + data: { + ...cls, + teacherId: null, + passwordHash, + termWeeks: Math.min(20, maxWeeks), + termStartDate: null, + }, + }); } - await tx.termWeek.deleteMany(); + if (fresh.plan.resetTermWeeks) await tx.termWeek.deleteMany(); await tx.appSetting.upsert({ where: { key: SETTING_KEYS.termStartDate }, - create: { key: SETTING_KEYS.termStartDate, value: `${year}-09-01` }, - update: { value: `${year}-09-01` }, + create: { key: SETTING_KEYS.termStartDate, value: fresh.termStartDate }, + update: { value: fresh.termStartDate }, }); await tx.promotionRun.update({ where: { year }, - data: { - status: 'done', - message: '已升级、归档并建立无班主任/任课关系的新班骨架', - finishedAt: new Date(), - }, + data: { status: 'done', message: '已按计划升级、归档并建立新班骨架', finishedAt: new Date() }, }); }, { timeout: 30000 }, ); } catch (error) { - if ((await prisma.promotionRun.findUnique({ where: { year } }))?.status === 'done') - return prisma.promotionRun.findUniqueOrThrow({ where: { year } }); - await recordPromotionFailure(year, snapshotPath, error, classes.length, studentCount); - throw ApiError.internal(`学年升级失败,事务已回滚:${String(error)}`); - } - await notifyScheduleChanged(); - return prisma.promotionRun.findUniqueOrThrow({ where: { year } }); -} - -/** 失败也留可见审计;事务内检查,不能把并发已成功的年份覆盖成失败。 */ -async function recordPromotionFailure( - year: number, - snapshotPath: string, - error: unknown, - classCount: number, - studentCount: number, -) { - await prisma.$transaction(async (tx) => { - const current = await tx.promotionRun.findUnique({ where: { year } }); - if (current?.status === 'done') return; - const data = { + const completed = await prisma.promotionRun.findUnique({ where: { year } }); + if (completed?.status === 'done') return { ...completed, alreadyDone: true }; + const failure = { status: 'failed', snapshotPath, - message: String(error), - classCount, - studentCount, + snapshotSha256, + operatorId: actor?.sub, + operatorName: actor?.name ?? '学年自动升级', + detailsJson: JSON.stringify(preview), + error: String(error), + message: '升级失败,业务事务未提交', finishedAt: new Date(), }; - if (current) await tx.promotionRun.update({ where: { year }, data }); - else await tx.promotionRun.create({ data: { year, ...data } }); - }); + await prisma.promotionRun.upsert({ where: { year }, create: { year, ...failure }, update: failure }); + if (error instanceof ApiError) throw error; + throw ApiError.internal(`学年升级失败,事务已回滚:${String(error)}`); + } + for (const track of preview.tracks) + for (const cls of track.classes) + emitToClass(cls.id, SOCKET_EVENTS.classUpdated, { classId: cls.id, action: 'updated' }); + await notifyScheduleChanged(); + return { ...(await prisma.promotionRun.findUniqueOrThrow({ where: { year } })), alreadyDone: false }; } let timer: ReturnType | null = null; export async function startPromotionScheduler() { if (timer) return; let observed = new Date(); - if ((await promotionStatus(observed)).pending) - logger.warn('有遗漏的学年升级,请管理员在学期周次页查看预览与快照说明后补跑'); + if ((await promotionStatus(observed)).pending) logger.warn('有遗漏的学年升级,请在学年升级页预览后补跑'); timer = setInterval(() => { void (async () => { const now = new Date(); - const { config } = await promotionStatus(now); - const due = new Date( - now.getFullYear(), - config.month - 1, - Math.min(config.day, new Date(now.getFullYear(), config.month, 0).getDate()), - config.hour, - ); - if (observed < due && now >= due) await runPromotion(now.getFullYear()); + const { config } = await loadConfig(); + if (dueYear(config, now) > dueYear(config, observed)) await runPromotion(now.getFullYear()); observed = now; - })().catch((e) => logger.error(`自动升级失败:${String(e)}`)); - }, 30_000); + })().catch((error) => logger.error(`自动升级失败:${String(error)}`)); + }, 30000); timer.unref(); } diff --git a/packages/server/src/modules/term/term.module.ts b/packages/server/src/modules/term/term.module.ts index 7f5318c..cd600ab 100644 --- a/packages/server/src/modules/term/term.module.ts +++ b/packages/server/src/modules/term/term.module.ts @@ -1,12 +1,14 @@ +import { assertGlobalPermission } from '../../lib/access.js'; import { z } from 'zod'; import { promotionStatus, + previewPromotion, promotionConfigSchema, configurePromotion, runPromotion, } from './promotion.service.js'; import { Router } from 'express'; -import { sendOk } from '../../lib/http.js'; +import { ApiError, sendOk } from '../../lib/http.js'; import { authenticate, getAuthUser, requirePermission } from '../../middleware/auth.js'; import { validate, validatedBody, validatedQuery } from '../../middleware/validate.js'; import { defineModule } from '../module.types.js'; @@ -22,22 +24,47 @@ import * as termService from './term.service.js'; const router = Router(); router.use(authenticate()); -router.get('/promotion', requirePermission('system:manage'), async (_req, res) => +router.use('/promotion', async (req, _res, next) => { + await assertGlobalPermission(getAuthUser(req), 'promotion:manage'); + next(); +}); +router.get('/promotion', requirePermission('promotion:manage'), async (_req, res) => sendOk(res, await promotionStatus()), ); router.put( '/promotion', - requirePermission('system:manage'), + requirePermission('promotion:manage'), validate({ body: promotionConfigSchema }), - async (req, res) => sendOk(res, await configurePromotion(req.body)), + async (req, res) => sendOk(res, await configurePromotion(req.body, getAuthUser(req))), +); +router.post( + '/promotion/preview', + requirePermission('promotion:manage'), + validate({ body: z.object({ year: z.number().int().min(2000).max(2100) }) }), + async (req, res) => sendOk(res, await previewPromotion(req.body.year)), ); router.post( '/promotion/run', - requirePermission('system:manage'), + requirePermission('promotion:manage'), + (req, _res, next) => { + if (req.body && 'dryRun' in req.body) + return next( + ApiError.badRequest('请更新页面:预览使用 /term/promotion/preview,执行需提供计划哈希并确认学年'), + ); + next(); + }, validate({ - body: z.object({ year: z.number().int().min(2000).max(2100), dryRun: z.boolean().default(true) }), + body: z + .object({ + year: z.number().int().min(2000).max(2100), + planHash: z.string().length(64), + confirm: z.literal(true), + catchUp: z.boolean().optional(), + }) + .strict(), }), - async (req, res) => sendOk(res, await runPromotion(req.body.year, req.body.dryRun)), + async (req, res) => + sendOk(res, await runPromotion(req.body.year, { ...req.body, actor: getAuthUser(req) })), ); /** diff --git a/packages/server/src/modules/term/term.service.ts b/packages/server/src/modules/term/term.service.ts index e119357..099145e 100644 --- a/packages/server/src/modules/term/term.service.ts +++ b/packages/server/src/modules/term/term.service.ts @@ -4,7 +4,8 @@ import { type HolidaySuggestionDto, type TermWeeksDto, } from '@classhelper/shared'; -import { assertCanManageClasses, assertClassAccess } from '../../lib/access.js'; +import { assertClassAccess } from '../../lib/access.js'; +import { assertPermission } from '../../lib/permissions.js'; import { env } from '../../config/env.js'; import { prisma } from '../../lib/db.js'; import { ApiError } from '../../lib/http.js'; @@ -17,7 +18,7 @@ import { SOCKET_EVENTS } from '@classhelper/shared'; import type { AutoTermWeeksInput, UpdateTermWeeksInput } from './term.schemas.js'; /** - * 学期周次(仅管理员可写)。 + * 学期周次(具有 term:write 的员工可写)。 * * 为什么需要逐周配置:`TERM_START_DATE + 每周七天`的线性推算遇到法定节假日调休、 * 周末补课、错峰开学就会**整体错位**——而课表、作业、成绩都按周次组织,错一周就全错。 @@ -65,7 +66,7 @@ export async function getTermWeeks(user: TokenPayload, classId?: string | null): } /** - * 保存逐周配置(整表覆盖)。仅管理员。 + * 保存逐周配置(整表覆盖)。需要 term:write。 * * 校验:日期格式、起止顺序、**区间不重叠**(重叠会让"当前第几周"出现歧义)。 * 保存后把新的周次口径下发给该班所有在线设备。 @@ -74,7 +75,7 @@ export async function updateTermWeeks( user: TokenPayload, input: UpdateTermWeeksInput, ): Promise { - assertCanManageClasses(user); + await assertPermission(user, 'term:write'); const scope = scopeOf(input.classId); const maximum = input.maxTermWeeks ?? (await getMaxTermWeeks()); @@ -135,13 +136,13 @@ export async function updateTermWeeks( } /** - * 按学期开始日期**一键生成**逐周区间(管理员再逐周微调)。仅管理员。 + * 按学期开始日期**一键生成**逐周区间(管理员再逐周微调)。需要 term:write。 * * 这是配置的起点:先生成一份"标准学期",再把调休那几周改掉, * 比从空白开始一周周填要快得多。 */ export async function autoTermWeeks(user: TokenPayload, input: AutoTermWeeksInput): Promise { - assertCanManageClasses(user); + await assertPermission(user, 'term:write'); const scope = scopeOf(input.classId); const context = await loadTermContext(scope || null); const termStartDate = input.termStartDate || context.termStartDate || env.termStartDate; diff --git a/packages/shared/package.json b/packages/shared/package.json index ce078e1..ba66d1a 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -1,6 +1,6 @@ { "name": "@classhelper/shared", - "version": "1.4.1", + "version": "1.4.2", "private": true, "description": "ClassHelper - 三端共享的类型、常量与工具函数", "type": "module", diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index b87bedc..7025b53 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -10,3 +10,4 @@ export * from './class-plan.js'; export * from './island-layout.js'; export type * from './types.js'; export * from './courseware.js'; +export * from './lifecycle.js'; diff --git a/packages/shared/src/lifecycle.ts b/packages/shared/src/lifecycle.ts new file mode 100644 index 0000000..2544e1f --- /dev/null +++ b/packages/shared/src/lifecycle.ts @@ -0,0 +1,52 @@ +import type { PermissionScope } from './class-plan.js'; + +/** 范围解析供保存、查询与展示共用;旧库空岗位范围会由调用方拒绝访问。 */ +export function parsePermissionScope(scope: PermissionScope, value = '', knownGrades?: readonly string[]) { + if (scope === 'ALL') return { scope, scopeValue: '', grades: [], scopeLabel: '全校' }; + if (scope === 'SELF_CLASS') { + if (value !== '' && value !== 'HEAD') throw new Error('关联班级范围只允许全部关联班级或仅班主任班'); + return { + scope, + scopeValue: value, + grades: [], + scopeLabel: value === 'HEAD' ? '仅班主任班' : '班主任与任课班级', + }; + } + if (scope !== 'GRADE' && scope !== 'DIVISION') throw new Error('未知的数据范围'); + const grades = [...new Set(value.split(/[,,、\s]+/u).filter(Boolean))].sort(); + if (!grades.length) throw new Error('请选择范围中的年级'); + if (scope === 'GRADE' && grades.length !== 1) throw new Error('年级范围只能选择一个年级'); + if (knownGrades && grades.some((grade) => !knownGrades.includes(grade))) + throw new Error('范围包含未知年级,请刷新选项'); + return { + scope, + scopeValue: grades.join(','), + grades, + scopeLabel: `${scope === 'GRADE' ? '年级' : '部'}:${grades.join('、')}`, + }; +} + +export interface PromotionPlan { + tracks: { + id: string; + name: string; + entryClassCount: number; + stages: { grade: string; terminal?: boolean }[]; + }[]; + termStartDate: string | null; + resetTermWeeks: boolean; +} +export function defaultPromotionPlan(classCount = 1): PromotionPlan { + return { + tracks: [ + { + id: 'high', + name: '高中', + entryClassCount: classCount, + stages: [{ grade: '高一' }, { grade: '高二' }, { grade: '高三', terminal: true }], + }, + ], + termStartDate: null, + resetTermWeeks: true, + }; +} diff --git a/packages/shared/src/permissions.ts b/packages/shared/src/permissions.ts index ffbb751..d596cdf 100644 --- a/packages/shared/src/permissions.ts +++ b/packages/shared/src/permissions.ts @@ -200,6 +200,7 @@ export const PERMISSION_KEYS = [ // 学期与归档 'term:read', 'term:write', + 'promotion:manage', 'archive:manage', 'dashboard:read', // 系统与安全 @@ -258,7 +259,7 @@ export const PERMISSION_LABELS: Record = { 'class:read': '查看班级与班级列表', 'class:manage': '创建 / 修改 / 删除班级(含班级账号密码)', 'roster:read': '查看学生名单', - 'roster:manage': '学生名单管理(增删 / 调班 / 导入)', + 'roster:manage': '学生名单管理(资料 / 调班 / 转入转出 / 停用)', 'teacher:read': '查看教师名单', 'teacher:manage': '教师账号管理(新建 / 导入 / 重置密码 / 删除)', 'schedule:read': '查看课表', @@ -285,6 +286,7 @@ export const PERMISSION_LABELS: Record = { 'remote:control': '远控客户端电脑(关机 / 重启 / 注销 / 锁屏)', 'term:read': '查看学期周次', 'term:write': '修改学期周次与开学日期', + 'promotion:manage': '配置、预览与执行学年升级', 'archive:manage': '毕业归档', 'dashboard:read': '查看统计看板', 'database:manage': '数据库快照 / 导出 / 切库', @@ -342,7 +344,10 @@ export const PERMISSION_CATEGORIES: readonly { title: string; keys: readonly Per title: '客户端设备', keys: ['client:read', 'client:manage', 'client:update', 'client:update-push', 'remote:control'], }, - { title: '学期与统计', keys: ['term:read', 'term:write', 'archive:manage', 'dashboard:read'] }, + { + title: '学期与统计', + keys: ['term:read', 'term:write', 'promotion:manage', 'archive:manage', 'dashboard:read'], + }, { title: '系统与安全', keys: [ @@ -399,6 +404,7 @@ export const PERMISSION_DEFAULT_ROLES: Record => api.patch(`${API_PATHS.classes}/${id}/student-grade-query`, { enabled }), /** 学年升级:只改年级,不归档、数据沿用 */ - promote: (classIds: string[], grade: string): Promise<{ promoted: number }> => - api.post(`${API_PATHS.classes}/promote`, { classIds, grade }), + promote: (classIds: string[]): Promise<{ promoted: number }> => + api.post(`${API_PATHS.classes}/promote`, { classIds }), }; /* ------------------------------------------------------------------ 课程 */ @@ -317,11 +316,16 @@ export const studentApi = { api.patch(`${API_PATHS.students}/${id}`, payload), remove: (id: string): Promise<{ id: string }> => api.delete(`${API_PATHS.students}/${id}`), /** 调班:单个与批量同一接口(studentIds 长度 1 即单个);学号不变、历史可查 */ - transfer: (studentIds: string[], toClassId: string | null, note?: string): Promise<{ moved: number }> => - api.post(`${API_PATHS.students}/transfer`, { studentIds, toClassId, note }), + transfer: ( + studentIds: string[], + toClassId: string | null, + note?: string, + allowGradeChange?: boolean, + ): Promise<{ moved: number }> => + api.post(`${API_PATHS.students}/transfer`, { studentIds, toClassId, note, allowGradeChange }), /** 转出(学籍离开本校,不是调班) */ - transferOut: (studentIds: string[], note?: string): Promise<{ transferred: number }> => - api.post(`${API_PATHS.students}/transfer-out`, { studentIds, note }), + transferOut: (studentIds: string[], note?: string, toSchool?: string): Promise<{ transferred: number }> => + api.post(`${API_PATHS.students}/transfer-out`, { studentIds, note, toSchool }), /** 某个学生的调班 / 转出历史 */ transfersOf: (id: string): Promise> => api.get(`${API_PATHS.students}/${id}/transfers`), @@ -380,8 +384,6 @@ export const archiveApi = { /** 归档班级的作业与通知(只读) */ classContent: (id: string, classId: string, limit = 200): Promise => api.get(`${API_PATHS.archives}/${id}/classes/${classId}/content`, { limit }), - create: (payload: CreateArchiveRequest): Promise => - api.post(API_PATHS.archives, payload), /** 撤销归档(误操作的退路,不删任何数据) */ remove: (id: string): Promise<{ id: string }> => api.delete(`${API_PATHS.archives}/${id}`), }; diff --git a/packages/web-admin/src/components/ArchiveIcon.vue b/packages/web-admin/src/components/ArchiveIcon.vue new file mode 100644 index 0000000..88dcb0e --- /dev/null +++ b/packages/web-admin/src/components/ArchiveIcon.vue @@ -0,0 +1,52 @@ + + + + + diff --git a/packages/web-admin/src/components/PromotionSettings.vue b/packages/web-admin/src/components/PromotionSettings.vue index 9198b60..3ca9bce 100644 --- a/packages/web-admin/src/components/PromotionSettings.vue +++ b/packages/web-admin/src/components/PromotionSettings.vue @@ -1,77 +1,297 @@ + diff --git a/packages/web-admin/src/components/TransferredStudents.vue b/packages/web-admin/src/components/TransferredStudents.vue index 883868f..0ff34aa 100644 --- a/packages/web-admin/src/components/TransferredStudents.vue +++ b/packages/web-admin/src/components/TransferredStudents.vue @@ -2,30 +2,77 @@ import { onMounted, ref } from 'vue'; import { ElMessageBox } from 'element-plus'; import { api } from '@/api/http'; +import { classApi } from '@/api'; +import { useAuthStore } from '@/stores/auth'; +import type { ClassDto } from '@classhelper/shared'; +const auth = useAuthStore(); const students = ref< { id: string; studentNo: string; name: string; transferNote: string; class: { name: string } | null }[] >([]); +const classes = ref([]), + target = ref(''), + restoring = ref(''), + visible = ref(false), + saving = ref(false); async function load() { students.value = await api.get('/archives/transferred'); } -async function restore(id: string) { - await ElMessageBox.confirm('撤回后学生恢复到原班,转出历史仍保留。', '撤回转出'); - await api.post(`/archives/transferred/${id}/restore`); - await load(); +async function openRestore(id: string) { + restoring.value = id; + target.value = ''; + classes.value = await classApi.list(); + visible.value = true; +} +async function restore() { + await ElMessageBox.confirm( + '恢复到所选在读班级;原转出历史保留。原班已归档时必须指定其他在读班。', + '撤回转出', + ); + saving.value = true; + try { + await api.post( + `/archives/transferred/${restoring.value}/restore`, + target.value ? { toClassId: target.value } : {}, + ); + visible.value = false; + await load(); + } finally { + saving.value = false; + } } onMounted(load); diff --git a/packages/web-admin/src/config/navigation.ts b/packages/web-admin/src/config/navigation.ts index 08e3007..696deb4 100644 --- a/packages/web-admin/src/config/navigation.ts +++ b/packages/web-admin/src/config/navigation.ts @@ -11,7 +11,7 @@ export const MANAGEMENT_GROUPS = [ id: 'school', title: '班级与人员', icon: 'School', - paths: ['/classes', '/students', '/teachers', '/roster', '/term-weeks', '/archives'], + paths: ['/classes', '/students', '/teachers', '/roster', '/term-weeks', '/promotion', '/archives'], }, { id: 'devices', title: '设备运维', icon: 'Monitor', paths: ['/clients'] }, { diff --git a/packages/web-admin/src/router/index.ts b/packages/web-admin/src/router/index.ts index 0ff28fd..7ac1262 100644 --- a/packages/web-admin/src/router/index.ts +++ b/packages/web-admin/src/router/index.ts @@ -58,13 +58,13 @@ const routes: RouteRecordRaw[] = [ path: 'classes', name: 'classes', component: () => import('@/views/ClassesView.vue'), - meta: { title: '班级管理', icon: 'School', roles: ['ADMIN'] }, + meta: { title: '班级管理', icon: 'School', permission: 'class:read' }, }, { path: 'students', name: 'students', component: () => import('@/views/StudentsView.vue'), - meta: { title: '学生管理', icon: 'User', roles: ['ADMIN'] }, + meta: { title: '学生管理', icon: 'User', permission: 'roster:read' }, }, { path: 'teachers', @@ -77,14 +77,20 @@ const routes: RouteRecordRaw[] = [ name: 'term-weeks', component: () => import('@/views/TermWeeksView.vue'), // 改周次会让全校/某班的课表口径整体变化,与「班级管理」同一档权限 - meta: { title: '学期周次', icon: 'Calendar', roles: ['ADMIN'] }, + meta: { title: '学期周次', icon: 'Calendar', permission: 'term:read' }, + }, + { + path: 'promotion', + name: 'promotion', + component: () => import('@/components/PromotionSettings.vue'), + meta: { title: '学年升级', icon: 'Calendar', permission: 'promotion:manage' }, }, { path: 'archives', name: 'archives', component: () => import('@/views/ArchivesView.vue'), // 毕业归档会改动整届班级与学生的状态,与「班级管理」同一档权限 - meta: { title: '毕业归档', icon: 'FolderOpened', roles: ['ADMIN'] }, + meta: { title: '毕业归档', icon: 'FolderOpened', permission: 'archive:manage' }, }, { path: 'schedules', diff --git a/packages/web-admin/src/views/ArchivesView.vue b/packages/web-admin/src/views/ArchivesView.vue index 8381a54..a5d65f6 100644 --- a/packages/web-admin/src/views/ArchivesView.vue +++ b/packages/web-admin/src/views/ArchivesView.vue @@ -85,7 +85,7 @@ async function openContent(row: ArchivedClassDto): Promise { async function undo(row: ArchivedYearDto): Promise { await ElMessageBox.confirm( `撤销「${row.name}」的毕业归档?\n` + - '该届班级会回到在读列表、毕业学生会恢复为在读;作业、通知与成绩一条都不会被删除。', + '仅撤销归档标记:毕业学生恢复在读,转出和停用状态保留。不回滚年级、不删除新班、不恢复已清空周次;完整回滚需手工恢复快照。', '撤销归档', { type: 'warning', confirmButtonText: '确认撤销' }, ); @@ -154,6 +154,7 @@ onMounted(loadYears); + @@ -187,6 +188,10 @@ onMounted(loadYears); :title="detail.note" /> +

    + 有转入记录 {{ detail.transferInCount }} 人 · 转出 {{ detail.transferred.length }} 人 · 停用 + {{ detail.inactive.length }} 人 +

    @@ -247,6 +252,13 @@ onMounted(loadYears); + + + + + + + diff --git a/packages/web-admin/src/views/ClassesView.vue b/packages/web-admin/src/views/ClassesView.vue index 43835e8..fff1426 100644 --- a/packages/web-admin/src/views/ClassesView.vue +++ b/packages/web-admin/src/views/ClassesView.vue @@ -17,8 +17,10 @@ import { type SubjectTeacherDto, type UpdateClassAccountRequest, type UserDto, + type PromotionPlan, } from '@classhelper/shared'; import { classApi, courseApi, teacherApi, termApi } from '@/api'; +import { api } from '@/api/http'; import { useAuthStore } from '@/stores/auth'; import { useRealtimeStore } from '@/stores/realtime'; import { useResponsive } from '@/composables/useResponsive'; @@ -30,7 +32,7 @@ const realtime = useRealtimeStore(); const { isMobile } = useResponsive(); /** 仅管理员可管理班级(增删改)与人员分配 —— 与后端权限矩阵一致 */ -const isAdmin = computed(() => auth.role === 'ADMIN'); +const canManageClass = computed(() => auth.can('class:manage')); /** 小屏下详情描述改为单列,避免文字被压成竖排 */ const detailColumns = computed(() => (isMobile.value ? 1 : 3)); @@ -39,7 +41,9 @@ const loading = ref(false); const classes = ref([]); const maxTermWeeks = ref(getMaxTermWeeks()); onMounted(async () => { - maxTermWeeks.value = getMaxTermWeeks((await termApi.get()).maxTermWeeks); + if (auth.can('term:read')) maxTermWeeks.value = getMaxTermWeeks((await termApi.get()).maxTermWeeks); + if (auth.can('promotion:manage')) + promotionPlan.value = (await api.get<{ plan: PromotionPlan }>('/term/promotion')).plan; }); const keyword = ref(''); @@ -81,7 +85,7 @@ const previewName = computed(() => formatClassName(form.enrollmentYear, form.cla const staffOptions = ref([]); async function loadStaffOptions(): Promise { - if (!isAdmin.value) return; + if (!canManageClass.value) return; if (staffOptions.value.length > 0) return; staffOptions.value = await teacherApi.list().catch(() => []); } @@ -125,13 +129,12 @@ async function submitForm(): Promise { if (editingId.value) { await classApi.update(editingId.value, { - grade: form.grade, enrollmentYear: form.enrollmentYear, classIndex: form.classIndex, termWeeks: form.termWeeks, }); // 编辑弹窗里也能直接换班主任(仅管理员;班主任决定谁能管这个班的课表与成绩) - if (isAdmin.value && form.headTeacherId) { + if (canManageClass.value && form.headTeacherId) { const target = classes.value.find((item) => item.id === editingId.value); if (target && target.teacher?.id !== form.headTeacherId) { await classApi.assignHeadTeacher(editingId.value, form.headTeacherId); @@ -147,7 +150,7 @@ async function submitForm(): Promise { // 服务端落库恒为默认 20,用户看到的是"设置了没生效"且没有任何提示。 termWeeks: form.termWeeks, ...(form.code.trim() ? { code: form.code.trim().toUpperCase() } : {}), - ...(isAdmin.value && form.headTeacherId ? { teacherId: form.headTeacherId } : {}), + ...(canManageClass.value && form.headTeacherId ? { teacherId: form.headTeacherId } : {}), }); ElMessage.success(`班级 ${previewName.value} 创建成功`); } @@ -417,28 +420,32 @@ async function toggleGradeQuery(enabled: boolean): Promise { } } -const promoteForm = reactive({ grade: '', saving: false }); - -/** 学年升级:只改年级,**不归档**,班级记录/学生名单/学号/作业成绩全部沿用 */ +const promotionPlan = ref(); +const promoteForm = reactive({ saving: false }); +const nextStage = computed(() => { + const track = promotionPlan.value?.tracks.find((track) => + track.stages.some((stage) => stage.grade === detail.value?.grade), + ); + const index = track?.stages.findIndex((stage) => stage.grade === detail.value?.grade) ?? -1; + return track && index >= 0 + ? track.stages[index].terminal + ? '毕业归档(请到学年升级页)' + : track.stages[index + 1].grade + : '请先配置升级计划'; +}); async function promote(): Promise { - if (!detail.value || !promoteForm.grade.trim()) { - ElMessage.warning('请填写升级后的年级,例如「高二」'); - return; - } - const target = promoteForm.grade.trim(); + if (!detail.value) return; await ElMessageBox.confirm( - `把「${detail.value.name}」的年级改为「${target}」?升级只改年级,不归档:` + - '班级记录、学生名单、学号、作业与成绩全部沿用。', - '学年升级', - { type: 'info', confirmButtonText: '确认升级' }, + `将「${detail.value.name}」从 ${detail.value.grade} 升至 ${nextStage.value},保留班级业务数据。`, + '确认升级', + { type: 'warning' }, ); promoteForm.saving = true; try { - await classApi.promote([detail.value.id], target); - ElMessage.success('已升级,数据继续沿用'); - promoteForm.grade = ''; + await classApi.promote([detail.value.id]); await refreshDetail(); await loadClasses(); + ElMessage.success('班级已升至下一学段'); } finally { promoteForm.saving = false; } @@ -470,7 +477,7 @@ onUnmounted(() => { @@ -537,9 +546,11 @@ onUnmounted(() => { @@ -596,12 +607,12 @@ onUnmounted(() => { - + - + { -
    +
    更换班主任 {
    - + 添加学生 共 {{ detail?.students.length ?? 0 }} 人 @@ -688,7 +705,9 @@ onUnmounted(() => { @@ -702,7 +721,9 @@ onUnmounted(() => { @@ -731,7 +752,7 @@ onUnmounted(() => { - + -
    +
    {
    -
    +
    学年升级
    @@ -813,12 +834,15 @@ onUnmounted(() => {
    - - 升级 + 下一学段:{{ nextStage }} + + 升级 +
    diff --git a/packages/web-admin/src/views/PermissionsView.vue b/packages/web-admin/src/views/PermissionsView.vue index f2615de..4ddb133 100644 --- a/packages/web-admin/src/views/PermissionsView.vue +++ b/packages/web-admin/src/views/PermissionsView.vue @@ -24,6 +24,7 @@ import { type UserRole, } from '@classhelper/shared'; import { permissionApi } from '@/api'; +import { api } from '@/api/http'; import { useAuthStore } from '@/stores/auth'; import { useResponsive } from '@/composables/useResponsive'; @@ -31,7 +32,7 @@ const { isMobile } = useResponsive(); const auth = useAuthStore(); /** 当前 Tab:权限组 / 成员 */ -const activeTab = ref<'groups' | 'members'>('groups'); +const activeTab = ref<'groups' | 'members' | 'audit'>('groups'); const loading = ref(false); /** 加载失败的中文提示(非空 = 页面顶部显示错误态 + 重试按钮) */ @@ -95,11 +96,13 @@ async function loadAll(): Promise { const [groupList, memberList, catalogData] = await Promise.all([ permissionApi.listGroups(), permissionApi.listMembers(), - catalog.value ? Promise.resolve(catalog.value) : permissionApi.catalog(), + permissionApi.catalog(), ]); groups.value = groupList; members.value = memberList; catalog.value = catalogData; + scopes.value = await api.get('/permissions/scopes'); + await loadAudits(); await auth.ensurePermissions(true); } catch (error) { loadError.value = errorMessage(error); @@ -171,7 +174,10 @@ function defaultRoleHint(key: PermissionKey): string { * 这里只打一个「高权限」标记提醒配置的人。 */ function isHighPrivilege(key: PermissionKey): boolean { - return catalog.value?.unremovable.includes(key) ?? false; + return ( + (catalog.value?.defaultRoles[key]?.length === 1 && catalog.value.defaultRoles[key][0] === 'ADMIN') || + (catalog.value?.unremovable.includes(key) ?? false) + ); } /** 分类勾选状态:全选 / 全不选 / 选了一部分(决定"全选-清空"按钮的文案) */ @@ -203,6 +209,10 @@ async function submitGroup(): Promise { ElMessage.warning(`已存在名为「${name}」的权限组`); return; } + await ElMessageBox.confirm(groupImpact.value, '权限变更影响预览', { + type: 'warning', + confirmButtonText: '确认保存', + }); groupDialog.saving = true; try { const payload = { @@ -264,9 +274,10 @@ const assignDialog = reactive({ }); function openAssign(member: PermissionMemberDto): void { - if (member.fixed) return; assignDialog.member = member; - assignDialog.groupIds = [...member.groupIds]; + assignDialog.groupIds = member.groupIds.length + ? [...member.groupIds] + : groups.value.filter((g) => g.defaultRole === member.role).map((g) => g.id); assignDialog.visible = true; } @@ -280,6 +291,10 @@ const roleDefaultCount = computed(() => { async function submitAssign(): Promise { const member = assignDialog.member; if (!member) return; + await ElMessageBox.confirm(assignmentImpact.value, '分组生效差异', { + type: 'warning', + confirmButtonText: '确认分配', + }); assignDialog.saving = true; try { // 空数组 = 回到角色默认模板(服务端契约) @@ -293,15 +308,102 @@ async function submitAssign(): Promise { assignDialog.saving = false; } } +const scopes = ref<{ grades: string[]; divisions: { name: string; grades: string[] }[] }>({ + grades: [], + divisions: [], +}); +const selectedGrades = computed({ + get: () => groupDialog.scopeValue.split(',').filter(Boolean), + set: (value: string[]) => { + groupDialog.scopeValue = value.join(','); + }, +}); +const memberSearch = ref(''), + memberRole = ref(''); +const filteredMembers = computed(() => + members.value.filter( + (member) => + (!memberRole.value || member.role === memberRole.value) && + `${member.name} ${member.username}`.includes(memberSearch.value.trim()), + ), +); +type Audit = { + id: string; + actorName: string; + action: string; + targetId: string; + beforeJson: string; + afterJson: string; + createdAt: string; +}; +const auditPage = ref(1), + audit = ref<{ items: Audit[]; total: number }>({ items: [], total: 0 }); +async function loadAudits() { + audit.value = await api.get('/permissions/audit', { page: auditPage.value, pageSize: 30 }); +} +const groupMembersVisible = ref(false), + selectedGroupMembers = ref([]); +async function showMembers(group: PermissionGroupDto) { + selectedGroupMembers.value = await api.get(`/permissions/groups/${group.id}/members`); + groupMembersVisible.value = true; +} +function copyGroup(group: PermissionGroupDto) { + openEditGroup(group); + groupDialog.id = ''; + groupDialog.name = `${group.name}副本`; +} +function effectiveKeys( + member: PermissionMemberDto, + groupIds: string[], + override?: { id: string; keys: PermissionKey[] }, +) { + const selected = groups.value.filter((group) => + groupIds.length ? groupIds.includes(group.id) : group.defaultRole === member.role, + ); + return [ + ...new Set([ + ...selected.flatMap((group) => (group.id === override?.id ? override.keys : group.keys)), + ...(member.role === 'ADMIN' ? (catalog.value?.unremovable ?? []) : []), + ]), + ]; +} +function keyChanges(before: PermissionKey[], after: PermissionKey[]) { + const labels = (keys: PermissionKey[]) => + keys.map((key) => catalog.value?.labels[key] ?? key).join('、') || '无'; + return `新增:${labels(after.filter((key) => !before.includes(key)))};移除:${labels(before.filter((key) => !after.includes(key)))}`; +} +const groupImpact = computed(() => { + const group = groups.value.find((group) => group.id === groupDialog.id); + const affected = members.value.filter((member) => + member.groupIds.length ? member.groupIds.includes(groupDialog.id) : group?.defaultRole === member.role, + ); + const added = new Set(), + removed = new Set(); + for (const member of affected) { + const next = effectiveKeys(member, member.groupIds, { id: groupDialog.id, keys: groupDialog.keys }); + next.filter((key) => !member.permissions.includes(key)).forEach((key) => added.add(key)); + member.permissions.filter((key) => !next.includes(key)).forEach((key) => removed.add(key)); + } + const label = (keys: Set) => + [...keys].map((key) => catalog.value?.labels[key] ?? key).join('、') || '无'; + return `将影响 ${affected.length} 名成员。新增:${label(added)};移除:${label(removed)}。范围按当前选择保存,可能改变成员可访问的班级。`; +}); +const assignmentImpact = computed(() => { + const member = assignDialog.member; + if (!member) return ''; + const next = effectiveKeys(member, assignDialog.groupIds); + const defaults = catalog.value?.roleDefaults[member.role] ?? []; + return `对当前权限:${keyChanges(member.permissions, next)}。对角色默认模板:${keyChanges(defaults, next)}。所选组替代默认模板,如需保留请同时选择同角色默认组。`; +}); + + @@ -361,9 +465,11 @@ async function submitAssign(): Promise { - + + + +