This commit is contained in:
zhutao
2025-11-20 18:00:34 +08:00
parent 701b99b138
commit b7239292d1
45 changed files with 1499 additions and 354 deletions

View File

@@ -0,0 +1,103 @@
enum RoomCommand {
///ping服务器用于心跳
ping("ping"),
///加入房间
joinRoom("into_room"),
///获取房间信息(没啥用)
getRoomInfo("room_data"),
///学生开关扬声器、摄像头、麦克风
switchCamera("mute_self"),
///学生上传文件
uploadFile("upload_file"),
///学生举手
handUp("handup"),
///老师开启自习室
openRoom("start_study_room"),
///老师关闭自习室
closeRoom("close_study_room"),
///老师开关学生的扬声器、关闭摄像头、关闭麦克风
switchStudentCamera("mute_user"),
///老师清除学生的举手
clearHandUp("clear_handup"),
///邀请学生进入白板
inviteStudent("invite_whiteboard");
final String value;
const RoomCommand(this.value);
}
enum RoomEvent {
///人员变化事件
changeUser("sys_room_user_changed"),
///学生端开启扬声器
openSpeaker("user_unmute_self_speeker"),
///学生端关闭扬声器
closeSpeaker("user_mute_self_speeker"),
///学生开启麦克风
openMic("user_unmute_self_microphone"),
///学生关闭麦克风
closeMic("user_mute_self_microphone"),
///学生开启摄像头
openCamera("user_unmute_self_camera"),
///学生关闭摄像头
closeCamera("user_mute_self_camera"),
///学生文件上传完毕
fileUploadComplete("sys_user_file_uploaded"),
///学生举手事件
handUp("sys_user_handup"),
///自习室以开启,进入自习室(学生用)
openRoom("sys_start_study_room"),
///自习室以关闭,退出自习室(学生用)
closeRoom("sys_close_study_room"),
///老师关闭学生的扬声器
closeStudentSpeaker("sys_control_mute_speeker"),
///老师打开学生的扬声器
openStudentSpeaker("sys_control_unmute_speeker"),
///老师关闭学生的麦克风
closeStudentMic("sys_control_mute_microphone"),
///老师关闭学生的摄像头
closeStudentCamera("sys_control_mute_camera"),
///老师清除学生的举手(学生用)
clearHandUp("sys_clear_handup"),
///学生收到白板邀请(学生用)
inviteWhiteboard("sys_invite_whiteboard");
final String value;
const RoomEvent(this.value);
/// 根据 值获取枚举
static RoomEvent fromStr(String value) {
return RoomEvent.values.firstWhere(
(e) => e.value == value,
orElse: () => throw ArgumentError('Invalid weather type value: $value'),
);
}
}

View File

@@ -0,0 +1,122 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:app/config/config.dart';
import 'package:app/request/api/room_api.dart';
import 'package:app/request/websocket/room_protocol.dart';
import 'package:logger/logger.dart';
import '../dto/room/rtc_token_dto.dart';
Logger logger = Logger();
class RoomWebSocket {
///单例设计模式
RoomWebSocket._();
static final RoomWebSocket _instance = RoomWebSocket._();
factory RoomWebSocket() => _instance;
/// WebSocket和心跳定时器
String url = "";
WebSocket? _socket;
Timer? _heartbeatTimer;
Timer? _reconnectTimer; //错误重连的定时器
///令牌
String wsToken = ""; //自习室的websocket令牌
int roomId = 0; //房间号
RtcTokenDto? rtcToken; // rtc的令牌
///用 StreamController 分化消息给订阅者
final StreamController<RoomMessage> _msgController = StreamController.broadcast();
Stream<RoomMessage> get stream => _msgController.stream;
///初始化令牌
/// -[id] 房间id
Future<void> initToken(int id) async {
roomId = id;
final rtcFuture = getRtcTokenApi(id);
final wsFuture = getWsTokenApi(id);
rtcToken = await rtcFuture;
wsToken = await wsFuture;
}
///开始连接
Future<void> connect() async {
try {
_socket = await WebSocket.connect(
"${Config.wsUrl()}?token=$wsToken&study_room_id=$roomId",
);
logger.i("连接成功");
_reconnectTimer?.cancel();
_reconnectTimer = null;
//监听消息
_socket!.listen(
(data) {
//监听事件
final jsonMap = jsonDecode(data);
RoomMessage msg = RoomMessage(RoomEvent.fromStr(jsonMap['action']), jsonMap['data']);
_msgController.add(msg);
},
onDone: () {},
onError: (_) {
logger.e("连接异常断开");
},
);
//心跳
_heartbeatTimer?.cancel();
_heartbeatTimer = Timer.periodic(Duration(seconds: 15), (_) {
logger.i("发送心跳");
send(RoomCommand.ping);
});
} catch (e) {
logger.e("连接失败");
_reconnect();
}
}
///发送指令
void send(RoomCommand action, [Map<String, dynamic>? params]) {
final msg = {
"action": action.value,
"data": params,
};
_socket!.add(jsonEncode(msg));
}
///连接错误事件
void _reconnect() {
_reconnectTimer?.cancel();
_reconnectTimer = Timer.periodic(Duration(seconds: 3), (timer) {
logger.e("正在重连");
connect();
});
}
void dispose() {
//心跳取消
_heartbeatTimer?.cancel();
_heartbeatTimer = null;
//socket取消
_socket?.close();
// 销毁事件流
_msgController.close();
// 错误重连取消
_reconnectTimer?.cancel();
_reconnectTimer = null;
logger.i("websocket销毁成功");
}
}
///websocket服务器发过来的事件和数据
class RoomMessage {
final RoomEvent event; //事件名称
final dynamic data; //事件数据
RoomMessage(this.event, this.data);
}