feat(students,teachers,classes,auth,web): 学生个人账号彻底清理,学生只是名单记录

学生不再有登录能力:User.passwordHash 以空串占位,role=STUDENT 在密码校验之前
一律 403 并提示改用班级登录(否则「密码错 401 / 密码对但已停用 403」的差异会把
学生账号密码变成可离线验证的预言机)。成绩、未交名单、叫人、已读全部按名单记。

配套改动:
- 删除 POST /students/:id/reset-password(调用返回 404,e2e 有负断言);
- 新建学生不设密码,StudentsView 移除密码输入与「重置密码」入口,
  列表标识仍自动生成为学号式(student01…);
- class-account.ts / auth.service.ts 收紧班级会话语义;
- 经管「修改密码」入口收敛到 Web 端:教师改本人密码走顶栏下拉,
  班级密码由管理员在班级管理里维护,教师可用 POST /teachers/:id/reset-password 设初始密码。
This commit is contained in:
LoveChengke committed 2026-10-02 00:25:04 +08:00
1 parent 7ea0e0f7c9
commit d27f9a4b9e
11 files changed
+121 -104

No files matched your search

+4 -1
View File
@@ -1,3 +1,4 @@
import { randomInt } from 'node:crypto';
import { prisma } from './db.js';
import { ApiError } from './http.js';
import { hashPassword, verifyPassword } from './password.js';
@@ -30,7 +31,9 @@ export async function generateClassCode(prefix = ''): Promise<string> {
const length = Math.max(4, 6 - prefix.length);
let body = '';
for (let index = 0; index < length; index += 1) {
body += CODE_ALPHABET[Math.floor(Math.random() * CODE_ALPHABET.length)];
// 用 CSPRNG 而不是 Math.random():班级码会作为学生端登录的账号名打印/张贴,
// 用可预测的 PRNG 生成等于把"半个凭证"送给猜码的人(randomInt 无取模偏差)
body += CODE_ALPHABET[randomInt(0, CODE_ALPHABET.length)];
}
const code = `${prefix}${body}`.toUpperCase();
const exists = await prisma.class.findUnique({ where: { code }, select: { id: true } });