Browse Source

feat(rtc): 添加一对一RTC频道功能

- 在api_urls.dart中新增createOneOnOneRtcChannel API路径
- 创建新的CallController用于管理通话相关逻辑
- 实现createOneOnOneRtcChannel方法支持音频视频通话
- 将call_manager.dart从message目录移动到rtc目录
- 在CallManager中集成RTC频道创建流程
- 更新通话消息发送逻辑包含频道ID信息
- 调整相关页面和组件的导入路径以适配目录变更
- 修复user_api.g.dart中的API路径格式问题
master
Jolie 3 months ago
parent
commit
a4d06f8435
11 changed files with 143 additions and 8 deletions
  1. 68
      lib/controller/message/call_controller.dart
  2. 5
      lib/controller/message/chat_controller.dart
  3. 2
      lib/im/im_manager.dart
  4. 2
      lib/network/api_urls.dart
  5. 6
      lib/network/rtc_api.dart
  6. 34
      lib/network/rtc_api.g.dart
  7. 2
      lib/network/user_api.g.dart
  8. 2
      lib/pages/message/chat_page.dart
  9. 2
      lib/pages/message/video_call_page.dart
  10. 26
      lib/rtc/call_manager.dart
  11. 2
      lib/widget/message/video_call_overlay_widget.dart

68
lib/controller/message/call_controller.dart

@ -0,0 +1,68 @@
import 'package:dating_touchme_app/model/rtc/rtc_channel_data.dart';
import 'package:dating_touchme_app/network/network_service.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
///
class CallController extends GetxController {
CallController({NetworkService? networkService})
: _networkService = networkService ?? Get.find<NetworkService>();
final NetworkService _networkService;
///
final Rxn<RtcChannelData> rtcChannel = Rxn<RtcChannelData>();
///
final RxBool isCreatingChannel = false.obs;
/// RTC频道
/// [type] 12
Future<RtcChannelData?> createOneOnOneRtcChannel({required int type}) async {
if (isCreatingChannel.value) {
print('⚠️ 正在创建频道,请稍候');
return null;
}
// type
if (type != 1 && type != 2) {
SmartDialog.showToast('类型参数错误:1为音频,2为视频');
return null;
}
isCreatingChannel.value = true;
try {
final response = await _networkService.rtcApi.createOneOnOneRtcChannel({
'type': type,
});
if (response.data.isSuccess && response.data.data != null) {
rtcChannel.value = response.data.data;
print('✅ 创建一对一RTC频道成功: ${response.data.data?.channelId}');
return response.data.data;
} else {
final message = response.data.message.isNotEmpty
? response.data.message
: '创建频道失败';
SmartDialog.showToast(message);
print('❌ 创建一对一RTC频道失败: $message');
return null;
}
} catch (e) {
final errorMessage = '创建频道异常:$e';
SmartDialog.showToast(errorMessage);
print('$errorMessage');
return null;
} finally {
isCreatingChannel.value = false;
}
}
@override
void onClose() {
super.onClose();
rtcChannel.value = null;
}
}

5
lib/controller/message/chat_controller.dart

