Browse Source

feat(rtc): 添加踢出RTC频道用户功能

- 新增API接口用于踢出RTC频道中的指定用户
- 在RoomController中实现踢出用户的业务逻辑
- 更新RTC API定义及自动生成的代码
- 添加解除连麦对话框组件DisconnectMicDialog
- 实现嘉宾连麦状态管理和UI展示逻辑
ios
Jolie 3 months ago
parent
commit
1ec54e306a
5 changed files with 272 additions and 0 deletions
  1. 34
      lib/controller/discover/room_controller.dart
  2. 2
      lib/network/api_urls.dart
  3. 6
      lib/network/rtc_api.dart
  4. 34
      lib/network/rtc_api.g.dart
  5. 196
      lib/widget/live/disconnect_mic_dialog.dart

34
lib/controller/discover/room_controller.dart

@ -552,4 +552,38 @@ class RoomController extends GetxController with WidgetsBindingObserver {
print(455);
matchmakerFlag.value = GlobalData().userData!.matchmakerFlag!;
}
/// RTC
Future<void> kickingRtcChannelUser({
required int channelId,
required int kickingUId,
}) async {
try {
final requestData = {
'channelId': channelId,
'kickingUId': kickingUId,
};
final response = await _networkService.rtcApi.kickingRtcChannelUser(
requestData,
);
if (response.data.isSuccess) {
SmartDialog.showToast('已踢出用户');
//
final channelName = RTCManager.instance.currentChannelId;
if (channelName != null && channelName.isNotEmpty) {
await fetchRtcChannelDetail(channelName);
}
} else {
final message = response.data.message.isNotEmpty
? response.data.message
: '踢出用户失败';
SmartDialog.showToast(message);
}
} catch (e) {
print('❌ 踢出用户异常: $e');
SmartDialog.showToast('踢出用户失败');
}
}
}

2
lib/network/api_urls.dart

@ -106,4 +106,6 @@ class ApiUrls {
'dating-agency-chat-audio/user/get/own-user-management';
static const String submitMatchmakerOrder = 'dating-agency-mall/user/submit/matchmaker-order';
static const String kickingRtcChannelUser =
'dating-agency-chat-audio/user/user//kicking/rtc-channel-user';
}

6
lib/network/rtc_api.dart

@ -82,4 +82,10 @@ abstract class RtcApi {
Future<HttpResponse<BaseResponse<dynamic>>> costImGift(
@Body() Map<String, dynamic> data,
);
/// RTC
@POST(ApiUrls.kickingRtcChannelUser)
Future<HttpResponse<BaseResponse<dynamic>>> kickingRtcChannelUser(
@Body() Map<String, dynamic> data,
);
}

34
lib/network/rtc_api.g.dart

