Browse Source

feat(call): 添加RTC通话消费响应处理和玫瑰余额显示

- 引入ConsumeRtcChannelResponse模型用于处理消费响应数据
- 在CallController中添加consumeResponse响应变量存储消费信息
- 移除延迟调用消费接口改为直接启动定时器
- 实现消费响应数据更新和状态检查逻辑
- 添加status为3时自动挂断通话的功能
- 实现免费通话检测和定时器停止机制
- 在视频通话页面显示玫瑰剩余数量和每分钟价格信息
- 优化通话界面UI布局添加余额显示组件
master
Jolie 2 months ago
parent
commit
832dc1a21d
4 changed files with 87 additions and 29 deletions
  1. 32
      lib/controller/message/call_controller.dart
  2. 3
      lib/network/rtc_api.dart
  3. 35
      lib/network/rtc_api.g.dart
  4. 46
      lib/pages/message/video_call_page.dart

32
lib/controller/message/call_controller.dart

@ -3,6 +3,7 @@ import 'package:agora_rtc_engine/agora_rtc_engine.dart';
import 'package:audioplayers/audioplayers.dart'; import 'package:audioplayers/audioplayers.dart';
import 'package:dating_touchme_app/generated/assets.dart'; import 'package:dating_touchme_app/generated/assets.dart';
import 'package:dating_touchme_app/model/rtc/chat_audio_product_model.dart'; import 'package:dating_touchme_app/model/rtc/chat_audio_product_model.dart';
import 'package:dating_touchme_app/model/rtc/consume_rtc_channel_response.dart';
import 'package:dating_touchme_app/model/rtc/rtc_channel_data.dart'; import 'package:dating_touchme_app/model/rtc/rtc_channel_data.dart';
import 'package:dating_touchme_app/network/network_service.dart'; import 'package:dating_touchme_app/network/network_service.dart';
import 'package:dating_touchme_app/rtc/rtc_manager.dart'; import 'package:dating_touchme_app/rtc/rtc_manager.dart';
@ -117,6 +118,9 @@ class CallController extends GetxController {
String? _callChannelId; String? _callChannelId;
int? _callUid; int? _callUid;
//
final Rxn<ConsumeRtcChannelResponse> consumeResponse = Rxn<ConsumeRtcChannelResponse>();
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
@ -450,9 +454,6 @@ class CallController extends GetxController {
// //
// _callChannelId // _callChannelId
if (_callChannelId != null && _callChannelId!.isNotEmpty) { if (_callChannelId != null && _callChannelId!.isNotEmpty) {
Future .delayed(Duration(seconds: 1), () async {
await _consumeOneOnOneRtcChannel();
});
_startConsumeTimer(); _startConsumeTimer();
print('✅ [CallController] 接收方接听后已启动消费定时器'); print('✅ [CallController] 接收方接听后已启动消费定时器');
} }
@ -591,6 +592,8 @@ class CallController extends GetxController {
void _stopConsumeTimer() { void _stopConsumeTimer() {
_consumeTimer?.cancel(); _consumeTimer?.cancel();
_consumeTimer = null; _consumeTimer = null;
//
consumeResponse.value = null;
} }
/// RTC频道接口 /// RTC频道接口
@ -606,7 +609,28 @@ class CallController extends GetxController {
'channelId': consumeChannelId, 'channelId': consumeChannelId,
}); });
if (response.data.isSuccess) { if (response.data.isSuccess) {
print('✅ [CallController] 已调用消费一对一RTC频道接口,channelId: $consumeChannelId');
final consumeData = response.data.data;
if (consumeData != null) {
//
consumeResponse.value = consumeData;
print('✅ [CallController] 已调用消费一对一RTC频道接口,channelId: $consumeChannelId, isFree: ${consumeData.isFree}, status: ${consumeData.status}, availableBalance: ${consumeData.availableBalance}, unitSellingBalance: ${consumeData.unitSellingBalance}');
// status == 3
if (consumeData.status == 3) {
print('⚠️ [CallController] 检测到 status=3,自动挂断通话');
await hangUpCall();
return;
}
//
if (consumeData.isFree) {
_stopConsumeTimer();
print('✅ [CallController] 检测到免费通话,已停止消费定时器');
}
} else {
print('✅ [CallController] 已调用消费一对一RTC频道接口,channelId: $consumeChannelId');
}
} else { } else {
print('⚠️ [CallController] 消费一对一RTC频道接口失败: ${response.data.message}'); print('⚠️ [CallController] 消费一对一RTC频道接口失败: ${response.data.message}');
} }

3
lib/network/rtc_api.dart

