Browse Source

上传新的支付代码

newdev_shunjiawei
liwei1dao 11 months ago
parent
commit
801c829fd9
  1. 13
      lib/data/models/goods.dart
  2. 7
      lib/data/services/ble_manager.dart
  3. 9
      lib/data/services/pay_service.dart
  4. 10
      lib/modules/agent/controllers/agent_controller.dart
  5. 60
      lib/modules/agent/views/agent_view.dart
  6. 47
      lib/modules/goods/controllers/goodsvip_controller.dart
  7. 812
      lib/modules/goods/views/goodsvip_view.dart
  8. 93
      lib/modules/settings/views/settings_view.dart
  9. 16
      local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt
  10. 2
      local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentServicePlugin.kt
  11. 6
      local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/BleMediaService.kt
  12. 13
      local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift
  13. 7
      local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/ChatApiService.kt
  14. 22
      local_plugins/chat_api/ios/chat_api/Sources/chat_api/ChatApiService.swift

13
lib/data/models/goods.dart

@ -10,7 +10,10 @@ enum UsageType {
/// 会议
Meet, // 2
/// 智能体
AI; // 3
AI, // 3
/// 智能体
Vip; // 4
/// Vip
// 解析支持 int 或 string(大小写不敏感)
static UsageType from(dynamic value) {
@ -24,6 +27,8 @@ enum UsageType {
return UsageType.Meet;
case 3:
return UsageType.AI;
case 4:
return UsageType.Vip;
default:
return UsageType.UsageNull;
}
@ -43,6 +48,8 @@ enum UsageType {
return UsageType.Meet;
case 'ai':
return UsageType.AI;
case 'vip':
return UsageType.Vip;
default:
return UsageType.UsageNull;
}
@ -60,6 +67,8 @@ enum UsageType {
return 2;
case UsageType.AI:
return 3;
case UsageType.Vip:
return 4;
}
}
@ -73,6 +82,8 @@ enum UsageType {
return 'Meet';
case UsageType.AI:
return 'AI';
case UsageType.Vip:
return 'Vip';
}
}
}

7
lib/data/services/ble_manager.dart

