新增当前用户切换麦位时的云信消息发送流程文档,详细描述了修复后的完整流程及关键修复点。同时,更新了 XPRoomViewController 和相关类以初始化当前用户麦位状态,处理其他用户麦位变化场景,确保麦位关系的正确更新和消息发送。优化了麦位中点矩形的绘制逻辑,提升了代码可维护性和用户体验。

This commit is contained in:
edwinQQQ
2025-09-09 19:17:21 +08:00
parent 68088e00e9
commit 58ab43c7d8
19 changed files with 447 additions and 58 deletions

View File

@@ -157,9 +157,14 @@
CGRect r = [self rectForViewAtIndex:right];
if (CGRectIsEmpty(l) || CGRectIsEmpty(r)) return CGRectZero;
CGFloat midX = (CGRectGetMidX(l) + CGRectGetMidX(r)) / 2.0;
CGFloat midY = (CGRectGetMidY(l) + CGRectGetMidY(r)) / 2.0;
// 🔧 使
CGFloat leftTopY = CGRectGetMinY(l);
CGFloat rightTopY = CGRectGetMinY(r);
CGFloat midTopY = (leftTopY + rightTopY) / 2.0;
CGFloat size = 75.0;
return CGRectMake(midX - size / 2.0, midY - size / 2.0, size, size);
return CGRectMake(midX - size / 2.0, midTopY, size, size);
}
- (void)onRoomEntered {

View File

@@ -121,9 +121,14 @@
if (CGRectIsEmpty(l) || CGRectIsEmpty(r)) return CGRectZero;
CGFloat midX = (CGRectGetMidX(l) + CGRectGetMidX(r)) / 2.0;
CGFloat midY = (CGRectGetMidY(l) + CGRectGetMidY(r)) / 2.0;
// 🔧 使
CGFloat leftTopY = CGRectGetMinY(l);
CGFloat rightTopY = CGRectGetMinY(r);
CGFloat midTopY = (leftTopY + rightTopY) / 2.0;
CGFloat size = 75.0;
return CGRectMake(midX - size / 2.0, midY - size / 2.0, size, size);
return CGRectMake(midX - size / 2.0, midTopY, size, size);
}
- (void)onRoomEntered {

View File

@@ -169,9 +169,14 @@
CGRect r = [self rectForViewAtIndex:right];
if (CGRectIsEmpty(l) || CGRectIsEmpty(r)) return CGRectZero;
CGFloat midX = (CGRectGetMidX(l) + CGRectGetMidX(r)) / 2.0;
CGFloat midY = (CGRectGetMidY(l) + CGRectGetMidY(r)) / 2.0;
// 🔧 使
CGFloat leftTopY = CGRectGetMinY(l);
CGFloat rightTopY = CGRectGetMinY(r);
CGFloat midTopY = (leftTopY + rightTopY) / 2.0;
CGFloat size = 75.0;
return CGRectMake(midX - size / 2.0, midY - size / 2.0, size, size);
return CGRectMake(midX - size / 2.0, midTopY, size, size);
}
/**

View File

@@ -148,9 +148,14 @@
if (CGRectIsEmpty(l) || CGRectIsEmpty(r)) return CGRectZero;
CGFloat midX = (CGRectGetMidX(l) + CGRectGetMidX(r)) / 2.0;
CGFloat midY = (CGRectGetMidY(l) + CGRectGetMidY(r)) / 2.0;
// 🔧 使
CGFloat leftTopY = CGRectGetMinY(l);
CGFloat rightTopY = CGRectGetMinY(r);
CGFloat midTopY = (leftTopY + rightTopY) / 2.0;
CGFloat size = 75.0;
return CGRectMake(midX - size / 2.0, midY - size / 2.0, size, size);
return CGRectMake(midX - size / 2.0, midTopY, size, size);
}
@end

View File

@@ -148,10 +148,15 @@ UIKIT_EXTERN NSString * const kRoomRoomLittleGameMiniStageNotificationKey;
CGRect r = [self rectForViewAtIndex:right];
if (CGRectIsEmpty(l) || CGRectIsEmpty(r)) return CGRectZero;
CGFloat midX = (CGRectGetMidX(l) + CGRectGetMidX(r)) / 2.0;
CGFloat midY = (CGRectGetMidY(l) + CGRectGetMidY(r)) / 2.0;
// 🔧 使
CGFloat leftTopY = CGRectGetMinY(l);
CGFloat rightTopY = CGRectGetMinY(r);
CGFloat midTopY = (leftTopY + rightTopY) / 2.0;
CGFloat size = 75.0;
// scrollView 使
return CGRectMake(midX - size / 2.0, midY - size / 2.0, size, size);
return CGRectMake(midX - size / 2.0, midTopY, size, size);
}
- (void)didSelectAtIndex:(NSInteger)index {

View File

@@ -92,9 +92,14 @@ UIKIT_EXTERN NSString * const kRoomRoomLittleGameMiniStageNotificationKey;
CGRect r = [self rectForViewAtIndex:right];
if (CGRectIsEmpty(l) || CGRectIsEmpty(r)) return CGRectZero;
CGFloat midX = (CGRectGetMidX(l) + CGRectGetMidX(r)) / 2.0;
CGFloat midY = (CGRectGetMidY(l) + CGRectGetMidY(r)) / 2.0;
// 🔧 使
CGFloat leftTopY = CGRectGetMinY(l);
CGFloat rightTopY = CGRectGetMinY(r);
CGFloat midTopY = (leftTopY + rightTopY) / 2.0;
CGFloat size = 75.0;
return CGRectMake(midX - size / 2.0, midY - size / 2.0, size, size);
return CGRectMake(midX - size / 2.0, midTopY, size, size);
}
- (void)didSelectAtIndex:(NSInteger)index {

View File

@@ -94,7 +94,11 @@
@"rightUid": @(rightUid)
};
if (leftUid <= 0 || rightUid <= 0 || cpList.count == 0) {
if (cpList.count < 2) {
return;
}
if (leftUid <= 0 && rightUid <= 0) {
return;
}
// SVGA

View File

@@ -193,9 +193,14 @@ static const NSInteger kMicCountPerRow = 5;
if (CGRectIsEmpty(l) || CGRectIsEmpty(r)) return CGRectZero;
CGFloat midX = (CGRectGetMidX(l) + CGRectGetMidX(r)) / 2.0;
CGFloat midY = (CGRectGetMidY(l) + CGRectGetMidY(r)) / 2.0;
// 🔧 使
CGFloat leftTopY = CGRectGetMinY(l);
CGFloat rightTopY = CGRectGetMinY(r);
CGFloat midTopY = (leftTopY + rightTopY) / 2.0;
CGFloat size = 75.0;
return CGRectMake(midX - size / 2.0, midY - size / 2.0, size, size);
return CGRectMake(midX - size / 2.0, midTopY, size, size);
}
@end

View File

@@ -1223,7 +1223,7 @@
if (CGRectIsEmpty(leftRect) || CGRectIsEmpty(rightRect)) {
return CGRectZero;
}
// y
// "行" y
CGFloat leftCenterY = CGRectGetMidY(leftRect);
CGFloat rightCenterY = CGRectGetMidY(rightRect);
if (fabs(leftCenterY - rightCenterY) > (CGRectGetHeight(leftRect) / 3.0)) {
@@ -1233,9 +1233,14 @@
CGFloat leftCenterX = CGRectGetMidX(leftRect);
CGFloat rightCenterX = CGRectGetMidX(rightRect);
CGFloat midX = (leftCenterX + rightCenterX) / 2.0;
CGFloat midY = (leftCenterY + rightCenterY) / 2.0;
// 🔧 使
CGFloat leftTopY = CGRectGetMinY(leftRect);
CGFloat rightTopY = CGRectGetMinY(rightRect);
CGFloat midTopY = (leftTopY + rightTopY) / 2.0;
CGFloat size = 75.0;
return CGRectMake(midX - size / 2.0, midY - size / 2.0, size, size);
return CGRectMake(midX - size / 2.0, midTopY, size, size);
}
#pragma mark - StageViewProtocol -

View File

@@ -115,9 +115,14 @@
if (CGRectIsEmpty(l) || CGRectIsEmpty(r)) return CGRectZero;
CGFloat midX = (CGRectGetMidX(l) + CGRectGetMidX(r)) / 2.0;
CGFloat midY = (CGRectGetMidY(l) + CGRectGetMidY(r)) / 2.0;
// 🔧 使
CGFloat leftTopY = CGRectGetMinY(l);
CGFloat rightTopY = CGRectGetMinY(r);
CGFloat midTopY = (leftTopY + rightTopY) / 2.0;
CGFloat size = 75.0;
return CGRectMake(midX - size / 2.0, midY - size / 2.0, size, size);
return CGRectMake(midX - size / 2.0, midTopY, size, size);
}
@end

View File

@@ -111,9 +111,14 @@ static const NSInteger kMicCountPerRow = 5;
CGRect r = [self rectForViewAtIndex:right];
if (CGRectIsEmpty(l) || CGRectIsEmpty(r)) return CGRectZero;
CGFloat midX = (CGRectGetMidX(l) + CGRectGetMidX(r)) / 2.0;
CGFloat midY = (CGRectGetMidY(l) + CGRectGetMidY(r)) / 2.0;
// 🔧 使
CGFloat leftTopY = CGRectGetMinY(l);
CGFloat rightTopY = CGRectGetMinY(r);
CGFloat midTopY = (leftTopY + rightTopY) / 2.0;
CGFloat size = 75.0;
return CGRectMake(midX - size / 2.0, midY - size / 2.0, size, size);
return CGRectMake(midX - size / 2.0, midTopY, size, size);
}

View File

@@ -1422,6 +1422,9 @@ XPCandyTreeInsufficientBalanceViewDelegate>
[self.functionView onRoomMiniEntered];
[self.functionView onRoomEntered];
[self.menuContainerView onRoomMiniEntered];
// 🔧
[self initializeCurrentUserMicStatusForMiniEnter];
}
[[XPRoomMiniManager shareManager] configRoomInfo:nil];
[[XPRoomMiniManager shareManager] configUserInfo:nil];
@@ -1500,6 +1503,9 @@ XPCandyTreeInsufficientBalanceViewDelegate>
[self.functionView onRoomMiniEntered];
[self.functionView onRoomEntered];
[self.menuContainerView onRoomMiniEntered];
// 🔧
[self initializeCurrentUserMicStatusForMiniEnter];
}
[[XPRoomMiniManager shareManager] configRoomInfo:nil];
[[XPRoomMiniManager shareManager] configUserInfo:nil];
@@ -1600,6 +1606,9 @@ XPCandyTreeInsufficientBalanceViewDelegate>
}
[self.messageContainerView onRoomMiniEntered];
// 🔧
[self initializeCurrentUserMicStatusForMiniEnter];
}
[self cleanMiniRoomStatues];
}
@@ -1936,6 +1945,9 @@ XPCandyTreeInsufficientBalanceViewDelegate>
[self.functionView onRoomMiniEntered];
[self.functionView onRoomEntered];
[self.menuContainerView onRoomMiniEntered];
// 🔧
[self initializeCurrentUserMicStatusForMiniEnter];
}
[self cleanMiniRoomStatues];
@@ -2139,9 +2151,76 @@ XPCandyTreeInsufficientBalanceViewDelegate>
NSDictionary* data = (NSDictionary *)content.ext;
UserInfoModel* userInfo = [UserInfoModel modelWithJSON:[data objectForKey:NIMChatroomEventInfoQueueChangeItemValueKey]];
NSInteger changeType = [data[NIMChatroomEventInfoQueueChangeTypeKey] integerValue];
NSLog(@"🔧 接收到麦序变化通知:用户 %ld变化类型 %ld", (long)userInfo.uid, (long)changeType);
//
if (changeType == 1 && userInfo.uid == [AccountInfoStorage instance].getUid.integerValue) {
[self cancelRoomArrangeMic];
}
// CP
if (changeType == 1 && userInfo.uid != [AccountInfoStorage instance].getUid.integerValue) {
// 🔧
[self handleOtherUserMicChange:userInfo changeType:changeType];
} else {
// API
if (changeType == 2 && userInfo.uid != [AccountInfoStorage instance].getUid.integerValue) {
// 🔧 mic
[self handleOtherUserMicChange:userInfo changeType:changeType];
} else {
NSNumber *key = nil;
if ([notiMsg respondsToSelector:@selector(attachContent)]) {
NSString *jsonString = (NSString *)[notiMsg performSelector:@selector(attachContent)];
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSError *error = nil;
NSDictionary *attachData = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];
if (!error) {
NSDictionary *dataDic = [attachData objectForKey:@"data"];
NSString *queueChange = [dataDic objectForKey:@"queueChange"];
if (![NSString isEmpty:queueChange]) {
jsonData = [queueChange dataUsingEncoding:NSUTF8StringEncoding];
NSError *e = nil;
NSDictionary *queueChangeData = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];
if (!e) {
key = [queueChangeData objectForKey:@"key"];
}
}
}
}
if (key) {
// key
NSInteger position = key.integerValue;
NSMutableDictionary<NSString *,MicroQueueModel *> *currentQueue = [self.stageView getMicroQueue];
if (currentQueue) {
for (NSString *key in [currentQueue allKeys]) {
if (key.integerValue == position) {
MicroQueueModel *model = [currentQueue objectForKey:key];
if (changeType == 2) {
// mic
model.userInfo = nil;
} else {
// mic
model.userInfo = userInfo;
}
break;
}
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self handleMicChangeForCP:currentQueue];
});
}
} else {
//
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSMutableDictionary<NSString *,MicroQueueModel *> *currentQueue = [self.stageView getMicroQueue];
if (currentQueue) {
[self handleMicChangeForCP:currentQueue];
}
});
}
}
}
}
break;
case NIMChatroomEventTypeAddManager: {
@@ -2482,40 +2561,56 @@ XPCandyTreeInsufficientBalanceViewDelegate>
}
}
/// CP
- (void)handleMicChangeForCP:(NSMutableDictionary<NSString *,MicroQueueModel *> *)queue {
NSLog(@"🔧 处理麦位变化相关的CP逻辑");
//
[self updateCurrentUserMicStatus:queue];
//
[self updateMicMidpointRectManagerSnapshot];
// CP
if (self.hasCompletedRoomInitialization) {
// CP
[self handleDownMicEventIfNeeded:queue];
// 🔧 micCP SVGA
[self handleMicSwitchScenarioIfNeeded:queue];
// CP API
[self callMicCpListByUidListOnMicChangeWithQueue:queue];
} else {
NSLog(@"🔧 进房初始化中跳过CP相关处理");
}
}
/// CP
- (void)handleMicRelationshipCPMessage:(AttachmentModel *)attachment {
NSLog(@"🔧 接收到麦位关系CP消息");
if (!attachment.data || ![attachment.data isKindOfClass:[NSString class]]) {
if (!attachment.data) {
NSLog(@"⚠️ 麦位关系CP消息data格式错误跳过处理");
return;
}
NSString *jsonString = (NSString *)attachment.data;
NSDictionary *jsonDic = (NSDictionary *)attachment.data;
NSString *jsonString = [jsonDic objectForKey:@"data"];
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSError *error = nil;
NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];
NSArray *dataArray = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];
if (error) {
NSLog(@"❌ 麦位关系CP消息JSON解析失败 - %@", error.localizedDescription);
return;
}
//
NSNumber *first = jsonDict[@"first"];
NSNumber *second = jsonDict[@"second"];
NSArray *dataArray = jsonDict[@"data"];
if (!first || !second || ![dataArray isKindOfClass:[NSArray class]]) {
if (![dataArray isKindOfClass:[NSArray class]]) {
NSLog(@"⚠️ 麦位关系CP消息消息格式错误跳过处理");
return;
}
if (first.integerValue != MicRelationship_Type || second.integerValue != MicRelationship_CP) {
NSLog(@"⚠️ 麦位关系CP消息消息类型不匹配跳过处理");
return;
}
// CP
NSMutableArray<MicCpInfoModel *> *cpList = [NSMutableArray array];
for (NSDictionary *cpDict in dataArray) {
@@ -3070,9 +3165,6 @@ XPCandyTreeInsufficientBalanceViewDelegate>
[self.functionView onRoomUpdate];
[self.functionView onMicroQueueUpdate:queue];
//
[self updateCurrentUserMicStatus:queue];
//
NSDictionary *currentStatus = [self getCurrentUserMicStatus:queue];
BOOL isOnMic = [currentStatus[@"isOnMic"] boolValue];
@@ -3083,18 +3175,8 @@ XPCandyTreeInsufficientBalanceViewDelegate>
self.anchorScrollView.scrollEnabled = YES;
}
// 🔧 micAPI
if (self.hasCompletedRoomInitialization) {
// CP
[self handleDownMicEventIfNeeded:queue];
// 🔧
[self updateMicMidpointRectManagerSnapshot];
[self callMicCpListByUidListOnMicChangeWithQueue:queue];
} else {
NSLog(@"🔧 进房初始化中,跳过 micCpListByUidList 调用");
}
// 🔧 CP NIMChatroomEventTypeQueueChange
// UICP
}
- (CGPoint)animationPointAtStageViewByUid:(NSString *)uid {
@@ -3116,6 +3198,8 @@ XPCandyTreeInsufficientBalanceViewDelegate>
- (void)getMicCpListByRoomUidSuccess:(NSArray <MicCpInfoModel *> *)cpList {
self.currentCpList = cpList;
// MicMidpointRectManager
[self updateMicMidpointRectManagerCache:cpList];
// CPSVGA
[self drawSocialStageMidpointRects];
}
@@ -3228,6 +3312,9 @@ XPCandyTreeInsufficientBalanceViewDelegate>
[self.functionView onRoomMiniEntered];
[self.functionView onRoomEntered];
[self.menuContainerView onRoomMiniEntered];
// 🔧
[self initializeCurrentUserMicStatusForMiniEnter];
}
[[XPRoomMiniManager shareManager] configRoomInfo:nil];
[[XPRoomMiniManager shareManager] configUserInfo:nil];
@@ -3718,6 +3805,8 @@ XPCandyTreeInsufficientBalanceViewDelegate>
NSInteger currentUserMicPosition = self.currentUserMicPosition;
if (currentUserMicPosition == -1) {
NSLog(@"🔧 获取mic用户列表当前用户不在麦上返回空数据");
// manager UID
[self removeCurrentUserCpDataFromManager];
return micUserUids;
}
@@ -3776,6 +3865,8 @@ XPCandyTreeInsufficientBalanceViewDelegate>
//
if (micUserUids.count == 1) {
NSLog(@"🔧 获取mic用户列表只有当前用户一个人返回空数据");
// manager UID
[self removeCurrentUserCpDataFromManager];
return [NSArray array];
}
@@ -3784,6 +3875,120 @@ XPCandyTreeInsufficientBalanceViewDelegate>
return micUserUids;
}
/// mic
- (void)handleOtherUserMicChange:(UserInfoModel *)userInfo changeType:(NSInteger)changeType {
if (!self.stageView || ![self.stageView respondsToSelector:@selector(midpointRectManager)]) {
NSLog(@"🔧 处理其他用户mic变化stageView 不支持 midpointRectManager跳过处理");
return;
}
MicMidpointRectManager *midpointRectManager = (MicMidpointRectManager *)[self.stageView performSelector:@selector(midpointRectManager)];
if (!midpointRectManager) {
NSLog(@"🔧 处理其他用户mic变化无法获取 midpointRectManager跳过处理");
return;
}
NSInteger userUid = userInfo.uid;
NSArray<NSNumber *> *userUids = @[@(userUid)];
if (changeType == 2) {
//
NSLog(@"🔧 处理其他用户下麦UID %ld", (long)userUid);
//
[self updateMicMidpointRectManagerSnapshot];
// cpSVGA
[midpointRectManager removeCpEntriesForUids:userUids];
[midpointRectManager removeMidpointRectsForUids:userUids];
//
[self drawSocialStageMidpointRects];
NSLog(@"🔧 其他用户下麦处理完成UID %ld", (long)userUid);
} else if (changeType == 1) {
//
NSLog(@"🔧 处理其他用户上麦UID %ld", (long)userUid);
//
[self updateMicMidpointRectManagerSnapshot];
// CPCP
[self drawSocialStageMidpointRects];
NSLog(@"🔧 其他用户上麦处理完成UID %ld", (long)userUid);
}
}
/// CPmanager
- (void)removeCurrentUserCpDataFromManager {
if (!self.stageView || ![self.stageView respondsToSelector:@selector(midpointRectManager)]) {
NSLog(@"🔧 移除当前用户CP数据stageView 不支持 midpointRectManager跳过处理");
return;
}
NSInteger currentUid = [AccountInfoStorage instance].getUid.integerValue;
NSArray<NSNumber *> *removedUids = @[@(currentUid)];
MicMidpointRectManager *midpointRectManager = (MicMidpointRectManager *)[self.stageView performSelector:@selector(midpointRectManager)];
if (midpointRectManager) {
//
[midpointRectManager removeCpEntriesForUids:removedUids];
// UI
[midpointRectManager removeMidpointRectsForUids:removedUids];
NSLog(@"🔧 移除当前用户CP数据完成UID %ld", (long)currentUid);
}
}
/// micCP SVGA
- (void)handleMicSwitchScenarioIfNeeded:(NSMutableDictionary<NSString *,MicroQueueModel *> *)queue {
if (!self.stageView || ![self.stageView respondsToSelector:@selector(midpointRectManager)]) {
NSLog(@"🔧 处理切换mic场景stageView 不支持 midpointRectManager跳过处理");
return;
}
// 🔧
//
BOOL hasMicPositionChanged = self.currentUserMicStatusChanged;
if (hasMicPositionChanged) {
NSLog(@"🔧 检测到当前用户麦位状态变化可能是切换mic场景");
//
NSInteger micCount = 0;
if (self.roomInfo) {
switch (self.roomInfo.type) {
case RoomType_Game: micCount = 9; break;
case RoomType_10Mic: micCount = 10; break;
case RoomType_15Mic: micCount = 15; break;
case RoomType_19Mic: micCount = 19; break;
case RoomType_20Mic: micCount = 20; break;
default: micCount = 9; break;
}
}
MicMidpointRectManager *midpointRectManager = (MicMidpointRectManager *)[self.stageView performSelector:@selector(midpointRectManager)];
if (midpointRectManager) {
// CP
// 1. SVGA
[midpointRectManager removeAllMidpointRects];
// 2.
[midpointRectManager rebuildMicSnapshotWithStageView:self.stageView micCount:micCount];
// 3. CP
[self drawSocialStageMidpointRects];
NSLog(@"🔧 用户麦位变化场景处理完成重新构建了所有CP关系");
}
// 🔧 API
// getMicCpListByUidListSuccess
} else {
NSLog(@"🔧 当前用户麦位状态未变化,跳过切换场景处理");
}
}
///
- (void)handleDownMicEventIfNeeded:(NSMutableDictionary<NSString *,MicroQueueModel *> *)queue {
if (!self.stageView || ![self.stageView respondsToSelector:@selector(midpointRectManager)]) {
@@ -3874,6 +4079,37 @@ XPCandyTreeInsufficientBalanceViewDelegate>
});
}
///
- (void)initializeCurrentUserMicStatusForMiniEnter {
NSLog(@"🔧 最小化进房:初始化当前用户麦位状态");
//
self.currentUserMicStatusChanged = NO;
self.currentUserWasOnMic = NO;
self.currentUserMicPosition = -1;
self.hasCompletedRoomInitialization = NO; //
//
NSMutableDictionary<NSString *,MicroQueueModel *> *currentQueue = [self.stageView getMicroQueue];
if (currentQueue && currentQueue.count > 0) {
NSDictionary *currentStatus = [self getCurrentUserMicStatus:currentQueue];
BOOL isOnMic = [currentStatus[@"isOnMic"] boolValue];
NSInteger micPosition = [currentStatus[@"micPosition"] integerValue];
self.currentUserWasOnMic = isOnMic;
self.currentUserMicPosition = micPosition;
NSLog(@"🔧 最小化进房初始化完成 - 当前用户是否在麦上: %@, 麦位: %ld",
isOnMic ? @"是" : @"否", (long)micPosition);
} else {
NSLog(@"🔧 最小化进房初始化完成 - 当前没有麦位数据");
}
//
self.hasCompletedRoomInitialization = YES;
NSLog(@"🔧 最小化进房初始化完成,后续麦位变动将触发 micCpListByUidList 调用");
}
///
- (NSDictionary *)getCurrentUserMicStatus:(NSMutableDictionary<NSString *,MicroQueueModel *> *)queue {
NSInteger currentUid = [AccountInfoStorage instance].getUid.integerValue;
@@ -3961,12 +4197,10 @@ XPCandyTreeInsufficientBalanceViewDelegate>
[cpDataArray addObject:cpDict];
}
NSDictionary *finalData = @{@"first": @(MicRelationship_Type),
@"second":@(MicRelationship_CP),
@"data":cpDataArray.copy};
NSError *error = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:finalData options:0 error:&error];
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:cpDataArray.copy
options:0
error:&error];
if (error) {
NSLog(@"❌ 发送麦位关系 NIM messageJSON序列化失败 - %@", error.localizedDescription);
return;
@@ -3975,11 +4209,15 @@ XPCandyTreeInsufficientBalanceViewDelegate>
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
NSLog(@"🔧 发送麦位关系 NIM messageJSON数据 - %@", jsonString);
NSDictionary *finalData = @{@"first": @(MicRelationship_Type),
@"second":@(MicRelationship_CP),
@"data":jsonString};
// AttachmentModel
AttachmentModel *attachment = [[AttachmentModel alloc] init];
attachment.first = MicRelationship_Type; // 1001
attachment.second = MicRelationship_CP; // 10011
attachment.data = jsonString;
attachment.data = finalData;
// NIM message
NIMMessage *message = [[NIMMessage alloc] init];

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -178,7 +178,6 @@ static UploadFile* manager;
}];
[[QCloudCOSTransferMangerService defaultCOSTransferManager] UploadObject:put];
}
/// Image

View File

@@ -0,0 +1,98 @@
# 当前用户切换 mic 时云信消息发送流程
## 修复后的完整流程
```
1. 用户切换 mic (位置A → 位置B)
2. 接收云信通知 NIMChatroomEventTypeQueueChange
- 下麦通知 (changeType == 2)
- 上麦通知 (changeType == 1)
3. 调用 handleMicChangeForCP:queue
4. 在 handleMicChangeForCP 中执行:
a) updateCurrentUserMicStatus:queue
- 检测到麦位变化
- 设置 currentUserMicStatusChanged = YES
b) updateMicMidpointRectManagerSnapshot
c) handleDownMicEventIfNeeded:queue
d) handleMicSwitchScenarioIfNeeded:queue
- 检测到 currentUserMicStatusChanged = YES
- 清除所有CP关系
- 重新构建CP关系
- 🔧 不重置状态标志(关键修复)
e) callMicCpListByUidListOnMicChangeWithQueue:queue
- 调用 micCpListByUidList API
5. API 成功回调 getMicCpListByUidListSuccess:cpList
6. 在 getMicCpListByUidListSuccess 中:
a) 更新 CP 缓存
b) 刷新绘制
c) 检查 currentUserMicStatusChanged = YES
d) 🔧 发送云信消息 sendMicRelationshipNIMessage:cpList
e) 重置状态标志 currentUserMicStatusChanged = NO
7. 其他用户收到云信消息更新CP关系
```
## 关键修复点
### 修复前的问题
- `handleMicSwitchScenarioIfNeeded` 中提前重置 `currentUserMicStatusChanged = NO`
- API 成功回调时状态标志已经是 `NO`
- 无法发送云信消息
### 修复后的逻辑
- `handleMicSwitchScenarioIfNeeded` 中不重置状态标志
- 状态标志保持 `YES` 直到 API 成功回调
- API 成功回调时正确发送云信消息
- 发送消息后重置状态标志
## 代码修改
### 修改前
```objc
// 在 handleMicSwitchScenarioIfNeeded 中
if (hasMicPositionChanged) {
// 处理麦位变化场景
[midpointRectManager removeAllMidpointRects];
[midpointRectManager rebuildMicSnapshotWithStageView:self.stageView micCount:micCount];
[self drawSocialStageMidpointRects];
// ❌ 提前重置状态标志
self.currentUserMicStatusChanged = NO;
}
```
### 修改后
```objc
// 在 handleMicSwitchScenarioIfNeeded 中
if (hasMicPositionChanged) {
// 处理麦位变化场景
[midpointRectManager removeAllMidpointRects];
[midpointRectManager rebuildMicSnapshotWithStageView:self.stageView micCount:micCount];
[self drawSocialStageMidpointRects];
// ✅ 不在这里重置状态标志等待API成功回调后再重置
// 状态标志将在 getMicCpListByUidListSuccess 中重置,确保能发送云信消息
}
```
## 验证要点
1. **当前用户切换mic时**
- ✅ 正确检测到麦位状态变化
- ✅ 重新构建所有CP关系
- ✅ 调用 micCpListByUidList API
- ✅ API成功回调时发送云信消息
- ✅ 状态标志在发送消息后正确重置
2. **其他用户切换mic时**
- ✅ 通过 handleOtherUserMicChange 处理
- ✅ 不发送云信消息(由切换用户发送)
3. **边界情况**
- ✅ 当前用户不在麦上时清理CP数据
- ✅ 只有当前用户一个人时清理CP数据