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

@@ -7,18 +7,21 @@ import { defineModule } from '../module.types.js';
import {
createStudentSchema,
listStudentsQuerySchema,
resetPasswordSchema,
updateStudentSchema,
type CreateStudentInput,
type ResetPasswordInput,
type UpdateStudentInput,
} from './students.schemas.js';
import * as studentService from './students.service.js';
const router = Router();
router.use(authenticate());
/** GET /api/students?classId=&keyword= - 学生名单(教师/管理员) */
/**
* GET /api/students?classId=&keyword= - 学生名单
*
* 注意是**仅管理员**(不是教师/管理员):学生名单自带用户名,而学生初始密码是统一的默认值,
* 因此名单属于管理员专属数据;教师查看本班学生请用 `GET /api/classes/:id/students`
* (那条按班级权限校验,班级设备也能读)。
*/
router.get('/', requireRole('ADMIN'), validate({ query: listStudentsQuerySchema }), async (req, res) => {
const user = getAuthUser(req);
const query = validatedQuery<{ classId?: string; keyword?: string }>(req);
@@ -51,20 +54,7 @@ router.patch(
},
);
/** POST /api/students/:id/reset-password - 重置密码 */
router.post(
'/:id/reset-password',
requireRole('ADMIN'),
validate({ params: idParamSchema, body: resetPasswordSchema }),
async (req, res) => {
const user = getAuthUser(req);
const { id } = validatedParams<{ id: string }>(req);
await studentService.resetPassword(user, id, req.body as ResetPasswordInput);
sendOk(res, { id }, '密码已重置');
},
);
/** DELETE /api/students/:id - 删除学生账号 */
/** DELETE /api/students - 删除学生名单记录 */
router.delete('/:id', requireRole('ADMIN'), validate({ params: idParamSchema }), async (req, res) => {
const user = getAuthUser(req);
const { id } = validatedParams<{ id: string }>(req);