Browse Source

上传优化代码

weicu
liwei1dao 1 year ago
parent
commit
203b9ab8fb
  1. 1
      lib/core/bindings/initial_binding.dart
  2. 219
      lib/data/services/pay_service.dart
  3. 76
      lib/modules/payment/controllers/payment_controller.dart
  4. 269
      lib/modules/payment/views/payment_view.dart
  5. 96
      local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift
  6. 190
      local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/SystemFunctionHandler.kt
  7. 2
      local_plugins/location_service/ios/location_service/Sources/location_service/LocationService.swift
  8. 5
      pubspec.yaml

1
lib/core/bindings/initial_binding.dart

@ -92,7 +92,6 @@ class InitialBinding extends Bindings {
// 悬浮窗管理器 // 悬浮窗管理器
Get.lazyPut<FloatingWindowManager>(() => FloatingWindowManager(), Get.lazyPut<FloatingWindowManager>(() => FloatingWindowManager(),
fenix: true); fenix: true);
// 支付服务 // 支付服务
Get.lazyPut<PayService>(() => PayService(), fenix: true); Get.lazyPut<PayService>(() => PayService(), fenix: true);

219
lib/data/services/pay_service.dart

@ -1,5 +1,6 @@
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:voitrans/data/services/network/api.dart'; import 'package:voitrans/data/services/network/api.dart';
import 'package:tobias/tobias.dart';
import '../../modules/login/controllers/weixinLogin.dart'; import '../../modules/login/controllers/weixinLogin.dart';
@ -8,6 +9,7 @@ typedef PaymentResultCallback = void Function(bool success, String message);
/// 支付服务 /// 支付服务
class PayService extends GetxService { class PayService extends GetxService {
late FlWX _flwx; late FlWX _flwx;
late Tobias _tobias; // 添加 Tobias 实例
@override @override
void onInit() { void onInit() {
@ -22,6 +24,39 @@ class PayService extends GetxService {
return ''; return '';
})); }));
_flwx = FlWX(); _flwx = FlWX();
// 初始化支付宝SDK
_tobias = Tobias(); // 初始化 Tobias 实例
_initAlipay();
}
/// 初始化支付宝SDK
Future<void> _initAlipay() async {
try {
// 检查支付宝是否安装 - 修复用法
final isInstalled = await _tobias.isAliPayInstalled;
print('支付宝是否安装: $isInstalled');
} catch (e) {
print('初始化支付宝SDK失败: $e');
}
}
/// 查询支付结果
///
/// [orderId] 订单ID
/// 返回支付状态:success, failed, pending
Future<String?> queryPaymentStatus(String orderId) async {
try {
// TODO: 调用后端API查询支付状态
// final response = await Api.queryPaymentStatus({'orderId': orderId});
// return response['status'] as String?;
// 临时返回,实际使用时需要删除
return 'pending';
} catch (e) {
print('查询支付状态失败: $e');
return null;
}
} }
/// 微信支付 /// 微信支付
@ -136,33 +171,8 @@ class PayService extends GetxService {
} }
} }
/// 查询支付结果
///
/// [orderId] 订单ID
/// 返回支付状态:success, failed, pending
Future<String?> queryPaymentStatus(String orderId) async {
try {
// TODO: 调用后端API查询支付状态
// final response = await Api.queryPaymentStatus({'orderId': orderId});
// return response['status'] as String?;
// 临时返回,实际使用时需要删除
return 'pending';
} catch (e) {
print('查询支付状态失败: $e');
return null;
}
}
/// 完整的支付流程
///
/// [orderId] 订单ID
/// [amount] 支付金额(分)
/// [description] 商品描述
/// [userId] 用户ID
/// [onResult] 支付结果回调
Future<bool> processWechatPayment({ Future<bool> processWechatPayment({
required String orderId, required String productId,
required int amount, required int amount,
required String description, required String description,
String? userId, String? userId,
@ -171,7 +181,7 @@ class PayService extends GetxService {
try { try {
// 1. 创建支付订单 // 1. 创建支付订单
final paymentData = await createWechatPayOrder( final paymentData = await createWechatPayOrder(
productId: 'prod_001', productId: productId,
productName: '会员充值', productName: '会员充值',
productDesc: '1个月会员服务', productDesc: '1个月会员服务',
amount: 1, // 29.99元,单位:分 amount: 1, // 29.99元,单位:分
@ -194,4 +204,159 @@ class PayService extends GetxService {
return false; return false;
} }
} }
/// 完整的支付宝支付流程
///
/// [productId] 产品ID
/// [amount] 支付金额(分)
/// [description] 商品描述
/// [onResult] 支付结果回调
Future<bool> processAlipayPayment({
required String productId,
required int amount,
required String description,
PaymentResultCallback? onResult,
}) async {
try {
// 检查支付宝是否安装
final isInstalled = await _tobias.isAliPayInstalled;
if (!isInstalled) {
onResult?.call(false, '支付宝未安装');
return false;
}
// 调用现有的 createAlipayOrder 方法创建订单
final orderInfo = await createAlipayOrder(
productId: productId,
productName: description,
productDesc: description,
amount: amount,
paymentMethod: 3, // 支付宝支付
notifyUrl: 'https://api.ideapsound.com/api/home/pay_alipaynotify',
);
if (orderInfo == null) {
onResult?.call(false, '创建支付订单失败');
return false;
}
print('支付宝支付订单: $orderInfo');
// 调用支付宝支付
final result = await _tobias.pay(orderInfo);
print('支付宝支付结果: $result');
// 处理支付结果
if (result != null) {
final resultStatus = result['resultStatus'] as String?;
final memo = result['memo'] as String?;
if (resultStatus == '9000') {
onResult?.call(true, '支付成功');
return true;
} else {
onResult?.call(false, memo ?? '支付失败');
return false;
}
} else {
onResult?.call(false, '支付结果为空');
return false;
}
} catch (e) {
onResult?.call(false, '支付异常: $e');
return false;
}
}
/// 支付宝支付
///
/// [orderInfo] 支付宝订单信息字符串
/// [onResult] 支付结果回调
Future<bool> alipay({
required String orderInfo,
PaymentResultCallback? onResult,
}) async {
try {
// 检查支付宝是否安装 - 使用实例方法
final isInstalled = await _tobias.isAliPayInstalled;
if (!isInstalled) {
onResult?.call(false, '支付宝未安装');
return false;
}
// 调用支付宝支付 - 使用实例方法
final result = await _tobias.pay(orderInfo);
if (result != null) {
// 解析支付结果
final resultStatus = result['resultStatus'] as String?;
final memo = result['memo'] as String?;
if (resultStatus == '9000') {
// 支付成功
onResult?.call(true, '支付成功');
return true;
} else {
// 支付失败或取消
onResult?.call(false, memo ?? '支付失败');
return false;
}
} else {
onResult?.call(false, '支付结果为空');
return false;
}
} catch (e) {
onResult?.call(false, '支付异常: $e');
return false;
}
}
/// 创建支付宝支付订单
///
/// [productId] 商品ID
/// [productName] 商品名称
/// [productDesc] 商品描述
/// [amount] 支付金额(分)
/// [paymentMethod] 支付方式(2=支付宝)
/// [clientIp] 客户端IP(可选)
/// [notifyUrl] 通知URL(可选)
/// [extraData] 扩展数据(可选)
///
/// 返回支付宝订单信息字符串,用于调用 [alipay] 方法
Future<String?> createAlipayOrder({
required String productId,
required String productName,
String? productDesc,
required int amount,
int paymentMethod = 2, // 2表示支付宝
String? clientIp,
String? notifyUrl,
String? extraData,
}) async {
try {
// 调用后端API创建支付订单
final response = await Api.paycreateorder({
'product_id': productId,
'product_name': productName,
'product_desc': productDesc ?? '',
'amount': amount,
'payment_method': paymentMethod,
'client_ip': clientIp ?? '',
'notify_url': notifyUrl ?? '',
'extra_data': extraData ?? '',
});
print('创建支付宝订单: $response');
// 检查响应是否成功
if (response != null && response['alipay_pay_params'] != null) {
// 返回支付宝订单信息字符串
return response['alipay_pay_params'] as String?;
} else {
print('创建支付宝订单失败: ${response?['message'] ?? '未知错误'}');
return null;
}
} catch (e) {
print('创建支付宝订单异常: $e');
return null;
}
}
} }

76
lib/modules/payment/controllers/payment_controller.dart

@ -10,26 +10,29 @@ class PaymentController extends GetxController {
final RxString errorMessage = ''.obs; final RxString errorMessage = ''.obs;
// 测试订单信息 // 测试订单信息
final RxString testOrderId = ''.obs; final RxString testProductId = 'vip_101'.obs;
final RxDouble testAmount = 0.01.obs; // 测试金额 1分钱 final RxDouble testAmount = 0.01.obs; // 测试金额 1分钱
// 支付方式选择
final RxInt selectedPaymentMethod = 1.obs; // 1=微信支付, 2=支付宝支付
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
// 生成测试订单ID // 生成测试订单ID
testOrderId.value = 'test_${DateTime.now().millisecondsSinceEpoch}'; // testProductId.value = 'test_${DateTime.now().millisecondsSinceEpoch}';
} }
/// 测试微信支付 /// 测试微信支付
Future<void> testWechatPayment() async { Future<void> testWechatPayment() async {
try { try {
isLoading.value = true; isLoading.value = true;
paymentStatus.value = '正在发起支付...'; paymentStatus.value = '正在发起微信支付...';
errorMessage.value = ''; errorMessage.value = '';
// 调用支付服务,使用回调处理结果 // 调用支付服务,使用回调处理结果
final launched = await _payService.processWechatPayment( final launched = await _payService.processWechatPayment(
orderId: testOrderId.value, productId: testProductId.value,
amount: (testAmount.value * 100).toInt(), // 转换为分 amount: (testAmount.value * 100).toInt(), // 转换为分
description: '微信支付测试', description: '微信支付测试',
onResult: (success, message) { onResult: (success, message) {
@ -37,7 +40,7 @@ class PaymentController extends GetxController {
isLoading.value = false; isLoading.value = false;
if (success) { if (success) {
paymentStatus.value = '支付成功!'; paymentStatus.value = '微信支付成功!';
errorMessage.value = ''; errorMessage.value = '';
Get.snackbar( Get.snackbar(
'支付成功', '支付成功',
@ -45,7 +48,7 @@ class PaymentController extends GetxController {
snackPosition: SnackPosition.TOP, snackPosition: SnackPosition.TOP,
); );
} else { } else {
paymentStatus.value = '支付失败'; paymentStatus.value = '微信支付失败';
errorMessage.value = message ?? '未知错误'; errorMessage.value = message ?? '未知错误';
Get.snackbar( Get.snackbar(
'支付失败', '支付失败',
@ -59,23 +62,78 @@ class PaymentController extends GetxController {
if (!launched) { if (!launched) {
// 如果支付没有成功启动 // 如果支付没有成功启动
isLoading.value = false; isLoading.value = false;
paymentStatus.value = '支付启动失败'; paymentStatus.value = '微信支付启动失败';
errorMessage.value = '无法启动微信支付'; errorMessage.value = '无法启动微信支付';
Get.snackbar('支付启动失败', '无法启动微信支付'); Get.snackbar('支付启动失败', '无法启动微信支付');
} }
} catch (e) { } catch (e) {
isLoading.value = false; isLoading.value = false;
paymentStatus.value = '支付异常'; paymentStatus.value = '微信支付异常';
errorMessage.value = e.toString(); errorMessage.value = e.toString();
Get.snackbar('支付异常', e.toString()); Get.snackbar('支付异常', e.toString());
} }
} }
/// 测试支付宝支付
Future<void> testAlipayPayment() async {
try {
isLoading.value = true;
paymentStatus.value = '正在发起支付宝支付...';
errorMessage.value = '';
// 调用支付宝支付服务
final launched = await _payService.processAlipayPayment(
productId: testProductId.value, // 这里传递的是产品ID
amount: (testAmount.value * 100).toInt(), // 转换为分
description: '支付宝支付测试',
onResult: (success, message) {
// 支付结果回调
isLoading.value = false;
if (success) {
paymentStatus.value = '支付宝支付成功!';
errorMessage.value = '';
} else {
paymentStatus.value = '支付宝支付失败';
errorMessage.value = message;
}
},
);
if (!launched) {
// 如果支付没有成功启动
isLoading.value = false;
paymentStatus.value = '支付宝支付启动失败';
errorMessage.value = '无法启动支付宝支付';
Get.snackbar('支付启动失败', '无法启动支付宝支付');
}
} catch (e) {
isLoading.value = false;
paymentStatus.value = '支付宝支付异常';
errorMessage.value = e.toString();
Get.snackbar('支付异常', e.toString());
}
}
/// 根据选择的支付方式进行支付
Future<void> processPayment() async {
if (selectedPaymentMethod.value == 1) {
await testWechatPayment();
} else if (selectedPaymentMethod.value == 2) {
await testAlipayPayment();
}
}
/// 选择支付方式
void selectPaymentMethod(int method) {
selectedPaymentMethod.value = method;
}
/// 重置状态 /// 重置状态
void resetStatus() { void resetStatus() {
paymentStatus.value = ''; paymentStatus.value = '';
errorMessage.value = ''; errorMessage.value = '';
testOrderId.value = 'test_${DateTime.now().millisecondsSinceEpoch}'; // testProductId.value = 'test_${DateTime.now().millisecondsSinceEpoch}';
} }
/// 更新测试金额 /// 更新测试金额

269
lib/modules/payment/views/payment_view.dart

@ -16,7 +16,7 @@ class PaymentView extends GetView<PaymentController> {
backgroundColor: isDarkMode ? Colors.black : Colors.white, backgroundColor: isDarkMode ? Colors.black : Colors.white,
elevation: 0.5, elevation: 0.5,
title: Text( title: Text(
'微信支付测试', '支付测试',
style: TextStyle( style: TextStyle(
fontSize: 18.sp, fontSize: 18.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -39,6 +39,8 @@ class PaymentView extends GetView<PaymentController> {
children: [ children: [
_buildTestInfoCard(isDarkMode), _buildTestInfoCard(isDarkMode),
SizedBox(height: 20.h), SizedBox(height: 20.h),
_buildPaymentMethodSelector(isDarkMode),
SizedBox(height: 20.h),
_buildAmountSelector(isDarkMode), _buildAmountSelector(isDarkMode),
SizedBox(height: 20.h), SizedBox(height: 20.h),
_buildPaymentButton(isDarkMode), _buildPaymentButton(isDarkMode),
@ -79,11 +81,15 @@ class PaymentView extends GetView<PaymentController> {
), ),
), ),
SizedBox(height: 12.h), SizedBox(height: 12.h),
Obx(() => _buildInfoRow('订单ID', controller.testOrderId.value, isDarkMode)), Obx(() => _buildInfoRow(
'商品ID', controller.testProductId.value, isDarkMode)),
SizedBox(height: 8.h), SizedBox(height: 8.h),
_buildInfoRow('商品描述', '微信支付测试', isDarkMode), _buildInfoRow('商品描述', '微信支付测试', isDarkMode),
SizedBox(height: 8.h), SizedBox(height: 8.h),
Obx(() => _buildInfoRow('支付金额', '¥${controller.testAmount.value.toStringAsFixed(2)}', isDarkMode)), Obx(() => _buildInfoRow(
'支付金额',
'¥${controller.testAmount.value.toStringAsFixed(2)}',
isDarkMode)),
], ],
), ),
); );
@ -151,26 +157,29 @@ class PaymentView extends GetView<PaymentController> {
runSpacing: 8.h, runSpacing: 8.h,
children: amounts.map((amount) { children: amounts.map((amount) {
return Obx(() => GestureDetector( return Obx(() => GestureDetector(
onTap: () => controller.updateTestAmount(amount), onTap: () => controller.updateTestAmount(amount),
child: Container( child: Container(
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h), padding:
decoration: BoxDecoration( EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h),
color: controller.testAmount.value == amount decoration: BoxDecoration(
? Colors.blue[600] color: controller.testAmount.value == amount
: (isDarkMode ? Colors.grey[800] : Colors.grey[200]), ? Colors.blue[600]
borderRadius: BorderRadius.circular(20.r), : (isDarkMode
), ? Colors.grey[800]
child: Text( : Colors.grey[200]),
'¥${amount.toStringAsFixed(2)}', borderRadius: BorderRadius.circular(20.r),
style: TextStyle( ),
fontSize: 14.sp, child: Text(
color: controller.testAmount.value == amount '¥${amount.toStringAsFixed(2)}',
? Colors.white style: TextStyle(
: (isDarkMode ? Colors.white : Colors.black), fontSize: 14.sp,
color: controller.testAmount.value == amount
? Colors.white
: (isDarkMode ? Colors.white : Colors.black),
),
),
), ),
), ));
),
));
}).toList(), }).toList(),
), ),
], ],
@ -178,53 +187,185 @@ class PaymentView extends GetView<PaymentController> {
); );
} }
/// 支付按钮 /// 支付方式选择器
Widget _buildPaymentButton(bool isDarkMode) { Widget _buildPaymentMethodSelector(bool isDarkMode) {
return Obx(() => SizedBox( return Container(
width: double.infinity, padding: EdgeInsets.all(16.w),
height: 50.h, decoration: BoxDecoration(
child: ElevatedButton( color: isDarkMode ? Colors.grey[900] : Colors.white,
onPressed: controller.isLoading.value ? null : controller.testWechatPayment, borderRadius: BorderRadius.circular(12.r),
style: ElevatedButton.styleFrom( boxShadow: [
backgroundColor: Colors.green[600], BoxShadow(
foregroundColor: Colors.white, color: Colors.black.withOpacity(0.05),
shape: RoundedRectangleBorder( blurRadius: 10,
borderRadius: BorderRadius.circular(12.r), offset: const Offset(0, 2),
), ),
elevation: 2, ],
), ),
child: controller.isLoading.value child: Column(
? Row( crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center, children: [
children: [ Text(
SizedBox( '选择支付方式',
width: 20.w, style: TextStyle(
height: 20.w, fontSize: 16.sp,
child: const CircularProgressIndicator( fontWeight: FontWeight.w600,
strokeWidth: 2, color: isDarkMode ? Colors.white : Colors.black,
valueColor: AlwaysStoppedAnimation<Color>(Colors.white), ),
), ),
), SizedBox(height: 16.h),
SizedBox(width: 12.w), // 移除外层的 Obx,因为内部的 _buildPaymentMethodOption 已经有 Obx 了
const Text('处理中...'), Column(
], children: [
) _buildPaymentMethodOption(
: Row( method: 1,
mainAxisAlignment: MainAxisAlignment.center, title: '微信支付',
children: [ icon: Icons.wechat,
Icon(Icons.payment, size: 20.w), iconColor: Colors.green,
SizedBox(width: 8.w), isDarkMode: isDarkMode,
Text( ),
'发起微信支付', SizedBox(height: 12.h),
_buildPaymentMethodOption(
method: 2,
title: '支付宝支付',
icon: Icons.account_balance_wallet,
iconColor: Colors.blue,
isDarkMode: isDarkMode,
),
],
),
],
),
);
}
/// 支付方式选项
Widget _buildPaymentMethodOption({
required int method,
required String title,
required IconData icon,
required Color iconColor,
required bool isDarkMode,
}) {
return Obx(() => GestureDetector(
onTap: () => controller.selectPaymentMethod(method),
child: Container(
padding: EdgeInsets.all(12.w),
decoration: BoxDecoration(
color: controller.selectedPaymentMethod.value == method
? (isDarkMode
? Colors.blue.withOpacity(0.2)
: Colors.blue.withOpacity(0.1))
: (isDarkMode ? Colors.grey[800] : Colors.grey[100]),
borderRadius: BorderRadius.circular(8.r),
border: Border.all(
color: controller.selectedPaymentMethod.value == method
? Colors.blue
: Colors.transparent,
width: 1.5,
),
),
child: Row(
children: [
Icon(
icon,
color: iconColor,
size: 24.sp,
),
SizedBox(width: 12.w),
Expanded(
child: Text(
title,
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 14.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w500,
color: isDarkMode ? Colors.white : Colors.black,
), ),
), ),
], ),
if (controller.selectedPaymentMethod.value == method)
Icon(
Icons.check_circle,
color: Colors.blue,
size: 20.sp,
),
],
),
),
));
}
/// 支付按钮
Widget _buildPaymentButton(bool isDarkMode) {
return Obx(() => Container(
width: double.infinity,
height: 50.h,
child: ElevatedButton(
onPressed: controller.isLoading.value
? null
: () {
controller.processPayment();
},
style: ElevatedButton.styleFrom(
backgroundColor: controller.isLoading.value
? Colors.grey
: (controller.selectedPaymentMethod.value == 1
? Colors.green
: Colors.blue),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.r),
), ),
), elevation: 2,
)); ),
child: controller.isLoading.value
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: 20.w,
height: 20.w,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor:
AlwaysStoppedAnimation<Color>(Colors.white),
),
),
SizedBox(width: 12.w),
Text(
'支付中...',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
color: Colors.white,
),
),
],
)
: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
controller.selectedPaymentMethod.value == 1
? Icons.wechat
: Icons.account_balance_wallet,
color: Colors.white,
size: 20.sp,
),
SizedBox(width: 8.w),
Text(
controller.selectedPaymentMethod.value == 1
? '微信支付 ¥${controller.testAmount.value.toStringAsFixed(2)}'
: '支付宝支付 ¥${controller.testAmount.value.toStringAsFixed(2)}',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
color: Colors.white,
),
),
],
),
),
));
} }
/// 状态卡片 /// 状态卡片

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