@ -1,6 +1,7 @@
import 'package:dating_touchme_app/model/discover/rtc_channel_model.dart'; import 'package:dating_touchme_app/model/discover/rtc_channel_model.dart';
import 'package:dating_touchme_app/model/live/gift_product_model.dart'; import 'package:dating_touchme_app/model/live/gift_product_model.dart';
import 'package:dating_touchme_app/model/rtc/chat_audio_product_model.dart'; import 'package:dating_touchme_app/model/rtc/chat_audio_product_model.dart';
import 'package:dating_touchme_app/model/rtc/consume_rtc_channel_response.dart';
import 'package:dating_touchme_app/model/rtc/link_mic_card_model.dart'; import 'package:dating_touchme_app/model/rtc/link_mic_card_model.dart';
import 'package:dating_touchme_app/model/rtc/rtc_channel_data.dart'; import 'package:dating_touchme_app/model/rtc/rtc_channel_data.dart';
import 'package:dating_touchme_app/model/rtc/rtc_channel_detail.dart'; import 'package:dating_touchme_app/model/rtc/rtc_channel_detail.dart';
@ -133,7 +134,7 @@ abstract class RtcApi {
/// RTC频道 /// RTC频道
@POST(ApiUrls.consumeOneOnOneRtcChannel) @POST(ApiUrls.consumeOneOnOneRtcChannel)
Future<HttpResponse<BaseResponse<dynamic>>> consumeOneOnOneRtcChannel(
Future<HttpResponse<BaseResponse<ConsumeRtcChannelResponse>>> consumeOneOnOneRtcChannel(
@Body() Map<String, dynamic> data, @Body() Map<String, dynamic> data,
); );
} }

35
lib/network/rtc_api.g.dart

@ -750,30 +750,33 @@ class _RtcApi implements RtcApi {
} }
@override @override
Future<HttpResponse<BaseResponse<dynamic>>> consumeOneOnOneRtcChannel(
Map<String, dynamic> data,
) async {
Future<HttpResponse<BaseResponse<ConsumeRtcChannelResponse>>>
consumeOneOnOneRtcChannel(Map<String, dynamic> data) async {
final _extra = <String, dynamic>{}; final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{}; final queryParameters = <String, dynamic>{};
final _headers = <String, dynamic>{}; final _headers = <String, dynamic>{};
final _data = <String, dynamic>{}; final _data = <String, dynamic>{};
_data.addAll(data); _data.addAll(data);
final _options = _setStreamType<HttpResponse<BaseResponse<dynamic>>>(
Options(method: 'POST', headers: _headers, extra: _extra)
.compose(
_dio.options,
'dating-agency-chat-audio/user/consume/one-on-one/rtc-channel',
queryParameters: queryParameters,
data: _data,
)
.copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
);
final _options =
_setStreamType<HttpResponse<BaseResponse<ConsumeRtcChannelResponse>>>(
Options(method: 'POST', headers: _headers, extra: _extra)
.compose(
_dio.options,
'dating-agency-chat-audio/user/consume/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); final _result = await _dio.fetch<Map<String, dynamic>>(_options);
late BaseResponse<dynamic> _value;
late BaseResponse<ConsumeRtcChannelResponse> _value;
try { try {
_value = BaseResponse<dynamic>.fromJson(
_value = BaseResponse<ConsumeRtcChannelResponse>.fromJson(
_result.data!, _result.data!,
(json) => json as dynamic,
(json) =>
ConsumeRtcChannelResponse.fromJson(json as Map<String, dynamic>),
); );
} on Object catch (e, s) { } on Object catch (e, s) {
errorLogger?.logError(e, s, _options); errorLogger?.logError(e, s, _options);

46
lib/pages/message/video_call_page.dart

@ -561,15 +561,45 @@ class _VideoCallPageState extends State<VideoCallPage> {
bottom: MediaQuery.of(context).size.height * 0.25, bottom: MediaQuery.of(context).size.height * 0.25,
left: 0, left: 0,
right: 0, right: 0,
child: Center(
child: Text(
isCallConnected ? _formatDuration(duration) : '正在呼叫中',
style: TextStyle(
color: Colors.white,
fontSize: 16.sp,
fontWeight: FontWeight.w500,
child: Column(
children: [
Center(
child: Text(
isCallConnected ? _formatDuration(duration) : '正在呼叫中',
style: TextStyle(
color: Colors.white,
fontSize: 16.sp,
fontWeight: FontWeight.w500,
),
),
), ),
),
Obx(() {
final callSession = _callController.currentCall.value;
final consumeData = _callController.consumeResponse.value;
// waitCalling
final isCalling = callSession?.status == CallStatus.waitCalling;
final isFree = consumeData?.isFree == true;
if (isCalling || isFree) {
return const SizedBox.shrink();
}
final availableBalance = consumeData?.availableBalance ?? 44;
final unitSellingBalance = consumeData?.unitSellingBalance ?? 35;
return Center(
child: Text(
'玫瑰剩余$availableBalance支($unitSellingBalance玫瑰/分钟)',
style: TextStyle(
color: Colors.white,
fontSize: 16.sp,
fontWeight: FontWeight.w500,
),
),
);
})
],
), ),
); );
}); });

Loading…
Cancel
Save