feat(websocket): 添加投屏实际声网UID支持
- 在MeetingRedisService中扩展setScreenSharing方法以支持screenShareActualUid参数 - 更新RoomState类型定义以包含screenShareActualUid字段 - 修改MeetingWebSocketGateway以传递screenShareActualUid数据 - 扩展MeetingJoinRoomSuccessData接口以包含screenShareActualUid - 在学生端组件中移除计算逻辑并直接使用后端返回的screenShareActualUid - 更新投屏开始和结束事件处理以正确设置screenShareActualUid状态 - 完善房间加入时的screenShareActualUid数据初始化逻辑
This commit is contained in:
@ -248,13 +248,19 @@ export class MeetingRedisService {
|
|||||||
* 设置投屏状态
|
* 设置投屏状态
|
||||||
* @param roomId - 房间 ID
|
* @param roomId - 房间 ID
|
||||||
* @param screenShareOwnerUid - 投屏人短 UID,null 表示停止投屏
|
* @param screenShareOwnerUid - 投屏人短 UID,null 表示停止投屏
|
||||||
|
* @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}`;
|
const key = `${this.KEY_PREFIX.ROOM_STATE}${roomId}`;
|
||||||
if (screenShareOwnerUid === null) {
|
if (screenShareOwnerUid === null) {
|
||||||
await this.getClient().hdel(key, 'screenShareOwnerUid');
|
await this.getClient().hdel(key, 'screenShareOwnerUid');
|
||||||
|
await this.getClient().hdel(key, 'screenShareActualUid');
|
||||||
} else {
|
} 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);
|
await this.getClient().expire(key, 24 * 60 * 60);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -293,6 +299,7 @@ export class MeetingRedisService {
|
|||||||
classStatus: 'finished' | 'in_class' | 'not_started';
|
classStatus: 'finished' | 'in_class' | 'not_started';
|
||||||
speakerUid?: number;
|
speakerUid?: number;
|
||||||
screenShareOwnerUid?: number;
|
screenShareOwnerUid?: number;
|
||||||
|
screenShareActualUid?: number;
|
||||||
teacherUid?: number;
|
teacherUid?: number;
|
||||||
endTimestamp?: number;
|
endTimestamp?: number;
|
||||||
}> {
|
}> {
|
||||||
@ -301,12 +308,14 @@ export class MeetingRedisService {
|
|||||||
const status = (map?.classStatus as any) || 'not_started';
|
const status = (map?.classStatus as any) || 'not_started';
|
||||||
const speakerUid = map?.speakerUid ? Number(map.speakerUid) : undefined;
|
const speakerUid = map?.speakerUid ? Number(map.speakerUid) : undefined;
|
||||||
const screenShareOwnerUid = map?.screenShareOwnerUid ? Number(map.screenShareOwnerUid) : 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 teacherUid = map?.teacherUid ? Number(map.teacherUid) : undefined;
|
||||||
const endTimestamp = map?.endTimestamp ? Number(map.endTimestamp) : undefined;
|
const endTimestamp = map?.endTimestamp ? Number(map.endTimestamp) : undefined;
|
||||||
return {
|
return {
|
||||||
classStatus: status,
|
classStatus: status,
|
||||||
...(Number.isFinite(speakerUid) ? { speakerUid } : {}),
|
...(Number.isFinite(speakerUid) ? { speakerUid } : {}),
|
||||||
...(Number.isFinite(screenShareOwnerUid) ? { screenShareOwnerUid } : {}),
|
...(Number.isFinite(screenShareOwnerUid) ? { screenShareOwnerUid } : {}),
|
||||||
|
...(Number.isFinite(screenShareActualUid) ? { screenShareActualUid } : {}),
|
||||||
...(Number.isFinite(teacherUid) ? { teacherUid } : {}),
|
...(Number.isFinite(teacherUid) ? { teacherUid } : {}),
|
||||||
...(Number.isFinite(endTimestamp) ? { endTimestamp } : {}),
|
...(Number.isFinite(endTimestamp) ? { endTimestamp } : {}),
|
||||||
};
|
};
|
||||||
|
|||||||
@ -364,6 +364,7 @@ export class MeetingWebSocketGateway implements OnGatewayConnection, OnGatewayDi
|
|||||||
isVideoMuted: userState.isVideoMuted,
|
isVideoMuted: userState.isVideoMuted,
|
||||||
classStatus: roomState.classStatus,
|
classStatus: roomState.classStatus,
|
||||||
screenShareOwnerUid: roomState.screenShareOwnerUid,
|
screenShareOwnerUid: roomState.screenShareOwnerUid,
|
||||||
|
screenShareActualUid: roomState.screenShareActualUid,
|
||||||
speakerUid,
|
speakerUid,
|
||||||
teacherUid,
|
teacherUid,
|
||||||
...(resolvedEndTimestamp ? { endTimestamp: resolvedEndTimestamp } : {}),
|
...(resolvedEndTimestamp ? { endTimestamp: resolvedEndTimestamp } : {}),
|
||||||
@ -715,8 +716,8 @@ export class MeetingWebSocketGateway implements OnGatewayConnection, OnGatewayDi
|
|||||||
if (!Number.isFinite(screenUid) || screenUid <= 0) {
|
if (!Number.isFinite(screenUid) || screenUid <= 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 保存投屏状态到 Redis
|
// 保存投屏状态到 Redis(同时保存短 UID 和实际 UID)
|
||||||
await this.redisService.setScreenSharing(data.roomId, targetUid);
|
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.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}`);
|
this.log(`创建者开始投屏:roomId=${data.roomId}, shortUid=${targetUid}, screenUid=${screenUid}`);
|
||||||
|
|||||||
@ -163,6 +163,8 @@ export interface MeetingJoinRoomSuccessData {
|
|||||||
classStatus: 'finished' | 'in_class' | 'not_started';
|
classStatus: 'finished' | 'in_class' | 'not_started';
|
||||||
/** 投屏状态:正在投屏的用户短 UID,undefined表示无人投屏 */
|
/** 投屏状态:正在投屏的用户短 UID,undefined表示无人投屏 */
|
||||||
screenShareOwnerUid?: number;
|
screenShareOwnerUid?: number;
|
||||||
|
/** 投屏的实际声网 UID(仅在有投屏时存在) */
|
||||||
|
screenShareActualUid?: number;
|
||||||
/** 上台的短 uid,默认为老师的 uid */
|
/** 上台的短 uid,默认为老师的 uid */
|
||||||
speakerUid: number;
|
speakerUid: number;
|
||||||
/** 老师的短 uid */
|
/** 老师的短 uid */
|
||||||
|
|||||||
@ -116,6 +116,8 @@
|
|||||||
const speakerUid = ref<number | string | null>(null);
|
const speakerUid = ref<number | string | null>(null);
|
||||||
/** 投屏用户 ID */
|
/** 投屏用户 ID */
|
||||||
const screenShareOwnerUid = ref<number | string | null>(null);
|
const screenShareOwnerUid = ref<number | string | null>(null);
|
||||||
|
/** 投屏的实际声网 UID(由后端返回) */
|
||||||
|
const screenShareActualUid = ref<number | null>(null);
|
||||||
const studentPrimaryOverrideUid = ref<number | string | null>(null);
|
const studentPrimaryOverrideUid = ref<number | string | null>(null);
|
||||||
|
|
||||||
const meeting = useAgoraMeeting(
|
const meeting = useAgoraMeeting(
|
||||||
@ -176,19 +178,6 @@
|
|||||||
|
|
||||||
const isTeacherDualStream = computed(() => Boolean(teacherUid.value && screenShareOwnerUid.value));
|
const isTeacherDualStream = computed(() => Boolean(teacherUid.value && screenShareOwnerUid.value));
|
||||||
|
|
||||||
/** 根据投屏用户短 UID 计算实际的声网 UID */
|
|
||||||
function calculateScreenActualUid(uid: number): number {
|
|
||||||
return uid + 1000000;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 获取当前投屏的实际声网 UID(如果有) */
|
|
||||||
const screenShareActualUid = computed<number | null>(() => {
|
|
||||||
if (typeof screenShareOwnerUid.value === 'number' && screenShareOwnerUid.value > 0) {
|
|
||||||
return calculateScreenActualUid(screenShareOwnerUid.value);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
const teacherPreferredPrimaryUid = computed<number | string | null>(() => {
|
const teacherPreferredPrimaryUid = computed<number | string | null>(() => {
|
||||||
// 如果有投屏,优先使用投屏的实际 UID
|
// 如果有投屏,优先使用投屏的实际 UID
|
||||||
if (screenShareActualUid.value) {
|
if (screenShareActualUid.value) {
|
||||||
@ -554,9 +543,11 @@
|
|||||||
}
|
}
|
||||||
case 'sev_start_screen_share': {
|
case 'sev_start_screen_share': {
|
||||||
const targetUid = packet.data?.targetUid;
|
const targetUid = packet.data?.targetUid;
|
||||||
|
const screenUid = packet.data?.screenUid;
|
||||||
if (typeof targetUid === 'number') {
|
if (typeof targetUid === 'number') {
|
||||||
screenShareOwnerUid.value = targetUid;
|
screenShareOwnerUid.value = targetUid;
|
||||||
console.log('[屏幕共享] 收到开始投屏消息:', { targetUid, calculatedActualUid: calculateScreenActualUid(targetUid) });
|
screenShareActualUid.value = typeof screenUid === 'number' ? screenUid : null;
|
||||||
|
console.log('[屏幕共享] 收到开始投屏消息:', { targetUid, screenUid });
|
||||||
Toast.info?.(`用户 ${targetUid} 开始投屏`);
|
Toast.info?.(`用户 ${targetUid} 开始投屏`);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -565,6 +556,7 @@
|
|||||||
const targetUid = packet.data?.targetUid;
|
const targetUid = packet.data?.targetUid;
|
||||||
if (typeof targetUid === 'number') {
|
if (typeof targetUid === 'number') {
|
||||||
screenShareOwnerUid.value = null;
|
screenShareOwnerUid.value = null;
|
||||||
|
screenShareActualUid.value = null;
|
||||||
console.log('[屏幕共享] 收到停止投屏消息:', { targetUid });
|
console.log('[屏幕共享] 收到停止投屏消息:', { targetUid });
|
||||||
Toast.info?.(`用户 ${targetUid} 停止投屏`);
|
Toast.info?.(`用户 ${targetUid} 停止投屏`);
|
||||||
}
|
}
|
||||||
@ -656,6 +648,9 @@
|
|||||||
if (typeof roomData.screenShareOwnerUid === 'number') {
|
if (typeof roomData.screenShareOwnerUid === 'number') {
|
||||||
screenShareOwnerUid.value = roomData.screenShareOwnerUid;
|
screenShareOwnerUid.value = roomData.screenShareOwnerUid;
|
||||||
}
|
}
|
||||||
|
if (typeof roomData.screenShareActualUid === 'number') {
|
||||||
|
screenShareActualUid.value = roomData.screenShareActualUid;
|
||||||
|
}
|
||||||
if (classStatus.value === 'in_class') {
|
if (classStatus.value === 'in_class') {
|
||||||
await meeting.initAndJoin(roomData);
|
await meeting.initAndJoin(roomData);
|
||||||
// ✅ 加入后先检查是否有已存在的远程用户
|
// ✅ 加入后先检查是否有已存在的远程用户
|
||||||
|
|||||||
@ -262,6 +262,8 @@ export interface OnJoinRoomData {
|
|||||||
classStatus?: ClassStatus;
|
classStatus?: ClassStatus;
|
||||||
/** 投屏状态:正在投屏的用户短 UID,undefined表示无人投屏 */
|
/** 投屏状态:正在投屏的用户短 UID,undefined表示无人投屏 */
|
||||||
screenShareOwnerUid?: number;
|
screenShareOwnerUid?: number;
|
||||||
|
/** 投屏的实际声网 UID(仅在有投屏时存在) */
|
||||||
|
screenShareActualUid?: number;
|
||||||
/** 主讲人短 UID(默认等于 teacherUid) */
|
/** 主讲人短 UID(默认等于 teacherUid) */
|
||||||
speakerUid: number;
|
speakerUid: number;
|
||||||
/** 老师短 UID */
|
/** 老师短 UID */
|
||||||
|
|||||||
Reference in New Issue
Block a user