@ -1077,17 +1077,20 @@ private func jsonToString(_ json: [String: Any]) -> String? {
print("百度导航链接: \(url.absoluteString)") print("百度导航链接: \(url.absoluteString)")
if UIApplication.shared.canOpenURL(url) { // 确保在主线程上执行UI操作
UIApplication.shared.open(url, options: [:]) { success in DispatchQueue.main.async {
if success { if UIApplication.shared.canOpenURL(url) {
print("成功打开百度地图") UIApplication.shared.open(url, options: [:]) { success in
} else { if success {
print("打开百度地图失败") print("成功打开百度地图")
} else {
print("打开百度地图失败")
}
} }
} else {
print("百度地图未安装")
// 可以在这里添加提示用户安装百度地图的逻辑
} }
} else {
print("百度地图未安装")
// 可以在这里添加提示用户安装百度地图的逻辑
} }
} }
@ -1135,17 +1138,20 @@ private func jsonToString(_ json: [String: Any]) -> String? {
print("高德导航链接: \(url.absoluteString)") print("高德导航链接: \(url.absoluteString)")
if UIApplication.shared.canOpenURL(url) { // 确保在主线程上执行UI操作
UIApplication.shared.open(url, options: [:]) { success in DispatchQueue.main.async {
if success { if UIApplication.shared.canOpenURL(url) {
print("成功打开高德地图") UIApplication.shared.open(url, options: [:]) { success in
} else { if success {
print("打开高德地图失败") print("成功打开高德地图")
} else {
print("打开高德地图失败")
}
} }
} else {
print("高德地图未安装")
// 可以在这里添加提示用户安装高德地图的逻辑
} }
} else {
print("高德地图未安装")
// 可以在这里添加提示用户安装高德地图的逻辑
} }
} }
@ -1160,25 +1166,28 @@ private func jsonToString(_ json: [String: Any]) -> String? {
URLQueryItem(name: "directionsmode", value: mode) URLQueryItem(name: "directionsmode", value: mode)
] ]
if let appUrl = components.url, UIApplication.shared.canOpenURL(appUrl) { // 确保在主线程上执行UI操作
UIApplication.shared.open(appUrl, options: [:]) { success in DispatchQueue.main.async {
if success { if let appUrl = components.url, UIApplication.shared.canOpenURL(appUrl) {
print("成功打开谷歌地图应用") UIApplication.shared.open(appUrl, options: [:]) { success in
} else { if success {
print("打开谷歌地图应用失败") print("成功打开谷歌地图应用")
} else {
print("打开谷歌地图应用失败")
}
} }
return
} }
return
}
// 方案2: 使用网页版谷歌地图 // 方案2: 使用网页版谷歌地图
let webUrlString = "https://www.google.com/maps/dir/?api=1&origin=Current+Location&destination=\(destination)&travelmode=\(mode)" let webUrlString = "https://www.google.com/maps/dir/?api=1&origin=Current+Location&destination=\(destination)&travelmode=\(mode)"
if let webUrl = URL(string: webUrlString) { if let webUrl = URL(string: webUrlString) {
UIApplication.shared.open(webUrl, options: [:]) { success in UIApplication.shared.open(webUrl, options: [:]) { success in
if success { if success {
print("成功打开谷歌地图网页版") print("成功打开谷歌地图网页版")
} else { } else {
print("打开谷歌地图网页版失败") print("打开谷歌地图网页版失败")
}
} }
} }
} }
@ -1198,16 +1207,19 @@ private func jsonToString(_ json: [String: Any]) -> String? {
return return
} }
if UIApplication.shared.canOpenURL(url) { // 确保在主线程上执行UI操作
UIApplication.shared.open(url, options: [:]) { success in DispatchQueue.main.async {
if success { if UIApplication.shared.canOpenURL(url) {
print("成功打开苹果地图") UIApplication.shared.open(url, options: [:]) { success in
} else { if success {
print("打开苹果地图失败") print("成功打开苹果地图")
} else {
print("打开苹果地图失败")
}
} }
} else {
print("苹果地图不可用")
} }
} else {
print("苹果地图不可用")
} }
} }