@ -1053,10 +1053,12 @@ class ChatController extends GetxController {
/// [callType] 'voice' 'video'
/// [callStatus] 'calling' 'missed' 'cancelled' 'rejected'
/// [callDuration] callStatus 'calling'
/// [channelId] RTC频道ID
Future<bool> sendCallMessage({
required String callType, // 'voice' 'video'
required String callStatus, // 'calling', 'missed', 'cancelled', 'rejected'
int? callDuration, //
String? channelId, // RTC频道ID
}) async {
try {
// Map<String, String>
@ -1067,6 +1069,9 @@ class ChatController extends GetxController {
if (callDuration != null) {
callParams['callDuration'] = callDuration.toString();
}
if (channelId != null && channelId.isNotEmpty) {
callParams['channelId'] = channelId;
}
// 使
final tempMessage = EMMessage.createCustomSendMessage(

2
lib/im/im_manager.dart

@ -18,7 +18,7 @@ import '../network/user_api.dart';
import '../widget/message/message_notification_dialog.dart';
import '../widget/message/video_call_invite_dialog.dart';
import '../pages/message/video_call_page.dart';
import '../controller/message/call_manager.dart';
import '../rtc/call_manager.dart';
import '../service/local_notification_service.dart';
//

2
lib/network/api_urls.dart

@ -42,6 +42,8 @@ class ApiUrls {
'dating-agency-chat-audio/user/get/sw/rtc/token';
static const String createRtcChannel =
'dating-agency-chat-audio/user/create/rtc-channel';
static const String createOneOnOneRtcChannel =
'dating-agency-chat-audio/user/create/one-on-one/rtc-channel';
static const String editOwnMarriageInformation =
'dating-agency-service/user/edit/own-marriage-information';
static const String getSwRtmToken =

6
lib/network/rtc_api.dart

@ -29,6 +29,12 @@ abstract class RtcApi {
@POST(ApiUrls.createRtcChannel)
Future<HttpResponse<BaseResponse<RtcChannelData>>> createRtcChannel();
/// RTC频道type: 12
@POST(ApiUrls.createOneOnOneRtcChannel)
Future<HttpResponse<BaseResponse<RtcChannelData>>> createOneOnOneRtcChannel(
@Body() Map<String, dynamic> data,
);
/// RTC
@GET(ApiUrls.getRtcChannelDetail)
Future<HttpResponse<BaseResponse<RtcChannelDetail>>> getRtcChannelDetail(

34
lib/network/rtc_api.g.dart

@ -114,6 +114,40 @@ class _RtcApi implements RtcApi {
return httpResponse;
}
@override
Future<HttpResponse<BaseResponse<RtcChannelData>>> createOneOnOneRtcChannel(
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<RtcChannelData>>>(
Options(method: 'POST', headers: _headers, extra: _extra)
.compose(
_dio.options,
'dating-agency-chat-audio/user/create/one-on-one/rtc-channel',
queryParameters: queryParameters,
data: _data,
)
.copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
);
final _result = await _dio.fetch<Map<String, dynamic>>(_options);
late BaseResponse<RtcChannelData> _value;
try {
_value = BaseResponse<RtcChannelData>.fromJson(
_result.data!,
(json) => RtcChannelData.fromJson(json as Map<String, dynamic>),
);
} on Object catch (e, s) {
errorLogger?.logError(e, s, _options);
rethrow;
}
final httpResponse = HttpResponse(_value, _result);
return httpResponse;
}
@override
Future<HttpResponse<BaseResponse<RtcChannelDetail>>> getRtcChannelDetail(
String channelId,

2
lib/network/user_api.g.dart

@ -1651,7 +1651,7 @@ class _UserApi implements UserApi {
Options(method: 'GET', headers: _headers, extra: _extra)
.compose(
_dio.options,
'/dating-agency-uec/user/get/app-version/update',
'dating-agency-uec/user/get/app-version/update',
queryParameters: queryParameters,
data: _data,
)

2
lib/pages/message/chat_page.dart

@ -4,7 +4,7 @@ import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:im_flutter_sdk/im_flutter_sdk.dart';
import '../../controller/message/call_manager.dart';
import '../../rtc/call_manager.dart';
import '../../controller/message/chat_controller.dart';
import '../../controller/message/voice_player_manager.dart';
// import '../../controller/message/call_manager.dart'; //

2
lib/pages/message/video_call_page.dart

@ -6,7 +6,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import '../../controller/message/call_manager.dart';
import '../../rtc/call_manager.dart';
import '../../controller/message/conversation_controller.dart';
import '../../controller/overlay_controller.dart';
import '../../model/home/marriage_data.dart';

lib/controller/message/call_manager.dart → lib/rtc/call_manager.dart

@ -4,9 +4,10 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:im_flutter_sdk/im_flutter_sdk.dart';
import '../../generated/assets.dart';
import '../../im/im_manager.dart';
import 'chat_controller.dart';
import '../generated/assets.dart';
import '../im/im_manager.dart';
import '../controller/message/chat_controller.dart';
import '../controller/message/call_controller.dart';
///
enum CallType {
@ -76,6 +77,19 @@ class CallManager extends GetxController {
print('📞 [CallManager] 发起${callType == CallType.video ? "视频" : "语音"}通话,目标用户: $targetUserId');
// CallController RTC
final callController = Get.put(CallController());
final type = callType == CallType.video ? 2 : 1; // 12
final channelData = await callController.createOneOnOneRtcChannel(type: type);
if (channelData == null) {
print('❌ [CallManager] 创建RTC频道失败,无法发起通话');
SmartDialog.showToast('创建通话频道失败');
return false;
}
print('✅ [CallManager] RTC频道创建成功: ${channelData.channelId}');
//
final session = CallSession(
targetUserId: targetUserId,
@ -92,6 +106,7 @@ class CallManager extends GetxController {
targetUserId: targetUserId,
callType: callTypeStr,
callStatus: 'missed', //
channelId: channelData.channelId, // ID
chatController: chatController,
);
@ -305,6 +320,7 @@ class CallManager extends GetxController {
required String callType,
required String callStatus,
int? callDuration,
String? channelId,
ChatController? chatController,
}) async {
try {
@ -314,6 +330,7 @@ class CallManager extends GetxController {
callType: callType,
callStatus: callStatus,
callDuration: callDuration,
channelId: channelId,
);
}
@ -325,6 +342,9 @@ class CallManager extends GetxController {
if (callDuration != null) {
callParams['callDuration'] = callDuration.toString();
}
if (channelId != null && channelId.isNotEmpty) {
callParams['channelId'] = channelId;
}
final message = await IMManager.instance.sendCustomMessage(
targetUserId,

2
lib/widget/message/video_call_overlay_widget.dart

@ -1,5 +1,5 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:dating_touchme_app/controller/message/call_manager.dart';
import 'package:dating_touchme_app/rtc/call_manager.dart';
import 'package:dating_touchme_app/extension/ex_widget.dart';
import 'package:dating_touchme_app/generated/assets.dart';
import 'package:dating_touchme_app/pages/message/video_call_page.dart';

Loading…
Cancel
Save