@ -467,6 +467,40 @@ class _RtcApi implements RtcApi {
return httpResponse;
}
@override
Future<HttpResponse<BaseResponse<dynamic>>> kickingRtcChannelUser(
Map<String, dynamic> data,
) async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
final _headers = <String, dynamic>{};
final _data = <String, dynamic>{};
_data.addAll(data);
final _options = _setStreamType<HttpResponse<BaseResponse<dynamic>>>(
Options(method: 'POST', headers: _headers, extra: _extra)
.compose(
_dio.options,
'dating-agency-chat-audio/user/user//kicking/rtc-channel-user',
queryParameters: queryParameters,
data: _data,
)
.copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
);
final _result = await _dio.fetch<Map<String, dynamic>>(_options);
late BaseResponse<dynamic> _value;
try {
_value = BaseResponse<dynamic>.fromJson(
_result.data!,
(json) => json as dynamic,
);
} on Object catch (e, s) {
errorLogger?.logError(e, s, _options);
rethrow;
}
final httpResponse = HttpResponse(_value, _result);
return httpResponse;
}
RequestOptions _setStreamType<T>(RequestOptions requestOptions) {
if (T != dynamic &&
!(requestOptions.responseType == ResponseType.bytes ||

196
lib/widget/live/disconnect_mic_dialog.dart

@ -0,0 +1,196 @@
import 'package:dating_touchme_app/controller/discover/room_controller.dart';
import 'package:dating_touchme_app/generated/assets.dart';
import 'package:dating_touchme_app/model/rtc/rtc_channel_detail.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
///
class DisconnectMicDialog extends StatelessWidget {
const DisconnectMicDialog({super.key});
@override
Widget build(BuildContext context) {
final roomController = Get.find<RoomController>();
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.w),
),
child: Container(
padding: EdgeInsets.all(20.w),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
//
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'解除连麦',
style: TextStyle(
fontSize: 18.sp,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
GestureDetector(
onTap: () => SmartDialog.dismiss(),
child: Icon(
Icons.close,
size: 24.w,
color: Colors.grey,
),
),
],
),
SizedBox(height: 20.w),
//
Obx(() {
final maleInfo = roomController.rtcChannelDetail.value?.maleInfo;
final femaleInfo = roomController.rtcChannelDetail.value?.femaleInfo;
//
if (maleInfo == null && femaleInfo == null) {
return Padding(
padding: EdgeInsets.symmetric(vertical: 20.w),
child: Text(
'暂无嘉宾连麦',
style: TextStyle(
fontSize: 14.sp,
color: Colors.grey,
),
),
);
}
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
//
if (maleInfo != null)
Expanded(
child: _buildGuestItem(
context: context,
userInfo: maleInfo,
roomController: roomController,
isMale: true,
),
),
if (maleInfo != null && femaleInfo != null)
SizedBox(width: 20.w),
//
if (femaleInfo != null)
Expanded(
child: _buildGuestItem(
context: context,
userInfo: femaleInfo,
roomController: roomController,
isMale: false,
),
),
],
);
}),
SizedBox(height: 10.w),
],
),
),
);
}
///
Widget _buildGuestItem({
required BuildContext context,
required RtcSeatUserInfo userInfo,
required RoomController roomController,
required bool isMale,
}) {
//
final isConnected = userInfo.isMicrophoneOn;
return Column(
children: [
//
ClipOval(
child: Image.network(
userInfo.profilePhoto,
width: 60.w,
height: 60.w,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
return Image.asset(
Assets.imagesUserAvatar,
width: 60.w,
height: 60.w,
);
},
),
),
SizedBox(height: 10.w),
//
Text(
userInfo.nickName,
style: TextStyle(
fontSize: 14.sp,
color: Colors.black87,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
SizedBox(height: 10.w),
//
GestureDetector(
onTap: isConnected
? () async {
//
await _disconnectMic(roomController, isMale);
}
: null,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 8.w),
decoration: BoxDecoration(
color: isConnected ? const Color(0xFF8B5CF6) : Colors.grey[300],
borderRadius: BorderRadius.circular(20.w),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Image.asset(
Assets.imagesMicOff,
width: 14.w,
color: isConnected ? Colors.white : Colors.grey[600],
),
SizedBox(width: 4.w),
Text(
isConnected ? '解除连麦' : '已解除连麦',
style: TextStyle(
fontSize: 12.sp,
color: isConnected ? Colors.white : Colors.grey[600],
),
),
],
),
),
),
],
);
}
///
Future<void> _disconnectMic(RoomController roomController, bool isMale) async {
// -
final currentDetail = roomController.rtcChannelDetail.value;
if (currentDetail != null) {
final newDetail = RtcChannelDetail(
channelId: currentDetail.channelId,
anchorInfo: currentDetail.anchorInfo,
maleInfo: isMale ? null : currentDetail.maleInfo,
femaleInfo: !isMale ? null : currentDetail.femaleInfo,
);
roomController.rtcChannelDetail.value = newDetail;
}
}
}
Loading…
Cancel
Save