joinClass method
Implementation
Future<void> joinClass() async {
TCICLog.info("joinClass: joinClass start", actionModule: ActionModule.tcicController.name, actionName: ActionName.joinClass.name);
// 复位上一次的失败诊断
_lastJoinClassErrorCode = null;
_lastJoinClassRequestId = null;
_lastJoinClassFailReason = null;
joinClassSuccess = false;
_classSessionActive = false;
_stateDataObs.updateIsJoinClassSuccess(false);
final smallWindowMode = isInSmallWindowMode();
if (smallWindowMode) {
TCICLog.info("joinClass: smallWindowMode is true", actionModule: ActionModule.tcicController.name, actionName: ActionName.joinClass.name);
joinClassSuccess = true;
_eventbus.fire(MainEvent(type: EventTypeEnum.joinClassSuccess));
EnterClassPerf.mark('joinClassSuccess.smallWindow');
return;
}
if (isGroupRoom() && isStudent() && (_config.groupLiveCode == null || _config.groupLiveCode!.isEmpty)) {
_eventbus.fire(MainEvent(type: EventTypeEnum.joinClassFailed));
throw StateError('groupLiveCode is required for student in group room');
}
var body = JoinClassBody(classId: _config.classId, role: getRole().index, groupLiveCode: isGroupRoom() ? _config.groupLiveCode : null);
EnterClassPerf.mark('joinClass.http.start');
var data = await networkService.joinClass(body);
EnterClassPerf.mark('joinClass.http.end');
if (data != null) {
if (data.errorCode == 0) {
joinClassTimeStamp = data.timestamp * 1000;
_membersInfoObs.updateJoinClassTime(data.nanoTimestamp);
_liveSteamInfoObs.updateLiveStreamInfo(data.streams);
_membersInfoObs.updateOnlineStudentCount(data.studentOnlineNumber);
final Map<String, Member> formatedMembers = {};
for (var item in data.members) {
formatedMembers[item.userId] = item;
}
_markClassSessionActive('joinClassHttpSuccess');
_membersInfoObs.updateIsSelfOnline(true, "joinClassHttpSuccess");
_ctrlBusinessLogic.initMembers(formatedMembers);
startHeartBeat();
userSig = data.userSig;
// 等待 IM 初始化完成(_initTIM 由 schoolInfoSuccess 事件触发,与 joinClass HTTP 并行)。
// 旧实现:盲等 1s(即便 IM 已就绪也强制 sleep;若未就绪则 1s 也不够)。
// 新实现:直接 await 初始化 Future,已就绪即 0ms 通过。
EnterClassPerf.mark('imInit.waitStart[ready=$imInitSuccess]');
if (!imInitSuccess) {
await (_imInitFuture ?? Future<void>.value());
}
EnterClassPerf.mark('imInit.waitEnd[ready=$imInitSuccess]');
EnterClassPerf.mark('tim.login.start');
var loginResult = await TIM.login(userId: _config.userId, userSig: data.userSig);
EnterClassPerf.mark('tim.login.end[ok=$loginResult]');
if (loginResult) {
imLoginSuccess = true;
_eventbus.fire(MainEvent(type: EventTypeEnum.imLoginSuccess));
EnterClassPerf.mark('joinImGroups.start');
var joinResult = await _joinImGroups(getImJoinTargets());
EnterClassPerf.mark('joinImGroups.end');
if (!joinResult) {
_lastJoinClassFailReason = 'im_join_group_failed';
await _rollbackJoinClassSession('im_join_group_failed');
_eventbus.fire(MainEvent(type: EventTypeEnum.someThingErrorInIm));
TCICLog.error('join im group failed', actionModule: ActionModule.tcicController.name, actionName: ActionName.joinClass.name);
return;
}
joinClassSuccess = true;
_stateDataObs.updateIsJoinClassSuccess(true);
_eventbus.fire(MainEvent(type: EventTypeEnum.joinClassSuccess));
EnterClassPerf.mark('joinClassSuccess');
// 加入课堂成功后,异步获取全量成员列表(不阻塞主流程)
_fetchAllMembersInBackground();
} else {
_lastJoinClassFailReason = 'im_login_failed';
await _rollbackJoinClassSession('im_login_failed');
_eventbus.fire(MainEvent(type: EventTypeEnum.someThingErrorInIm));
TCICLog.error('im login failed', actionModule: ActionModule.tcicController.name, actionName: ActionName.joinClass.name);
}
} else {
_lastJoinClassErrorCode = data.errorCode;
_lastJoinClassRequestId = data.requestId;
_lastJoinClassFailReason = 'backend_reject';
TCICLog.error(
'joinClass: backend rejected, errorCode=${data.errorCode} requestId=${data.requestId} classId=${_config.classId}',
actionModule: ActionModule.tcicController.name,
actionName: ActionName.joinClass.name);
_eventbus.fire(MainEvent(type: EventTypeEnum.joinClassFailed));
fireMainBreakEvent(MainProcessBreakenEventEnum.joinClassFailed, data.errorCode, requestId: data.requestId);
}
} else {
_lastJoinClassErrorCode = null;
_lastJoinClassRequestId = null;
_lastJoinClassFailReason = 'http_null_response';
TCICLog.error(
'joinClass: HTTP returned null (network/timeout/parse) classId=${_config.classId}',
actionModule: ActionModule.tcicController.name,
actionName: ActionName.joinClass.name);
_eventbus.fire(MainEvent(type: EventTypeEnum.joinClassFailed));
fireMainBreakEvent(MainProcessBreakenEventEnum.joinClassFailed, 0, requestId: "");
}
TCICLog.info('userId: ${_config.userId}', actionModule: ActionModule.tcicController.name, actionName: ActionName.joinClass.name);
}