feat(meeting): 实现学生排序功能并优化Redis存储
- 将Redis中的学生排序存储从长ID改为短ID - 在监控端按studentListForSort顺序重新排列学生列表 - 添加新加入学生的处理逻辑 - 实现WebSocket消息处理学生排序变更事件 - 更新类型定义支持学生排序变更数据结构 - 优化Redis服务注释说明短ID存储格式
This commit is contained in:
@ -97,21 +97,21 @@ export class MeetingService {
|
||||
|
||||
// 如果提供了 roomId,从 Redis 获取排序并应用
|
||||
if (roomId) {
|
||||
const orderedIds = await this.redis.getStudentOrder(roomId);
|
||||
if (orderedIds.length > 0) {
|
||||
// 创建映射表
|
||||
const orderedShortIds = await this.redis.getStudentOrder(roomId);
|
||||
if (orderedShortIds.length > 0) {
|
||||
// 创建短 ID 到学生的映射
|
||||
const studentMap = new Map<number, StudentDetailInfo>();
|
||||
finalResults.forEach((student) => {
|
||||
studentMap.set(student.longId, student);
|
||||
studentMap.set(student.shortId, student);
|
||||
});
|
||||
|
||||
// 按 Redis 中的顺序重新排列
|
||||
// 按 Redis 中的短 ID 顺序重新排列
|
||||
const orderedResults: StudentDetailInfo[] = [];
|
||||
for (const longId of orderedIds) {
|
||||
const student = studentMap.get(longId);
|
||||
for (const shortId of orderedShortIds) {
|
||||
const student = studentMap.get(shortId);
|
||||
if (student) {
|
||||
orderedResults.push(student);
|
||||
studentMap.delete(longId); // 移除已添加的学生
|
||||
studentMap.delete(shortId); // 移除已添加的学生
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -74,8 +74,8 @@ export class MeetingRedisService {
|
||||
* 用途:存储学生列表的排序顺序
|
||||
* 数据结构:List
|
||||
* Key 格式:meeting:student-order:{roomId}
|
||||
* List 内容:按顺序存储学生的长 ID (字符串)
|
||||
* 适用场景:教师端拖动排序后,监控端按相同顺序显示
|
||||
* List 内容:按顺序存储学生的短 ID (数字字符串)
|
||||
* 适用场景:教师端拖动排序后,监控端和学生端按相同顺序显示
|
||||
* 过期时间:24 小时
|
||||
*/
|
||||
STUDENT_ORDER: 'meeting:student-order:',
|
||||
@ -518,7 +518,7 @@ export class MeetingRedisService {
|
||||
/**
|
||||
* 获取学生排序顺序
|
||||
* @param roomId - 房间 ID
|
||||
* @returns 学生长 ID 数组 (按排序顺序),未设置返回空数组
|
||||
* @returns 学生短 ID 数组 (按排序顺序),未设置返回空数组
|
||||
*/
|
||||
async getStudentOrder(roomId: string): Promise<number[]> {
|
||||
const key = `${this.KEY_PREFIX.STUDENT_ORDER}${roomId}`;
|
||||
|
||||
@ -173,7 +173,31 @@
|
||||
*/
|
||||
const studentUsers = computed(() => {
|
||||
const users = meeting.remoteUsers.value as (IAgoraRTCRemoteUser & { userName?: string })[];
|
||||
return users.filter((u) => (teacherUid.value ? u.uid !== teacherUid.value : true));
|
||||
const filteredUsers = users.filter((u) => (teacherUid.value ? u.uid !== teacherUid.value : true));
|
||||
|
||||
// 按照 studentListForSort 中的顺序重新排列
|
||||
if (studentListForSort.value.length > 0) {
|
||||
// 创建 shortId -> user 的映射
|
||||
const userMap = new Map<number, typeof filteredUsers[0]>();
|
||||
filteredUsers.forEach((user) => {
|
||||
userMap.set(Number(user.uid), user);
|
||||
});
|
||||
|
||||
// 按照 studentListForSort 的顺序重新排列
|
||||
const sortedUsers: typeof filteredUsers = [];
|
||||
studentListForSort.value.forEach((student) => {
|
||||
const user = userMap.get(student.shortId);
|
||||
if (user) {
|
||||
sortedUsers.push(user);
|
||||
userMap.delete(student.shortId);
|
||||
}
|
||||
});
|
||||
|
||||
// 添加 studentListForSort 中没有的用户 (新加入的学生)
|
||||
return [...sortedUsers, ...Array.from(userMap.values())];
|
||||
}
|
||||
|
||||
return filteredUsers;
|
||||
});
|
||||
|
||||
/**
|
||||
@ -285,14 +309,15 @@
|
||||
selectedUserUid.value = packet.data.targetUid;
|
||||
break;
|
||||
}
|
||||
// 学生排序变更事件 (类型定义待添加)
|
||||
// case 'sev_student_order_changed': {
|
||||
// const newOrder = packet.data.studentShortIds;
|
||||
// if (Array.isArray(newOrder) && newOrder.length > 0) {
|
||||
// console.log('[监控端] 学生排序已更新:', newOrder);
|
||||
// }
|
||||
// break;
|
||||
// }
|
||||
// 学生排序变更事件
|
||||
case 'sev_student_order_changed': {
|
||||
const newOrder = packet.data.studentShortIds;
|
||||
if (Array.isArray(newOrder) && newOrder.length > 0) {
|
||||
console.log('[监控端] 学生排序已更新,重新获取学生列表');
|
||||
fetchStudentList();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'sev_class_started': {
|
||||
classStatus.value = 'in_class';
|
||||
console.log('[监控端] 收到上课消息');
|
||||
|
||||
@ -518,6 +518,14 @@
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'sev_student_order_changed': {
|
||||
const newOrder = packet.data.studentShortIds;
|
||||
if (Array.isArray(newOrder) && newOrder.length > 0) {
|
||||
console.log('[教师端] 学生排序已更新,重新获取学生列表');
|
||||
fetchStudentList();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'sev_class_started': {
|
||||
classStatus.value = 'in_class';
|
||||
|
||||
|
||||
@ -323,6 +323,19 @@ export interface OnSetMainVideoData extends OnControlMessageData {
|
||||
targetUid: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 控制消息数据 - 学生排序变更
|
||||
*/
|
||||
export interface OnStudentOrderChangedData extends OnControlMessageData {
|
||||
/** 学生短 ID 数组 (按排序顺序) */
|
||||
studentShortIds: number[];
|
||||
}
|
||||
|
||||
/**
|
||||
* WebSocket 消息包 - 服务端下发:学生排序变更
|
||||
*/
|
||||
export type OnStudentOrderChangedPacket = WebSocketPacket<'sev_student_order_changed', OnStudentOrderChangedData>;
|
||||
|
||||
/**
|
||||
* WebSocket 消息包 - 客户端接收:加入房间成功响应
|
||||
*/
|
||||
@ -402,6 +415,7 @@ export type DownlinkControlMessagePacket =
|
||||
| OnScreenShareStartedPacket
|
||||
| OnScreenShareStoppedPacket
|
||||
| OnSetMainVideoPacket
|
||||
| OnStudentOrderChangedPacket
|
||||
| OnUnmuteAudioPacket
|
||||
| OnUnmuteVideoPacket;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user