Browse Source

refactor(agent): 重构消息模型为独立文件并统一发送方法

将Message类从agent_controller.dart移出到独立的message_model.dart文件,提高代码可维护性
将sendTextMessage()重命名为sendMessage()以支持多种消息类型发送
移除未使用的导入和注释掉的代码
weicu
lxm 1 year ago
parent
commit
254d86d0b2
  1. 217
      lib/modules/agent/controllers/agent_controller.dart
  2. 3
      lib/modules/agent/controllers/report_controller.dart
  3. 5
      lib/modules/agent/views/agent_view.dart

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

@ -4,7 +4,6 @@ import 'dart:io';
import 'package:amap_service/amap_service.dart'; import 'package:amap_service/amap_service.dart';
import 'package:deep_voice/data/services/music_manager.dart'; import 'package:deep_voice/data/services/music_manager.dart';
import 'package:deep_voice/data/services/navigation_manager.dart'; import 'package:deep_voice/data/services/navigation_manager.dart';
import 'package:deep_voice/routes/app_routes.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:agent_service/agent_service.dart'; import 'package:agent_service/agent_service.dart';
@ -12,32 +11,11 @@ import 'package:chat_storage/chat_storage.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:get_storage/get_storage.dart'; import 'package:get_storage/get_storage.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import 'package:app_settings/app_settings.dart';
import 'package:deep_voice/core/utils/logger.dart'; import 'package:deep_voice/core/utils/logger.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:photo_view/photo_view.dart'; import 'package:photo_view/photo_view.dart';
import '../../../core/utils/permission_util.dart'; import '../../../core/utils/permission_util.dart';
import '../../../data/models/message_model.dart';
class Message {
final bool isUser; //标识信息是否来自用户还是AI
final String text; //信息文本内容
final DateTime timestamp; // 消息时间戳
final bool isRecognizing; // 是否为语音识别中的临时消息
final bool hasImage; // 是否包含图片
final String? imagePath; // 图片路径
final Map? meta; //卡片数据
Message({
// 构造函数
required this.isUser,
required this.text,
DateTime? timestamp,
this.isRecognizing = false,
this.hasImage = false,
this.imagePath,
this.meta,
}) : this.timestamp = timestamp ?? DateTime.now();
}
class AgentController extends GetxController { class AgentController extends GetxController {
static AgentController get to => Get.find(); //单例访问模式 static AgentController get to => Get.find(); //单例访问模式
@ -214,7 +192,7 @@ class AgentController extends GetxController {
} }
} }
// 添加欢迎消息 // 添加初始欢迎消息
void _addWelcomeMessage() { void _addWelcomeMessage() {
messages.add(Message( messages.add(Message(
isUser: false, isUser: false,
@ -418,7 +396,7 @@ class AgentController extends GetxController {
break; break;
case AgentServiceEventType.functionCallResult: case AgentServiceEventType.functionCallResult:
final responseId = event.data['responseId'] ?? ''; final responseId = event.data['responseId'] ?? '';
// final result = event.data['result']; // final result = event.data['result'];
final metaStr = event.data['meta'] ?? ''; final metaStr = event.data['meta'] ?? '';
// final functionCall = event.data['function_call'] ?? ''; // final functionCall = event.data['function_call'] ?? '';
@ -521,8 +499,10 @@ class AgentController extends GetxController {
} }
// 发送消息(包括所有支持的消息类型) // 发送消息(包括所有支持的消息类型)
Future<void> sendTextMessage() async { //场景1:用户在文本输入框中输入文本,点击发送按钮,调用sendMessage()方法。
// 如果图片输入是活跃的,使用选中的图片和文本框的内容 //场景2:当用户选择图片后在文本框输入内容并发送时,调用sendMessage()转发给sendImageMessage()。
Future<void> sendMessage() async {
// 1. 处理图片+文本的混合输入
if (isImageInputActive.value && selectedImagePath.value != null) { if (isImageInputActive.value && selectedImagePath.value != null) {
final text = textController.text.trim(); final text = textController.text.trim();
final imagePath = selectedImagePath.value!; // 使用!强制断言非空 final imagePath = selectedImagePath.value!; // 使用!强制断言非空
@ -533,6 +513,7 @@ class AgentController extends GetxController {
return; return;
} }
// 2. 处理纯文本消息
final text = textController.text.trim(); final text = textController.text.trim();
if (text.isEmpty) return; if (text.isEmpty) return;
@ -541,7 +522,7 @@ class AgentController extends GetxController {
textController.clear(); textController.clear();
// 使用reverse:true时通常不需要手动滚动 // 使用reverse:true时通常不需要手动滚动
// 3. 调用AI服务处理
try { try {
isProcessing.value = true; isProcessing.value = true;
// 标记为新的AI回复 // 标记为新的AI回复
@ -636,36 +617,6 @@ class AgentController extends GetxController {
} }
} }
// 保存消息到持久化存储
// Future<void> _saveMessageToStorage(Message message) async {
// try {
// // 准备元数据,包含图片信息
// Map<String, dynamic> metadata = {};
// if (message.meta != null) {
// metadata = Map<String, dynamic>.from(message.meta!);
// }
// // 添加图片相关信息到元数据
// if (message.hasImage) {
// metadata['hasImage'] = true;
// metadata['imagePath'] = message.imagePath;
// }
// // 保存消息
// await ChatStorage.saveMessage(
// sessionId: sessionId,
// sender: message.isUser ? 'user' : 'assistant',
// message: message.text,
// metadata: metadata.isNotEmpty ? jsonEncode(metadata) : null,
// //timestamp: message.timestamp.toIso8601String(),
// );
// Logger.i(TAG, '消息已保存到持久化存储');
// } catch (e) {
// Logger.e(TAG, '保存消息到持久化存储失败: $e');
// }
// }
// 切换输入模式 // 切换输入模式
void toggleInputMode() async { void toggleInputMode() async {
// 如果当前在图片输入模式,先清除 // 如果当前在图片输入模式,先清除
@ -703,156 +654,6 @@ class AgentController extends GetxController {
} }
} }
// 检查麦克风权限,返回true表示已授权,false表示未授权
Future<bool> _checkMicrophonePermission() async {
try {
// 1.先获取当前麦克风权限状态
final micStatus = await Permission.microphone.status;
// 2.如果麦克风权限已授予,返回true
if (micStatus.isGranted) {
return true;
}
// 3.创建待请求权限列表(这里只请求麦克风权限)
List<Permission> permissionsToRequest = [];
permissionsToRequest.add(Permission.microphone);
// 4.弹出权限说明对话框(首次拒绝/还没授权时展示)
if (micStatus.isPermanentlyDenied) {
_showPermissionDialog(true);
} else {
_showPermissionDialog(false);
}
// 5.请求权限,并获取返回的权限状态集合
final statuses = await permissionsToRequest.request();
// 6.用于记录被拒绝的权限(便于后续判断)
Permission? deniedPermission;
// 7.遍历权限状态集合,检查是否有任何权限被拒绝
statuses.forEach((permission, status) {
if (!status.isGranted) {
deniedPermission = permission;
Logger.e(TAG, '权限 ${permission.toString()} 未被授予: $status');
}
});
// // 8. 如果是“永久拒绝”(用户勾选了“不再询问”)
// if (micStatus.isPermanentlyDenied) {
// _showPermissionDialog(true);
// return false;
// }
// // 9. 普通的拒绝情况,再次弹出说明对话框
// if (deniedPermission != null) {
// _showPermissionDialog(false);
// }
// 10. 如果权限未被授予,返回 false
return false;
} catch (e) {
Logger.e(TAG, '检查麦克风权限失败: $e');
return false;
}
}
// 显示权限提示对话框
void _showPermissionDialog(bool isPermanent) {
Get.generalDialog(
barrierDismissible: false,
barrierLabel: '权限提示',
transitionBuilder: (context, anim1, anim2, child) {
return SlideTransition(
position: Tween<Offset>(
begin: Offset(0, -1), // 从上方滑入
end: Offset(0, 0),
).animate(CurvedAnimation(parent: anim1, curve: Curves.easeOut)),
child: child,
);
},
pageBuilder: (context, anim1, anim2) {
return SafeArea(
child: Align(
alignment: Alignment.topCenter, // 关键:设置为顶部对齐
child: Padding(
padding: const EdgeInsets.only(top: 80, left: 24, right: 24),
child: Material(
borderRadius: BorderRadius.circular(12),
color: Colors.white,
child: Padding(
padding: const EdgeInsets.all(16),
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: 400),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('需要麦克风权限',
style: TextStyle(
fontSize: 18, fontWeight: FontWeight.bold)),
const SizedBox(height: 12),
Text(isPermanent
? '语音功能需要麦克风权限。请前往设置页面手动开启权限。'
: '语音功能需要麦克风权限。请允许使用麦克风权限以继续使用语音功能。'),
const SizedBox(height: 16),
const Text('权限使用说明:',
style: TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
const Text('• 麦克风权限:用于录制您的语音,实现语音翻译、语音助手对话等核心功能'),
const SizedBox(height: 8),
const Text('• 我们仅在您主动使用语音功能时访问麦克风'),
const SizedBox(height: 8),
const Text('• 您可以随时在系统设置中关闭权限'),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () => Get.back(),
child: const Text('取消'),
),
const SizedBox(width: 8),
TextButton(
onPressed: () {
Get.back();
if (isPermanent) {
AppSettings.openAppSettings();
} else {
Permission.microphone.request();
}
},
child: Text(isPermanent ? '前往设置' : '授权'),
),
],
),
],
),
),
),
),
),
),
);
},
);
}
// 获取Android SDK版本
Future<int> _getAndroidSdkVersion() async {
if (Platform.isAndroid) {
try {
// 这里我们简单返回一个较高的值,确保请求所有可能需要的权限
// 在实际应用中,你可能需要使用platform channel获取真实的SDK版本
return 31; // 对应Android 12
} catch (e) {
Logger.e(TAG, '获取Android SDK版本失败: $e');
}
}
return 0;
}
// 开始语音输入 // 开始语音输入
Future<void> startVoiceInput() async { Future<void> startVoiceInput() async {
if (isListening.value) return; if (isListening.value) return;

3
lib/modules/agent/controllers/report_controller.dart

@ -1,7 +1,8 @@
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'agent_controller.dart'; // 确保导入Message模型 //import 'agent_controller.dart'; // 确保导入Message模型
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../../data/services/network/api.dart'; import '../../../data/services/network/api.dart';
import '../../../data/models/message_model.dart';
class ReportController extends GetxController { class ReportController extends GetxController {
// 当前举报的消息 // 当前举报的消息

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

@ -8,6 +8,7 @@ import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../../../core/theme/app_colors.dart'; import '../../../core/theme/app_colors.dart';
import 'package:haptic_feedback/haptic_feedback.dart'; import 'package:haptic_feedback/haptic_feedback.dart';
import '../../../data/models/message_model.dart';
class AgentView extends StatelessWidget { class AgentView extends StatelessWidget {
const AgentView({Key? key}) : super(key: key); const AgentView({Key? key}) : super(key: key);
@ -384,12 +385,12 @@ class AgentView extends StatelessWidget {
vertical: isImageActive ? 12 : 0), vertical: isImageActive ? 12 : 0),
), ),
onSubmitted: (_) => onSubmitted: (_) =>
controller.sendTextMessage(), controller.sendMessage(),
); );
}), }),
), ),
GestureDetector( GestureDetector(
onTap: () => controller.sendTextMessage(), onTap: () => controller.sendMessage(),
child: Container( child: Container(
padding: const EdgeInsets.all(6), padding: const EdgeInsets.all(6),
child: Icon( child: Icon(

Loading…
Cancel
Save