feat(class-account): 学生端主体改为班级(班级码 + 班级密码)+ 数据按班级隔离
服务端:
- Class 新增 code(唯一)/passwordHash 与迁移(老数据回填 C00001 形式, 未设密码禁止登录)
- POST /auth/class-login: 班级码 + 班级密码 -> classSession 会话(sub=classId)
- PATCH /classes/:id/class-account: 管理员改班级码/重置密码(唯一性、格式校验)
- auth 中间件/socket 握手支持班级会话(sub 为 classId, 回查 Class 表, 加入 user:{classId} 房间)
- lib/session.ts: 班级设备代全班操作(通知已读/作业完成/成绩总览按全班读写), 普通学生账号行为不变
- 叫人消息同时广播到 user:{classId}, 班级设备立即上岛
- 班级列表/详情按权限返回班级码与密码状态(哈希不外泄)
客户端:
- 登录页改为班级码 + 班级密码(个人学生入口移除, /auth/login 接口保留兼容)
- 作业按钮/成绩标题按班级会话显示(全班标记完成 / 本班成绩)
- 冒烟脚本自动准备班级账号凭据(兼容全新与升级安装)
Web 管理端:
- 班级管理新增「班级账号」列与设置弹窗(改班级码/重置密码/随机生成)
- 新建班级可选自定义班级码, 默认由服务端生成
验证: verify:e2e 125/125(新增班级账号 18 项) verify:web 18/18 verify:desktop 40/40
(login=高一(1)班 classSession=true)
文档: README 新增「学生端班级账号」章节、API/迁移/验收/默认账号更新; 安装包 README 同步
This commit is contained in:
1 parent
8b85079d81
commit
1073436ad6
38 files changed
+1289
-157
No files matched your search
@@ -9,6 +9,7 @@ import {
|
||||
import { prisma } from '../../lib/db.js';
|
||||
import type { TokenPayload } from '../../lib/jwt.js';
|
||||
import { toHomeworkDto, toNotificationDto } from '../../lib/mappers.js';
|
||||
import { personalIdWhere, resolvePersonalIds } from '../../lib/session.js';
|
||||
|
||||
const courseSelect = { select: { id: true, name: true } } as const;
|
||||
const creatorSelect = { select: { id: true, name: true, username: true } } as const;
|
||||
@@ -25,6 +26,9 @@ export async function getDashboardSummary(user: TokenPayload): Promise<Dashboard
|
||||
const where = classScopeWhere(scope);
|
||||
const classWhere = classScopeIdFilter(scope);
|
||||
const now = new Date();
|
||||
// 班级账号(班级设备)以全班学生为范围;普通学生即自己
|
||||
const personalIds = await resolvePersonalIds(user);
|
||||
const personalWhere = personalIdWhere(personalIds);
|
||||
|
||||
const [
|
||||
classCount,
|
||||
@@ -47,15 +51,15 @@ export async function getDashboardSummary(user: TokenPayload): Promise<Dashboard
|
||||
prisma.homework.count({ where }),
|
||||
prisma.notification.count({ where }),
|
||||
prisma.grade.count({ where }),
|
||||
prisma.notification.count({ where: { ...where, reads: { none: { userId: user.sub } } } }),
|
||||
prisma.notification.count({ where: { ...where, reads: { none: personalWhere } } }),
|
||||
student
|
||||
? prisma.homework.count({
|
||||
where: { ...where, statuses: { none: { userId: user.sub, completed: true } } },
|
||||
where: { ...where, statuses: { none: { ...personalWhere, completed: true } } },
|
||||
})
|
||||
: prisma.homework.count({ where: { ...where, dueAt: { gte: now } } }),
|
||||
prisma.notification.findMany({
|
||||
where,
|
||||
include: { creator: creatorSelect, reads: { where: { userId: user.sub } } },
|
||||
include: { creator: creatorSelect, reads: { where: personalWhere } },
|
||||
orderBy: { createdAt: 'desc' },
|
||||
take: 5,
|
||||
}),
|
||||
@@ -64,7 +68,7 @@ export async function getDashboardSummary(user: TokenPayload): Promise<Dashboard
|
||||
include: {
|
||||
course: courseSelect,
|
||||
creator: creatorSelect,
|
||||
statuses: student ? { where: { userId: user.sub } } : true,
|
||||
statuses: student ? { where: personalWhere } : true,
|
||||
},
|
||||
orderBy: { createdAt: 'desc' },
|
||||
take: 5,
|
||||
@@ -74,7 +78,7 @@ export async function getDashboardSummary(user: TokenPayload): Promise<Dashboard
|
||||
include: {
|
||||
course: courseSelect,
|
||||
creator: creatorSelect,
|
||||
statuses: student ? { where: { userId: user.sub } } : true,
|
||||
statuses: student ? { where: personalWhere } : true,
|
||||
},
|
||||
orderBy: { dueAt: 'asc' },
|
||||
take: 5,
|
||||
@@ -92,13 +96,13 @@ export async function getDashboardSummary(user: TokenPayload): Promise<Dashboard
|
||||
unreadNotificationCount,
|
||||
pendingHomeworkCount,
|
||||
recentNotifications: recentNotifications.map((item): NotificationDto =>
|
||||
toNotificationDto(item, { userId: user.sub }),
|
||||
toNotificationDto(item, { userIds: personalIds }),
|
||||
),
|
||||
recentHomeworks: recentHomeworks.map((item): HomeworkDto =>
|
||||
toHomeworkDto(item, { userId: student ? user.sub : null, withStatus: !student }),
|
||||
toHomeworkDto(item, { userIds: student ? personalIds : [], withStatus: !student }),
|
||||
),
|
||||
upcomingDeadlines: upcoming.map((item): HomeworkDto =>
|
||||
toHomeworkDto(item, { userId: student ? user.sub : null, withStatus: !student }),
|
||||
toHomeworkDto(item, { userIds: student ? personalIds : [], withStatus: !student }),
|
||||
),
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user