From a70817745ce523659fc707fb660938c9805ace47 Mon Sep 17 00:00:00 2001 From: tanlongsheng <252620078@qq.com> Date: Tue, 1 Apr 2025 18:44:07 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=9A=E8=AE=AE=E5=8A=A9=E6=89=8B=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E4=BC=9A=E8=AE=AE=E6=80=BB=E7=BB=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/data/services/ai_service.dart | 6 + lib/data/services/db/database_helper.dart | 6 +- lib/data/services/db/sqflite_api.dart | 12 +- lib/data/services/deapsound_ai_service.dart | 8 ++ lib/data/services/kouzi_ai_service.dart | 8 ++ lib/data/services/volcano_ai_service.dart | 1 + lib/main.dart | 12 ++ .../controllers/meeting_edit_controller.dart | 31 +++++ .../meeting_record_controller.dart | 69 +++++++---- .../meeting/model/meeting_details_model.dart | 5 +- lib/modules/meeting/model/meeting_model.dart | 9 ++ .../meeting/views/meeting_details_view.dart | 4 +- .../meeting/views/meeting_edit_view.dart | 12 +- .../meeting/views/meeting_record_view.dart | 2 +- lib/modules/meeting/views/meeting_view.dart | 110 +++++++++++------- pubspec.yaml | 2 +- 16 files changed, 226 insertions(+), 71 deletions(-) diff --git a/lib/data/services/ai_service.dart b/lib/data/services/ai_service.dart index 91ab6fe7d..37a1f5df0 100644 --- a/lib/data/services/ai_service.dart +++ b/lib/data/services/ai_service.dart @@ -1,5 +1,11 @@ /// AI回复服务接口 abstract class AiService { + /// 非流式输出方法 + Future sendMessage({ + required List> messages, + required String systemPrompt, + }); + /// 流式输出方法 Stream sendMessageStream({ required List> messages, diff --git a/lib/data/services/db/database_helper.dart b/lib/data/services/db/database_helper.dart index 319935941..2c62e6709 100644 --- a/lib/data/services/db/database_helper.dart +++ b/lib/data/services/db/database_helper.dart @@ -32,6 +32,9 @@ class DatabaseHelper { title TEXT, content TEXT, seconds INTEGER, + address TEXT, + longitude TEXT, + latitude TEXT, creationtime INTEGER ) '''); @@ -41,7 +44,8 @@ class DatabaseHelper { id INTEGER PRIMARY KEY, meetingid INTEGER, audio TEXT, - content TEXT + content TEXT, + aisummary TEXT ) '''); await batch.commit(); diff --git a/lib/data/services/db/sqflite_api.dart b/lib/data/services/db/sqflite_api.dart index 21b68ea83..5af8dbf8f 100644 --- a/lib/data/services/db/sqflite_api.dart +++ b/lib/data/services/db/sqflite_api.dart @@ -41,12 +41,16 @@ class SqfliteApi { 'title': meeting['title'], 'content': meeting['firstContent'], 'seconds': meeting['seconds'], + 'address': meeting['address'], + 'longitude': meeting['longitude'], + 'latitude': meeting['latitude'], 'creationtime': meeting['creationTime'], }); await txn.insert('meetingdetails', { 'meetingid': id, 'audio': meeting['audio'], 'content': meeting['content'], + 'aisummary': meeting['aisummary'], }); }); return id; @@ -73,13 +77,19 @@ class SqfliteApi { 'title': meeting['title'], 'content': meeting['firstContent'], 'seconds': meeting['seconds'], + 'address': meeting['address'], + 'longitude': meeting['longitude'], + 'latitude': meeting['latitude'], }, where: 'id = ?', whereArgs: [id], ); await txn.update( 'meetingdetails', - {'content': meeting['content']}, + { + 'content': meeting['content'], + 'aisummary': meeting['aisummary'], + }, where: 'meetingid = ?', whereArgs: [id], ); diff --git a/lib/data/services/deapsound_ai_service.dart b/lib/data/services/deapsound_ai_service.dart index 5036923b9..5d1e652b1 100644 --- a/lib/data/services/deapsound_ai_service.dart +++ b/lib/data/services/deapsound_ai_service.dart @@ -14,6 +14,14 @@ class DeapsoundAIService implements AiService { apiKey = storage.read('logintoken'); } + @override + Future sendMessage({ + required List> messages, + required String systemPrompt, + }) async { + return ''; + } + @override Stream sendMessageStream({ required List> messages, diff --git a/lib/data/services/kouzi_ai_service.dart b/lib/data/services/kouzi_ai_service.dart index b2496c42d..a7300a320 100644 --- a/lib/data/services/kouzi_ai_service.dart +++ b/lib/data/services/kouzi_ai_service.dart @@ -10,6 +10,14 @@ class KouziAIService implements AiService { 'pat_QGDh6UGm3PaeL9GRS95JxrJ9aoU92ehMLQQ2NCre1TeBC5YeTcjpDmlz3efsJ8lh'; final String modelId = '7481292724306657316'; + @override + Future sendMessage({ + required List> messages, + required String systemPrompt, + }) async { + return ''; + } + @override Stream sendMessageStream({ required List> messages, diff --git a/lib/data/services/volcano_ai_service.dart b/lib/data/services/volcano_ai_service.dart index 576721642..e998a8270 100644 --- a/lib/data/services/volcano_ai_service.dart +++ b/lib/data/services/volcano_ai_service.dart @@ -32,6 +32,7 @@ class VolcanoAIService implements AiService { } // 非流式输出方法 + @override Future sendMessage({ required List> messages, required String systemPrompt, diff --git a/lib/main.dart b/lib/main.dart index 47dbe1b08..72b596679 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:get/get.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; @@ -82,6 +83,17 @@ class MainApp extends StatelessWidget { locale: AppTranslations.deviceLocale(), fallbackLocale: AppTranslations.fallbackLocale, supportedLocales: AppTranslations.supportedLocales, + builder: EasyLoading.init( + builder: (BuildContext context, Widget? widget) { + EasyLoading.instance.userInteractions = false; + return MediaQuery( + data: MediaQuery.of(context).copyWith( + textScaler: TextScaler.noScaling, + ), + child: widget!, + ); + }, + ), ); }, ); diff --git a/lib/modules/meeting/controllers/meeting_edit_controller.dart b/lib/modules/meeting/controllers/meeting_edit_controller.dart index 9b5702d80..d214735bc 100644 --- a/lib/modules/meeting/controllers/meeting_edit_controller.dart +++ b/lib/modules/meeting/controllers/meeting_edit_controller.dart @@ -1,13 +1,18 @@ import 'package:flutter/material.dart'; +import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:get/get.dart'; import 'package:just_audio/just_audio.dart'; +import '../../../data/services/ai_service.dart'; import '../../../data/services/db/sqflite_api.dart'; +import '../../../data/services/volcano_ai_service.dart'; import 'meeting_controller.dart'; import 'meeting_details_controller.dart'; class MeetingEditController extends GetxController with GetSingleTickerProviderStateMixin { + late AiService _aiService; + final MeetingController meetingController = Get.find(); final MeetingDetailsController meetingDetailsController = Get.find(); @@ -21,6 +26,10 @@ class MeetingEditController extends GetxController int creationTime = 0; + RxString address = ''.obs; + String _longitude = ''; + String _latitude = ''; + int id = 0; RxBool isPlay = false.obs; @@ -51,6 +60,9 @@ class MeetingEditController extends GetxController text: meetingDetailsController.dataDetails.value.content, ); creationTime = meetingDetailsController.meetingData.value.creationtime; + address.value = meetingDetailsController.meetingData.value.address; + _longitude = meetingDetailsController.meetingData.value.longitude; + _latitude = meetingDetailsController.meetingData.value.latitude; audioPlayer.setAudioSource( AudioSource.file(meetingDetailsController.dataDetails.value.audio)); audioPlayer.playerStateStream.listen((state) async { @@ -76,6 +88,15 @@ class MeetingEditController extends GetxController } void confirm() async { + EasyLoading.show(); + _aiService = Get.find(); + String aiSummary = await _aiService.sendMessage( + systemPrompt: '', + messages: [ + {'role': 'user', 'content': contentController.text}, + {'role': 'user', 'content': '帮我总结一下这次会议'}, + ], + ); String content = contentController.text; String firstContent = content.length >= 30 ? content.substring(0, 30) : content; @@ -87,6 +108,10 @@ class MeetingEditController extends GetxController 'firstContent': firstContent.replaceAll("\n", ""), 'seconds': meetingDetailsController.seconds, 'content': contentController.text, + 'aisummary': aiSummary, + 'address': address.value, + 'longitude': _longitude, + 'latitude': _latitude, }; final result = await SqfliteApi.editMeeting(id, data); if (result != null) { @@ -99,14 +124,20 @@ class MeetingEditController extends GetxController dataMap['title'] = title; dataMap['content'] = firstContent.replaceAll("\n", ""); dataMap['seconds'] = meetingDetailsController.seconds; + dataMap['address'] = address.value; meetingController.dataList[index] = dataMap; } meetingDetailsController.meetingData.value.title = title; + meetingDetailsController.meetingData.value.address = address.value; + meetingDetailsController.meetingData.value.longitude = _longitude; + meetingDetailsController.meetingData.value.latitude = _latitude; meetingDetailsController.dataDetails.value.content = contentController.text; + meetingDetailsController.dataDetails.value.aisummary = aiSummary; meetingDetailsController.meetingData.refresh(); meetingDetailsController.dataDetails.refresh(); Get.back(); } + EasyLoading.dismiss(); } } diff --git a/lib/modules/meeting/controllers/meeting_record_controller.dart b/lib/modules/meeting/controllers/meeting_record_controller.dart index 25f0d4c5f..ba449a942 100644 --- a/lib/modules/meeting/controllers/meeting_record_controller.dart +++ b/lib/modules/meeting/controllers/meeting_record_controller.dart @@ -3,18 +3,23 @@ import 'dart:io'; import 'dart:typed_data'; import 'package:flutter/material.dart'; +import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:get/get.dart'; import 'package:path_provider/path_provider.dart'; import 'package:permission_handler/permission_handler.dart'; import '../../../core/utils/logger.dart'; +import '../../../data/services/ai_service.dart'; import '../../../data/services/asr_service.dart'; import '../../../data/services/db/sqflite_api.dart'; import '../../../data/services/speech_impl/xunfei_asr_service.dart'; +import '../../../data/services/volcano_ai_service.dart'; import 'meeting_controller.dart'; class MeetingRecordController extends GetxController with GetSingleTickerProviderStateMixin { + late AiService _aiService; + final MeetingController meetingController = Get.find(); late AnimationController lottieController; @@ -28,6 +33,10 @@ class MeetingRecordController extends GetxController String title = ''; int creationTime = 0; + RxString address = '深圳市高新技术产业园区W2-A座'.obs; + String _longitude = ''; + String _latitude = ''; + RxString intermediateContent = ''.obs; RxString finalContent = ''.obs; @@ -75,7 +84,7 @@ class MeetingRecordController extends GetxController } // 暂停 - void _pause() async { + Future _pause() async { await _stopRecorder(); lottieController.stop(); isRecording.value = false; @@ -150,7 +159,9 @@ class MeetingRecordController extends GetxController // 完成 void complete() async { if (await Permission.storage.request().isGranted) { - _save(); + EasyLoading.show(); + await _pause(); + _aiSummary(); } else { Get.snackbar( 'error'.tr, // 错误 @@ -162,7 +173,19 @@ class MeetingRecordController extends GetxController } } - void _save() async { + void _aiSummary() async { + _aiService = Get.find(); + String aiSummary = await _aiService.sendMessage( + systemPrompt: '', + messages: [ + {'role': 'user', 'content': finalContent.value}, + {'role': 'user', 'content': '帮我总结一下这次会议'}, + ], + ); + _save(aiSummary); + } + + void _save(String aiSummary) async { String firstContent = finalContent.value.length >= 30 ? finalContent.value.substring(0, 30) : finalContent.value; @@ -176,26 +199,32 @@ class MeetingRecordController extends GetxController Uint8List wavData = Uint8List.fromList([...wavHeader, ...audio]); String audiopPath = await _saveAudioToFile(wavData, 'meeting_$creationTime.wav'); - if (audiopPath.isEmpty) return; - Map data = { - 'title': title, - 'firstContent': firstContent.replaceAll("\n", ""), - 'seconds': seconds.value, - 'creationTime': creationTime, - 'audio': audiopPath, - 'content': finalContent.value, - }; - final result = await SqfliteApi.insertMeeting(data); - if (result != null) { - meetingController.dataList.insert(0, { - 'id': result, + if (audiopPath.isNotEmpty) { + Map data = { 'title': title, - 'content': firstContent, + 'firstContent': firstContent.replaceAll("\n", ""), 'seconds': seconds.value, - 'creationtime': creationTime, - }); - Get.back(); + 'creationTime': creationTime, + 'audio': audiopPath, + 'content': finalContent.value, + 'aisummary': aiSummary, + 'address': address.value, + 'longitude': _longitude, + 'latitude': _latitude, + }; + final result = await SqfliteApi.insertMeeting(data); + if (result != null) { + meetingController.dataList.insert(0, { + 'id': result, + 'title': title, + 'content': firstContent, + 'seconds': seconds.value, + 'creationtime': creationTime, + }); + Get.back(); + } } + EasyLoading.dismiss(); } Future _saveAudioToFile(Uint8List audioData, String fileName) async { diff --git a/lib/modules/meeting/model/meeting_details_model.dart b/lib/modules/meeting/model/meeting_details_model.dart index 2b2845eff..fa7e08f97 100644 --- a/lib/modules/meeting/model/meeting_details_model.dart +++ b/lib/modules/meeting/model/meeting_details_model.dart @@ -5,13 +5,15 @@ class MeetingDetailsModel { late int meetingid; late String audio; late String content; + late String aisummary; factory MeetingDetailsModel.fromJson(Map map) => MeetingDetailsModel() ..id = map["id"] ..meetingid = map["meetingid"] ..audio = map["audio"] - ..content = map["content"]; + ..content = map["content"] + ..aisummary = map["aisummary"]; Map toJson() { final data = {}; @@ -19,6 +21,7 @@ class MeetingDetailsModel { data['meetingid'] = meetingid; data['audio'] = audio; data['content'] = content; + data['aisummary'] = aisummary; return data; } } diff --git a/lib/modules/meeting/model/meeting_model.dart b/lib/modules/meeting/model/meeting_model.dart index 7cf2b6873..36f9d3940 100644 --- a/lib/modules/meeting/model/meeting_model.dart +++ b/lib/modules/meeting/model/meeting_model.dart @@ -5,6 +5,9 @@ class MeetingModel { late String title; late String content; late int seconds; + late String address; + late String longitude; + late String latitude; late int creationtime; factory MeetingModel.fromJson(Map map) => MeetingModel() @@ -12,6 +15,9 @@ class MeetingModel { ..title = map["title"] ..content = map["content"] ..seconds = map["seconds"] + ..address = map["address"] + ..longitude = map["longitude"] + ..latitude = map["latitude"] ..creationtime = map["creationtime"]; Map toJson() { @@ -20,6 +26,9 @@ class MeetingModel { data['title'] = title; data['content'] = content; data['seconds'] = seconds; + data['address'] = address; + data['longitude'] = longitude; + data['latitude'] = latitude; data['creationtime'] = creationtime; return data; } diff --git a/lib/modules/meeting/views/meeting_details_view.dart b/lib/modules/meeting/views/meeting_details_view.dart index 476411f46..9947c0b7c 100644 --- a/lib/modules/meeting/views/meeting_details_view.dart +++ b/lib/modules/meeting/views/meeting_details_view.dart @@ -90,7 +90,7 @@ class MeetingDetailsView extends GetView { color: isDarkMode ? Colors.grey[400] : Colors.grey[500], ), Text( - '深圳市高新技术产业园区W2-A座', + controller.meetingData.value.address, style: TextStyle( color: isDarkMode ? Colors.grey[400] : Colors.grey[500], fontSize: 14.sp, @@ -268,7 +268,7 @@ class MeetingDetailsView extends GetView { children: [ SingleChildScrollView( child: Text( - controller.dataDetails.value.content, + controller.dataDetails.value.aisummary, ), ), SingleChildScrollView( diff --git a/lib/modules/meeting/views/meeting_edit_view.dart b/lib/modules/meeting/views/meeting_edit_view.dart index 19a9e0f22..ce28c2fca 100644 --- a/lib/modules/meeting/views/meeting_edit_view.dart +++ b/lib/modules/meeting/views/meeting_edit_view.dart @@ -119,11 +119,13 @@ class MeetingEditView extends GetView { size: 16.w, color: isDarkMode ? Colors.grey[400] : Colors.grey[500], ), - Text( - '深圳市高新技术产业园区W2-A座', - style: TextStyle( - color: isDarkMode ? Colors.grey[400] : Colors.grey[500], - fontSize: 14.sp, + Obx( + () => Text( + controller.address.value, + style: TextStyle( + color: isDarkMode ? Colors.grey[400] : Colors.grey[500], + fontSize: 14.sp, + ), ), ), ], diff --git a/lib/modules/meeting/views/meeting_record_view.dart b/lib/modules/meeting/views/meeting_record_view.dart index ede144d9c..7aa297e24 100644 --- a/lib/modules/meeting/views/meeting_record_view.dart +++ b/lib/modules/meeting/views/meeting_record_view.dart @@ -131,7 +131,7 @@ class MeetingRecordView extends GetView { color: isDarkMode ? Colors.grey[400] : Colors.grey[500], ), Text( - '深圳市高新技术产业园区W2-A座', + controller.address.value, style: TextStyle( color: isDarkMode ? Colors.grey[400] : Colors.grey[500], fontSize: 14.sp, diff --git a/lib/modules/meeting/views/meeting_view.dart b/lib/modules/meeting/views/meeting_view.dart index 8f05c498b..263bdc6e7 100644 --- a/lib/modules/meeting/views/meeting_view.dart +++ b/lib/modules/meeting/views/meeting_view.dart @@ -161,13 +161,12 @@ class MeetingView extends GetView { ), ], ), - child: Row( + child: Column( children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( + Row( + children: [ + Expanded( + child: Text( data.title, style: TextStyle( color: isDarkMode ? Colors.white : Colors.black87, @@ -175,44 +174,77 @@ class MeetingView extends GetView { ), overflow: TextOverflow.ellipsis, ), - Text( - data.content.isNotEmpty ? data.content : '无内容', - style: TextStyle( - color: isDarkMode ? Colors.white70 : Colors.black54, - fontSize: 12.sp, - ), - overflow: TextOverflow.ellipsis, + ), + 10.horizontalSpace, + Text( + DateUtil.formatDateMs( + data.creationtime, + format: DateFormats.y_mo_d_h_m, ), - Text( + style: TextStyle( + color: isDarkMode ? Colors.white70 : Colors.black54, + fontSize: 12.sp, + ), + ), + ], + ), + Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + data.content.isNotEmpty ? data.content : '无内容', + style: TextStyle( + color: isDarkMode ? Colors.white70 : Colors.black54, + fontSize: 12.sp, + ), + overflow: TextOverflow.ellipsis, + ), + Row( + children: [ + Icon( + Icons.location_on, + size: 14.w, + color: isDarkMode ? Colors.white70 : Colors.black54, + ), + Expanded( + child: Text( + data.address, + style: TextStyle( + color: isDarkMode + ? Colors.white70 + : Colors.black54, + fontSize: 12.sp, + ), + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + ], + ), + ), + Container( + width: 76.w, + height: 28.w, + margin: EdgeInsets.only(left: 10.w), + alignment: Alignment.center, + decoration: BoxDecoration( + color: isDarkMode ? AppColors.primary : Colors.blue, + borderRadius: BorderRadius.circular(28.w), + ), + child: Text( DateUtil.formatDateMs( - data.creationtime, - format: DateFormats.y_mo_d_h_m, - ), - style: TextStyle( - color: isDarkMode ? Colors.white70 : Colors.black54, - fontSize: 12.sp, + data.seconds * 1000, + format: 'HH:mm:ss', + isUtc: true, ), + style: TextStyle(color: Colors.white, fontSize: 14.sp), ), - ], - ), - ), - Container( - width: 76.w, - height: 28.w, - margin: EdgeInsets.only(left: 10.w), - alignment: Alignment.center, - decoration: BoxDecoration( - color: isDarkMode ? AppColors.primary : Colors.blue, - borderRadius: BorderRadius.circular(28.w), - ), - child: Text( - DateUtil.formatDateMs( - data.seconds * 1000, - format: 'HH:mm:ss', - isUtc: true, ), - style: TextStyle(color: Colors.white, fontSize: 14.sp), - ), + ], ), ], ), diff --git a/pubspec.yaml b/pubspec.yaml index 4b3f58883..e4b2f4e6e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -31,7 +31,7 @@ dependencies: flutter_blue_plus: ^1.31.16 just_audio: ^0.9.36 webview_flutter: ^4.10.0 - + flutter_easyloading: ^3.0.5 window_size: git: