feat(meeting): 添加学生列表排序功能

- 实现学生列表拖拽排序并保存到 Redis
- 添加 WebSocket 消息类型支持学生排序变更
- 在教师端界面添加学生列表排序按钮和对话框
- 修改数据传输方式以支持排序信息同步
- 更新 API 接口以传递房间 ID 获取排序数据
- 添加 Redis 存储学生排序顺序的功能
This commit is contained in:
2026-03-14 18:11:31 +08:00
parent ff6dde5f4d
commit 3686bf1c1f
12 changed files with 530 additions and 63 deletions

View File

@ -59,7 +59,7 @@ export class MeetingRedisService {
* 用途:存储房间的全局状态信息
* 数据结构Hash
* Key 格式meeting:room:{roomId}
* Hash 字段
* Hash 字段:
* - classStatus: 课堂状态not_started | in_class | finished
* - speakerUid: 当前主讲人短 UID可选
* - teacherUid: 老师短 UID可选
@ -68,6 +68,17 @@ export class MeetingRedisService {
* 过期时间24 小时
*/
ROOM_STATE: 'meeting:room:',
/**
* 学生排序 Key
* 用途:存储学生列表的排序顺序
* 数据结构List
* Key 格式meeting:student-order:{roomId}
* List 内容:按顺序存储学生的长 ID (字符串)
* 适用场景:教师端拖动排序后,监控端按相同顺序显示
* 过期时间24 小时
*/
STUDENT_ORDER: 'meeting:student-order:',
};
constructor(private readonly redisService: RedisService) {
@ -382,7 +393,9 @@ export class MeetingRedisService {
const socketEntries = Array.isArray(currentState.socketEntries) ? currentState.socketEntries : [];
const nextEntries = socketEntries.length > 0 ? socketEntries.filter((e) => e.socketId !== socketId) : [];
const nextSocketIds =
nextEntries.length > 0 ? [...new Set(nextEntries.map((e) => e.socketId).filter(Boolean))] : (currentState.socketIds || []).filter((id) => id !== socketId);
nextEntries.length > 0
? [...new Set(nextEntries.map((e) => e.socketId).filter(Boolean))]
: (currentState.socketIds || []).filter((id) => id !== socketId);
if (nextSocketIds.length > 0) {
const hm: Record<string, string> = { socketIds: JSON.stringify(nextSocketIds) };
@ -486,6 +499,42 @@ export class MeetingRedisService {
return !socketChecks.includes(true);
}
/**
* 设置学生排序顺序
* @param roomId - 房间 ID
* @param studentLongIds - 学生长 ID 数组 (按排序顺序)
*/
async setStudentOrder(roomId: string, studentLongIds: number[]): Promise<void> {
const key = `${this.KEY_PREFIX.STUDENT_ORDER}${roomId}`;
const client = this.getClient();
await client.del(key); // 先删除旧数据
if (studentLongIds.length > 0) {
const stringIds = studentLongIds.map((id) => String(id));
await client.rpush(key, ...stringIds);
await client.expire(key, 24 * 60 * 60);
}
}
/**
* 获取学生排序顺序
* @param roomId - 房间 ID
* @returns 学生长 ID 数组 (按排序顺序),未设置返回空数组
*/
async getStudentOrder(roomId: string): Promise<number[]> {
const key = `${this.KEY_PREFIX.STUDENT_ORDER}${roomId}`;
const ids = await this.getClient().lrange(key, 0, -1);
return ids.map((id) => Number(id)).filter((id) => Number.isFinite(id));
}
/**
* 清理学生排序
* @param roomId - 房间 ID
*/
async clearStudentOrder(roomId: string): Promise<void> {
const key = `${this.KEY_PREFIX.STUDENT_ORDER}${roomId}`;
await this.getClient().del(key);
}
// ==================== 清理 ====================
/**