From 49281d503ab59afbdc3f6311a173ca8f4dfdce6d Mon Sep 17 00:00:00 2001 From: tianfeng <769204422@qq.com> Date: Tue, 7 Apr 2026 18:19:37 +0800 Subject: [PATCH] =?UTF-8?q?fix(live):=20=E4=BF=AE=E5=A4=8D=E4=B8=8A?= =?UTF-8?q?=E9=BA=A6=20goUp=20=E7=9A=84=20NPE=20=E9=A3=8E=E9=99=A9?= =?UTF-8?q?=E3=80=81=E5=BC=82=E5=B8=B8=E5=90=9E=E5=99=AC=E5=92=8C=E9=87=8D?= =?UTF-8?q?=E5=A4=8D=20HTTP=20=E8=B0=83=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit P0 - BUG 修复: - 将 liveMicrophone 和 micUser 的 null 检查移至使用前,彻底消除 NPE 风险 - 替换 catch(Exception) 全吞逻辑:业务异常直接上抛保留原始错误码, 不再将所有错误误报为"麦克风已被占用",改用 log.error 记录完整堆栈 P1 - 性能优化: - checkMicAvailableUse 前移至加锁后第一步,麦位已占时提前返回, 避免后续 5 次 HTTP 调用空跑 - userProfileClient.getByUserId 由 2 次减为 1 次: 先获取 UserProfileDTO 后直接传给 toLiveMicUser, 删除 getLiveMicUser 内部的重复调用 Co-Authored-By: Claude Sonnet 4.6 (cherry picked from commit 8e9ca4f3434762969e7bbe76d69dc64aec08df89) --- .../repository/LiveMicrophoneGatewayImpl.java | 58 +++++++++++-------- 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/rc-service/rc-service-live/live-infrastructure/src/main/java/com/red/circle/live/infra/repository/LiveMicrophoneGatewayImpl.java b/rc-service/rc-service-live/live-infrastructure/src/main/java/com/red/circle/live/infra/repository/LiveMicrophoneGatewayImpl.java index 25faa077..b0655295 100644 --- a/rc-service/rc-service-live/live-infrastructure/src/main/java/com/red/circle/live/infra/repository/LiveMicrophoneGatewayImpl.java +++ b/rc-service/rc-service-live/live-infrastructure/src/main/java/com/red/circle/live/infra/repository/LiveMicrophoneGatewayImpl.java @@ -42,6 +42,7 @@ import com.red.circle.other.inner.model.cmd.live.LiveHeartbeatCmd; import com.red.circle.other.inner.model.cmd.live.MemberRolesQryCmd; import com.red.circle.other.inner.model.dto.live.RoomProfileDTO; import com.red.circle.other.inner.model.dto.live.RoomProfileManagerDTO; +import com.red.circle.other.inner.model.dto.user.UserProfileDTO; import com.red.circle.other.inner.model.dto.user.WealthAndCharmLevelDTO; import com.red.circle.tool.core.collection.CollectionUtils; import com.red.circle.tool.core.func.VoidConsumer; @@ -159,27 +160,38 @@ public class LiveMicrophoneGatewayImpl implements LiveMicrophoneGateway { @Override public LiveMicrophoneNoticeDTO goUp(Long roomId, Integer micIndex, Long userId) { - micOpsLock(roomId, micIndex); -// RLock lock = redissonClient.getLock(roomId.toString()+micIndex.toString()+"room"); -// lock.lock(); + micOpsLock(roomId, micIndex); try { - // 验证用户是否真实存在 - ResponseAssert.notNull(UserErrorCode.USER_INFO_NOT_FOUND, userProfileClient.getByUserId(userId)); - - RoomProfileDTO roomProfile = roomManagerClient.getById(roomId).getBody(); - // 房间是否真实存在 - ResponseAssert.notNull(RoomErrorCode.ROOM_NOT_EXISTS, roomProfile); - // 获取上麦克风需要数据 - LiveMicrophone liveMicrophone = getLiveMicrophone(roomId, micIndex, userId); - liveMicrophone.getUser().setCharmLevel(userLevelClient.getUserLevel(SysOriginPlatformEnum.valueOf(roomProfile.getSysOrigin()),userId).getBody().getCharmLevel()); - ResponseAssert.notNull(LiveMicErrorCode.MIC_INDEX_NOT_FOUND, liveMicrophone); - ResponseAssert.notNull(UserErrorCode.USER_INFO_NOT_FOUND, liveMicrophone.getUser()); + // P1: 优先检查麦克风可用性,失败立即返回,避免后续 HTTP 调用空跑 checkMicAvailableUse(roomId, micIndex); + + // P1: 获取用户信息一次,后续复用,避免 getLiveMicUser 内部重复调用 + UserProfileDTO userProfile = userProfileClient.getByUserId(userId).getBody(); + ResponseAssert.notNull(UserErrorCode.USER_INFO_NOT_FOUND, userProfile); + + RoomProfileDTO roomProfile = roomManagerClient.getById(roomId).getBody(); + ResponseAssert.notNull(RoomErrorCode.ROOM_NOT_EXISTS, roomProfile); + + // P0: 先判 null 再使用,避免 NPE 被 catch 吞掉误报为"麦克风已被占用" + LiveMicSettingSeat micSeat = liveMicSettingService.getMicSeat(roomId, micIndex); + ResponseAssert.notNull(LiveMicErrorCode.MIC_INDEX_NOT_FOUND, micSeat); + + // P1: 直接用已有 userProfile 构建 micUser,跳过重复的 HTTP 调用 + LiveMicUser micUser = liveMicrophoneInfraConvertor.toLiveMicUser(userProfile); + ResponseAssert.notNull(UserErrorCode.USER_INFO_NOT_FOUND, micUser); + micUser.setRoles(Objects.toString(roomMemberClient.getMemberRoles(roomId, userId).getBody())); + micUser.setCharmLevel(userLevelClient.getUserLevel( + SysOriginPlatformEnum.valueOf(roomProfile.getSysOrigin()), userId).getBody().getCharmLevel()); + + LiveMicrophone liveMicrophone = new LiveMicrophone() + .setMicIndex(micSeat.getMicIndex()) + .setUser(micUser) + .updateLastActivityTime(); + // 下掉已上麦克风 List lives = liveMicCacheService.listLiveMicrophone(roomId, userId); if (CollectionUtils.isNotEmpty(lives)) { - liveMicCacheService.goDown(roomId, - lives.stream().map(LiveMicrophone::getMicIndex).toList()); + liveMicCacheService.goDown(roomId, lives.stream().map(LiveMicrophone::getMicIndex).toList()); } // 上麦克风 @@ -190,13 +202,12 @@ public class LiveMicrophoneGatewayImpl implements LiveMicrophoneGateway { // 发送通知 return micUserChangeNotice(roomProfile); - }catch (Exception e){ - e.printStackTrace(); - ResponseAssert.isTrue(LiveMicErrorCode.MICROPHONE_OCCUPIED,false); - return null; - } - finally { -// lock.unlock(); + + } catch (RuntimeException e) { + // P0: 不再吞掉所有异常,业务异常直接上抛保留原始错误码,记录日志便于排查 + log.error("上麦失败 roomId={}, micIndex={}, userId={}", roomId, micIndex, userId, e); + throw e; + } finally { micOpsUnlock(roomId, micIndex); } } @@ -522,7 +533,6 @@ public class LiveMicrophoneGatewayImpl implements LiveMicrophoneGateway { } Map liveMicrophoneMap = mapLiveMicrophone(roomId); - log.info("在线取麦克风列表信息 -Map: {}", liveMicrophoneMap); return liveMicSettingSeats.stream().map(seat -> new LiveMicrophoneDTO() .setRoomId(roomId) .setMicIndex(seat.getMicIndex())