190
local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/SystemFunctionHandler.kt

@ -81,10 +81,32 @@ class SystemFunctionHandler(private val context: Context? = null) {
handler = SendTextMessageHandler(context) handler = SendTextMessageHandler(context)
) )
// 注册获取联系人列表函数
client.registerLocalFunction(
name = "get_contacts_list",
description = "获取用户通信录联系人列表",
parameters = mapOf(
"type" to "object",
"properties" to mapOf(
"limit" to mapOf(
"type" to "integer",
"description" to "返回联系人数量限制,默认20个",
"default" to 20
),
"search" to mapOf(
"type" to "string",
"description" to "搜索关键词,可选参数"
)
),
"required" to emptyList<String>()
),
handler = GetContactsListHandler(context)
)
// 注册拨打电话函数 // 注册拨打电话函数
client.registerLocalFunction( client.registerLocalFunction(
name = "make_phone_call", name = "make_phone_call",
description = "拨打电话", description = "拨打电话给指定联系人",
parameters = mapOf( parameters = mapOf(
"type" to "object", "type" to "object",
"properties" to mapOf( "properties" to mapOf(
@ -1328,6 +1350,172 @@ private class MaxVolumeHandler(private val context: Context?) : FunctionHandler
} }
/**
* 获取联系人列表处理器
*/
private class GetContactsListHandler(private val context: Context?) : FunctionHandler {
override suspend fun handle(arguments: Map<String, Any>): String {
if (context == null) {
return JSONObject().apply {
put("error", JSONObject().apply {
put("code", "context_error")
put("message", "上下文未初始化")
})
}.toString()
}
val handler = SystemFunctionHandler(context)
// 检查读取联系人权限
if (!handler.checkPermissionAndGuide(Manifest.permission.READ_CONTACTS, "读取联系人", "获取联系人列表")) {
return JSONObject().apply {
put("error", JSONObject().apply {
put("code", "permission_denied")
put("message", "需要联系人权限才能获取通信录,请在权限管理中开启联系人权限")
})
}.toString()
}
return try {
val limit = (arguments["limit"] as? Number)?.toInt() ?: 20
val searchKeyword = arguments["search"] as? String
val contacts = getContactsList(context, limit, searchKeyword)
JSONObject().apply {
put("result", "成功获取联系人列表")
put("count", contacts.size)
put("contacts", contacts)
}.toString()
} catch (e: Exception) {
Log.e(SystemFunctionHandler.TAG, "获取联系人列表失败: ${e.message}", e)
JSONObject().apply {
put("error", JSONObject().apply {
put("code", "fetch_failed")
put("message", "获取联系人列表失败: ${e.message}")
})
}.toString()
}
}
/**
* 获取联系人列表
* @param context 上下文
* @param limit 返回数量限制
* @param searchKeyword 搜索关键词
* @return 联系人列表
*/
private fun getContactsList(context: Context, limit: Int, searchKeyword: String?): List<Map<String, String>> {
val contacts = mutableListOf<Map<String, String>>()
try {
val contentResolver = context.contentResolver
// 构建查询条件
val selection = if (!searchKeyword.isNullOrBlank()) {
"${ContactsContract.Contacts.DISPLAY_NAME} LIKE ?"
} else {
null
}
val selectionArgs = if (!searchKeyword.isNullOrBlank()) {
arrayOf("%$searchKeyword%")
} else {
null
}
val projection = arrayOf(
ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.Contacts.HAS_PHONE_NUMBER
)
// 按姓名排序,限制数量
val sortOrder = "${ContactsContract.Contacts.DISPLAY_NAME} ASC LIMIT $limit"
contentResolver.query(
ContactsContract.Contacts.CONTENT_URI,
projection,
selection,
selectionArgs,
sortOrder
)?.use { cursor ->
val idColumn = cursor.getColumnIndex(ContactsContract.Contacts._ID)
val nameColumn = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)
val hasPhoneColumn = cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)
while (cursor.moveToNext() && contacts.size < limit) {
val contactId = cursor.getString(idColumn)
val displayName = cursor.getString(nameColumn) ?: "未知联系人"
val hasPhoneNumber = cursor.getInt(hasPhoneColumn) > 0
if (hasPhoneNumber) {
// 获取该联系人的电话号码
val phoneNumbers = getPhoneNumbersForContact(context, contactId)
if (phoneNumbers.isNotEmpty()) {
contacts.add(mapOf(
"id" to contactId,
"name" to displayName,
"phone" to phoneNumbers.first(), // 使用第一个电话号码
"allPhones" to phoneNumbers.joinToString(", ") // 所有电话号码
))
}
}
}
}
} catch (e: Exception) {
Log.e(SystemFunctionHandler.TAG, "查询联系人失败: ${e.message}", e)
}
return contacts
}
/**
* 获取指定联系人的所有电话号码
* @param context 上下文
* @param contactId 联系人ID
* @return 电话号码列表
*/
private fun getPhoneNumbersForContact(context: Context, contactId: String): List<String> {
val phoneNumbers = mutableListOf<String>()
try {
val phoneUri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI
val phoneProjection = arrayOf(
ContactsContract.CommonDataKinds.Phone.NUMBER,
ContactsContract.CommonDataKinds.Phone.TYPE
)
val phoneSelection = "${ContactsContract.CommonDataKinds.Phone.CONTACT_ID} = ?"
val phoneSelectionArgs = arrayOf(contactId)
context.contentResolver.query(
phoneUri,
phoneProjection,
phoneSelection,
phoneSelectionArgs,
null
)?.use { phoneCursor ->
val numberColumn = phoneCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)
while (phoneCursor.moveToNext()) {
val phoneNumber = phoneCursor.getString(numberColumn)
if (!phoneNumber.isNullOrBlank()) {
phoneNumbers.add(phoneNumber)
}
}
}
} catch (e: Exception) {
Log.e(SystemFunctionHandler.TAG, "获取联系人电话号码失败: ${e.message}", e)
}
return phoneNumbers
}
}
/** /**
* 更新用户画像 * 更新用户画像
*/ */

2
local_plugins/location_service/ios/location_service/Sources/location_service/LocationService.swift

@ -323,7 +323,7 @@ extension LocationService: CLLocationManagerDelegate {
// 过滤过旧的位置(超过5秒) // 过滤过旧的位置(超过5秒)
let locationAge = -location.timestamp.timeIntervalSinceNow let locationAge = -location.timestamp.timeIntervalSinceNow
guard locationAge < 5.0 else { guard locationAge < 360 else {
print("[LocationService] 位置数据过旧(\(locationAge)秒),忽略") print("[LocationService] 位置数据过旧(\(locationAge)秒),忽略")
return return
} }

5
pubspec.yaml

@ -61,6 +61,7 @@ dependencies:
lottie: ^3.3.1 lottie: ^3.3.1
app_settings: 5.2.0 app_settings: 5.2.0
fluwx: ^5.7.1 fluwx: ^5.7.1
tobias: ^5.2.0 # 支付宝SDK
url_launcher: ^6.3.1 url_launcher: ^6.3.1
firebase_core: ^4.0.0 firebase_core: ^4.0.0
firebase_auth: ^6.0.1 firebase_auth: ^6.0.1
@ -142,3 +143,7 @@ flutter:
- assets/images/music.png - assets/images/music.png
- .env - .env
- .mcp.json - .mcp.json
# 添加 tobias 配置
tobias:
url_scheme: voitrans # 你的应用的 URL Scheme

Loading…
Cancel
Save