@ -9,7 +9,7 @@ import 'package:get/get.dart';
import 'package:ble_service/ble_service.dart';
import 'package:agent_service/agent_service.dart';
import '../../data/models/appconfig_model.dart';
import '../../data/models/user_Info.dart' as userInfo;
import '../../data/models/appconfig.dart';
import '../../core/utils/logger.dart';
import 'bluetooth_manager.dart';
@ -706,6 +706,9 @@ class BleManager extends GetxService {
Logger.info('启动BLE服务,musicServiceType: ${musicServiceType}');
final isTtsInterruptEnabled = await _loadTtsInterrupe();
final vipExpiryTime = userInfo.User.instance.vipexptime;
Logger.info('会员过期时间: $vipExpiryTime');
// 根据APP语言动态配置语音识别语言
List<String> supportedLanguages = [];
try {
@ -776,7 +779,7 @@ class BleManager extends GetxService {
'musicServiceType': musicServiceType,
"navigationMode": navigationMode,
"isttsinterrupt": isTtsInterruptEnabled ? "true" : "false",
"aiintegral": 100, //ai积分
"vipexptime": vipExpiryTime, //会员过期时间
"callingToolText": "callingTool".tr,
"insufficientIntegralText": "insufficientIntegral".tr,
};

9
lib/data/services/pay_service.dart

@ -2,6 +2,7 @@ import 'package:get/get.dart';
import 'dart:convert';
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:in_app_purchase/in_app_purchase.dart';
import 'package:in_app_purchase_android/in_app_purchase_android.dart';
import 'package:package_info_plus/package_info_plus.dart';
@ -229,6 +230,14 @@ class PayService extends GetxService {
}
return ok;
} catch (e) {
if (e is PlatformException &&
(e.code.contains('purchase_cancelled') ||
e.code.contains('storekit2_purchase_cancelled'))) {
onResult?.call(false, '已取消支付');
print(
'[IAP] 用户取消支付(同步抛出) code=${e.code} message=${e.message} product=${product.id}');
return false;
}
onResult?.call(false, '发起内购失败: $e');
print('[IAP] 发起内购异常: $e');
return false;

10
lib/modules/agent/controllers/agent_controller.dart

@ -46,7 +46,7 @@ class AgentController extends GetxController with WidgetsBindingObserver {
// 聊天消息列表
final messages = <Message>[].obs;
final currsessionid = ''.obs;
final aichatintegral = 0.obs;
final vipExpiryTime = 0.obs;
// UI控制器
final TextEditingController textController = TextEditingController();
final ScrollController scrollController = ScrollController();
@ -166,7 +166,10 @@ class AgentController extends GetxController with WidgetsBindingObserver {
@override
void onInit() {
super.onInit();
aichatintegral.value = User.isLoggedIn() ? User.instance.aichatintegral : 0;
vipExpiryTime.value = User.isLoggedIn() ? User.instance.vipexptime : 0;
ever<int>(User.refreshTick, (_) {
vipExpiryTime.value = User.instance.vipexptime;
});
_initAppDir();
_loadTtsPreference(); //加载TTS偏好设置
_loadTtsInterrupe(); //加载TTS播报打断偏好设置
@ -837,7 +840,7 @@ class AgentController extends GetxController with WidgetsBindingObserver {
),
onPressed: () {
Get.back();
Get.toNamed(Routes.goodsai);
Get.toNamed(Routes.goodsvip);
},
child: const Text('去充值'),
),
@ -1084,7 +1087,6 @@ class AgentController extends GetxController with WidgetsBindingObserver {
final sessionid = event.data['sessionid'] ?? '';
final integral = event.data['integral'] ?? 0;
User.instance.aichatintegral = integral;
aichatintegral.value = integral;
break;
default:
break;

60
lib/modules/agent/views/agent_view.dart

@ -13,6 +13,7 @@ import 'message_bubble.dart';
import '../widgets/full_screen_editor.dart';
import '../widgets/sound_bar_widget.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:intl/intl.dart';
class AgentView extends StatefulWidget {
const AgentView({Key? key}) : super(key: key);
@ -119,14 +120,27 @@ class _AgentViewState extends State<AgentView> with WidgetsBindingObserver {
),
),
SizedBox(width: 4.w),
Obx(() => Text(
'剩余积分: ${controller.aichatintegral.value}',
style: TextStyle(
fontSize: 12.sp,
color: Colors.green,
fontWeight: FontWeight.w500,
),
)),
Obx(() {
final vipexptime = controller.vipExpiryTime.value;
DateTime? expiry;
if (vipexptime > 0) {
expiry = vipexptime > 1000000000000
? DateTime.fromMillisecondsSinceEpoch(vipexptime)
: DateTime.fromMillisecondsSinceEpoch(
vipexptime * 1000);
}
final text = expiry != null
? '到期时间: ${DateFormat('yyyy-MM-dd').format(expiry)}'
: '未开启';
return Text(
text,
style: TextStyle(
fontSize: 12.sp,
color: Colors.green,
fontWeight: FontWeight.w500,
),
);
}),
],
),
],
@ -326,18 +340,18 @@ class _AgentViewState extends State<AgentView> with WidgetsBindingObserver {
getOverlayPosition:
IntroOverlayBuilder.overlayPosition,
overlayBuilder: (params) {
return IntroOverlayBuilder.aiBuilder(
params,
controller
.noviceGuideService
.aiStepsList[controller
.noviceGuideService.aiStep]
['endKey']
.tr,
onFinish: () async {
if (controller
.noviceGuideService.aiStep <
5) {
return IntroOverlayBuilder.aiBuilder(
params,
controller
.noviceGuideService
.aiStepsList[controller
.noviceGuideService
.aiStep]['endKey']
.tr,
onFinish: () async {
if (controller
.noviceGuideService.aiStep <
5) {
await Future.delayed(const Duration(
milliseconds: 300));
controller
@ -627,8 +641,8 @@ class _AgentViewState extends State<AgentView> with WidgetsBindingObserver {
controller
.noviceGuideService
.aiStepsList[controller
.noviceGuideService.aiStep]
['startKey']
.noviceGuideService
.aiStep]['startKey']
.tr,
buttonText: 'confirmInput'.tr,
onNext: () {
@ -877,7 +891,7 @@ class _AgentViewState extends State<AgentView> with WidgetsBindingObserver {
await controller
.noviceGuideService.aiStepsList[
controller.noviceGuideService
.aiStep]['method']();
.aiStep]['method']();
params.onNext!();
controller.sendMessage();
},

47
lib/modules/goods/controllers/goodsvip_controller.dart

@ -1,5 +1,3 @@
import 'package:get/get.dart';
import '../../../routes/app_routes.dart';
import '../../../data/models/goods.dart';
import '../../../data/services/network/api.dart';
import 'goods_base_controller.dart';
@ -7,23 +5,36 @@ import 'goods_base_controller.dart';
class GoodsvipController extends GoodsBaseController {
@override
Future<List<DBGoods>> fetchProducts() async {
final resp = await Api.getGoods({'usagetype': UsageType.UsageNull.toInt});
if (resp == null || resp is! Map) return [];
if (resp.containsKey('error') || resp.containsKey('msg')) {
final msg = resp['error'] ?? resp['msg'] ?? '服务器错误';
throw Exception(msg);
final types = [
UsageType.AI,
UsageType.Meet,
UsageType.Translation,
UsageType.Vip,
];
final Map<String, DBGoods> all = {};
for (final t in types) {
final resp = await Api.getGoods({'usagetype': t.toInt});
if (resp == null || resp is! Map) continue;
if (resp.containsKey('error') || resp.containsKey('msg')) {
final msg = resp['error'] ?? resp['msg'] ?? '服务器错误';
throw Exception(msg);
}
final container =
(resp is Map && resp['data'] is Map) ? resp['data'] : resp;
final data = (container is Map)
? (container['goodss'] ?? container['goodes'] ?? container['goods'])
: null;
if (data is List) {
final list = data
.whereType<Map<String, dynamic>>()
.map(DBGoods.fromJson)
.toList();
for (final g in list) {
all[g.id] = g;
}
}
}
final container =
(resp is Map && resp['data'] is Map) ? resp['data'] : resp;
final data = (container is Map)
? (container['goodss'] ?? container['goodes'] ?? container['goods'])
: null;
if (data is List) {
final list =
data.whereType<Map<String, dynamic>>().map(DBGoods.fromJson).toList();
return list.where((g) => g.usagetype == UsageType.UsageNull).toList();
}
return [];
return all.values.toList();
}
// Future<void> purchase(DBGoods item) async => super.purchase(item);

812
lib/modules/goods/views/goodsvip_view.dart

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:voitrans/data/models/goods.dart';
import '../../../data/models/user_Info.dart';
import '../controllers/goodsvip_controller.dart';
import '../utils/goods_i18n.dart';
@ -15,14 +16,19 @@ class GoodsvipView extends GetView<GoodsvipController> {
return Scaffold(
backgroundColor: isDarkMode ? Colors.black : Colors.grey[50],
appBar: AppBar(
backgroundColor: isDarkMode ? Colors.black : Colors.white,
backgroundColor: Colors.blue[600],
foregroundColor: Colors.white,
elevation: 0.5,
leading: IconButton(
icon: const Icon(Icons.arrow_back_ios_new),
onPressed: () => Get.back(),
),
title: Text(
'premiumMember'.tr, // 高级会员
'购买',
style: TextStyle(
fontSize: 18.sp,
fontWeight: FontWeight.w600,
color: isDarkMode ? Colors.white : Colors.black,
color: Colors.white,
),
),
centerTitle: true,
@ -34,20 +40,38 @@ class GoodsvipView extends GetView<GoodsvipController> {
} else if (controller.error.isNotEmpty) {
content = Center(child: Text(controller.error.value));
} else {
content = ListView.separated(
final Map<UsageType, List<DBGoods>> grouped = {};
for (final g in controller.items) {
grouped.putIfAbsent(g.usagetype, () => []).add(g);
}
final order = [
UsageType.Vip,
UsageType.Translation,
UsageType.Meet,
UsageType.AI,
];
final children = <Widget>[];
children.add(_buildFeaturesSection(isDarkMode));
children.add(SizedBox(height: 16.h));
// children.add(_buildScenesSection(isDarkMode));
// children.add(SizedBox(height: 16.h));
for (int i = 0; i < order.length; i++) {
final t = order[i];
final list = grouped[t];
if (list == null || list.isEmpty) continue;
children.add(_buildTypeSection(t, list, isDarkMode));
if (i != order.length - 1) {
children.add(SizedBox(height: 16.h));
children.add(
Divider(color: isDarkMode ? Colors.white24 : Colors.black12));
children.add(SizedBox(height: 16.h));
}
}
children.add(_buildFooterSection(isDarkMode));
content = ListView(
padding: EdgeInsets.all(16.w),
itemCount: controller.items.length + 2,
separatorBuilder: (_, __) => SizedBox(height: 12.h),
itemBuilder: (_, index) {
if (index < controller.items.length) {
final item = controller.items[index];
return _buildGoodsCard(item, isDarkMode);
}
if (index == controller.items.length) {
return _buildFeaturesSection(isDarkMode);
}
return _buildFooterSection(isDarkMode);
},
children: children,
);
}
return Stack(
@ -71,92 +95,6 @@ class GoodsvipView extends GetView<GoodsvipController> {
);
}
Widget _buildGoodsCard(DBGoods item, bool isDarkMode) {
return Container(
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: isDarkMode ? Colors.grey[900] : Colors.white,
borderRadius: BorderRadius.circular(12.r),
boxShadow: [
BoxShadow(
color: isDarkMode
? Colors.black.withOpacity(0.2)
: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: Row(
children: [
Container(
width: 42.w,
height: 42.w,
decoration: BoxDecoration(
color: isDarkMode
? Colors.amber[900]!.withOpacity(0.3)
: Colors.amber[100]!,
borderRadius: BorderRadius.circular(21.r),
),
child: Icon(Icons.workspace_premium,
color: isDarkMode ? Colors.amber[300]! : Colors.amber[600]!),
),
SizedBox(width: 12.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(item.name,
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
color: isDarkMode ? Colors.white : Colors.black,
)),
SizedBox(height: 4.h),
Text(localizedGoodsDesc(item),
style: TextStyle(
fontSize: 12.sp,
color: isDarkMode ? Colors.white70 : Colors.grey[600],
)),
],
),
),
SizedBox(width: 12.w),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text('¥${item.price.toStringAsFixed(2)}',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
color: isDarkMode ? Colors.white : Colors.black,
)),
SizedBox(height: 8.h),
GestureDetector(
onTap: () => controller.purchase(item),
child: Container(
padding:
EdgeInsets.symmetric(horizontal: 12.w, vertical: 6.h),
decoration: BoxDecoration(
color: Colors.blue[600],
borderRadius: BorderRadius.circular(20.r),
),
child: Text(
'purchase'.tr,
style: TextStyle(
fontSize: 12.sp,
color: Colors.white,
),
),
),
),
],
),
],
),
);
}
Widget _buildFeaturesSection(bool isDarkMode) {
final textColor = isDarkMode ? Colors.white : Colors.black;
final subColor = isDarkMode ? Colors.white70 : Colors.grey[700];
@ -317,4 +255,674 @@ class GoodsvipView extends GetView<GoodsvipController> {
),
);
}
Widget _buildTypeHeader(UsageType type, bool isDarkMode) {
final titles = {
UsageType.AI: 'aiFunction'.tr,
UsageType.Meet: 'conferenceAssistant'.tr,
UsageType.Translation: 'simultaneousTranslation'.tr,
UsageType.Vip: 'premiumMember'.tr,
};
final iconAndColor = _iconColorForType(type, isDarkMode);
final subColor = isDarkMode ? Colors.white70 : Colors.grey[700];
String titleStr = titles[type] ?? type.name;
String? vipSubtitle;
if (type == UsageType.Vip) {
int viplv = 0;
int vipexptime = 0;
if (User.isLoggedIn()) {
final u = User.instance;
viplv = u.viplv;
vipexptime = u.vipexptime;
}
DateTime? expiry;
if (vipexptime > 0) {
expiry = vipexptime > 1000000000000
? DateTime.fromMillisecondsSinceEpoch(vipexptime)
: DateTime.fromMillisecondsSinceEpoch(vipexptime * 1000);
}
titleStr = 'Vip $viplv';
vipSubtitle = expiry != null ? _fmtDate(expiry) : '未开启';
}
return Container(
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: isDarkMode ? Colors.grey[900] : Colors.white,
borderRadius: BorderRadius.circular(12.r),
boxShadow: [
BoxShadow(
color: isDarkMode
? Colors.black.withOpacity(0.2)
: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Container(
// width: 44.w,
// height: 44.w,
// decoration: BoxDecoration(
// color: iconAndColor.background,
// borderRadius: BorderRadius.circular(22.r),
// ),
// child: Icon(iconAndColor.icon, color: iconAndColor.foreground),
// ),
// SizedBox(width: 12.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
titleStr,
style: TextStyle(
fontSize: 18.sp,
fontWeight: FontWeight.w700,
color: isDarkMode ? Colors.white : Colors.black,
),
),
SizedBox(height: 8.h),
if (type == UsageType.Vip && vipSubtitle != null)
Text(
vipSubtitle,
style: TextStyle(fontSize: 12.sp, color: subColor),
)
else
_buildMetricsInline(type, isDarkMode),
],
),
),
],
),
);
}
Widget _buildTypeSection(
UsageType type, List<DBGoods> list, bool isDarkMode) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildTypeHeader(type, isDarkMode),
SizedBox(height: 12.h),
_buildGoodsGrid(list, type, isDarkMode),
],
);
}
Widget _buildMetricsInline(UsageType type, bool isDarkMode) {
final textColor = isDarkMode ? Colors.white : Colors.black;
final subColor = isDarkMode ? Colors.white70 : Colors.grey[700];
if (type == UsageType.Vip) {
int viplv = 0;
int vipexptime = 0;
if (User.isLoggedIn()) {
final u = User.instance;
viplv = u.viplv;
vipexptime = u.vipexptime;
}
DateTime? expiry;
if (vipexptime > 0) {
expiry = vipexptime > 1000000000000
? DateTime.fromMillisecondsSinceEpoch(vipexptime)
: DateTime.fromMillisecondsSinceEpoch(vipexptime * 1000);
}
return Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Lv.$viplv',
style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.w600,
color: textColor,
),
),
SizedBox(height: 4.h),
Text(
expiry != null
? '${'validityPeriod'.tr}${_fmtDate(expiry)}'
: 'notBound'.tr,
style: TextStyle(fontSize: 12.sp, color: subColor),
),
],
),
),
// Container(
// padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 4.h),
// decoration: BoxDecoration(
// color: isDarkMode ? Colors.white10 : Colors.black12,
// borderRadius: BorderRadius.circular(16.r),
// ),
// child: Text(
// viplv > 0 ? 'enabled'.tr : 'disabled'.tr,
// style: TextStyle(fontSize: 11.sp, color: subColor),
// ),
// ),
],
);
}
int total = 0;
int remain = 0;
if (User.isLoggedIn()) {
final u = User.instance;
if (type == UsageType.AI) {
total = u.aichattotalintegral;
remain = u.aichatintegral;
} else if (type == UsageType.Meet) {
total = u.meettotalintegral;
remain = u.meetintegral;
} else if (type == UsageType.Translation) {
total = u.tradetotalintegral;
remain = u.tradeintegral;
}
}
final progress = (total > 0) ? (remain / total).clamp(0.0, 1.0) : 0.0;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LinearProgressIndicator(
value: progress,
minHeight: 8.h,
backgroundColor: isDarkMode ? Colors.grey[800] : Colors.grey[300],
valueColor: AlwaysStoppedAnimation<Color>(
isDarkMode ? Colors.blue[300]! : Colors.blue[600]!),
),
SizedBox(height: 8.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${'totalCount'.tr}:$total',
style: TextStyle(fontSize: 12.sp, color: subColor),
),
Text(
'${'remaining'.tr}:$remain',
style: TextStyle(fontSize: 12.sp, color: subColor),
),
],
),
],
);
}
Widget _buildGoodsCardByType(DBGoods item, UsageType type, bool isDarkMode) {
final iconAndColor = _iconColorForType(type, isDarkMode);
return Container(
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: isDarkMode ? Colors.grey[900] : Colors.white,
borderRadius: BorderRadius.circular(12.r),
boxShadow: [
BoxShadow(
color: isDarkMode
? Colors.black.withOpacity(0.2)
: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: Row(
children: [
Container(
width: 42.w,
height: 42.w,
decoration: BoxDecoration(
color: iconAndColor.background,
borderRadius: BorderRadius.circular(21.r),
),
child: Icon(iconAndColor.icon, color: iconAndColor.foreground),
),
SizedBox(width: 12.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(localizedGoodsName(item),
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
color: isDarkMode ? Colors.white : Colors.black,
)),
SizedBox(height: 4.h),
Text(localizedGoodsDesc(item),
style: TextStyle(
fontSize: 12.sp,
color: isDarkMode ? Colors.white70 : Colors.grey[600],
)),
],
),
),
SizedBox(width: 12.w),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text('¥${item.priceYuan.toStringAsFixed(2)}',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
color: isDarkMode ? Colors.white : Colors.black,
)),
SizedBox(height: 8.h),
GestureDetector(
onTap: () => controller.purchase(item),
child: Container(
padding:
EdgeInsets.symmetric(horizontal: 12.w, vertical: 6.h),
decoration: BoxDecoration(
color: Colors.blue[600],
borderRadius: BorderRadius.circular(20.r),
),
child: Text(
'purchase'.tr,
style: TextStyle(
fontSize: 12.sp,
color: Colors.white,
),
),
),
),
],
),
],
),
);
}
Widget _buildMetricsForType(UsageType type, bool isDarkMode) {
if (type == UsageType.Vip) {
return _buildVipMetricsCard(isDarkMode);
}
return _buildQuotaCard(type, isDarkMode);
}
Widget _buildVipMetricsCard(bool isDarkMode) {
final textColor = isDarkMode ? Colors.white : Colors.black;
final subColor = isDarkMode ? Colors.white70 : Colors.grey[700];
int viplv = 0;
int vipexptime = 0;
if (User.isLoggedIn()) {
final u = User.instance;
viplv = u.viplv;
vipexptime = u.vipexptime;
}
DateTime? expiry;
if (vipexptime > 0) {
expiry = vipexptime > 1000000000000
? DateTime.fromMillisecondsSinceEpoch(vipexptime)
: DateTime.fromMillisecondsSinceEpoch(vipexptime * 1000);
}
final iconColors = _iconColorForType(UsageType.Vip, isDarkMode);
return Container(
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: isDarkMode ? Colors.grey[900] : Colors.white,
borderRadius: BorderRadius.circular(12.r),
boxShadow: [
BoxShadow(
color: isDarkMode
? Colors.black.withOpacity(0.2)
: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: Row(
children: [
Container(
width: 42.w,
height: 42.w,
decoration: BoxDecoration(
color: iconColors.background,
borderRadius: BorderRadius.circular(21.r),
),
child: Icon(iconColors.icon, color: iconColors.foreground),
),
SizedBox(width: 12.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Lv.$viplv',
style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.w600,
color: textColor,
),
),
SizedBox(height: 4.h),
Text(
expiry != null
? '${'validityPeriod'.tr}${_fmtDate(expiry)}'
: 'notBound'.tr,
style: TextStyle(fontSize: 12.sp, color: subColor),
),
],
),
),
Container(
padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 4.h),
decoration: BoxDecoration(
color: isDarkMode ? Colors.white10 : Colors.black12,
borderRadius: BorderRadius.circular(16.r),
),
child: Text(
viplv > 0 ? 'enabled'.tr : 'disabled'.tr,
style: TextStyle(fontSize: 11.sp, color: subColor),
),
),
],
),
);
}
Widget _buildQuotaCard(UsageType type, bool isDarkMode) {
int total = 0;
int remain = 0;
if (User.isLoggedIn()) {
final u = User.instance;
if (type == UsageType.AI) {
total = u.aichattotalintegral;
remain = u.aichatintegral;
} else if (type == UsageType.Meet) {
total = u.meettotalintegral;
remain = u.meetintegral;
} else if (type == UsageType.Translation) {
total = u.tradetotalintegral;
remain = u.tradeintegral;
}
}
final progress = (total > 0) ? (remain / total).clamp(0.0, 1.0) : 0.0;
final iconColors = _iconColorForType(type, isDarkMode);
final subColor = isDarkMode ? Colors.white70 : Colors.grey[700];
final textColor = isDarkMode ? Colors.white : Colors.black;
return Container(
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: isDarkMode ? Colors.grey[900] : Colors.white,
borderRadius: BorderRadius.circular(12.r),
boxShadow: [
BoxShadow(
color: isDarkMode
? Colors.black.withOpacity(0.2)
: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
width: 36.w,
height: 36.w,
decoration: BoxDecoration(
color: iconColors.background,
borderRadius: BorderRadius.circular(18.r),
),
child: Icon(iconColors.icon, color: iconColors.foreground),
),
SizedBox(width: 8.w),
Text(
'remainingQuota'.tr,
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
color: textColor,
),
),
],
),
SizedBox(height: 12.h),
LinearProgressIndicator(
value: progress,
minHeight: 8.h,
backgroundColor: isDarkMode ? Colors.grey[800] : Colors.grey[300],
valueColor: AlwaysStoppedAnimation<Color>(
isDarkMode ? Colors.blue[300]! : Colors.blue[600]!),
),
SizedBox(height: 8.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${'totalCount'.tr}:$total',
style: TextStyle(fontSize: 12.sp, color: subColor),
),
Text(
'${'remaining'.tr}:$remain',
style: TextStyle(fontSize: 12.sp, color: subColor),
),
],
),
],
),
);
}
Widget _buildGoodsGrid(List<DBGoods> list, UsageType type, bool isDarkMode) {
return GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 12.h,
crossAxisSpacing: 12.w,
childAspectRatio: 1.15,
),
itemCount: list.length,
itemBuilder: (_, i) => _buildGridItemCard(list[i], type, isDarkMode),
);
}
Widget _buildGridItemCard(DBGoods item, UsageType type, bool isDarkMode) {
final iconColors = _iconColorForType(type, isDarkMode);
return Container(
padding: EdgeInsets.all(12.w),
decoration: BoxDecoration(
color: isDarkMode ? Colors.grey[900] : Colors.white,
borderRadius: BorderRadius.circular(12.r),
boxShadow: [
BoxShadow(
color: isDarkMode
? Colors.black.withOpacity(0.2)
: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
width: 34.w,
height: 34.w,
decoration: BoxDecoration(
color: iconColors.background,
borderRadius: BorderRadius.circular(17.r),
),
child: Icon(iconColors.icon, color: iconColors.foreground),
),
SizedBox(width: 8.w),
Expanded(
child: Text(
localizedGoodsName(item),
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.w600,
color: isDarkMode ? Colors.white : Colors.black,
),
),
),
],
),
SizedBox(height: 6.h),
Expanded(
child: Text(
localizedGoodsDesc(item),
maxLines: 3,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 12.sp,
color: isDarkMode ? Colors.white70 : Colors.grey[700],
),
),
),
SizedBox(height: 6.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'¥${item.priceYuan.toStringAsFixed(2)}',
style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.w600,
color: isDarkMode ? Colors.white : Colors.black,
),
),
GestureDetector(
onTap: () => controller.purchase(item),
child: Container(
padding:
EdgeInsets.symmetric(horizontal: 10.w, vertical: 6.h),
decoration: BoxDecoration(
color: Colors.blue[600],
borderRadius: BorderRadius.circular(8.r),
),
child: Text(
'purchase'.tr,
style: TextStyle(fontSize: 12.sp, color: Colors.white),
),
),
),
],
),
],
),
);
}
String _fmtDate(DateTime d) {
final y = d.year.toString().padLeft(4, '0');
final m = d.month.toString().padLeft(2, '0');
final day = d.day.toString().padLeft(2, '0');
return '$y-$m-$day';
}
_IconColors _iconColorForType(UsageType type, bool isDarkMode) {
switch (type) {
case UsageType.AI:
return _IconColors(
icon: Icons.smart_toy,
background: isDarkMode
? Colors.teal[900]!.withOpacity(0.3)
: Colors.teal[100]!,
foreground: isDarkMode ? Colors.teal[300]! : Colors.teal[600]!,
);
case UsageType.Meet:
return _IconColors(
icon: Icons.people_alt,
background: isDarkMode
? Colors.pink[900]!.withOpacity(0.3)
: Colors.pink[100]!,
foreground: isDarkMode ? Colors.pink[300]! : Colors.pink[600]!,
);
case UsageType.Translation:
return _IconColors(
icon: Icons.translate,
background: isDarkMode
? Colors.indigo[900]!.withOpacity(0.3)
: Colors.indigo[100]!,
foreground: isDarkMode ? Colors.indigo[300]! : Colors.indigo[600]!,
);
case UsageType.Vip:
case UsageType.UsageNull:
return _IconColors(
icon: Icons.workspace_premium,
background: isDarkMode
? Colors.amber[900]!.withOpacity(0.3)
: Colors.amber[100]!,
foreground: isDarkMode ? Colors.amber[300]! : Colors.amber[600]!,
);
}
}
Widget _buildScenesSection(bool isDarkMode) {
final textColor = isDarkMode ? Colors.white : Colors.black;
final subColor = isDarkMode ? Colors.white70 : Colors.grey[700];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'scene'.tr,
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
color: textColor,
),
),
SizedBox(height: 10.h),
Wrap(
spacing: 12.w,
runSpacing: 12.h,
children: [
_buildFeatureCard(
isDarkMode,
icon: Icons.smart_toy,
title: 'aiFunction'.tr,
lines: ['aiHeaderDesc1'.tr, 'aiHeaderDesc2'.tr],
colorSeed: Colors.teal,
),
_buildFeatureCard(
isDarkMode,
icon: Icons.people_alt,
title: 'conferenceAssistant'.tr,
lines: ['meetingHeaderDesc1'.tr, 'meetingHeaderDesc2'.tr],
colorSeed: Colors.pink,
),
_buildFeatureCard(
isDarkMode,
icon: Icons.translate,
title: 'simultaneousTranslation'.tr,
lines: ['tradeHeaderDesc1'.tr, 'tradeHeaderDesc2'.tr],
colorSeed: Colors.indigo,
),
],
),
SizedBox(height: 12.h),
Text(
'vipDisclaimer'.tr,
style: TextStyle(fontSize: 12.sp, color: subColor),
),
],
);
}
}
class _IconColors {
final IconData icon;
final Color background;
final Color foreground;
const _IconColors({
required this.icon,
required this.background,
required this.foreground,
});
}

