import 'package:get/get.dart'; import 'package:flutter/material.dart'; import 'package:get_storage/get_storage.dart'; import 'dart:convert'; import '../../../data/services/volcano_ai_service.dart'; import '../../../data/services/volcano_tts_service.dart'; import '../models/message_model.dart'; class ChatController extends GetxController { late final String agentId; late final String agentName; late final String agentAvatar; late final String? agentSubtitle; late final String systemPrompt; late final String welcomeMessage; late final bool playVoiceOnEnter; final VolcanoAIService _aiService = VolcanoAIService(); late final VolcanoTtsService _ttsService; static const String _storagePrefix = 'chat_history_'; final messages = [].obs; final isLoading = false.obs; late final TextEditingController textController; late final ScrollController scrollController; final _storage = GetStorage(); // 用于存储当前正在流式生成的消息 final RxString currentStreamMessage = ''.obs; Message? _currentAssistantMessage; String _pendingTtsText = ''; // 用于累积TTS文本 static const int _minTtsLength = 20; // TTS最小文本长度 bool _isDisposed = false; // 添加销毁标记 ChatController() { _initControllers(); _initTtsService(); _initFromArguments(); } void _initFromArguments() { final arguments = Get.arguments as Map; agentId = arguments['agentId'] as String; agentName = arguments['agentName'] as String; agentAvatar = arguments['agentAvatar'] as String; agentSubtitle = arguments['agentSubtitle'] as String?; systemPrompt = arguments['systemPrompt'] as String; welcomeMessage = arguments['welcomeMessage'] as String; playVoiceOnEnter = arguments['playVoiceOnEnter'] as bool? ?? false; } void _initControllers() { textController = TextEditingController(); scrollController = ScrollController(); } void _initTtsService() { try { _ttsService = Get.find(); } catch (_) { _ttsService = Get.put(VolcanoTtsService(), permanent: true); } _ttsService.isEnabled.value = true; } @override void onInit() async { super.onInit(); await _initializeController(); // 如果需要在进入时播放语音,延迟一下以确保页面已加载 if (playVoiceOnEnter) { Future.delayed(const Duration(milliseconds: 500), () { generateSimpleGreeting(); }); } } Future _initializeController() async { try { await _loadChatHistory(); await _showWelcomeMessages(); } catch (e) { Get.snackbar( 'Error', 'Failed to initialize chat: $e', snackPosition: SnackPosition.BOTTOM, ); } } Future _showWelcomeMessages() async { try { if (messages.isEmpty && agentName.isNotEmpty) { // 生成动态问候语 await _generatePersonalizedGreeting(); } // 保存聊天记录并滚动到底部 await _saveChatHistory(); Future.delayed(const Duration(milliseconds: 100), _scrollToBottom); } catch (e) { print('显示欢迎消息失败: $e'); // 如果生成问候语失败,确保至少显示静态欢迎消息 if (_ttsService.isEnabled.value) { _ttsService.speak(welcomeMessage); } } } Future _generatePersonalizedGreeting() async { if (_isDisposed) return; String fullGreeting = ''; try { currentStreamMessage.value = ''; _currentAssistantMessage = Message( role: 'assistant', content: '', timestamp: DateTime.now(), ); messages.add(_currentAssistantMessage!); // 构建用于生成个性化问候语的消息列表 final greetingMessages = [ {'role': 'system', 'content': systemPrompt}, { 'role': 'user', 'content': '请根据你的身份和性格,用一句简短的话向我打个招呼,要体现你的特点,不要超过18个字。' }, ]; await for (final chunk in _aiService.sendMessageStream( messages: greetingMessages, systemPrompt: systemPrompt, )) { if (_isDisposed) break; // 更新消息内容 currentStreamMessage.value += chunk; _updateAssistantMessage(currentStreamMessage.value); fullGreeting += chunk; } // 完整问候语生成完毕后,一次性播放 if (!_isDisposed && _ttsService.isEnabled.value && fullGreeting.isNotEmpty) { await _ttsService.speak(fullGreeting); } } catch (e) { print('生成问候语失败: $e'); // 移除未完成的消息 if (_currentAssistantMessage != null) { messages.remove(_currentAssistantMessage); } } finally { if (!_isDisposed) { _currentAssistantMessage = null; currentStreamMessage.value = ''; } } } @override void onClose() { _cleanupResources(); super.onClose(); } @override void dispose() { _cleanupResources(); super.dispose(); } void _cleanupResources() { if (_isDisposed) return; // 防止重复清理 _isDisposed = true; try { if (textController.hasListeners) { textController.dispose(); } } catch (e) { print('Disposing textController failed: $e'); } try { if (scrollController.hasClients) { scrollController.dispose(); } } catch (e) { print('Disposing scrollController failed: $e'); } try { _ttsService.stop(); } catch (e) { print('Stopping TTS service failed: $e'); } // 清理其他资源 currentStreamMessage.value = ''; _currentAssistantMessage = null; _pendingTtsText = ''; } Future _loadChatHistory() async { try { final historyJson = _storage.read('${_storagePrefix}${agentId}'); if (historyJson != null) { final List historyList = json.decode(historyJson); messages.value = historyList.map((item) => Message.fromJson(item)).toList(); } } catch (e) { Get.snackbar( 'Error', 'Failed to load chat history: $e', snackPosition: SnackPosition.BOTTOM, ); } } Future _saveChatHistory() async { try { final historyJson = json.encode(messages.map((msg) => msg!.toJson()).toList()); await _storage.write('${_storagePrefix}${agentId}', historyJson); } catch (e) { Get.snackbar( 'Error', 'Failed to save chat history: $e', snackPosition: SnackPosition.BOTTOM, ); } } void _scrollToBottom() { if (scrollController.hasClients) { scrollController.animateTo( scrollController.position.maxScrollExtent, duration: const Duration(milliseconds: 300), curve: Curves.easeOut, ); } } void _updateAssistantMessage(String content) { if (_currentAssistantMessage != null) { final index = messages.indexWhere((msg) => msg.timestamp == _currentAssistantMessage?.timestamp && msg.role == _currentAssistantMessage?.role); if (index != -1) { messages[index] = Message( role: 'assistant', content: content, timestamp: _currentAssistantMessage!.timestamp, ); _scrollToBottom(); } } } void sendMessage(String text) async { if (text.trim().isEmpty) return; final userMessage = Message( role: 'user', content: text, timestamp: DateTime.now(), ); messages.add(userMessage); textController.clear(); await _saveChatHistory(); _scrollToBottom(); isLoading.value = true; try { currentStreamMessage.value = ''; _currentAssistantMessage = Message( role: 'assistant', content: '', timestamp: DateTime.now(), ); messages.add(_currentAssistantMessage!); _pendingTtsText = ''; await for (final chunk in _aiService.sendMessageStream( messages: messages .map((m) => { 'role': m.role, 'content': m.content, }) .toList(), systemPrompt: systemPrompt, )) { if (_isDisposed) break; currentStreamMessage.value += chunk; _updateAssistantMessage(currentStreamMessage.value); // 累积文本并合成 _pendingTtsText += chunk; if (!_isDisposed && _ttsService.isEnabled.value) { // 检查是否达到最小长度 if (_pendingTtsText.length >= _minTtsLength) { // 找到最后一个句子结束的位置 int lastSentenceEnd = _findLastSentenceEnd(_pendingTtsText); if (lastSentenceEnd > 0) { // 播放到最后一个句子结束的位置 String textToSpeak = _pendingTtsText.substring(0, lastSentenceEnd + 1); _ttsService.speak(textToSpeak); // 保留剩余的文本 _pendingTtsText = _pendingTtsText.substring(lastSentenceEnd + 1); } } } } // 处理剩余的文本 if (!_isDisposed && _ttsService.isEnabled.value && _pendingTtsText.isNotEmpty) { _ttsService.speak(_pendingTtsText); } _pendingTtsText = ''; if (!_isDisposed) { await _saveChatHistory(); } } catch (e) { if (!_isDisposed) { if (_currentAssistantMessage != null) { messages.remove(_currentAssistantMessage); } Get.snackbar( 'Error', 'Failed to get response from AI: $e', snackPosition: SnackPosition.BOTTOM, ); } } finally { if (!_isDisposed) { _currentAssistantMessage = null; currentStreamMessage.value = ''; isLoading.value = false; } } } int _findLastSentenceEnd(String text) { final sentenceEnds = [ text.lastIndexOf('。'), text.lastIndexOf('!'), text.lastIndexOf('?'), text.lastIndexOf('.'), text.lastIndexOf('!'), text.lastIndexOf('?'), ]; return sentenceEnds.reduce((max, pos) => pos > max ? pos : max); } Future clearChat() async { try { messages.clear(); await _storage.remove('${_storagePrefix}${agentId}'); await _showWelcomeMessages(); } catch (e) { Get.snackbar( 'Error', 'Failed to clear chat: $e', snackPosition: SnackPosition.BOTTOM, ); } } void toggleTTS() { _ttsService.toggleEnabled(); } /// 生成简单的问候语 Future generateSimpleGreeting() async { try { currentStreamMessage.value = ''; _currentAssistantMessage = Message( role: 'assistant', content: '', timestamp: DateTime.now(), ); messages.add(_currentAssistantMessage!); // 构建用于生成问候语的消息列表 final greetingMessages = [ {'role': 'system', 'content': systemPrompt}, {'role': 'user', 'content': '请用一句简短的话向我打个招呼,要体现你的特点,不要超过12个字。'}, ]; String fullGreeting = ''; await for (final chunk in _aiService.sendMessageStream( messages: greetingMessages, systemPrompt: systemPrompt, )) { if (_isDisposed) break; // 更新消息内容 currentStreamMessage.value += chunk; _updateAssistantMessage(currentStreamMessage.value); fullGreeting += chunk; } // 完整问候语生成完毕后,一次性播放 if (!_isDisposed && _ttsService.isEnabled.value && fullGreeting.isNotEmpty) { await _ttsService.speak(fullGreeting); } } catch (e) { print('生成问候语失败: $e'); // 移除未完成的消息 if (_currentAssistantMessage != null) { messages.remove(_currentAssistantMessage); } } finally { if (!_isDisposed) { _currentAssistantMessage = null; currentStreamMessage.value = ''; } } } }