feat(websocket): 添加投屏实际声网UID支持

- 在MeetingRedisService中扩展setScreenSharing方法以支持screenShareActualUid参数
- 更新RoomState类型定义以包含screenShareActualUid字段
- 修改MeetingWebSocketGateway以传递screenShareActualUid数据
- 扩展MeetingJoinRoomSuccessData接口以包含screenShareActualUid
- 在学生端组件中移除计算逻辑并直接使用后端返回的screenShareActualUid
- 更新投屏开始和结束事件处理以正确设置screenShareActualUid状态
- 完善房间加入时的screenShareActualUid数据初始化逻辑
This commit is contained in:
2026-03-15 21:01:51 +08:00
parent a49e027613
commit 2da9c45b61
5 changed files with 27 additions and 18 deletions

View File

@ -248,13 +248,19 @@ export class MeetingRedisService {
* 设置投屏状态
* @param roomId - 房间 ID
* @param screenShareOwnerUid - 投屏人短 UIDnull 表示停止投屏
* @param screenShareActualUid - 投屏的实际声网 UID可选
*/
async setScreenSharing(roomId: string, screenShareOwnerUid: number | null): Promise<void> {
async setScreenSharing(roomId: string, screenShareOwnerUid: number | null, screenShareActualUid?: number | null): Promise<void> {
const key = `${this.KEY_PREFIX.ROOM_STATE}${roomId}`;
if (screenShareOwnerUid === null) {
await this.getClient().hdel(key, 'screenShareOwnerUid');
await this.getClient().hdel(key, 'screenShareActualUid');
} else {
await this.getClient().hset(key, { screenShareOwnerUid: String(screenShareOwnerUid) });
const data: Record<string, string> = { screenShareOwnerUid: String(screenShareOwnerUid) };
if (typeof screenShareActualUid === 'number') {
data.screenShareActualUid = String(screenShareActualUid);
}
await this.getClient().hset(key, data);
await this.getClient().expire(key, 24 * 60 * 60);
}
}
@ -293,6 +299,7 @@ export class MeetingRedisService {
classStatus: 'finished' | 'in_class' | 'not_started';
speakerUid?: number;
screenShareOwnerUid?: number;
screenShareActualUid?: number;
teacherUid?: number;
endTimestamp?: number;
}> {
@ -301,12 +308,14 @@ export class MeetingRedisService {
const status = (map?.classStatus as any) || 'not_started';
const speakerUid = map?.speakerUid ? Number(map.speakerUid) : undefined;
const screenShareOwnerUid = map?.screenShareOwnerUid ? Number(map.screenShareOwnerUid) : undefined;
const screenShareActualUid = map?.screenShareActualUid ? Number(map.screenShareActualUid) : undefined;
const teacherUid = map?.teacherUid ? Number(map.teacherUid) : undefined;
const endTimestamp = map?.endTimestamp ? Number(map.endTimestamp) : undefined;
return {
classStatus: status,
...(Number.isFinite(speakerUid) ? { speakerUid } : {}),
...(Number.isFinite(screenShareOwnerUid) ? { screenShareOwnerUid } : {}),
...(Number.isFinite(screenShareActualUid) ? { screenShareActualUid } : {}),
...(Number.isFinite(teacherUid) ? { teacherUid } : {}),
...(Number.isFinite(endTimestamp) ? { endTimestamp } : {}),
};

View File

@ -364,6 +364,7 @@ export class MeetingWebSocketGateway implements OnGatewayConnection, OnGatewayDi
isVideoMuted: userState.isVideoMuted,
classStatus: roomState.classStatus,
screenShareOwnerUid: roomState.screenShareOwnerUid,
screenShareActualUid: roomState.screenShareActualUid,
speakerUid,
teacherUid,
...(resolvedEndTimestamp ? { endTimestamp: resolvedEndTimestamp } : {}),
@ -715,8 +716,8 @@ export class MeetingWebSocketGateway implements OnGatewayConnection, OnGatewayDi
if (!Number.isFinite(screenUid) || screenUid <= 0) {
return;
}
// 保存投屏状态到 Redis
await this.redisService.setScreenSharing(data.roomId, targetUid);
// 保存投屏状态到 Redis(同时保存短 UID 和实际 UID
await this.redisService.setScreenSharing(data.roomId, targetUid, screenUid);
// 广播给整个房间:有人开始投屏
this.server?.to(data.roomId).emit('message', { type: 'sev_start_screen_share', data: { fromRoomId: data.roomId, targetUid, screenUid } });
this.log(`创建者开始投屏roomId=${data.roomId}, shortUid=${targetUid}, screenUid=${screenUid}`);

View File

@ -163,6 +163,8 @@ export interface MeetingJoinRoomSuccessData {
classStatus: 'finished' | 'in_class' | 'not_started';
/** 投屏状态:正在投屏的用户短 UIDundefined表示无人投屏 */
screenShareOwnerUid?: number;
/** 投屏的实际声网 UID仅在有投屏时存在 */
screenShareActualUid?: number;
/** 上台的短 uid默认为老师的 uid */
speakerUid: number;
/** 老师的短 uid */