93
lib/modules/settings/views/settings_view.dart

@ -243,46 +243,59 @@ class SettingsView extends GetView<SettingsController> {
),
],
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildCategoryItem(
icon: Icons.workspace_premium,
label: 'premiumMember'.tr,
iconBg: isDarkMode
? Colors.amber[900]!.withOpacity(0.3)
: Colors.amber[100]!,
iconColor: isDarkMode ? Colors.amber[300]! : Colors.amber[600]!,
onTap: () => Get.toNamed(Routes.goodsvip),
),
_buildCategoryItem(
icon: Icons.translate,
label: 'interpretation'.tr,
iconBg: isDarkMode
? Colors.indigo[900]!.withOpacity(0.3)
: Colors.indigo[100]!,
iconColor: isDarkMode ? Colors.indigo[300]! : Colors.indigo[600]!,
onTap: () => Get.toNamed(Routes.goodstrade),
),
_buildCategoryItem(
icon: Icons.file_open,
label: 'conferenceAssistant'.tr,
iconBg: isDarkMode
? Colors.pink[900]!.withOpacity(0.3)
: Colors.pink[100]!,
iconColor: isDarkMode ? Colors.pink[300]! : Colors.pink[600]!,
onTap: () => Get.toNamed(Routes.goodsmeet),
),
_buildCategoryItem(
icon: Icons.smart_toy,
label: 'aiFunction'.tr,
iconBg: isDarkMode
? Colors.teal[900]!.withOpacity(0.3)
: Colors.teal[100]!,
iconColor: isDarkMode ? Colors.teal[300]! : Colors.teal[600]!,
onTap: () => Get.toNamed(Routes.goodsai),
),
],
child: InkWell(
onTap: () => Get.toNamed(Routes.goodsvip),
borderRadius: BorderRadius.circular(16.r),
child: Row(
children: [
Container(
width: 42.w,
height: 42.w,
decoration: BoxDecoration(
color: isDarkMode
? Colors.amber[900]!.withOpacity(0.3)
: Colors.amber[100]!,
borderRadius: BorderRadius.circular(21.r),
),
child: Icon(
Icons.workspace_premium,
size: 20.w,
color: isDarkMode ? Colors.amber[300]! : Colors.amber[600]!,
),
),
SizedBox(width: 12.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'premiumMember'.tr,
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
color: Colors.white,
),
),
SizedBox(height: 4.h),
Text(
'${'higherQuota'.tr} · ${'fastResponse'.tr}',
style: TextStyle(
fontSize: 12.sp,
color: Colors.white70,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
),
),
Icon(
Icons.arrow_forward_ios,
size: 16.w,
color: Colors.white70,
),
],
),
),
);
}

16
local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt

@ -136,7 +136,7 @@ object AgentService : CoroutineScope {
private var currentAiJob: Job? = null
private var currsessionId: String = ""
private var aiintegral:Int = 0
private var vipExptime:Int = 0
// 空闲检测相关
private var idleCheckJob: Job? = null
@ -207,8 +207,8 @@ object AgentService : CoroutineScope {
usertoken = config["usertoken"]?.toString() ?: "";
callingToolText = config["callingToolText"]?.toString() ?: "";
insufficientIntegralText = config["insufficientIntegralText"]?.toString() ?: "";
aiintegral = config["aiintegral"]?.toString()?.toIntOrNull() ?: 0;
vipExptime = config["vipexptime"]?.toString()?.toIntOrNull() ?: 0;
Log.d(TAG, "Vip 过期时间 ${vipExptime}")
// 初始化上下文和服务
this.context = appContext.applicationContext
@ -1013,7 +1013,7 @@ object AgentService : CoroutineScope {
}
chatApiService.sendMessageStream(
sessionid,
aiintegral,
vipExptime,
messagesWithSystemPrompt,
object : StreamCallback {
@ -1033,7 +1033,7 @@ object AgentService : CoroutineScope {
val key = "AICHAT_FUNCTION_" + funcName
actions[key] = (actions[key] ?: 0) + 1
}
sendStatisticsEvent(actions)
// sendStatisticsEvent(actions)
}catch (e: Exception) {
Log.e(TAG, "liwei--------------- AI Call onUsage 异常", e)
}
@ -2597,13 +2597,13 @@ object AgentService : CoroutineScope {
}
for ((k, v) in dataMap) {
Log.d(TAG, "统计事件发送成功,响应: $k -> $v")
if(k == "3"){ //AI积分更新
aiintegral = v
if(k == "4"){ //AI积分更新
vipExptime = v
// 广播一个细化事件,便于前端单独处理 AI 积分更新
sendEvent(
"assistant_usages",
mapOf(
"integral" to aiintegral
"vipxptime" to vipExptime
)
)
}

2
local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentServicePlugin.kt

@ -80,7 +80,7 @@ class AgentServicePlugin : FlutterPlugin, MethodCallHandler, EventChannel.Stream
config["isttsinterrupt"]?.toString()?.let { intent.putExtra("isttsinterrupt", it) }
config["callingToolText"]?.toString()?.let { intent.putExtra("callingToolText", it) }
config["insufficientIntegralText"]?.toString()?.let { intent.putExtra("insufficientIntegralText", it) }
config["aiintegral"]?.toString()?.let { intent.putExtra("aiintegral", it) }
config["vipexptime"]?.toString()?.let { intent.putExtra("vipexptime", it) }
// 添加 supportedLanguages 参数处理

6
local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/BleMediaService.kt

@ -70,7 +70,7 @@ class BleMediaService : Service(), CoroutineScope {
private const val APP_NAVIGATION_MODE = "navigationMode"
private const val APP_CALLING_TOOL_TEXT = "callingToolText" // 工具调用提示
private const val APP_INSUFFICIENT_INTEGRAL_TEXT = "insufficientIntegralText" // 积分不足提示
private const val KEY_AI_INTEGRAL = "aiIntegral" // 积分不足提示
private const val KEY_VIP_EXPTIME = "vipexptime" // 会员到期时间
// 用于跟踪服务是否正在运行
private val isRunning = AtomicBoolean(false)
@ -582,7 +582,7 @@ private var companionManager: BleCompanionManager? = null
it.getStringExtra(APP_NAVIGATION_MODE)?.let { value -> config[APP_NAVIGATION_MODE] = value }
it.getStringExtra(APP_CALLING_TOOL_TEXT)?.let { value -> config[APP_CALLING_TOOL_TEXT] = value }
it.getStringExtra(APP_INSUFFICIENT_INTEGRAL_TEXT)?.let { value -> config[APP_INSUFFICIENT_INTEGRAL_TEXT] = value }
it.getIntExtra(KEY_AI_INTEGRAL, config[KEY_AI_INTEGRAL] as? Int ?: 0)
it.getIntExtra(KEY_VIP_EXPTIME, config[KEY_VIP_EXPTIME] as? Int ?: 0)
it.getStringExtra("isttsinterrupt")?.let { value -> config["isttsinterrupt"] = value }
// 提取 supportedLanguages 参数
@ -647,7 +647,7 @@ private var companionManager: BleCompanionManager? = null
config[APP_NAVIGATION_MODE] = getConfigString(prefs, APP_NAVIGATION_MODE, "")
config[APP_CALLING_TOOL_TEXT] = getConfigString(prefs, APP_CALLING_TOOL_TEXT, "")
config[APP_INSUFFICIENT_INTEGRAL_TEXT] = getConfigString(prefs, APP_INSUFFICIENT_INTEGRAL_TEXT, "")
config[KEY_AI_INTEGRAL] = getConfigInt(prefs, KEY_AI_INTEGRAL, 0)
config[KEY_VIP_EXPTIME] = getConfigInt(prefs, KEY_VIP_EXPTIME, 0)
// 添加 supportedLanguages 参数处理
val supportedLanguagesStr = getConfigString(prefs, KEY_SUPPORTED_LANGUAGES, "")

13
local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift

@ -69,6 +69,8 @@ class AgentServiceImpl: NSObject {
public var insufficientIntegralText = "" // 积分不足提示
private var navigationMode = ""
//会员过期时间
public var vipexptime = 0
private var isInitialized: Bool = false
private var isRecognizing: Bool = false
@ -183,7 +185,12 @@ class AgentServiceImpl: NSObject {
if let callingToolText = config["callingToolText"] as? String {
self.callingToolText = callingToolText
}
if let vipexptime = config["vipexptime"] as? Int {
self.vipexptime = vipexptime
}
//打印日志输出vipexptime
os_log("Vip 过期时间 %d", log: logger, type: .info, vipexptime)
if let insufficientIntegralText = config["insufficientIntegralText"] as? String {
self.insufficientIntegralText = insufficientIntegralText
}
@ -343,8 +350,6 @@ class AgentServiceImpl: NSObject {
sendError("初始化ChatAPI服务失败", code: "CHATAPI_INIT_ERROR")
return false
}
return true
}
@ -719,7 +724,7 @@ class AgentServiceImpl: NSObject {
azureTtsHelper?.startspeak(sessionid: sessionid)
os_log("设置回调并调用sendMessageStream", log: logger, type: .info)
chatApiService.setStreamCallback(sessionid,callback)
chatApiService.sendMessageStream(sessionid,messages: messages)
chatApiService.sendMessageStream(sessionid,vipexptime: vipexptime,messages: messages)
audioPlayer?.playAwaitSound()
os_log("sendMessageStream调用完成", log: logger, type: .info)
}

7
local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/ChatApiService.kt

@ -347,11 +347,12 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor
/**
* 发送消息(流式输出)- 兼容JSONArray格式
*/
fun sendMessageStream(sessionid: String,integral: Int, messages: org.json.JSONArray, callback: StreamCallback) {
fun sendMessageStream(sessionid: String,vipexptime: Int, messages: org.json.JSONArray, callback: StreamCallback) {
// 1. 立即中止旧会话
abortCurrentSession()
if (integral <= 0) {
callback.onError(sessionid,2001,"积分不足")
// 判断时间是否过期:如果 vipexptime 小于当前时间戳(秒),则视为过期
if (vipexptime <= (System.currentTimeMillis() / 1000)) {
callback.onError(sessionid,2001,"会员过期")
return
}
currSessionId = sessionid

22
local_plugins/chat_api/ios/chat_api/Sources/chat_api/ChatApiService.swift

@ -271,12 +271,13 @@ public class ChatApiService: NSObject {
throw ChatApiException("无效的响应格式")
}
/// 发送消息(流式输出)
// MARK: - 流式请求实现
public func sendMessageStream(_ sessionId: String, messages: [[String: Any]]) {
/// 发送消息(流式输出)
// MARK: - 流式请求实现
public func sendMessageStream(_ sessionId: String,vipexptime: Int, messages: [[String: Any]]) {
// 1. 立即中止旧会话
abortCurrentSession()
// 2. 创建新会话
currSessionId = sessionId
@ -289,6 +290,11 @@ public func sendMessageStream(_ sessionId: String, messages: [[String: Any]]) {
getSessionCallback(sessionId)?.onError(sessionId,1000,"当前网络不可用")
return
}
let nowSec = Int(Date().timeIntervalSince1970)
if vipexptime <= 0 || nowSec >= vipexptime {
getSessionCallback(sessionId)?.onError(sessionId,2001,"会员已过期")
return
}
// 保存当前消息和回调
currentMessages = messages
@ -400,7 +406,7 @@ public func sendMessageStream(_ sessionId: String, messages: [[String: Any]]) {
self.getSessionCallback(sessionId)?.onUsage(sessionId, promptTokens, completionTokens, totalTokens)
// 处理工具调用
let hasToolCalls = await self.processToolCalls(sessionId)
let hasToolCalls = await self.processToolCalls(sessionId,vipexptime: vipexptime)
if !hasToolCalls {
self.getSessionCallback(sessionId)?.onComplete(sessionId)
self.clearCurrentSession()
@ -444,7 +450,7 @@ private func roughTokenCount(text: String) -> Int {
}
/// 处理工具调用
private func processToolCalls(_ sessionId:String) async -> Bool {
private func processToolCalls(_ sessionId:String,vipexptime: Int) async -> Bool {
// 验证工具调用集合不为空
if toolCalls.isEmpty {
print("[Session: \(sessionId)] 没有工具调用需要处理")
@ -563,6 +569,7 @@ private func processToolCalls(_ sessionId:String) async -> Bool {
// 将结果发送回OpenAI继续对话
await self.sendFunctionCallResultInternal(
sessionId: sessionId,
vipexptime: vipexptime,
messages: self.currentMessages,
functionCall: functionCall,
functionResult: self.jsonToString(result) ?? "{}"
@ -575,6 +582,7 @@ private func processToolCalls(_ sessionId:String) async -> Bool {
let errorMessage = "工具调用处理失败: \(error.localizedDescription)"
await self.sendFunctionCallResultInternal(
sessionId: sessionId,
vipexptime: vipexptime,
messages: self.currentMessages,
functionCall: functionCall,
functionResult: errorMessage
@ -623,6 +631,7 @@ private func convertMapToJsonObject(_ map: [String: Any]) -> [String: Any] {
/// 发送函数调用结果(内部方法)
private func sendFunctionCallResultInternal(
sessionId:String,
vipexptime: Int,
messages: [[String: Any]],
functionCall: [String: Any],
functionResult: String
@ -656,7 +665,7 @@ private func convertMapToJsonObject(_ map: [String: Any]) -> [String: Any] {
])
// 重新发起对话,让AI基于工具调用结果生成回复
sendMessageStream(sessionId,messages: fullMessages)
sendMessageStream(sessionId,vipexptime: vipexptime,messages: fullMessages)
}
/// 取消当前流式请求
@ -1090,4 +1099,3 @@ private class LocalFunctionHandler: FunctionHandler {
return "LOCAL_FUNCTION:\(functionName)"
}
}

Loading…
Cancel
Save