Browse Source

优化会议助手

weicu
tanlongsheng 1 year ago
parent
commit
bcff1191c9
  1. 4
      lib/core/bindings/initial_binding.dart
  2. 302
      lib/data/services/meeting/meeting_task_service.dart
  3. 15
      lib/data/services/meeting/meeting_upload_service.dart
  4. 124
      lib/modules/meeting/controllers/meeting_controller.dart
  5. 332
      lib/modules/meeting/controllers/meeting_details_controller.dart
  6. 1
      lib/modules/meeting/views/meeting_view.dart

4
lib/core/bindings/initial_binding.dart

@ -2,6 +2,7 @@ import 'package:deep_voice/data/services/location_manager.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:get/get.dart'; import 'package:get/get.dart';
import '../../data/services/meeting/meeting_task_service.dart';
import '../../data/services/meeting/meeting_upload_service.dart'; import '../../data/services/meeting/meeting_upload_service.dart';
import '../../data/services/speech_factory.dart'; import '../../data/services/speech_factory.dart';
import '../../data/services/volcano_translation_service.dart'; import '../../data/services/volcano_translation_service.dart';
@ -51,6 +52,9 @@ class InitialBinding extends Bindings {
Get.lazyPut<VersionUpdateService>(() => VersionUpdateService(), Get.lazyPut<VersionUpdateService>(() => VersionUpdateService(),
fenix: true); fenix: true);
// 会议助手任务服务
Get.lazyPut<MeetingTaskService>(() => MeetingTaskService(), fenix: true);
// 会议助手音频上传 // 会议助手音频上传
Get.lazyPut<MeetingUploadService>(() => MeetingUploadService(), Get.lazyPut<MeetingUploadService>(() => MeetingUploadService(),
fenix: true); fenix: true);

302
lib/data/services/meeting/meeting_task_service.dart

@ -0,0 +1,302 @@
import 'dart:convert';
import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
import 'package:http/http.dart' as http;
import '../../../core/utils/logger.dart';
import '../../../core/utils/synchrodata.dart';
import '../../models/appconfig.dart';
import '../db/sqflite_api.dart';
import '../deapsound_ai_service.dart';
import 'meeting_upload_service.dart';
class MeetingTaskService extends GetxService {
final GetStorage _storage = GetStorage();
MeetingUploadService get _uploadService => Get.find<MeetingUploadService>();
RxList taskList = [].obs;
bool _isTask = false;
int _handled = 0;
String _systemPrompt = '';
String _userId = '';
final String _appid = '2316081933';
final String _token = 'mcJTh6OelBqU_L78lgGd77Nyrc_-5-En';
final String _cluster = 'volc_auc_common_flash';
final String _serviceUrl = 'https://openspeech.bytedance.com/api/v1/auc';
Map<String, String> _headers = {};
RxList textList = [].obs;
String allText = '';
@override
void onInit() {
super.onInit();
_headers = {
'Authorization': 'Bearer; $_token',
'Content-Type': 'application/json',
};
_getAgent();
_getTaskList();
}
void _getAgent() async {
Map userInfo = _storage.read("user_info");
_userId = userInfo['user']['uid'];
_systemPrompt = AppConfig.agentsystemPrompt("meet_assistant") ?? '';
}
void _getTaskList() {
taskList.value = _storage.read("meeting_task_list") ?? [];
_executeTask();
}
Future<void> addTask(int id, String audiourl, String taskid) async {
if (audiourl.isEmpty) {
Map uploadData = _uploadService.uploadList.firstWhere(
(element) => element['id'] == id,
orElse: () => {},
);
if (uploadData.isNotEmpty) {
if (uploadData['audiourl'].isEmpty) {
uploadData['isTask'] = true;
} else {
await addTaskList(id, uploadData['audiourl'], taskid);
}
}
} else {
await addTaskList(id, audiourl, taskid);
}
}
Future<void> addTaskList(int id, String audiourl, String taskId) async {
if (taskId.isEmpty) {
taskId = await _submitTask(audiourl);
}
await SqfliteApi.editMeeting(id, {'tasktype': 1, 'taskid': taskId});
taskList.add({
'id': id,
'taskid': taskId,
'tasktype': 1,
});
_storage.write("meeting_task_list", taskList);
_executeTask();
}
void _executeTask() async {
if (_isTask) return;
_isTask = true;
_handled = 0;
while (_handled < taskList.length) {
await Future.delayed(const Duration(seconds: 2));
int completed = 0;
for (var element in taskList) {
if (element['tasktype'] == 1) {
await _fileRecognize(element);
} else {
completed++;
}
}
_handled = completed;
}
_isTask = false;
}
void removeTask(int id, {bool isDeleteTask = false}) {
Map taskData = taskList.firstWhere(
(element) => element['id'] == id,
orElse: () => {},
);
if (taskData.isNotEmpty) {
if (isDeleteTask || taskData['tasktype'] >= 3) {
taskList.remove(taskData);
_storage.write("meeting_task_list", taskList);
if (taskList.isEmpty) {
_handled = 0;
}
}
}
}
Future<String> _submitTask(String audiourl) async {
String format = _getFileType(audiourl);
final requestBody = jsonEncode({
"app": {
"appid": _appid,
"token": _token,
"cluster": _cluster,
},
"user": {
"uid": _userId,
},
"audio": {
"format": format,
"url": audiourl,
},
"additions": {
"with_speaker_info": "True",
}
});
try {
final response = await http.post(
Uri.parse('$_serviceUrl/submit'),
headers: _headers,
body: requestBody,
);
if (response.statusCode != 200) {
throw Exception('Submit failed with status: ${response.statusCode}');
}
final respDic = jsonDecode(response.body);
final taskId = respDic['resp']['id'] as String;
Logger.debug('taskId: $taskId');
return taskId;
} catch (e) {
Logger.error('Submit error: ${e.toString()}');
rethrow;
}
}
String _getFileType(String path) {
List<String> array = path.split('.');
return array[array.length - 1];
}
Future<Map<String, dynamic>> _queryTask(String taskId) async {
final queryBody = jsonEncode({
"appid": _appid,
"token": _token,
"id": taskId,
"cluster": _cluster,
});
try {
final response = await http.post(
Uri.parse('$_serviceUrl/query'),
headers: _headers,
body: queryBody,
);
if (response.statusCode != 200) {
throw Exception('Query failed with status: ${response.statusCode}');
}
final respDic = jsonDecode(response.body);
return respDic;
} catch (e) {
Logger.error('Query error: ${e.toString()}');
rethrow;
}
}
Future<void> _fileRecognize(Map item) async {
try {
var respDic = await _queryTask(item['taskid']);
var code = respDic['resp']['code'] as int;
if (code == 1000) {
await _addTextList(item, respDic['resp']['utterances']);
return;
} else if (code < 2000) {
Logger.debug("FileRecognize failed");
return;
}
} catch (e) {
Logger.error('Error occurred: ${e.toString()}');
}
}
Future<void> _addTextList(Map item, List utterances) async {
String text = '';
String personnel = '';
List<Map<String, Object?>> list = [];
for (var element in utterances) {
String speaker = '';
if (element['additions'].isNotEmpty) {
speaker = 'Speaker ${element['additions']['speaker']}';
text += '[$speaker]: ';
if (!personnel.contains(speaker)) {
personnel += '[$speaker] ';
}
}
list.add({
'meetingid': item['id'],
'content': element['text'],
'starttime': element['start_time'],
'endtime': element['end_time'],
'speaker': speaker,
});
text += '${element['text']}\n';
}
var result = await SqfliteApi.insertMeetingSpeaker(list);
if (result != null) {
await SqfliteApi.editMeeting(
item['id'],
{'tasktype': 2, 'personnel': personnel},
);
List speakerList = await SqfliteApi.getMeetingSpeaker(item['id']);
textList.value = List.from(speakerList);
Synchrodata.saveOperationRecord('update', {
'original': jsonEncode(textList),
'id': item['id'],
});
item['tasktype'] = 2;
}
allText = text;
_aiServiceMessage(item);
}
void _aiServiceMessage(Map item) {
int awaitQuantity = 0;
String summaryText = '';
String overviewText = '';
DeapsoundAIService().sendMessage(
messages: [
{
'role': 'system',
'content':
'您将扮演一名会议总结专家,拥有超强的信息归纳与细分能力。不仅能高效处理各类通用场景会议,精准提炼会议核心内容,为后续思维导图生成奠定坚实基础。若涉及人员,人员名称保留[]。并根据内容只输出对应语言。',
},
{'role': 'user', 'content': allText},
],
).then((value) {
awaitQuantity++;
summaryText = value;
if (awaitQuantity >= 2) {
_aiServiceValue(item, summaryText, overviewText);
}
});
DeapsoundAIService().sendMessage(
messages: [
{
'role': 'system',
'content': '您将扮演一名会议事件归纳专家,能准确抓住会议中事件的关键点,并每个事件只需一句话精简总结。',
},
{'role': 'user', 'content': allText},
],
).then((value) {
awaitQuantity++;
overviewText = value;
if (awaitQuantity >= 2) {
_aiServiceValue(item, summaryText, overviewText);
}
});
}
void _aiServiceValue(Map item, String summaryText, String overviewText) {
SqfliteApi.editMeeting(item['id'], {
'tasktype': 3,
'summary': summaryText,
'overview': overviewText,
});
item['tasktype'] = 3;
}
}

15
lib/data/services/meeting/meeting_upload_service.dart

@ -4,8 +4,11 @@ import 'package:get_storage/get_storage.dart';
import '../../../core/utils/logger.dart'; import '../../../core/utils/logger.dart';
import '../../../core/utils/upload_oss.dart'; import '../../../core/utils/upload_oss.dart';
import '../db/sqflite_api.dart'; import '../db/sqflite_api.dart';
import 'meeting_task_service.dart';
class MeetingUploadService extends GetxService { class MeetingUploadService extends GetxService {
final _taskService = Get.find<MeetingTaskService>();
final GetStorage _storage = GetStorage(); final GetStorage _storage = GetStorage();
RxInt meetingId = 0.obs; RxInt meetingId = 0.obs;
@ -22,7 +25,7 @@ class MeetingUploadService extends GetxService {
} }
void _getUploadList() { void _getUploadList() {
uploadList.value = _storage.read("upload_list") ?? []; uploadList.value = _storage.read("meeting_upload_list") ?? [];
_executeUpload(); _executeUpload();
} }
@ -32,8 +35,9 @@ class MeetingUploadService extends GetxService {
'filepath': filePath, 'filepath': filePath,
'rootdir': rootDir, 'rootdir': rootDir,
'audiourl': '', 'audiourl': '',
'isTask': false,
}); });
_storage.write("upload_list", uploadList); _storage.write("meeting_upload_list", uploadList);
_executeUpload(); _executeUpload();
} }
@ -58,9 +62,12 @@ class MeetingUploadService extends GetxService {
element['id'], element['id'],
{'audiourl': audiourl}, {'audiourl': audiourl},
); );
if (element['isTask']) {
await _taskService.addTaskList(element['id'], audiourl, '');
}
element['audiourl'] = audiourl; element['audiourl'] = audiourl;
uploadProgress.value = 0.0; uploadProgress.value = 0.0;
_storage.write("upload_list", uploadList); _storage.write("meeting_upload_list", uploadList);
} catch (e) { } catch (e) {
Logger.error('Upload failed: $e'); Logger.error('Upload failed: $e');
} }
@ -78,7 +85,7 @@ class MeetingUploadService extends GetxService {
if (uploadData.isNotEmpty) { if (uploadData.isNotEmpty) {
if (isDeleteTask || uploadData['audiourl'].isNotEmpty) { if (isDeleteTask || uploadData['audiourl'].isNotEmpty) {
uploadList.remove(uploadData); uploadList.remove(uploadData);
_storage.write("upload_list", uploadList); _storage.write("meeting_upload_list", uploadList);
if (uploadList.isEmpty) { if (uploadList.isEmpty) {
_handled = 0; _handled = 0;
} }

124
lib/modules/meeting/controllers/meeting_controller.dart

@ -1,11 +1,9 @@
import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:file_picker/file_picker.dart'; import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import 'package:just_audio/just_audio.dart'; import 'package:just_audio/just_audio.dart';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
@ -19,11 +17,13 @@ import '../../../core/utils/permission_util.dart';
import 'package:share_plus/share_plus.dart'; import 'package:share_plus/share_plus.dart';
import '../../../data/services/meeting/meeting_task_service.dart';
import '../../../data/services/meeting/meeting_upload_service.dart'; import '../../../data/services/meeting/meeting_upload_service.dart';
import '../../../routes/app_routes.dart'; import '../../../routes/app_routes.dart';
class MeetingController extends GetxController { class MeetingController extends GetxController {
final meetingUploadService = Get.find<MeetingUploadService>(); final meetingUploadService = Get.find<MeetingUploadService>();
final meetingTaskService = Get.find<MeetingTaskService>();
final ScrollController listController = ScrollController(); final ScrollController listController = ScrollController();
final AudioPlayer _audioPlayer = AudioPlayer(); final AudioPlayer _audioPlayer = AudioPlayer();
@ -40,27 +40,15 @@ class MeetingController extends GetxController {
TextEditingController titleController = TextEditingController(); // 录音标题 TextEditingController titleController = TextEditingController(); // 录音标题
String titleCSuffix = ""; // 录音标题后缀 String titleCSuffix = ""; // 录音标题后缀
RxList dataList = [].obs; RxList dataList = [].obs;
// 蓝牙服务 // 蓝牙服务
final bleManager = Get.find<BleManager>(); final bleManager = Get.find<BleManager>();
List dataListTask = [];
bool isDisposed = false;
String filePath = '';
final String _appid = '2316081933';
final String _token = 'mcJTh6OelBqU_L78lgGd77Nyrc_-5-En';
final String _cluster = 'volc_auc_common_flash';
final String _serviceUrl = 'https://openspeech.bytedance.com/api/v1/auc';
Map<String, String> _headers = {};
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
listController.addListener(_onScroll); listController.addListener(_onScroll);
_requestStoragePermission(); _requestStoragePermission();
_headers = {
'Authorization': 'Bearer; $_token',
'Content-Type': 'application/json',
};
_initGetDataList(); _initGetDataList();
addMeetingAudio(); addMeetingAudio();
final Map<String, dynamic> args = Get.arguments ?? {}; final Map<String, dynamic> args = Get.arguments ?? {};
@ -79,7 +67,6 @@ class MeetingController extends GetxController {
listController.dispose(); listController.dispose();
titleController.dispose(); titleController.dispose();
_audioPlayer.dispose(); _audioPlayer.dispose();
isDisposed = true;
super.onClose(); super.onClose();
} }
@ -107,7 +94,6 @@ class MeetingController extends GetxController {
void _getDataList() async { void _getDataList() async {
List list = await SqfliteApi.getMeetingList((page - 1) * 10 + addQuantity); List list = await SqfliteApi.getMeetingList((page - 1) * 10 + addQuantity);
dataList.addAll(list); dataList.addAll(list);
queryDataListTask(list);
addQuantity = 0; addQuantity = 0;
if (list.length < 10) { if (list.length < 10) {
isGetMore = false; isGetMore = false;
@ -401,6 +387,7 @@ class MeetingController extends GetxController {
SqfliteApi.deleteMeeting(id); SqfliteApi.deleteMeeting(id);
_deleteFile(filepath); _deleteFile(filepath);
meetingUploadService.removeUpload(id, isDeleteTask: true); meetingUploadService.removeUpload(id, isDeleteTask: true);
meetingTaskService.removeTask(id, isDeleteTask: true);
addQuantity--; addQuantity--;
} }
@ -412,107 +399,4 @@ class MeetingController extends GetxController {
await file.delete(); await file.delete();
} }
} }
// 查询转写状态
void queryDataListTask(List list, {bool isAdd = true}) {
for (var element in List.from(list)) {
if (element['tasktype'] == 1) {
if (isAdd) {
dataListTask.add(element);
}
final taskId = element['taskid'];
_fileRecognize(element['id'], taskId);
}
}
}
Future<void> _fileRecognize(int id, String taskId) async {
try {
final startTime = DateTime.now().millisecondsSinceEpoch;
if (isDisposed) return;
var respDic = await _queryTask(taskId);
var code = respDic['resp']['code'] as int;
if (code == 1000) {
await SqfliteApi.editMeetingTitle(id, {'tasktype': 2});
int index = dataList.indexWhere((item) => item['id'] == id);
if (index != -1) {
final dataMap = Map<String, dynamic>.from(dataList[index]);
dataMap['tasktype'] = 2;
dataList[index] = dataMap;
int taskIndex = dataListTask.indexWhere((item) => item['id'] == id);
if (taskIndex != -1) {
dataListTask.removeAt(taskIndex);
}
}
return;
} else if (code < 2000) {
Logger.debug("FileRecognize failed");
return;
}
while (!isDisposed) {
await Future.delayed(const Duration(seconds: 2));
if (isDisposed) return;
respDic = await _queryTask(taskId);
code = respDic['resp']['code'] as int;
if (code == 1000) {
await SqfliteApi.editMeetingTitle(id, {'tasktype': 2});
int index = dataList.indexWhere((item) => item['id'] == id);
if (index != -1) {
final dataMap = Map<String, dynamic>.from(dataList[index]);
dataMap['tasktype'] = 2;
dataList[index] = dataMap;
int taskIndex = dataListTask.indexWhere((item) => item['id'] == id);
if (taskIndex != -1) {
dataListTask.removeAt(taskIndex);
}
}
return;
} else if (code < 2000) {
Logger.debug("FileRecognize failed");
return;
}
final currentTime = DateTime.now().millisecondsSinceEpoch;
if (currentTime - startTime > 300 * 1000) {
Logger.debug('wait time exceeds 300s');
return;
}
}
} catch (e) {
Logger.error('Error occurred: ${e.toString()}');
}
}
Future<Map<String, dynamic>> _queryTask(String taskId) async {
final queryBody = jsonEncode({
"appid": _appid,
"token": _token,
"id": taskId,
"cluster": _cluster,
});
try {
final response = await http.post(
Uri.parse('$_serviceUrl/query'),
headers: _headers,
body: queryBody,
);
if (response.statusCode != 200) {
throw Exception('Query failed with status: ${response.statusCode}');
}
final respDic = jsonDecode(response.body);
return respDic;
} catch (e) {
Logger.error('Query error: ${e.toString()}');
rethrow;
}
}
} }

332
lib/modules/meeting/controllers/meeting_details_controller.dart

@ -9,20 +9,14 @@ import 'package:flutter/services.dart' show rootBundle;
import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
import 'package:http/http.dart' as http;
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import '../../../core/utils/logger.dart';
import '../../../core/utils/synchrodata.dart'; import '../../../core/utils/synchrodata.dart';
import '../../../core/utils/upload_oss.dart';
import '../../../data/models/appconfig.dart';
import '../../../data/services/db/sqflite_api.dart'; import '../../../data/services/db/sqflite_api.dart';
import '../../../data/services/deapsound_ai_service.dart'; import '../../../data/services/meeting/meeting_task_service.dart';
import '../../../data/services/meeting/meeting_upload_service.dart'; import '../../../data/services/meeting/meeting_upload_service.dart';
import '../model/meeting_details_model.dart'; import '../model/meeting_details_model.dart';
import '../model/meeting_model.dart'; import '../model/meeting_model.dart';
import 'meeting_controller.dart';
class MeetingDetailsController extends GetxController class MeetingDetailsController extends GetxController
with GetTickerProviderStateMixin { with GetTickerProviderStateMixin {
@ -32,9 +26,13 @@ class MeetingDetailsController extends GetxController
late AnimationController lottieController; late AnimationController lottieController;
final _meetingUploadService = Get.find<MeetingUploadService>(); final _meetingUploadService = Get.find<MeetingUploadService>();
final _meetingController = Get.find<MeetingController>(); final _meetingTaskService = Get.find<MeetingTaskService>();
final PlayerController playerController = PlayerController(); final PlayerController playerController = PlayerController();
StreamSubscription? _currentDurationSubscription;
StreamSubscription? _playerStateSubscription;
RxBool isTop = false.obs; RxBool isTop = false.obs;
RxInt durationMax = 0.obs; RxInt durationMax = 0.obs;
RxDouble dragOffset = 210.w.obs; RxDouble dragOffset = 210.w.obs;
@ -66,17 +64,6 @@ class MeetingDetailsController extends GetxController
RxBool isDownload = false.obs; RxBool isDownload = false.obs;
RxDouble downloadProgress = 0.0.obs; RxDouble downloadProgress = 0.0.obs;
String _systemPrompt = '';
String _userId = '';
bool _isDisposed = false;
final String _appid = '2316081933';
final String _token = 'mcJTh6OelBqU_L78lgGd77Nyrc_-5-En';
final String _cluster = 'volc_auc_common_flash';
final String _serviceUrl = 'https://openspeech.bytedance.com/api/v1/auc';
Map<String, String> _headers = {};
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
@ -91,66 +78,23 @@ class MeetingDetailsController extends GetxController
..duration = const Duration(seconds: 2); ..duration = const Duration(seconds: 2);
final Map<String, dynamic> args = Get.arguments ?? {}; final Map<String, dynamic> args = Get.arguments ?? {};
id = args['id']; id = args['id'];
_headers = {
'Authorization': 'Bearer; $_token',
'Content-Type': 'application/json',
};
_getAgent();
_getDataDetails(); _getDataDetails();
_loadJS(); _loadJS();
} }
@override @override
void onClose() { void onClose() {
_currentDurationSubscription?.cancel();
_playerStateSubscription?.cancel();
tabController.dispose(); tabController.dispose();
playerController.dispose(); playerController.dispose();
_isDisposed = true;
lottieController.dispose(); lottieController.dispose();
super.onClose(); super.onClose();
} }
void popInvokedWithResult(didPop, result) async { void popInvokedWithResult(didPop, result) async {
if (meetingData.value.tasktype != meetingDetails.value.tasktype) {
await SqfliteApi.editMeetingTitle(
id, {'tasktype': meetingDetails.value.tasktype});
int index =
_meetingController.dataList.indexWhere((item) => item['id'] == id);
if (index != -1) {
final dataMap =
Map<String, dynamic>.from(_meetingController.dataList[index]);
dataMap['tasktype'] = meetingDetails.value.tasktype;
if (dataMap['taskid'].isEmpty) {
dataMap['taskid'] = meetingDetails.value.taskid;
}
_meetingController.dataList[index] = dataMap;
int taskIndex = _meetingController.dataListTask
.indexWhere((item) => item['id'] == id);
if (taskIndex != -1) {
if (meetingDetails.value.tasktype == 1) {
_meetingController.dataListTask[taskIndex] = dataMap;
} else {
_meetingController.dataListTask.removeAt(taskIndex);
}
} else {
if (meetingDetails.value.tasktype == 1) {
_meetingController.dataListTask.add(dataMap);
}
}
}
}
_meetingController.isDisposed = false;
_meetingController.queryDataListTask(
_meetingController.dataListTask,
isAdd: false,
);
_meetingUploadService.removeUpload(id); _meetingUploadService.removeUpload(id);
} _meetingTaskService.removeTask(id);
void _getAgent() async {
final GetStorage storage = GetStorage();
Map userInfo = storage.read("user_info");
_userId = userInfo['user']['uid'];
_systemPrompt = AppConfig.agentsystemPrompt("meet_assistant") ?? '';
} }
void _getDataDetails() async { void _getDataDetails() async {
@ -168,10 +112,6 @@ class MeetingDetailsController extends GetxController
List speakerList = await SqfliteApi.getMeetingSpeaker(id); List speakerList = await SqfliteApi.getMeetingSpeaker(id);
textList.value = List.from(speakerList); textList.value = List.from(speakerList);
} }
if (meetingDetails.value.tasktype == 1 ||
meetingDetails.value.tasktype == 2) {
summaryCreate();
}
isLoading.value = false; isLoading.value = false;
} }
@ -183,7 +123,8 @@ class MeetingDetailsController extends GetxController
); );
durationMax.value = await playerController.getDuration(DurationType.max); durationMax.value = await playerController.getDuration(DurationType.max);
playerController.setFinishMode(finishMode: FinishMode.pause); playerController.setFinishMode(finishMode: FinishMode.pause);
playerController.onCurrentDurationChanged.listen((int position) { _currentDurationSubscription =
playerController.onCurrentDurationChanged.listen((int position) {
currentDuration.value = position; currentDuration.value = position;
if (speakerAllEndtime.value > 0) { if (speakerAllEndtime.value > 0) {
if (position >= speakerAllEndtime.value) { if (position >= speakerAllEndtime.value) {
@ -193,9 +134,9 @@ class MeetingDetailsController extends GetxController
} }
} }
}); });
playerController.onPlayerStateChanged.listen((PlayerState playerState) { _playerStateSubscription =
playerController.onPlayerStateChanged.listen((PlayerState playerState) {
isPlay.value = playerState.isPlaying; isPlay.value = playerState.isPlaying;
if (_isDisposed) return;
if (playerState.isPlaying) { if (playerState.isPlaying) {
lottieController.repeat(); lottieController.repeat();
} else { } else {
@ -283,253 +224,12 @@ class MeetingDetailsController extends GetxController
} }
void summaryCreate() async { void summaryCreate() async {
if (meetingDetails.value.tasktype <= 1) {
await _audioSpeaker();
}
if (allText.isEmpty) {
String text = '';
for (var element in textList) {
if (element['speaker'].isNotEmpty) {
text += '[${element['speaker']}]: ';
}
text += '${element['content']}\n';
}
allText = text;
}
if (allText.isEmpty) return;
_aiServiceMessage();
}
void _aiServiceMessage() {
int awaitQuantity = 0;
int verifyQuantity = 0;
String summaryText = meetingDetails.value.summary;
String overviewText = meetingDetails.value.overview;
if (summaryText.isEmpty) {
verifyQuantity++;
DeapsoundAIService().sendMessage(
messages: [
{
'role': 'system',
'content':
'您将扮演一名会议总结专家,拥有超强的信息归纳与细分能力。不仅能高效处理各类通用场景会议,精准提炼会议核心内容,为后续思维导图生成奠定坚实基础。若涉及人员,人员名称保留[]。并根据内容只输出对应语言。',
},
{'role': 'user', 'content': allText},
],
).then((value) {
awaitQuantity++;
summaryText = value;
if (awaitQuantity >= verifyQuantity) {
_aiServiceValue(summaryText, overviewText);
}
});
}
if (overviewText.isEmpty) {
verifyQuantity++;
DeapsoundAIService().sendMessage(
messages: [
{
'role': 'system',
'content': '您将扮演一名会议事件归纳专家,能准确抓住会议中事件的关键点,并每个事件只需一句话精简总结。',
},
{'role': 'user', 'content': allText},
],
).then((value) {
awaitQuantity++;
overviewText = value;
if (awaitQuantity >= verifyQuantity) {
_aiServiceValue(summaryText, overviewText);
}
});
}
}
void _aiServiceValue(String summaryText, String overviewText) {
SqfliteApi.editMeeting(id, {
'tasktype': 3,
'summary': summaryText,
'overview': overviewText,
});
meetingDetails.value.tasktype = 3;
meetingDetails.value.summary = summaryText;
meetingDetails.value.overview = overviewText;
meetingDetails.refresh();
}
Future<void> _audioSpeaker() async {
EasyLoading.show(); EasyLoading.show();
String audiourl = meetingData.value.audiourl; String audiourl = meetingData.value.audiourl;
if (audiourl.isEmpty) {
audiourl = await UploadOss.upload(
filepath: meetingData.value.filepath,
rootDir: 'audio',
);
meetingData.value.audiourl = audiourl;
}
String taskId = meetingDetails.value.taskid; String taskId = meetingDetails.value.taskid;
if (taskId.isEmpty) { await _meetingTaskService.addTask(id, audiourl, taskId);
taskId = await _submitTask(audiourl); meetingDetails.value.tasktype = 1;
await SqfliteApi.editMeeting(id, {'tasktype': 1, 'taskid': taskId}); meetingDetails.refresh();
meetingDetails.value.taskid = taskId;
meetingDetails.value.tasktype = 1;
meetingDetails.refresh();
}
EasyLoading.dismiss(); EasyLoading.dismiss();
await _fileRecognize(taskId);
}
Future<String> _submitTask(String audiourl) async {
String format = _getFileType(audiourl);
final requestBody = jsonEncode({
"app": {
"appid": _appid,
"token": _token,
"cluster": _cluster,
},
"user": {
"uid": _userId,
},
"audio": {
"format": format,
"url": audiourl,
},
"additions": {
"with_speaker_info": "True",
}
});
try {
final response = await http.post(
Uri.parse('$_serviceUrl/submit'),
headers: _headers,
body: requestBody,
);
if (response.statusCode != 200) {
throw Exception('Submit failed with status: ${response.statusCode}');
}
final respDic = jsonDecode(response.body);
final taskId = respDic['resp']['id'] as String;
Logger.debug('taskId: $taskId');
return taskId;
} catch (e) {
Logger.error('Submit error: ${e.toString()}');
rethrow;
}
}
String _getFileType(String path) {
List<String> array = path.split('.');
return array[array.length - 1];
}
Future<Map<String, dynamic>> _queryTask(String taskId) async {
final queryBody = jsonEncode({
"appid": _appid,
"token": _token,
"id": taskId,
"cluster": _cluster,
});
try {
final response = await http.post(
Uri.parse('$_serviceUrl/query'),
headers: _headers,
body: queryBody,
);
if (response.statusCode != 200) {
throw Exception('Query failed with status: ${response.statusCode}');
}
final respDic = jsonDecode(response.body);
return respDic;
} catch (e) {
Logger.error('Query error: ${e.toString()}');
rethrow;
}
}
Future<void> _fileRecognize(String taskId) async {
try {
final startTime = DateTime.now().millisecondsSinceEpoch;
if (_isDisposed) return;
var respDic = await _queryTask(taskId);
var code = respDic['resp']['code'] as int;
if (code == 1000) {
await _addTextList(respDic['resp']['utterances']);
return;
} else if (code < 2000) {
Logger.debug("FileRecognize failed");
return;
}
while (!_isDisposed) {
await Future.delayed(const Duration(seconds: 2));
if (_isDisposed) return;
respDic = await _queryTask(taskId);
code = respDic['resp']['code'] as int;
if (code == 1000) {
await _addTextList(respDic['resp']['utterances']);
return;
} else if (code < 2000) {
Logger.debug("FileRecognize failed");
return;
}
final currentTime = DateTime.now().millisecondsSinceEpoch;
if (currentTime - startTime > 300 * 1000) {
Logger.debug('wait time exceeds 300s');
return;
}
}
} catch (e) {
Logger.error('Error occurred: ${e.toString()}');
}
}
Future<void> _addTextList(List utterances) async {
String text = '';
String personnel = '';
List<Map<String, Object?>> list = [];
for (var element in utterances) {
String speaker = '';
if (element['additions'].isNotEmpty) {
speaker = 'Speaker ${element['additions']['speaker']}';
text += '[$speaker]: ';
if (!personnel.contains(speaker)) {
personnel += '[$speaker] ';
}
}
list.add({
'meetingid': id,
'content': element['text'],
'starttime': element['start_time'],
'endtime': element['end_time'],
'speaker': speaker,
});
text += '${element['text']}\n';
}
var result = await SqfliteApi.insertMeetingSpeaker(list);
if (result != null) {
await SqfliteApi.editMeeting(id, {'tasktype': 2, 'personnel': personnel});
List speakerList = await SqfliteApi.getMeetingSpeaker(id);
textList.value = List.from(speakerList);
Synchrodata.saveOperationRecord('update', {
'original': jsonEncode(textList),
'id': id,
});
meetingDetails.value.tasktype = 2;
meetingDetails.value.personnel = personnel;
meetingDetails.refresh();
}
allText = text;
} }
} }

1
lib/modules/meeting/views/meeting_view.dart

@ -472,7 +472,6 @@ class MeetingView extends GetView<MeetingController> {
Widget _item(BuildContext context, bool isDarkMode, MeetingModel data) { Widget _item(BuildContext context, bool isDarkMode, MeetingModel data) {
return GestureDetector( return GestureDetector(
onTap: () { onTap: () {
controller.isDisposed = true;
Get.toNamed(Routes.meetingDetails, arguments: { Get.toNamed(Routes.meetingDetails, arguments: {
'id': data.id, 'id': data.id,
}); });

Loading…
Cancel
Save