From 7fdd71ecdc989207a48e7d75f87c6986252c21ab Mon Sep 17 00:00:00 2001 From: tanlongsheng <252620078@qq.com> Date: Sat, 10 Jan 2026 12:25:06 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E9=87=8D=E6=9E=84=E4=BC=9A=E8=AE=AE?= =?UTF-8?q?=E5=8A=A9=E6=89=8B=EF=BC=8C=E4=BD=BF=E7=94=A8=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E7=AB=AF=E8=BD=AC=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/core/bindings/initial_binding.dart | 16 +- lib/core/utils/synchrodata.dart | 131 +-- lib/data/services/account_service.dart | 7 +- lib/data/services/db/sqflite_api.dart | 29 +- .../meeting/meeting_task_aggregator.dart | 105 -- .../services/meeting/meeting_task_post.dart | 205 ++++ .../meeting/meeting_task_service.dart | 478 --------- .../meeting/meeting_task_service2.dart | 971 ------------------ .../meeting/meeting_upload_service.dart | 66 +- .../meeting/summary_task_service.dart | 187 ---- lib/data/services/network/api.dart | 59 +- .../meeting_details_controller.dart | 126 +-- .../controllers/meeting_home_controller.dart | 82 +- .../bottomSheet/generate_bottom_sheet.dart | 48 +- .../views/bottomSheet/more_bottom_sheet.dart | 2 +- .../views/bottomSheet/share_bottom_sheet.dart | 23 +- .../bottomSheet/speaker_all_bottom_sheet.dart | 5 - .../bottomSheet/speaker_bottom_sheet.dart | 9 - .../meeting/views/meeting_details_view.dart | 44 +- .../meeting/views/meeting_home_view.dart | 57 +- .../splash/controllers/splash_controller.dart | 8 - 21 files changed, 544 insertions(+), 2114 deletions(-) delete mode 100644 lib/data/services/meeting/meeting_task_aggregator.dart create mode 100644 lib/data/services/meeting/meeting_task_post.dart delete mode 100644 lib/data/services/meeting/meeting_task_service.dart delete mode 100644 lib/data/services/meeting/meeting_task_service2.dart delete mode 100644 lib/data/services/meeting/summary_task_service.dart diff --git a/lib/core/bindings/initial_binding.dart b/lib/core/bindings/initial_binding.dart index 2870209ff..cc7b629c2 100644 --- a/lib/core/bindings/initial_binding.dart +++ b/lib/core/bindings/initial_binding.dart @@ -1,4 +1,4 @@ -import '../../data/services/meeting/summary_task_service.dart'; +import '../../data/services/meeting/meeting_task_post.dart'; import '/data/services/pay_service.dart'; import '/data/services/qq_music.dart'; @@ -10,9 +10,6 @@ import '../../../data/services/navigation_manager.dart'; import '../../../data/services/ast_service.dart'; import '../../../data/services/speech_impl/azure_ast_service.dart'; import 'package:get/get.dart'; -import '../../data/services/meeting/meeting_task_service.dart'; -import '../../data/services/meeting/meeting_task_service2.dart'; -import '../../data/services/meeting/meeting_task_aggregator.dart'; import '../../data/services/meeting/meeting_upload_service.dart'; import '../../data/services/novice_guide_service.dart'; import '../../data/services/speech_factory.dart'; @@ -71,16 +68,7 @@ class InitialBinding extends Bindings { fenix: true); // 会议助手任务服务 - Get.lazyPut(() => MeetingTaskService(), fenix: true); - // 会议助手任务服务(极速识别版,2小时内音频) - Get.lazyPut(() => MeetingTaskService2(), fenix: true); - // 会议助手任务服务聚合器(统一列表与状态) - Get.lazyPut(() => MeetingTaskAggregator(), - fenix: true); - - // 会议助手总结任务服务 - Get.lazyPut(() => SummaryTaskService(), fenix: true); - + Get.lazyPut(() => MeetingTaskPost(), fenix: true); // 会议助手音频上传 Get.lazyPut(() => MeetingUploadService(), fenix: true); diff --git a/lib/core/utils/synchrodata.dart b/lib/core/utils/synchrodata.dart index a7fa19d1d..e5940e025 100644 --- a/lib/core/utils/synchrodata.dart +++ b/lib/core/utils/synchrodata.dart @@ -1,9 +1,11 @@ import 'dart:convert'; +import 'package:get/get.dart'; import 'package:get_storage/get_storage.dart'; import '../../data/services/db/database_helper.dart'; import '../../data/services/db/sqflite_api.dart'; +import '../../data/services/meeting/meeting_task_post.dart'; import '../../data/services/network/api.dart'; class Synchrodata { @@ -76,35 +78,35 @@ class Synchrodata { static Future uploadOperationRecords({bool isSynchro = false}) async { if (_isSynchro) return; _isSynchro = true; - if (_records.isNotEmpty || _delids.isNotEmpty) { - // 获取需要修改的全部数据 - List putRecords = []; - for (var element in _records) { - Map result = {'details': {}}; - List speakerList = []; - if (!_delids.contains(element['id'])) { - result = await SqfliteApi.getMeetingDetails(element['number']); - speakerList = await SqfliteApi.getMeetingSpeaker(element['number']); - } - putRecords.add({ - ...Map.from(result), - ...Map.from(result['details']), - 'rtype': result['type'], - 'original': jsonEncode(speakerList), - ...Map.from(element), - }); - } - var result = await Api.putOperationRecord({ - 'delids': _delids, - 'records': putRecords, - }); - if (result != null) { - _records = []; - _delids = []; - _storage.write("operation_records", _records); - _storage.write("operation_delids", _delids); - } - } + // if (_records.isNotEmpty || _delids.isNotEmpty) { + // // 获取需要修改的全部数据 + // List putRecords = []; + // for (var element in _records) { + // Map result = {'details': {}}; + // List speakerList = []; + // if (!_delids.contains(element['id'])) { + // result = await SqfliteApi.getMeetingDetails(element['number']); + // speakerList = await SqfliteApi.getMeetingSpeaker(element['number']); + // } + // putRecords.add({ + // ...Map.from(result), + // ...Map.from(result['details']), + // 'rtype': result['type'], + // 'original': jsonEncode(speakerList), + // ...Map.from(element), + // }); + // } + // var result = await Api.putOperationRecord({ + // 'delids': _delids, + // 'records': putRecords, + // }); + // if (result != null) { + // _records = []; + // _delids = []; + // _storage.write("operation_records", _records); + // _storage.write("operation_delids", _delids); + // } + // } if (isSynchro) { await _synchrodata(); } @@ -113,32 +115,40 @@ class Synchrodata { // 同步数据 static Future _synchrodata() async { - var result = await Api.getOperationRecord(); + var result = await Api.getOperationAllRecord(); List records = result['records'] ?? []; if (records.isEmpty) return; + final meetingTaskService = Get.find(); + List taskList = []; final db = await DatabaseHelper().database; await db.transaction((txn) async { for (var element in records) { + if (element['state'] > 0 && element['state'] < 5) { + taskList.add({ + 'id': element['id'], + 'tasktype': element['state'], + }); + } final List existList = await txn.query( 'meeting', where: 'id = ?', - whereArgs: [element['number']], + whereArgs: [element['id']], ); Map meetingData = { - 'id': element['number'], + 'id': element['id'], 'title': element['title'] ?? '', 'type': element['rtype'] ?? '', 'seconds': element['seconds'] ?? 0, 'audiourl': element['audiourl'] ?? '', - 'tasktype': element['tasktype'] ?? 0, - 'creationtime': element['creationtime'] ?? 0, + 'tasktype': element['state'] ?? 0, + 'creationtime': (element['creationtime'] ?? 0) * 1000, }; Map meetingDetailsData = { - 'meetingid': element['number'], + 'meetingid': element['id'], 'address': element['address'] ?? '', 'personnel': element['personnel'] ?? '', 'taskid': element['taskid'] ?? '', - 'tasktype': element['tasktype'] ?? 0, + 'tasktype': element['state'] ?? 0, 'overview': element['overview'] ?? '', 'summary': element['summary'] ?? '', 'mindmap': '', @@ -148,13 +158,13 @@ class Synchrodata { 'meeting', meetingData, where: 'id = ?', - whereArgs: [element['number']], + whereArgs: [element['id']], ); await txn.update( 'meetingdetails', meetingDetailsData, where: 'meetingid = ?', - whereArgs: [element['number']], + whereArgs: [element['id']], ); } else { await txn.insert('meeting', { @@ -163,48 +173,21 @@ class Synchrodata { }); await txn.insert('meetingdetails', meetingDetailsData); } - if (element['original'].isNotEmpty) { - final List existSpeakerList = await txn.query( + if (element['translate'].isNotEmpty) { + List speakerList = jsonDecode(element['translate']); + await txn.delete( 'meetingspeaker', where: 'meetingid = ?', - whereArgs: [element['number']], + whereArgs: [element['id']], ); - List speakerList = jsonDecode(element['original']); - if (existSpeakerList.isNotEmpty) { - final batch = txn.batch(); - for (var i = 0; i < existSpeakerList.length; i++) { - Map item = speakerList[i] ?? {}; - if (item.isNotEmpty) { - batch.update( - 'meetingspeaker', - { - 'meetingid': item['meetingid'], - 'content': item['content'], - 'starttime': item['starttime'], - 'endtime': item['endtime'], - 'speaker': item['speaker'], - }, - where: 'id = ?', - whereArgs: [existSpeakerList[i]['id']], - ); - } - } - await batch.commit(); - } else { - final batch = txn.batch(); - for (var item in speakerList) { - batch.insert('meetingspeaker', { - 'meetingid': item['meetingid'], - 'content': item['content'], - 'starttime': item['starttime'], - 'endtime': item['endtime'], - 'speaker': item['speaker'], - }); - } - await batch.commit(); + final batch = txn.batch(); + for (var item in speakerList) { + batch.insert('meetingspeaker', item); } + await batch.commit(); } } }); + meetingTaskService.startTask(taskList); } } diff --git a/lib/data/services/account_service.dart b/lib/data/services/account_service.dart index 1293ecec5..8a0e5cb70 100644 --- a/lib/data/services/account_service.dart +++ b/lib/data/services/account_service.dart @@ -3,11 +3,10 @@ import 'package:get_storage/get_storage.dart'; import 'package:logger/logger.dart'; import '../../core/utils/synchrodata.dart'; import '../../modules/login/controllers/googleLogin.dart'; -import '../../modules/login/controllers/facebookLogin.dart'; import '../services/network/api.dart'; import 'db/database_helper.dart'; -import 'meeting/meeting_task_service.dart'; import '../../data/models/user_Info.dart'; +import 'meeting/meeting_upload_service.dart'; /// 账号管理服务 /// @@ -84,8 +83,8 @@ class AccountService extends GetxService { Future signOut() async { try { // 清除会议记录任务列表 - final taskService = Get.find(); - taskService.cancelTask(); + final uploadService = Get.find(); + uploadService.cancelUpload(); // 上传会议记录操作记录 await Synchrodata.uploadOperationRecords(); diff --git a/lib/data/services/db/sqflite_api.dart b/lib/data/services/db/sqflite_api.dart index 45fc7f898..8481cf800 100644 --- a/lib/data/services/db/sqflite_api.dart +++ b/lib/data/services/db/sqflite_api.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import '../../../core/utils/synchrodata.dart'; import 'database_helper.dart'; class SqfliteApi { @@ -58,11 +57,10 @@ class SqfliteApi { static Future insertMeeting(Map meeting) async { final db = await DatabaseHelper().database; try { - int id = 0; await db.transaction((txn) async { - id = await txn.insert('meeting', meeting); + await txn.insert('meeting', meeting); await txn.insert('meetingdetails', { - 'meetingid': id, + 'meetingid': meeting['id'], 'address': '', 'personnel': '', 'taskid': '', @@ -71,12 +69,8 @@ class SqfliteApi { 'summary': '', 'mindmap': '', }); - Synchrodata.saveOperationRecord('insert', { - ...meeting, - 'id': id, - }); }); - return id; + return true; } catch (e) { Get.snackbar( 'error'.tr, // 错误 @@ -99,10 +93,6 @@ class SqfliteApi { where: 'meetingid = ?', whereArgs: [id], ); - Synchrodata.saveOperationRecord('update', { - ...meeting, - 'id': id, - }); return true; } catch (e) { Get.snackbar( @@ -168,14 +158,16 @@ class SqfliteApi { } } + // 删除商务会议助手转写内容 + static Future deleteMeetingSpeaker(id) async { + final db = await DatabaseHelper().database; + await db.delete('meetingspeaker', where: 'meetingid = ?', whereArgs: [id]); + } + // 修改商务会议助手标题 static Future editMeetingTitle(id, Map meeting) async { final db = await DatabaseHelper().database; await db.update('meeting', meeting, where: 'id = ?', whereArgs: [id]); - Synchrodata.saveOperationRecord('update', { - ...meeting, - 'id': id, - }); } // 删除商务会议助手 @@ -187,9 +179,6 @@ class SqfliteApi { .delete('meetingdetails', where: 'meetingid = ?', whereArgs: [id]); await txn .delete('meetingspeaker', where: 'meetingid = ?', whereArgs: [id]); - Synchrodata.saveOperationRecord('delete', { - 'id': id, - }); }); } diff --git a/lib/data/services/meeting/meeting_task_aggregator.dart b/lib/data/services/meeting/meeting_task_aggregator.dart deleted file mode 100644 index 5de375cda..000000000 --- a/lib/data/services/meeting/meeting_task_aggregator.dart +++ /dev/null @@ -1,105 +0,0 @@ -import 'package:get/get.dart'; -import 'package:get_storage/get_storage.dart'; - -import 'meeting_task_service.dart'; -import 'meeting_task_service2.dart'; - -/// 会议助手任务服务聚合器(统一列表与状态) -class MeetingTaskAggregator extends GetxService { - final GetStorage _storage = GetStorage(); - - final MeetingTaskService s1 = Get.find(); - final MeetingTaskService2 s2 = Get.find(); - - RxList taskFinishList = [].obs; - RxInt tasktype = 1.obs; - int meetingId = 0; - List textList = []; - - Worker? _w1; - Worker? _w2; - Worker? _wTasktype; - - @override - void onInit() { - super.onInit(); - _w1 = ever(s1.taskFinishList, (_) => _rebuild()); - _w2 = ever(s2.taskFinishList, (_) => _rebuild()); - _rebuild(); - } - - void setMeeting(int id) { - meetingId = id; - final String svc = - (_storage.read("meeting_service_type_$id") ?? '').toString(); - final bool isFast = svc == 'fast'; - _wTasktype?.dispose(); - if (isFast) { - _wTasktype = ever(s2.tasktype, (value) { - tasktype.value = value; - if (value == 2) { - textList = s2.textList; - } - }); - } else { - _wTasktype = ever(s1.tasktype, (value) { - tasktype.value = value; - if (value == 2) { - textList = s1.textList; - } - }); - } - } - - void clearMeeting() { - meetingId = 0; - _wTasktype?.dispose(); - _wTasktype = null; - } - - void _rebuild() { - final Set ids = {}; - for (var e in s1.taskFinishList) { - if (e is Map && e['id'] is int) ids.add(e['id'] as int); - } - for (var e in s2.taskFinishList) { - if (e is Map && e['id'] is int) ids.add(e['id'] as int); - } - final List unified = []; - for (final id in ids) { - final String svc = - (_storage.read("meeting_service_type_$id") ?? '').toString(); - final Map a = s1.taskFinishList.firstWhere( - (x) => x['id'] == id, - orElse: () => {}, - ); - final Map b = s2.taskFinishList.firstWhere( - (x) => x['id'] == id, - orElse: () => {}, - ); - int va = a.isNotEmpty ? ((a['tasktype'] ?? 0) as int) : -999; - int vb = b.isNotEmpty ? ((b['tasktype'] ?? 0) as int) : -999; - int v; - if (svc == 'fast') { - if (va != -999 && vb != -999) { - v = va >= vb ? va : vb; - } else { - v = vb != -999 ? vb : va; - } - } else if (svc == 'standard') { - if (va != -999 && vb != -999) { - v = va >= vb ? va : vb; - } else { - v = va != -999 ? va : vb; - } - } else { - if (va == -999 && vb == -999) { - continue; - } - v = va == -999 ? vb : (vb == -999 ? va : (va >= vb ? va : vb)); - } - unified.add({'id': id, 'tasktype': v}); - } - taskFinishList.value = unified; - } -} diff --git a/lib/data/services/meeting/meeting_task_post.dart b/lib/data/services/meeting/meeting_task_post.dart new file mode 100644 index 000000000..4937cf7a8 --- /dev/null +++ b/lib/data/services/meeting/meeting_task_post.dart @@ -0,0 +1,205 @@ +import 'dart:convert'; + +import 'package:get/get.dart'; + +import '../db/sqflite_api.dart'; +import '../network/api.dart'; +import 'meeting_upload_service.dart'; + +class MeetingTaskPost extends GetxService { + MeetingUploadService get _uploadService => Get.find(); + + List _taskList = []; //待处理的会议任务列表 + + bool _isTask = false; //是否有正在处理的任务 + + RxInt updateListState = 0.obs; //是否更新首页列表 + int meetingId = 0; + RxInt tasktype = 1.obs; //任务类型,0-初始,1-等待转写中,2-转写中,3-等待总结中,4-总结中,5-完成 + List textList = []; + String personnel = ''; + String summary = ''; + String overview = ''; + + Future addTask( + int id, + String audiourl, + String formlanguage, + String tolanguage, + bool isdistinguishspeaker, + int templateid, + ) async { + if (audiourl.isEmpty) { + Map uploadData = _uploadService.uploadList.firstWhere( + (element) => element['id'] == id, + orElse: () => {}, + ); + if (uploadData.isNotEmpty) { + if (uploadData['audiourl'].isEmpty) { + uploadData['isTask'] = true; + uploadData['formlanguage'] = formlanguage; + uploadData['tolanguage'] = tolanguage; + uploadData['isdistinguishspeaker'] = isdistinguishspeaker; + uploadData['templateid'] = templateid; + } else { + await submitTask( + id, + formlanguage, + tolanguage, + isdistinguishspeaker, + templateid, + ); + } + } + } else { + await submitTask( + id, + formlanguage, + tolanguage, + isdistinguishspeaker, + templateid, + ); + } + } + + Future submitTask( + int id, + String formlanguage, + String tolanguage, + bool isdistinguishspeaker, + int templateid, + ) async { + await Api.startTaskEchomeet({ + 'id': id, + 'formlanguage': formlanguage, + 'tolanguage': tolanguage, + 'isdistinguishspeaker': isdistinguishspeaker, + 'templateid': templateid, + }); + _taskList.add({ + 'id': id, + 'tasktype': 1, + }); + await SqfliteApi.editMeetingTitle( + id, + {'tasktype': 1}, + ); + updateListState.value++; + _executeTask(); + } + + Future refreshTask(int id, int templateid) async { + tasktype.value = 3; + await Api.refreshTaskEchomeet({ + 'id': id, + 'templateid': templateid, + }); + _taskList.add({ + 'id': id, + 'tasktype': 3, + }); + await SqfliteApi.editMeetingTitle( + id, + {'tasktype': 3}, + ); + updateListState.value++; + _executeTask(); + } + + Future readTask(int id) async { + await Api.readRecordEchomeet({'id': id}); + await SqfliteApi.editMeetingTitle( + id, + {'tasktype': 6}, + ); + updateListState.value++; + } + + void _executeTask() async { + if (_isTask) return; + _isTask = true; + while (_taskList.isNotEmpty) { + await Future.delayed(const Duration(seconds: 5)); + await _queryTask(); + } + _isTask = false; + } + + Future removeTask(int id) async { + _taskList.removeWhere((element) => element['id'] == id); + await Api.delRecordEchomeet({ + 'ids': [id], + }); + } + + void startTask(List data) { + _taskList = data; + _executeTask(); + } + + void stopTask() { + _taskList.clear(); + } + + Future _queryTask() async { + if (_taskList.isEmpty) return; + List ids = _taskList.map((e) => e['id']).toList(); + var response = await Api.getMultitermRecord({ + 'ids': ids, + }); + List queryList = response['records'] ?? []; + for (var element in queryList) { + int id = element['id']; + int newTasktype = element['state']; + Map taskData = _taskList.firstWhere( + (i) => i['id'] == id, + orElse: () => {}, + ); + if (taskData.isNotEmpty) { + int oldTasktype = taskData['tasktype']; + if (oldTasktype != newTasktype) { + if (oldTasktype < 3 && newTasktype >= 3) { + // 转写完成 + List decodeList = jsonDecode(element['translate']); + List> speakerList = + decodeList.map((e) => Map.from(e)).toList(); + await SqfliteApi.deleteMeetingSpeaker(id); + await SqfliteApi.insertMeetingSpeaker(speakerList); + await SqfliteApi.editMeeting( + id, + {'tasktype': newTasktype}, + ); + if (id == meetingId) { + textList = speakerList; + tasktype.value = newTasktype; + } + } + if (oldTasktype < 5 && newTasktype >= 5) { + // 总结完成 + await SqfliteApi.editMeeting(id, { + 'tasktype': newTasktype, + 'personnel': element['personnel'] ?? '', + 'summary': element['summary'] ?? '', + 'overview': element['overview'] ?? '', + }); + if (id == meetingId) { + personnel = element['personnel'] ?? ''; + summary = element['summary'] ?? ''; + overview = element['overview'] ?? ''; + tasktype.value = newTasktype; + } + } + await SqfliteApi.editMeetingTitle( + id, + {'tasktype': newTasktype}, + ); + taskData['tasktype'] = newTasktype; + updateListState.value++; + } + } + if (newTasktype >= 5) { + _taskList.removeWhere((i) => i['id'] == id); + } + } + } +} diff --git a/lib/data/services/meeting/meeting_task_service.dart b/lib/data/services/meeting/meeting_task_service.dart deleted file mode 100644 index 3d7886b19..000000000 --- a/lib/data/services/meeting/meeting_task_service.dart +++ /dev/null @@ -1,478 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; - -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/meeting_usage_stats.dart'; -import '../db/sqflite_api.dart'; -import '../usage_stats_service.dart'; -import 'meeting_upload_service.dart'; -import '../../../data/models/appconfig.dart'; -import 'summary_task_service.dart'; - -///会议任务服务类,负责管理会议音频的转写任务处理流程,包括任务提交、状态轮询、结果处理和AI分析等功能。 -///火山引擎录音文件识别大模型-标准版 -class MeetingTaskService extends GetxService { - final GetStorage _storage = GetStorage(); - - MeetingUploadService get _uploadService => Get.find(); - SummaryTaskService get _summaryTaskService => Get.find(); - - List _taskList = []; //待处理的会议任务列表 - RxList taskFinishList = [].obs; //已处理的会议任务列表 - - bool _isTask = false; //是否有正在处理的任务 - - String _userId = ''; - - // final String _serviceUrl = 'https://openspeech.bytedance.com/api/v1/auc'; // - // final String _cluster = 'volc_auc_video_flash'; -//字节跳动(火山引擎)语音服务配置 - // final String _appid = '2316081933'; - // final String _token = 'mcJTh6OelBqU_L78lgGd77Nyrc_-5-En'; - // final String _cluster1 = 'volc.bigasr.auc'; - - // final String _serviceUrl1 = - // 'https://openspeech.bytedance.com/api/v3/auc/bigmodel'; //大模型录音文件识别api地址 - late final String _appid; - late final String _token; - late final String _cluster1; - late final String _serviceUrl1; - late final String _modelName; - late final String _modelVersion; - Map _headers = {}; - Map _headers1 = {}; - - int meetingId = 0; - RxInt tasktype = 1.obs; //任务类型,1-转写中,2-总结中,3-完成 - List textList = []; //转写结果列表 - - @override - void onInit() { - super.onInit(); - - _initialize(); - - _getAgent(); - _getTaskList(); - } - - void cancelTask() { - _isTask = false; - _taskList.clear(); - _storage.remove("meeting_task_list"); - _storage.remove("meeting_task_finish_list"); - _uploadService.cancelUpload(); - _summaryTaskService.cancelTask(); - } - - Future _initialize() async { - try { - _appid = AppConfig.env('VOLC_OPENSPEECH_APP_ID') ?? ''; - _token = AppConfig.env('VOLC_OPENSPEECH_ACCESS_TOKEN') ?? ''; - _cluster1 = AppConfig.env('VOLC_OPENSPEECH_CLUSTER_BIGMODEL') ?? ''; - _serviceUrl1 = AppConfig.env('VOLC_OPENSPEECH_BASE_URL_BIGMODEL') ?? ''; - _modelName = AppConfig.env('VOLC_OPENSPEECH_MODEL_NAME') ?? 'bigmodel'; - _modelVersion = AppConfig.env('VOLC_OPENSPEECH_MODEL_VERSION') ?? '400'; - - if (_appid.isEmpty || - _token.isEmpty || - _cluster1.isEmpty || - _serviceUrl1.isEmpty || - _modelName.isEmpty || - _modelVersion.isEmpty) { - Logger.error('初始化会议任务服务失败:配置参数为空'); - return false; - } - - _headers = { - 'Authorization': 'Bearer; $_token', - 'Content-Type': 'application/json', - }; - _headers1 = { - 'X-Api-App-Key': _appid, - 'X-Api-Access-Key': _token, - 'X-Api-Resource-Id': _cluster1, - 'X-Api-Request-Id': '', - 'X-Api-Sequence': '-1', - }; - } catch (e) { - Logger.error('初始化会议任务服务失败:$e'); - return false; - } - return true; - } - - void _getAgent() async { - Map userInfo = _storage.read("user_info"); - _userId = userInfo['user']['uid']; - } - - void _getTaskList() { - _taskList = _storage.read("meeting_task_list") ?? []; - taskFinishList.value = _storage.read("meeting_task_finish_list") ?? []; - _executeTask(); - } - - ///添加会议任务 - ///[id] 会议ID - ///[audiourl] 音频URL - ///[taskid] 任务ID - ///[isSpeaker] 是否为发言人 - ///[language] 语言 - ///[prompt] 提示词 - ///[outline] 大纲 - Future addTask(int id, String audiourl, String taskid, bool isSpeaker, - String language, String prompt, String outline) async { - if (audiourl.isEmpty) { - Map uploadData = _uploadService.uploadList.firstWhere( - (element) => element['id'] == id, - orElse: () => {}, - ); - if (uploadData.isNotEmpty) { - if (uploadData['audiourl'].isEmpty) { - uploadData['isTask'] = true; - uploadData['isSpeaker'] = isSpeaker; - uploadData['language'] = language; - uploadData['prompt'] = prompt; - uploadData['outline'] = outline; - } else { - await addTaskList(id, uploadData['audiourl'], taskid, isSpeaker, - language, prompt, outline); - } - } - } else { - await addTaskList( - id, audiourl, taskid, isSpeaker, language, prompt, outline); - } - } - - ///添加会议任务列表 - ///[id] 会议ID - ///[audiourl] 音频URL - ///[taskId] 任务ID - ///[isSpeaker] 是否为发言人 - ///[language] 语言 - ///[prompt] 提示词 - ///[outline] 大纲 - Future addTaskList(int id, String audiourl, String taskId, - bool isSpeaker, String language, String prompt, String outline) async { - if (taskId.isEmpty) { - taskId = await _submitTask(audiourl, isSpeaker, language); - } - await SqfliteApi.editMeeting(id, {'tasktype': 1, 'taskid': taskId}); - try { - final detail = await SqfliteApi.getMeetingDetails(id); - final seconds = (detail['seconds'] ?? 0) as int; - final filepath = (detail['filepath'] ?? '') as String; - int fileSizeBytes = 0; - if (filepath.isNotEmpty) { - try { - fileSizeBytes = await File(filepath).length(); - } catch (_) {} - } - final record = MeetingAsrRecord( - id: taskId, - meetingId: id, - taskId: taskId, - audioSeconds: seconds, - fileSizeBytes: fileSizeBytes, - language: language, - submitTimestamp: DateTime.now().millisecondsSinceEpoch, - status: asrStatusProcessing, - transcriptBytes: 0, - completeTimestamp: 0, - ); - try { - final svc = Get.isRegistered() - ? Get.find() - : Get.put(UsageStatsService(), permanent: true); - svc.addMeetingAsrRecord(record); - } catch (e) { - Logger.w('MeetingASRrecord', '提交用量记录失败: ' + e.toString()); - } - } catch (_) {} - _taskList.add({ - 'id': id, - 'taskid': taskId, - 'tasktype': 1, - 'prompt': prompt, - 'outline': outline, - }); - taskFinishList.add({ - 'id': id, - 'tasktype': 1, - }); - _storage.write("meeting_task_list", _taskList); - _storage.write("meeting_task_finish_list", taskFinishList); - _executeTask(); - } - - ///执行会议任务 - void _executeTask() async { - if (_isTask) return; - _isTask = true; - while (_taskList.isNotEmpty) { - await Future.delayed(const Duration(seconds: 2)); - Logger.info("lxm--轮询---会议任务列表$_taskList"); - for (var task in List.from(_taskList)) { - await _fileRecognize(task); - } - } - _isTask = false; - } - - void removeTask(int id, {bool isDeleteTask = false}) { - Map taskFinishData = taskFinishList.firstWhere( - (element) => element['id'] == id, - orElse: () => {}, - ); - if (taskFinishData.isNotEmpty) { - if (isDeleteTask || taskFinishData['tasktype'] >= 3) { - taskFinishList.remove(taskFinishData); - _storage.write("meeting_task_finish_list", taskFinishList); - } - } - if (isDeleteTask) { - _taskList.removeWhere((element) => element['id'] == id); - _storage.write("meeting_task_list", _taskList); - _summaryTaskService.removeTask(id); - } - } - -// - Future _submitTask( - String audiourl, bool isSpeaker, String language) async { - String format = _getFileType(audiourl); - final taskId = DateTime.now().millisecondsSinceEpoch.toString(); - - _headers1['X-Api-Request-Id'] = taskId; - Logger.info("lxm--大模型asr请求头$_headers1"); - - final requestBody1 = jsonEncode({ - "user": { - "uid": _userId, - }, - "audio": { - "url": audiourl, - "language": language, - "format": format, - }, - "request": { - "model_name": _modelName, // 模型名称,目前只有bigmodel - "model_version": _modelVersion, // 模型版本,400模型性能略有提升,且ITN有较大优化 - //"enable_itn": true, // 启用文本规范化,默认为true - "enable_punc": true, // 启用标点,默认为false - //"enable_ddc": false, // 启用语义顺滑,默认为false - "enable_speaker_info": isSpeaker, // 启用说话人聚类分离,10人以内效果较好 - //"enable_channel_split": false, // 启用双声道识别,默认为false - //"show_utterances": true, // 输出语音停顿、分句、分词信息 - //"show_speech_rate": false, // 分句信息携带语速 - //"show_volume": false, // 分句信息携带音量 - //"enable_lid": false, // 启用语种识别 - } - }); - - Logger.info("lxm--大模型asr请求体$requestBody1"); - - // final requestBody = jsonEncode({ - // "app": { - // "appid": _appid, - // "token": _token, - // "cluster": _cluster, - // }, - // "user": { - // "uid": _userId, - // }, - // "audio": { - // "format": format, - // "url": audiourl, - // }, - // "additions": { - // "language": language, - // "with_speaker_info": isSpeaker ? "True" : "False", - // } - // }); - - try { - final response = await http.post( - Uri.parse('$_serviceUrl1/submit'), - headers: _headers1, - body: requestBody1, - ); - - Logger.info("lxm--大模型asr响应体${response.body}"); - Logger.info("lxm--大模型asr上传响应头${response.headers['x-tt-logid']}"); - Logger.info( - "lxm--大模型asr上传服务端返回的状态码${response.headers['x-api-status-code']}"); - Logger.info("lxm--大模型asr上传服务端返回的消息${response.headers['x-api-message']}"); - - 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 array = path.split('.'); - return array[array.length - 1]; - } - - ///轮询语音转文字任务状态 - ///[taskId] 任务ID - Future> _queryTask(String taskId) async { - // final queryBody = jsonEncode({ - // "appid": _appid, - // "token": _token, - // "id": taskId, - // "cluster": _cluster, - // }); - - try { - _headers1['X-Api-Request-Id'] = taskId; - final response = await http.post( - Uri.parse('$_serviceUrl1/query'), - headers: _headers1, - body: jsonEncode({}), - ); - - // Logger.info("lxm---大模型asr响应体${response.body}"); - // Logger.info("lxm---大模型asr响应头${response.headers}"); - // Logger.info("lxm---大模型asr响应状态码${response.statusCode}"); - // Logger.info("lxm---大模型asr响应体logid${response.headers['x-tt-logid']}"); - // Logger.info("lxm---大模型asr服务端返回的状态码${response.headers['x-api-status-code']}"); - // Logger.info("lxm---大模型asr服务端返回的消息${response.headers['x-api-message']}"); - - 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; - } - } - - ///轮询语音转文字任务状态,若任务完成则处理转写结果 - ///[item] 包含任务ID、会议ID、AI总结提示词、AI概览提示词的Map - Future _fileRecognize(Map item) async { - try { - var respDic = await _queryTask(item['taskid']); - //var code = respDic['resp']['code'] as int; - - Logger.info("lxm---大模型asr响应体结果$respDic['result']['text']"); - - if (respDic['result']['text'] != null && - respDic['result']['text'] != '') { - await _addTextList( - item['id'], //会议ID - respDic['result']['utterances'], //语音转文字结果 - item['prompt'], //AI总结提示词 - item['outline'], //AI概览提示词 - ); - return; - } else { - Logger.debug("FileRecognize failed"); - return; - } - } catch (e) { - Logger.error('Error occurred: ${e.toString()}'); - } - } - - ///处理语音识别完成后的转写结果,包括数据库存储、状态更新和触发AI分析 - ///[id] 会议ID - ///[utterances] 语音转文字结果 - ///[prompt] AI总结提示词 - ///[outline] AI概览提示词 - Future _addTextList( - int id, List utterances, String prompt, String outline) async { - String text = ''; - String personnel = ''; - List> 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); - List newSpeakerList = List.from(speakerList); - Synchrodata.saveOperationRecord('update', { - 'original': jsonEncode(newSpeakerList), - 'id': id, - }); - if (id == meetingId) { - textList = newSpeakerList; - tasktype.value = 2; - } - _taskList.removeWhere((element) => element['id'] == id); - Map taskFinishData = taskFinishList.firstWhere( - (element) => element['id'] == id, - orElse: () => {}, - ); - if (taskFinishData.isNotEmpty) { - taskFinishData['tasktype'] = 2; - taskFinishList.refresh(); - } - _storage.write("meeting_task_list", _taskList); - _storage.write("meeting_task_finish_list", taskFinishList); - } - try { - final details = await SqfliteApi.getMeetingDetails(id); - final tid = (details['taskid'] ?? '') as String; - final transcriptBytes = utf8.encode(text).length; - try { - final svc = Get.isRegistered() - ? Get.find() - : Get.put(UsageStatsService(), permanent: true); - svc.completeMeetingAsrRecord( - id: tid, - transcriptBytes: transcriptBytes, - completeTimestamp: DateTime.now().millisecondsSinceEpoch, - ); - } catch (e) { - Logger.w('MeetingASRrecord', '完成用量记录失败: ' + e.toString()); - } - } catch (_) {} - - // 触发AI分析,分别请求总结和概览 - _summaryTaskService.addTaskList(id, text, prompt, outline); - } -} diff --git a/lib/data/services/meeting/meeting_task_service2.dart b/lib/data/services/meeting/meeting_task_service2.dart deleted file mode 100644 index ff69e20ff..000000000 --- a/lib/data/services/meeting/meeting_task_service2.dart +++ /dev/null @@ -1,971 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; - -import 'package:get/get.dart'; -import 'package:get_storage/get_storage.dart'; -import 'package:http/http.dart' as http; - -import '../db/sqflite_api.dart'; -import '../../../data/models/appconfig.dart'; -import '../../../core/utils/logger.dart'; -import '../../../core/utils/synchrodata.dart'; -import '../usage_stats_service.dart'; -import '../../../data/models/meeting_usage_stats.dart'; -import '../deapsound_ai_service.dart'; -import 'meeting_upload_service.dart'; -// 移除标准版服务桥接,改为通过聚合器对外暴露统一状态 -import 'summary_task_service.dart'; - -//火山引擎录音文件识别大模型-极速版 -class MeetingTaskService2 extends GetxService { - final GetStorage _storage = GetStorage(); - - MeetingUploadService get _uploadService => Get.find(); - SummaryTaskService get _summaryTaskService => Get.find(); - - List _taskList = []; - RxList taskFinishList = [].obs; - - bool _isTask = false; - - String _systemPrompt = ''; - String _userId = ''; - - late final String _appid; - late final String _token; - late final String _serviceUrl; - - int meetingId = 0; - RxInt tasktype = 1.obs; - List textList = []; - String summary = ''; - String overview = ''; - - @override - void onInit() { - super.onInit(); - _getAgent(); - _getTaskList(); - } - - void _getAgent() async { - Map userInfo = _storage.read("user_info"); - _userId = userInfo['user']['uid']; - _systemPrompt = AppConfig.isInitialized() - ? (AppConfig.agentsystemPrompt("meet_assistant") ?? '') - : ''; - final String base = AppConfig.env('VOLC_OPENSPEECH_BASE_URL_BIGMODEL') ?? - 'https://openspeech.bytedance.com/api/v3/auc/bigmodel'; - _appid = AppConfig.env('VOLC_OPENSPEECH_APP_ID') ?? ''; - _token = AppConfig.env('VOLC_OPENSPEECH_ACCESS_TOKEN') ?? ''; - _serviceUrl = - base.endsWith('/recognize/flash') ? base : (base + '/recognize/flash'); - } - - void _getTaskList() { - _taskList = _storage.read("meeting_task_list_v2") ?? []; - taskFinishList.value = _storage.read("meeting_task_finish_list_v2") ?? []; - _executeTask(); - } - - Future addTask( - int id, - String audiourl, { - bool isSpeaker = false, - String language = '', - String targetLanguage = '', - String prompt = '', - String outline = '', - }) async { - if (audiourl.isEmpty) { - Map uploadData = _uploadService.uploadList.firstWhere( - (element) => element['id'] == id, - orElse: () => {}, - ); - if (uploadData.isNotEmpty) { - if (uploadData['audiourl'].isEmpty) { - uploadData['isTask'] = true; - uploadData['template'] = { - 'isSpeaker': isSpeaker, - 'language': language, - 'targetLanguage': targetLanguage, - 'prompt': prompt, - 'outline': outline, - }; - _storage.write("meeting_upload_list", _uploadService.uploadList); - } else { - await addTaskList( - id, - uploadData['audiourl'], - isSpeaker: isSpeaker, - language: language, - targetLanguage: targetLanguage, - prompt: prompt, - outline: outline, - ); - } - } - } else { - await addTaskList( - id, - audiourl, - isSpeaker: isSpeaker, - language: language, - targetLanguage: targetLanguage, - prompt: prompt, - outline: outline, - ); - } - } - - Future addTaskList( - int id, - String audiourl, { - bool isSpeaker = false, - String language = '', - String targetLanguage = '', - String prompt = '', - String outline = '', - }) async { - final String taskId = DateTime.now().millisecondsSinceEpoch.toString(); - await SqfliteApi.editMeeting(id, {'tasktype': 1, 'taskid': taskId}); - - try { - final detail = await SqfliteApi.getMeetingDetails(id); - final seconds = (detail['seconds'] ?? 0) as int; - final filepath = (detail['filepath'] ?? '') as String; - int fileSizeBytes = 0; - if (filepath.isNotEmpty) { - try { - fileSizeBytes = await File(filepath).length(); - } catch (_) {} - } - final record = MeetingAsrRecord( - id: taskId, - meetingId: id, - taskId: taskId, - audioSeconds: seconds, - fileSizeBytes: fileSizeBytes, - language: language, - submitTimestamp: DateTime.now().millisecondsSinceEpoch, - status: asrStatusProcessing, - transcriptBytes: 0, - completeTimestamp: 0, - ); - try { - final svc = Get.isRegistered() - ? Get.find() - : Get.put(UsageStatsService(), permanent: true); - svc.addMeetingAsrRecord(record); - } catch (e) { - Logger.w('MeetingASRrecord', '提交用量记录失败: ' + e.toString()); - } - } catch (_) {} - _taskList.add({ - 'id': id, - 'audiourl': audiourl, - 'tasktype': 1, - 'taskid': taskId, - 'template': { - 'isSpeaker': isSpeaker, - 'language': language, - 'targetLanguage': targetLanguage, - 'prompt': prompt, - 'outline': outline, - }, - }); - taskFinishList.add({'id': id, 'tasktype': 1}); - _storage.write("meeting_task_list_v2", _taskList); - _storage.write("meeting_task_finish_list_v2", taskFinishList); - // 由聚合器统一对外呈现状态,不直接写入标准版列表 - _executeTask(); - } - - void _executeTask() async { - if (_isTask) return; - _isTask = true; - Logger.debug('开始执行任务,任务数量:${_taskList.length}'); - - while (_taskList.isNotEmpty) { - Logger.debug('任务循环执行,剩余任务:${_taskList.length}'); - await Future.delayed(const Duration(seconds: 2)); - - // 创建任务列表的副本,避免在迭代过程中修改原列表 - List tasksCopy = List.from(_taskList); - - for (var element in tasksCopy) { - Logger.debug('处理任务:${element['id']},音频:${element['audiourl']}'); - - // 检查任务是否仍在列表中(可能已被其他地方移除) - if (_taskList.any((task) => task['id'] == element['id'])) { - try { - await _fileRecognize(element); - } catch (e) { - Logger.error('处理任务失败:${element['id']},错误:$e'); - // 确保失败的任务被移除,避免无限循环 - _taskList.removeWhere((task) => task['id'] == element['id']); - _storage.write("meeting_task_list_v2", _taskList); - - // 更新任务状态为失败 - await SqfliteApi.editMeeting(element['id'], {'tasktype': 0}); - } - } else { - Logger.debug('任务 ${element['id']} 已从任务列表移除'); - } - } - - // 如果没有任务被处理,避免无限循环 - if (_taskList.length == tasksCopy.length) { - Logger.warning('本次循环未处理任何任务,为避免无限执行终止循环'); - break; - } - } - - _isTask = false; - Logger.debug('任务执行完成,剩余任务:${_taskList.length}'); - } - - void removeTask(int id, {bool isDeleteTask = false}) { - Map taskFinishData = taskFinishList.firstWhere( - (element) => element['id'] == id, - orElse: () => {}, - ); - if (taskFinishData.isNotEmpty) { - if (isDeleteTask || taskFinishData['tasktype'] >= 3) { - taskFinishList.remove(taskFinishData); - _storage.write("meeting_task_finish_list_v2", taskFinishList); - } - } - if (isDeleteTask) { - _taskList.removeWhere((element) => element['id'] == id); - _storage.write("meeting_task_list_v2", _taskList); - } - } - - Future> _recognizeAudio( - String audiourl, { - bool enableSpeakerInfo = true, - String language = '', - String? requestIdOverride, - }) async { - String requestId = - requestIdOverride ?? DateTime.now().millisecondsSinceEpoch.toString(); - - // 为每次请求生成新的headers - 适配大模型极速版API - Map requestHeaders = { - 'X-Api-App-Key': _appid, - 'X-Api-Access-Key': _token, - 'X-Api-Resource-Id': 'volc.bigasr.auc_turbo', - 'X-Api-Request-Id': requestId, - 'X-Api-Sequence': '-1', - 'Content-Type': 'application/json', - }; - - // 使用大模型极速版API的新请求格式 - final Map requestPayload = { - "user": {"uid": _appid}, - "audio": {"url": audiourl}, - "request": { - "model_name": "bigmodel", - "enable_itn": true, - "enable_punc": true, - "enable_ddc": true, - "enable_speaker_info": enableSpeakerInfo, - "word_info": 1, - "show_utterances": true - } - }; - - if (language.isNotEmpty) { - requestPayload['request']['language'] = language; - } - - final String requestBody = jsonEncode(requestPayload); - - try { - // 记录请求详情 - Logger.debug('API 请求地址:$_serviceUrl'); - Logger.debug('API 请求头:$requestHeaders'); - Logger.debug('API 请求体:$requestBody'); - - final response = await http.post( - Uri.parse(_serviceUrl), - headers: requestHeaders, - body: requestBody, - ); - - Logger.debug('API 响应状态码:${response.statusCode}'); - Logger.debug('API 响应头:${response.headers}'); - - if (response.statusCode != 200) { - Logger.error('识别失败,状态码:${response.statusCode},响应体:${response.body}'); - throw Exception('识别失败,状态码:${response.statusCode}'); - } - - final respDic = jsonDecode(response.body); - Logger.debug('识别接口响应:$respDic'); - - // 检查响应中是否包含资源相关信息 - if (respDic.containsKey('resource_id') || respDic.containsKey('model')) { - Logger.debug('资源校验 - 使用的资源ID:volc.bigasr.auc_turbo'); - Logger.debug( - '响应中的资源信息:${respDic['resource_id'] ?? respDic['model'] ?? '响应未包含'}'); - } - - return respDic; - } catch (e) { - Logger.error('识别请求异常:${e.toString()}'); - rethrow; - } - } - - String _getFileType(String path) { - List array = path.split('.'); - return array[array.length - 1]; - } - - int _parseTime(dynamic timeValue) { - if (timeValue == null) return 0; - - if (timeValue is int) { - return timeValue; - } else if (timeValue is double) { - return timeValue.round(); - } else if (timeValue is String) { - try { - double parsed = double.parse(timeValue); - return parsed.round(); - } catch (e) { - Logger.error('时间值解析失败:$timeValue'); - return 0; - } - } else { - Logger.error('未知的时间值类型:${timeValue.runtimeType},值:$timeValue'); - return 0; - } - } - - Future _fileRecognize(Map item) async { - try { - Logger.debug('开始识别音频:${item['audiourl']}'); - // 识别语言选取:若设置了目标输出语言且与录音语言一致,优先让ASR自动识别,避免错配 - final String tplLang = - (((item['template'] ?? {})['language']) ?? '').toString(); - final String tplTarget = - (((item['template'] ?? {})['targetLanguage']) ?? '').toString(); - String asrLanguage = tplLang; - if (tplTarget.isNotEmpty && tplTarget == tplLang) { - asrLanguage = ''; - } - var respDic = await _recognizeAudio( - item['audiourl'], - enableSpeakerInfo: - (((item['template'] ?? {})['isSpeaker']) ?? false) as bool, - language: asrLanguage, - requestIdOverride: (item['taskid'] ?? '').toString().isEmpty - ? null - : (item['taskid'] ?? '').toString(), - ); - - Logger.debug('API 全量响应结构:${respDic.toString()}'); - Logger.debug('响应包含的键:${respDic.keys.toList()}'); - - // 检查响应格式并提取utterances - List utterances = []; - - // 检查不同可能的响应格式 - if (respDic['result'] != null && - respDic['result']['utterances'] != null) { - utterances = respDic['result']['utterances']; - Logger.debug('在 result.utterances 中找到 ${utterances.length} 条语句'); - } else if (respDic['resp'] != null && - respDic['resp']['utterances'] != null) { - utterances = respDic['resp']['utterances']; - Logger.debug('在 resp.utterances 中找到 ${utterances.length} 条语句'); - } else if (respDic['utterances'] != null) { - utterances = respDic['utterances']; - Logger.debug('在根级 utterances 中找到 ${utterances.length} 条语句'); - } else if (respDic['data'] != null && - respDic['data']['utterances'] != null) { - utterances = respDic['data']['utterances']; - Logger.debug('在 data.utterances 中找到 ${utterances.length} 条语句'); - } else if (respDic['response'] != null && - respDic['response']['utterances'] != null) { - utterances = respDic['response']['utterances']; - Logger.debug('在 response.utterances 中找到 ${utterances.length} 条语句'); - } else { - // 检查是否有错误信息 - if (respDic['error'] != null) { - Logger.error('接口返回错误:${respDic['error']}'); - } else if (respDic['message'] != null) { - Logger.error('接口返回消息:${respDic['message']}'); - } else { - Logger.error('未知的响应格式。可用键:${respDic.keys.toList()}'); - // 尝试查找任何包含文本的字段 - respDic.forEach((key, value) { - Logger.debug('键:$key,值类型:${value.runtimeType},值:$value'); - }); - } - } - - if (utterances.isNotEmpty) { - Logger.debug('开始处理 ${utterances.length} 条语句'); - await _addTextList(item['id'], utterances, item['template']); - // 从任务列表中移除已完成的任务 - _taskList.removeWhere((element) => element['id'] == item['id']); - _storage.write("meeting_task_list_v2", _taskList); - Logger.debug('音频识别成功,会议ID:${item['id']}'); - - // 更新UI状态 - 如果是当前会议,触发UI更新 - if (item['id'] == meetingId) { - tasktype.value = 2; // 转写完成,进入分析阶段 - } - } else { - Logger.error('响应中未发现语句。完整响应:$respDic'); - Logger.debug('未检测到语句,开始执行无声音频检查'); - Logger.debug('无声音频检测调试信息'); - Logger.debug('响应键列表:${respDic.keys.toList()}'); - Logger.debug('响应代码:${respDic['code']}'); - Logger.debug('响应消息:${respDic['message']}'); - - // 检查是否为无声音频 - bool isSilentAudio = _checkIfSilentAudio(respDic); - Logger.debug('无声检测结果:$isSilentAudio'); - - if (isSilentAudio) { - Logger.debug('检测到无声音频,设置任务类型为 -1'); - // 设置无声音频状态 (使用特殊值 -1) - await SqfliteApi.editMeeting(item['id'], {'tasktype': -1}); - if (item['id'] == meetingId) { - Logger.debug('当前会议设置 tasktype.value 为 -1'); - tasktype.value = -1; // 无声音频 - } - - // 更新taskFinishList中的状态 - Map taskFinishData = taskFinishList.firstWhere( - (element) => element['id'] == item['id'], - orElse: () => {}, - ); - if (taskFinishData.isNotEmpty) { - taskFinishData['tasktype'] = -1; - taskFinishList.refresh(); - } - _storage.write("meeting_task_finish_list_v2", taskFinishList); - // 由聚合器统一对外呈现状态,不直接写入标准版列表 - // 由聚合器统一对外呈现状态,不直接写入标准版列表 - } else { - Logger.error('识别失败,原因未知'); - // 识别失败,更新数据库状态为失败 - await SqfliteApi.editMeeting(item['id'], {'tasktype': 0}); - if (item['id'] == meetingId) { - tasktype.value = 0; // 识别失败 - } - - // 更新taskFinishList中的状态 - Map taskFinishData = taskFinishList.firstWhere( - (element) => element['id'] == item['id'], - orElse: () => {}, - ); - if (taskFinishData.isNotEmpty) { - taskFinishData['tasktype'] = 0; - taskFinishList.refresh(); - } - _storage.write("meeting_task_finish_list_v2", taskFinishList); - } - - // 从任务列表中移除 - _taskList.removeWhere((element) => element['id'] == item['id']); - _storage.write("meeting_task_list_v2", _taskList); - } - } catch (e) { - Logger.error('文件识别流程发生异常:${e.toString()}'); - Logger.error('堆栈信息:${StackTrace.current}'); - - // 更新数据库状态为失败 - try { - await SqfliteApi.editMeeting(item['id'], {'tasktype': 0}); - } catch (dbError) { - Logger.error('数据库更新会议状态失败:$dbError'); - } - - // 出错时也要从任务列表中移除,避免无限重试 - _taskList.removeWhere((element) => element['id'] == item['id']); - _storage.write("meeting_task_list_v2", _taskList); - - // 更新UI状态 - 如果是当前会议,显示错误状态 - if (item['id'] == meetingId) { - tasktype.value = 0; // 识别失败 - } - } - } - - /// 检查是否为无声音频 - bool _checkIfSilentAudio(Map response) { - Logger.debug('检查是否为无声音频,响应:${response.toString()}'); - - // 检查API响应中的错误信息或状态码 - // 根据实际API响应格式调整检测逻辑 - - // 检查是否有错误码表示无声音频 - if (response['code'] != null) { - int code = response['code']; - Logger.debug('检测到响应代码:$code'); - // 常见的无声音频错误码 - if (code == 40001 || - code == 40002 || - code == 40003 || - code == 50001 || - code == 50002 || - code == 50003) { - Logger.debug('根据错误码判断为无声音频:$code'); - return true; - } - } - - // 检查错误消息 - if (response['message'] != null) { - String message = response['message'].toString().toLowerCase(); - Logger.debug('检测到响应消息:$message'); - if (message.contains('silent') || - message.contains('no audio') || - message.contains('无声') || - message.contains('静音') || - message.contains('empty audio') || - message.contains('no speech') || - message.contains('audio too short') || - message.contains('insufficient audio')) { - Logger.debug('根据消息判断为无声音频:$message'); - return true; - } - } - - // 检查result中的错误信息 - if (response['result'] != null && response['result']['error'] != null) { - String error = response['result']['error'].toString().toLowerCase(); - Logger.debug('检测到 result.error:$error'); - if (error.contains('silent') || - error.contains('no audio') || - error.contains('无声') || - error.contains('静音') || - error.contains('empty audio') || - error.contains('no speech') || - error.contains('audio too short')) { - Logger.debug('根据 result.error 判断为无声音频:$error'); - return true; - } - } - - // 检查是否有音频时长信息且时长为0或极短 - if (response['result'] != null && response['result']['duration'] != null) { - double duration = (response['result']['duration'] as num).toDouble(); - Logger.debug('检测到音频时长:$duration 秒'); - if (duration <= 0.5) { - // 音频时长小于0.5秒认为是无声 - Logger.debug('根据时长过短判断为无声音频:$duration'); - return true; - } - } - - // 检查utterances是否为空且没有其他明确的成功标识 - bool hasUtterances = false; - if (response['result'] != null && - response['result']['utterances'] != null) { - hasUtterances = (response['result']['utterances'] as List).isNotEmpty; - } else if (response['resp'] != null && - response['resp']['utterances'] != null) { - hasUtterances = (response['resp']['utterances'] as List).isNotEmpty; - } else if (response['utterances'] != null) { - hasUtterances = (response['utterances'] as List).isNotEmpty; - } else if (response['data'] != null && - response['data']['utterances'] != null) { - hasUtterances = (response['data']['utterances'] as List).isNotEmpty; - } else if (response['response'] != null && - response['response']['utterances'] != null) { - hasUtterances = (response['response']['utterances'] as List).isNotEmpty; - } - - // 检查文本内容是否为空 - String textContent = ''; - if (response['result'] != null && response['result']['text'] != null) { - textContent = response['result']['text'].toString().trim(); - } else if (response['text'] != null) { - textContent = response['text'].toString().trim(); - } else if (response['data'] != null && response['data']['text'] != null) { - textContent = response['data']['text'].toString().trim(); - } - - // 如果文本内容为空且音频时长大于1秒,很可能是无声音频 - if (textContent.isEmpty) { - double audioDuration = 0; - if (response['audio_info'] != null && - response['audio_info']['duration'] != null) { - audioDuration = (response['audio_info']['duration'] as num).toDouble() / - 1000; // 转换为秒 - } else if (response['result'] != null && - response['result']['additions'] != null && - response['result']['additions']['duration'] != null) { - audioDuration = - (response['result']['additions']['duration'] as num).toDouble() / - 1000; // 转换为秒 - } - - Logger.debug('文本内容为空,音频时长:$audioDuration 秒'); - if (audioDuration > 1.0) { - // 音频时长大于1秒但没有文本内容,认为是无声音频 - Logger.debug('判断为无声音频:文本为空且时长充足'); - return true; - } - } - - // 如果没有utterances且响应状态正常,可能是无声音频 - if (!hasUtterances) { - // 检查是否有明确的成功状态码 - bool hasSuccessCode = false; - if (response['code'] != null) { - int code = response['code']; - hasSuccessCode = (code == 0 || code == 200); - } - - // 如果有成功状态码但没有utterances,很可能是无声音频 - if (hasSuccessCode) { - Logger.debug('判断为无声音频:成功状态码但无语句'); - return true; - } - } - - Logger.debug('未判定为无声音频'); - return false; - } - - Future _addTextList(int id, List utterances, - [Map? templateData]) async { - Logger.debug('处理会议 $id 的 ${utterances.length} 条语句'); - String text = ''; - String personnel = ''; - List> list = []; - - for (int i = 0; i < utterances.length; i++) { - var element = utterances[i]; - Logger.debug('处理第 $i 条语句:${element.toString()}'); - - String speaker = ''; - String content = ''; - int startTime = 0; - int endTime = 0; - - // 提取说话人信息 - 检查多种可能的字段名 - if (element['additions'] != null && element['additions'].isNotEmpty) { - if (element['additions']['speaker'] != null) { - speaker = 'Speaker ${element['additions']['speaker']}'; - } - } else if (element['speaker'] != null) { - speaker = 'Speaker ${element['speaker']}'; - } else if (element['speaker_id'] != null) { - speaker = 'Speaker ${element['speaker_id']}'; - } - - // 提取文本内容 - 检查多种可能的字段名 - if (element['text'] != null) { - content = element['text'].toString(); - } else if (element['content'] != null) { - content = element['content'].toString(); - } else if (element['transcript'] != null) { - content = element['transcript'].toString(); - } else { - Logger.error('语句中未找到文本内容:$element'); - continue; - } - - // 提取时间信息 - 检查多种可能的字段名和格式 - if (element['start_time'] != null) { - startTime = _parseTime(element['start_time']); - } else if (element['startTime'] != null) { - startTime = _parseTime(element['startTime']); - } else if (element['begin'] != null) { - startTime = _parseTime(element['begin']); - } - - if (element['end_time'] != null) { - endTime = _parseTime(element['end_time']); - } else if (element['endTime'] != null) { - endTime = _parseTime(element['endTime']); - } else if (element['end'] != null) { - endTime = _parseTime(element['end']); - } - - if (speaker.isNotEmpty) { - text += '[$speaker]: '; - if (!personnel.contains(speaker)) { - personnel += '[$speaker] '; - } - } - - list.add({ - 'meetingid': id, - 'content': content, - 'starttime': startTime, - 'endtime': endTime, - 'speaker': speaker, - }); - text += '$content\n'; - - Logger.debug('已添加语句:说话人=$speaker,内容=$content,开始=$startTime,结束=$endTime'); - } - - Logger.debug('处理完成的语句总数:${list.length}'); - var result = await SqfliteApi.insertMeetingSpeaker(list); - if (result != null) { - // 初始用于总结/概览的文本,先用原始拼接文本作为默认值 - String summarySourceText = text; - // 根据目标语言决定是否需要翻译文本列表 - final String targetLanguage = - ((templateData?['targetLanguage']) ?? '').toString(); - final String sourceLanguage = - ((templateData?['language']) ?? '').toString(); - bool needTranslate = false; - if (targetLanguage.isNotEmpty) { - // 若未指定录音语言,默认认为需要翻译;否则当目标语言与录音语言主码不同则翻译 - if (sourceLanguage.isEmpty) { - needTranslate = true; - } else { - String srcPrefix = sourceLanguage.toLowerCase().substring(0, 2); - String tgtPrefix = targetLanguage.toLowerCase().substring(0, 2); - needTranslate = srcPrefix != tgtPrefix; - } - } - if (needTranslate) { - try { - // 保持在“正在转写”状态,先进行翻译为目标语言 - await _translateTextList(id, targetLanguage); - } catch (e) { - Logger.error('转写文本翻译失败:${e.toString()}'); - } - // 翻译完成后再写入 personnel、设置已完成并刷新 UI - await SqfliteApi.editMeeting( - id, {'tasktype': 2, 'personnel': personnel}); - List speakerList = await SqfliteApi.getMeetingSpeaker(id); - List newSpeakerList = List.from(speakerList); - Synchrodata.saveOperationRecord('update', { - 'original': jsonEncode(newSpeakerList), - 'id': id, - }); - if (id == meetingId) { - textList = newSpeakerList; - tasktype.value = 2; - } - // 由聚合器统一对外呈现状态,不直接写入标准版列表 - // 由聚合器统一对外呈现状态,不直接写入标准版列表 - // 用最新(已翻译)的文本重建总结/概览输入 - summarySourceText = ''; - for (var element in newSpeakerList) { - final String spk = (element['speaker'] ?? '').toString(); - final String cnt = (element['content'] ?? '').toString(); - if (spk.isNotEmpty) { - summarySourceText += '[$spk]: '; - } - summarySourceText += '$cnt\n'; - } - Map taskFinishData = taskFinishList.firstWhere( - (element) => element['id'] == id, - orElse: () => {}, - ); - if (taskFinishData.isNotEmpty) { - taskFinishData['tasktype'] = 2; - taskFinishList.refresh(); - } - _storage.write("meeting_task_finish_list_v2", taskFinishList); - } else { - // 原有中文直出流程 - await SqfliteApi.editMeeting( - id, {'tasktype': 2, 'personnel': personnel}); - List speakerList = await SqfliteApi.getMeetingSpeaker(id); - List newSpeakerList = List.from(speakerList); - Synchrodata.saveOperationRecord('update', { - 'original': jsonEncode(newSpeakerList), - 'id': id, - }); - if (id == meetingId) { - textList = newSpeakerList; - tasktype.value = 2; - } - // 仍使用最新DB中的文本构建总结/概览输入,避免与原始拼接文本不一致 - summarySourceText = ''; - for (var element in newSpeakerList) { - final String spk = (element['speaker'] ?? '').toString(); - final String cnt = (element['content'] ?? '').toString(); - if (spk.isNotEmpty) { - summarySourceText += '[$spk]: '; - } - summarySourceText += '$cnt\n'; - } - Map taskFinishData = taskFinishList.firstWhere( - (element) => element['id'] == id, - orElse: () => {}, - ); - if (taskFinishData.isNotEmpty) { - taskFinishData['tasktype'] = 2; - taskFinishList.refresh(); - } - _storage.write("meeting_task_finish_list_v2", taskFinishList); - } - // 使用统一的总结服务生成概览与总结 - final String tplPrompt = ((templateData?['prompt']) ?? '').toString(); - final String tplOutline = ((templateData?['outline']) ?? '').toString(); - final String outCode = (((templateData?['targetLanguage']) ?? - (templateData?['language']) ?? - '') as String) - .toLowerCase(); - String langDesc = ''; - if (outCode.startsWith('zh')) { - langDesc = '中文'; - } else if (outCode.startsWith('en')) { - langDesc = '英文'; - } else if (outCode.startsWith('ja')) { - langDesc = '日文'; - } else if (outCode.startsWith('ko')) { - langDesc = '韩文'; - } else if (outCode.startsWith('fr')) { - langDesc = '法文'; - } else if (outCode.startsWith('de')) { - langDesc = '德文'; - } else if (outCode.startsWith('es')) { - langDesc = '西班牙文'; - } else if (outCode.startsWith('ru')) { - langDesc = '俄文'; - } else if (outCode.startsWith('it')) { - langDesc = '意大利文'; - } else if (outCode.startsWith('pt')) { - langDesc = '葡萄牙文'; - } else if (outCode.startsWith('th')) { - langDesc = '泰文'; - } else if (outCode.startsWith('vi')) { - langDesc = '越南文'; - } - final String promptToUse = tplPrompt.isEmpty - ? (langDesc.isEmpty - ? '' - : '输出语言:' + langDesc + '。请全程使用' + langDesc + '输出。') - : (langDesc.isEmpty - ? tplPrompt - : tplPrompt + ' 输出语言:' + langDesc + '。请全程使用' + langDesc + '输出。'); - final String outlineToUse = tplOutline.isEmpty - ? (langDesc.isEmpty - ? '' - : '输出语言:' + langDesc + '。请全程使用' + langDesc + '输出。') - : (langDesc.isEmpty - ? tplOutline - : tplOutline + ' 输出语言:' + langDesc + '。请全程使用' + langDesc + '输出。'); - try { - final details = await SqfliteApi.getMeetingDetails(id); - final tid = (details['taskid'] ?? '') as String; - final transcriptBytes = utf8.encode(summarySourceText).length; - final svc = Get.isRegistered() - ? Get.find() - : Get.put(UsageStatsService(), permanent: true); - svc.completeMeetingAsrRecord( - id: tid, - transcriptBytes: transcriptBytes, - completeTimestamp: DateTime.now().millisecondsSinceEpoch, - ); - } catch (e) { - Logger.w('MeetingASRrecord', '完成用量记录失败: ' + e.toString()); - } - - _summaryTaskService.addTaskList( - id, summarySourceText, promptToUse, outlineToUse); - } - } - - Future _translateTextList(int id, String targetLanguage) async { - // 读取刚插入的文本条目 - List speakerList = await SqfliteApi.getMeetingSpeaker(id); - // 构造简洁的可翻译列表 - List toTranslate = speakerList - .map((e) => { - 'id': e['id'], - 'speaker': (e['speaker'] ?? '').toString(), - 'content': (e['content'] ?? '').toString(), - }) - .toList(); - - final String userJson = jsonEncode({ - 'targetLanguage': targetLanguage, - 'items': toTranslate, - }); - - // 根据目标语言构建提示语 - String tgtName = targetLanguage.toLowerCase(); - String langDesc = '目标语言'; - if (tgtName.startsWith('zh')) { - langDesc = '中文'; - } else if (tgtName.startsWith('en')) { - langDesc = '英文'; - } else if (tgtName.startsWith('ja')) { - langDesc = '日文'; - } else if (tgtName.startsWith('ko')) { - langDesc = '韩文'; - } else if (tgtName.startsWith('fr')) { - langDesc = '法文'; - } else if (tgtName.startsWith('de')) { - langDesc = '德文'; - } else if (tgtName.startsWith('es')) { - langDesc = '西班牙文'; - } else if (tgtName.startsWith('ru')) { - langDesc = '俄文'; - } else if (tgtName.startsWith('it')) { - langDesc = '意大利文'; - } else if (tgtName.startsWith('pt')) { - langDesc = '葡萄牙文'; - } else if (tgtName.startsWith('th')) { - langDesc = '泰文'; - } else if (tgtName.startsWith('vi')) { - langDesc = '越南文'; - } - - final String systemPrompt = '你是专业的翻译助手。请将每条items中的content精准翻译为' + - langDesc + - ',保持术语专业、语气自然。仅返回JSON数组,元素形如{"id":<原id>,"content":"<翻译后文本>"},不要额外说明,不要漏掉任何一条。'; - - final String? translated = await DeapsoundAIService().sendMessage( - messages: [ - {'role': 'system', 'content': systemPrompt}, - {'role': 'user', 'content': userJson}, - ], - ); - - if (translated == null || translated.isEmpty) return; - List arr; - try { - arr = jsonDecode(translated); - } catch (_) { - // 若模型返回包裹对象,尝试取字段 - try { - final obj = jsonDecode(translated); - arr = (obj is Map && obj['data'] is List) ? (obj['data'] as List) : []; - } catch (e) { - Logger.error('翻译结果 JSON 解析失败:${e.toString()}'); - return; - } - } - if (arr.isEmpty) return; - // 批量更新 - for (var item in arr) { - if (item is Map && item['id'] != null) { - await SqfliteApi.editMeetingSpeaker([ - { - 'id': item['id'], - 'content': (item['content'] ?? '').toString(), - } - ]); - } - } - // 刷新缓存 - if (id == meetingId) { - textList = await SqfliteApi.getMeetingSpeaker(id); - // 主动触发监听刷新 UI(MeetingDetailsController 通过监听 tasktype 同步 textList) - try { - tasktype.refresh(); - } catch (_) {} - } - } -} diff --git a/lib/data/services/meeting/meeting_upload_service.dart b/lib/data/services/meeting/meeting_upload_service.dart index 4472cc409..c0c15a0cf 100644 --- a/lib/data/services/meeting/meeting_upload_service.dart +++ b/lib/data/services/meeting/meeting_upload_service.dart @@ -4,14 +4,14 @@ import 'package:get_storage/get_storage.dart'; import '../../../core/utils/logger.dart'; import '../../../core/utils/upload_oss.dart'; +import '../network/api.dart'; import '../usage_stats_service.dart'; import '../db/sqflite_api.dart'; -import 'meeting_task_service.dart'; -import 'meeting_task_service2.dart'; +import 'meeting_task_post.dart'; /// 管理会议音频文件上传服务 class MeetingUploadService extends GetxService { - final _taskService = Get.find(); + final _taskService = Get.find(); final GetStorage _storage = GetStorage(); @@ -47,26 +47,20 @@ class MeetingUploadService extends GetxService { String rootDir, { bool isUpload = true, }) async { - bool flash = false; int fileSize = 0; try { - fileSize = await File(filePath).length(); //获取文件大小 - final details = await SqfliteApi.getMeetingDetails(id); - final int seconds = (details['seconds'] ?? 0) as int; - flash = seconds > 0 && seconds <= 7200; + fileSize = await File(filePath).length(); } catch (_) {} - _storage.write("meeting_service_type_$id", flash ? 'fast' : 'standard'); uploadList.add({ 'id': id, 'filepath': filePath, 'rootdir': rootDir, 'audiourl': '', 'isTask': false, - 'isSpeaker': true, - 'language': '', - 'prompt': '', - 'outline': '', - 'useFlashASR': flash, + 'formlanguage': '', + 'tolanguage': '', + 'isdistinguishspeaker': true, + 'templateid': 0, 'fileSizeBytes': fileSize, }); _storage.write("meeting_upload_list", uploadList); @@ -85,18 +79,20 @@ class MeetingUploadService extends GetxService { if (element['audiourl'].isEmpty) { meetingId.value = element['id']; try { - int _sent = 0; - int _total = 0; + int totalProgress = 0; final audiourl = await UploadOss.upload( filepath: element['filepath'], rootDir: element['rootdir'], onSendProgress: (int count, int total) { // uploadProgress.value = count / total; - _sent = count; - _total = total; + totalProgress = total; uploadProgress.value = total > 0 ? count / total : 0.0; }, ); + await Api.upRecordEchomeet({ + 'id': element['id'], + 'audiourl': audiourl, + }); await SqfliteApi.editMeetingTitle( element['id'], {'audiourl': audiourl}, @@ -110,7 +106,8 @@ class MeetingUploadService extends GetxService { try { fileSizeBytes = element['fileSizeBytes'] ?? 0; } catch (_) {} - final transferred = _total > 0 ? _total : fileSizeBytes; + final transferred = + totalProgress > 0 ? totalProgress : fileSizeBytes; svc.recordMeetingObjectStorage( meetingId: element['id'], storageUrl: audiourl, @@ -119,31 +116,16 @@ class MeetingUploadService extends GetxService { uploadTimestamp: DateTime.now().millisecondsSinceEpoch, ); } catch (e) { - Logger.w('MeetingOSrecord', '对象存储用量记录失败: ' + e.toString()); + Logger.w('MeetingOSrecord', '对象存储用量记录失败: $e'); } if (element['isTask']) { - if (element['useFlashASR'] == true) { - final service2 = Get.find(); - await service2.addTaskList( - element['id'], - audiourl, - isSpeaker: element['isSpeaker'], - language: element['language'], - targetLanguage: '', - prompt: element['prompt'], - outline: element['outline'], - ); - } else { - await _taskService.addTaskList( - element['id'], - audiourl, - '', - element['isSpeaker'], - element['language'], - element['prompt'], - element['outline'], - ); - } + await _taskService.submitTask( + element['id'], + element['formlanguage'], + element['tolanguage'], + element['isdistinguishspeaker'], + element['templateid'], + ); } element['audiourl'] = audiourl; uploadProgress.value = 0.0; diff --git a/lib/data/services/meeting/summary_task_service.dart b/lib/data/services/meeting/summary_task_service.dart deleted file mode 100644 index c29741359..000000000 --- a/lib/data/services/meeting/summary_task_service.dart +++ /dev/null @@ -1,187 +0,0 @@ -import 'package:get/get.dart'; -import 'package:get_storage/get_storage.dart'; - -import '../../../core/utils/logger.dart'; -import '../db/sqflite_api.dart'; -import '../deapsound_ai_service.dart'; -import 'meeting_task_service.dart'; -import 'meeting_task_service2.dart'; - -class SummaryTaskService extends GetxService { - final _taskService = Get.find(); - final _taskService2 = Get.find(); - - final GetStorage _storage = GetStorage(); - - List _taskList = []; - - bool _isTask = false; - - String summary = ''; //AI总结内容 - String overview = ''; //AI概览内容 - - @override - void onInit() { - super.onInit(); - _getTaskList(); - } - - void cancelTask() { - _taskList.clear(); - _storage.remove("summary_task_list"); - } - - void _getTaskList() { - _taskList = _storage.read("summary_task_list") ?? []; - _executeTask(); - } - - Future addTaskList( - int id, String text, String prompt, String outline) async { - Logger.debug("添加总结任务列表"); - _taskList.add({ - 'id': id, - 'text': text, - 'prompt': prompt, - 'outline': outline, - }); - _storage.write("summary_task_list", _taskList); - _executeTask(); - } - - void _executeTask() async { - if (_taskList.isEmpty) return; - if (_isTask) return; - _isTask = true; - var task = _taskList.first; - _aiServiceMessage( - task['id'], - task['text'], - task['prompt'], - task['outline'], - ); - Logger.debug("执行总结任务列表"); - } - - void removeTask(int id) { - _taskList.removeWhere((element) => element['id'] == id); - _storage.write("summary_task_list", _taskList); - } - - void _aiServiceMessage(int id, String text, String prompt, String outline) { - final ai = DeapsoundAIService(); - String summaryText = ''; - String overviewText = ''; - var summaryDone = false; - var overviewDone = false; - var summarySuccess = false; - var overviewSuccess = false; - - void tryFinish() { - if (summaryDone && overviewDone) { - if (summarySuccess && - overviewSuccess && - summaryText.isNotEmpty && - overviewText.isNotEmpty) { - _aiServiceValue(id, summaryText, overviewText); - } else { - _isTask = false; - _executeTask(); - } - } - } - - final summaryStream = ai.sendMessageStream(messages: [ - {'role': 'system', 'content': prompt}, - {'role': 'user', 'content': text}, - ]).timeout(const Duration(seconds: 120)); - - final overviewStream = ai.sendMessageStream(messages: [ - {'role': 'system', 'content': outline}, - {'role': 'user', 'content': text}, - ]).timeout(const Duration(seconds: 120)); - - summaryStream.listen((chunk) { - summaryText += chunk; - }, onError: (error) { - summaryDone = true; - summarySuccess = false; - tryFinish(); - }, onDone: () { - summaryDone = true; - summarySuccess = true; - tryFinish(); - }, cancelOnError: true); - - overviewStream.listen((chunk) { - overviewText += chunk; - }, onError: (error) { - overviewDone = true; - overviewSuccess = false; - tryFinish(); - }, onDone: () { - overviewDone = true; - overviewSuccess = true; - tryFinish(); - }, cancelOnError: true); - } - - Future _aiServiceValue( - int id, String summaryText, String overviewText) async { - final exists = _taskList.any((item) => item['id'] == id); - if (!exists) { - _isTask = false; - _executeTask(); - return; - } - await SqfliteApi.editMeeting(id, { - 'tasktype': 3, - 'summary': summaryText, - 'overview': overviewText, - }); - - // 更新 V1 Service 状态 - if (id == _taskService.meetingId) { - summary = summaryText; - overview = overviewText; - _taskService.tasktype.value = 3; - } - - // 更新 V2 Service 状态 - if (id == _taskService2.meetingId) { - summary = summaryText; - overview = overviewText; - _taskService2.summary = summaryText; - _taskService2.overview = overviewText; - _taskService2.tasktype.value = 3; - } - - // 更新 V1 任务完成列表 - Map taskFinishData = _taskService.taskFinishList.firstWhere( - (element) => element['id'] == id, - orElse: () => {}, - ); - if (taskFinishData.isNotEmpty) { - taskFinishData['tasktype'] = 3; - _taskService.taskFinishList.refresh(); - } - _storage.write("meeting_task_finish_list", _taskService.taskFinishList); - - // 更新 V2 任务完成列表 - Map taskFinishData2 = _taskService2.taskFinishList.firstWhere( - (element) => element['id'] == id, - orElse: () => {}, - ); - if (taskFinishData2.isNotEmpty) { - taskFinishData2['tasktype'] = 3; - _taskService2.taskFinishList.refresh(); - } - _storage.write("meeting_task_finish_list_v2", _taskService2.taskFinishList); - - _taskList.removeWhere((element) => element['id'] == id); - _storage.write("summary_task_list", _taskList); - _isTask = false; - _executeTask(); - Logger.debug("总结任务完成"); - } -} diff --git a/lib/data/services/network/api.dart b/lib/data/services/network/api.dart index 77615c04d..07a26ad71 100644 --- a/lib/data/services/network/api.dart +++ b/lib/data/services/network/api.dart @@ -119,7 +119,16 @@ class Api { } // 获取会议助手数据 - static getOperationRecord([params]) { + static getOperationAllRecord([params]) { + return DioManager().request( + NWMethod.post, + '/api/home/echomeet_getallrecords', + params: params, + ); + } + + // 轮询多条会议助手转写结果 + static getMultitermRecord([params]) { return DioManager().request( NWMethod.post, '/api/home/echomeet_getrecords', @@ -291,4 +300,52 @@ class Api { params: params, ); } + + static addRecordEchomeet([params]) { + return DioManager().request( + NWMethod.post, + '/api/home/echomeet_addrecord', + params: params, + ); + } + + static upRecordEchomeet([params]) { + return DioManager().request( + NWMethod.post, + '/api/home/echomeet_uprecord', + params: params, + ); + } + + static startTaskEchomeet([params]) { + return DioManager().request( + NWMethod.post, + '/api/home/echomeet_starttask', + params: params, + ); + } + + static refreshTaskEchomeet([params]) { + return DioManager().request( + NWMethod.post, + '/api/home/echomeet_summary', + params: params, + ); + } + + static readRecordEchomeet([params]) { + return DioManager().request( + NWMethod.post, + '/api/home/echomeet_readrecord', + params: params, + ); + } + + static delRecordEchomeet([params]) { + return DioManager().request( + NWMethod.post, + '/api/home/echomeet_delrecords', + params: params, + ); + } } diff --git a/lib/modules/meeting/controllers/meeting_details_controller.dart b/lib/modules/meeting/controllers/meeting_details_controller.dart index 927bb9f96..6cfbbf1c1 100644 --- a/lib/modules/meeting/controllers/meeting_details_controller.dart +++ b/lib/modules/meeting/controllers/meeting_details_controller.dart @@ -9,16 +9,12 @@ import 'package:flutter/services.dart' show rootBundle; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; -import 'package:get_storage/get_storage.dart'; import 'package:path_provider/path_provider.dart'; -import '../../../core/utils/synchrodata.dart'; import '../../../data/services/db/sqflite_api.dart'; -import '../../../data/services/meeting/meeting_task_service.dart'; -import '../../../data/services/meeting/meeting_task_service2.dart'; +import '../../../data/services/meeting/meeting_task_post.dart'; import '../../../data/services/meeting/meeting_upload_service.dart'; -import '../../../data/services/meeting/summary_task_service.dart'; -import '../../../data/services/meeting/meeting_task_aggregator.dart'; +import '../../../data/services/network/api.dart'; import '../model/meeting_details_model.dart'; import '../model/meeting_model.dart'; @@ -31,12 +27,9 @@ class MeetingDetailsController extends GetxController late AnimationController lottieController; late final Worker _tasktypeListener; - late final Worker _tasktypeListenerV2; final _meetingUploadService = Get.find(); - final _meetingTaskService = Get.find(); - final _meetingTaskService2 = Get.find(); - final _summaryTaskService = Get.find(); + final _meetingTaskService = Get.find(); final PlayerController playerController = PlayerController(); @@ -95,39 +88,19 @@ class MeetingDetailsController extends GetxController ..duration = const Duration(seconds: 2); final Map args = Get.arguments ?? {}; id = args['id']; - final storage = GetStorage(); - final String svc = - (storage.read("meeting_service_type_$id") ?? '').toString(); - final bool isFast = svc == 'fast'; _meetingTaskService.meetingId = id; - _meetingTaskService2.meetingId = id; - Get.find().setMeeting(id); _getDataDetails(); _loadJS(); _tasktypeListener = ever(_meetingTaskService.tasktype, (value) { - if (isFast && value != 3) return; meetingDetails.value.tasktype = value; if (_everType <= value) { - if (!isFast && value == 2) { - textList.value = _meetingTaskService.textList; - } else if (value == 3) { - meetingDetails.value.summary = _summaryTaskService.summary; - meetingDetails.value.overview = _summaryTaskService.overview; - summaryNumberModifications.value++; - } - } - meetingDetails.refresh(); - }); - _tasktypeListenerV2 = ever(_meetingTaskService2.tasktype, (value) { - if (!isFast) return; - meetingDetails.value.tasktype = value; - if (_everType <= value) { - if (value == 2) { - textList.value = _meetingTaskService2.textList; - } else if (value == 3) { - meetingDetails.value.summary = _summaryTaskService.summary; - meetingDetails.value.overview = _summaryTaskService.overview; + if (value >= 5) { + meetingDetails.value.personnel = _meetingTaskService.personnel; + meetingDetails.value.summary = _meetingTaskService.summary; + meetingDetails.value.overview = _meetingTaskService.overview; summaryNumberModifications.value++; + } else if (value >= 3) { + textList.value = _meetingTaskService.textList; } } meetingDetails.refresh(); @@ -137,22 +110,17 @@ class MeetingDetailsController extends GetxController @override void onClose() { _tasktypeListener.dispose(); - _tasktypeListenerV2.dispose(); _currentDurationSubscription?.cancel(); _playerStateSubscription?.cancel(); tabController.dispose(); playerController.dispose(); lottieController.dispose(); - Get.find().clearMeeting(); super.onClose(); } void popInvokedWithResult(didPop, result) async { _meetingUploadService.removeUpload(id); - _meetingTaskService.removeTask(id); - _meetingTaskService2.removeTask(id); _meetingTaskService.meetingId = 0; - _meetingTaskService2.meetingId = 0; } void _getDataDetails() async { @@ -161,14 +129,17 @@ class MeetingDetailsController extends GetxController meetingDetails.value = MeetingDetailsModel.fromJson(data['details']); _everType = meetingDetails.value.tasktype + 1; _meetingTaskService.tasktype.value = meetingDetails.value.tasktype; - if (meetingDetails.value.tasktype >= 3) { + if (meetingDetails.value.tasktype == 5) { + await _meetingTaskService.readTask(id); + } + if (meetingDetails.value.tasktype >= 5) { dragOffset.value = 0; isTop.value = true; } if (meetingData.value.filepath.isNotEmpty) { await _loadPlayerController(); } - if (meetingDetails.value.tasktype >= 2) { + if (meetingDetails.value.tasktype >= 3) { List speakerList = await SqfliteApi.getMeetingSpeaker(id); textList.value = List.from(speakerList); } @@ -267,10 +238,6 @@ class MeetingDetailsController extends GetxController textList[index] = dataMap; } } - Synchrodata.saveOperationRecord('update', { - 'original': jsonEncode(textList), - 'id': id, - }); isSpeakerText.value = false; editSpeakerTextId.value = 0; editSpeakerTextList = []; @@ -284,64 +251,33 @@ class MeetingDetailsController extends GetxController } void summaryCreate( - bool isSpeaker, String language, String prompt, String outline) async { + String formlanguage, + String tolanguage, + bool isdistinguishspeaker, + int templateid, + ) async { EasyLoading.show(); _everType = 2; final String audiourl = meetingData.value.audiourl; - final int seconds = meetingData.value.seconds; - final storage = GetStorage(); - storage.write("meeting_service_type_$id", - (seconds > 0 && seconds <= 7200) ? 'fast' : 'standard'); - if (seconds > 0 && seconds <= 7200) { - await _meetingTaskService2.addTask( - id, - audiourl, - isSpeaker: isSpeaker, - language: language, - targetLanguage: '', - prompt: prompt, - outline: outline, - ); - } else { - final String taskId = meetingDetails.value.taskid; - await _meetingTaskService.addTask( - id, - audiourl, - taskId, - isSpeaker, - language, - prompt, - outline, - ); - } + await _meetingTaskService.addTask( + id, + audiourl, + formlanguage, + tolanguage, + isdistinguishspeaker, + templateid, + ); meetingDetails.value.tasktype = 1; meetingDetails.refresh(); EasyLoading.dismiss(); } - void refreshSummary(String prompt, String outline) async { - String text = ''; - for (var element in textList) { - if (element['speaker'].isNotEmpty) { - text += '[${element['speaker']}]: '; - } - text += '${element['content']}\n'; - } - _everType = 3; + void refreshSummary(int templateid) async { EasyLoading.show(); - await SqfliteApi.editMeeting(id, {'tasktype': 2}); - meetingDetails.value.tasktype = 2; + _everType = 4; + await _meetingTaskService.refreshTask(id, templateid); + meetingDetails.value.tasktype = 3; meetingDetails.refresh(); - final storage = GetStorage(); - final String svc = - (storage.read("meeting_service_type_$id") ?? '').toString(); - final bool isFast = svc == 'fast'; - if (isFast) { - _meetingTaskService2.tasktype.value = 2; - } else { - _meetingTaskService.tasktype.value = 2; - } - _summaryTaskService.addTaskList(id, text, prompt, outline); EasyLoading.dismiss(); } } diff --git a/lib/modules/meeting/controllers/meeting_home_controller.dart b/lib/modules/meeting/controllers/meeting_home_controller.dart index 7eaab1b26..d86ee674d 100644 --- a/lib/modules/meeting/controllers/meeting_home_controller.dart +++ b/lib/modules/meeting/controllers/meeting_home_controller.dart @@ -18,11 +18,9 @@ import '../../../core/utils/permission_util.dart'; import 'package:share_plus/share_plus.dart'; -import '../../../data/services/meeting/meeting_task_service.dart'; -import '../../../data/services/meeting/meeting_task_service2.dart'; -import '../../../data/services/meeting/meeting_task_aggregator.dart'; +import '../../../data/services/meeting/meeting_task_post.dart'; import '../../../data/services/meeting/meeting_upload_service.dart'; -import '../../../data/services/meeting/summary_task_service.dart'; +import '../../../data/services/network/api.dart'; import '../../../routes/app_routes.dart'; import 'meeting_controller.dart'; @@ -30,10 +28,7 @@ import 'meeting_controller.dart'; class MeetingHomeController extends GetxController { final meetingController = Get.find(); final meetingUploadService = Get.find(); - final meetingTaskService = Get.find(); - final meetingTaskService2 = Get.find(); - final taskAggregator = Get.find(); - final summaryTaskService = Get.find(); + final meetingTaskService = Get.find(); final AudioPlayer _audioPlayer = AudioPlayer(); @@ -53,6 +48,8 @@ class MeetingHomeController extends GetxController { // 蓝牙服务 final bleManager = Get.find(); + late final Worker _updateListStateListener; + @override void onInit() { super.onInit(); @@ -68,6 +65,10 @@ class MeetingHomeController extends GetxController { final String path = args['filePath']; addAudio(path); } + _updateListStateListener = + ever(meetingTaskService.updateListState, (value) async { + _getDataList(); + }); } @override @@ -75,11 +76,13 @@ class MeetingHomeController extends GetxController { titleController.dispose(); filtrateController.dispose(); _audioPlayer.dispose(); + _updateListStateListener.dispose(); super.onClose(); } void onPopInvokedWithResult(didPop, result) { - Synchrodata.uploadOperationRecords(isSynchro: true); + // Synchrodata.uploadOperationRecords(isSynchro: true); + meetingTaskService.stopTask(); } void _initGetDataList() async { @@ -246,8 +249,16 @@ class MeetingHomeController extends GetxController { }; // 插入数据库 - final meetingId = await SqfliteApi.insertMeeting(data); - if (meetingId != null) { + final response = await Api.addRecordEchomeet({ + 'rtype': 'LOCAL', + ...data, + }); + final meetingId = response['record']['id']; + final meetingResult = await SqfliteApi.insertMeeting({ + 'id': meetingId, + ...data, + }); + if (meetingResult != null) { // 安排后台上传任务 meetingUploadService.addUpload(meetingId, newPath, 'LocalAudio'); @@ -359,8 +370,16 @@ class MeetingHomeController extends GetxController { 'creationtime': creationTime, }; - final meetingId = await SqfliteApi.insertMeeting(data); - if (meetingId != null) { + final response = await Api.addRecordEchomeet({ + 'rtype': recordType, + ...data, + }); + final meetingId = response['record']['id']; + final meetingResult = await SqfliteApi.insertMeeting({ + 'id': meetingId, + ...data, + }); + if (meetingResult != null) { // 中文注释:上传分类按目录区分 final uploadBucket = archiveToLocal ? 'LocalAudio' : 'TranslatAudio'; meetingUploadService.addUpload(meetingId, usePath, uploadBucket); @@ -475,8 +494,13 @@ class MeetingHomeController extends GetxController { 'tasktype': 0, 'creationtime': creationTime, }; - final meetingId = await SqfliteApi.insertMeeting(data); - if (meetingId != null) { + final response = await Api.addRecordEchomeet({'rtype': type, ...data}); + final meetingId = response['record']['id']; + final meetingResult = await SqfliteApi.insertMeeting({ + 'id': meetingId, + ...data, + }); + if (meetingResult != null) { // 中文注释:根据类型选择上传分类;LOCAL 走 LocalAudio,其它走 ExternalAudio final uploadBucket = type == 'LOCAL' ? 'LocalAudio' : 'ExternalAudio'; meetingUploadService.addUpload( @@ -544,19 +568,18 @@ class MeetingHomeController extends GetxController { return targetPath; } -// 移除原来的_saveFile方法,替换为上面的_copyFileToLocal - - Future _saveFile(PlatformFile file) async { - final directory = await getApplicationDocumentsDirectory(); - final directoryDir = Directory('${directory.path}/ExternalAudio'); - if (!await directoryDir.exists()) { - await directoryDir.create(recursive: true); - } - final String filePath = '${directory.path}/ExternalAudio/${file.name}'; - final File localFile = File(filePath); - await localFile.writeAsBytes(file.bytes!); - return filePath; - } + // 移除原来的_saveFile方法,替换为上面的_copyFileToLocal + // Future _saveFile(PlatformFile file) async { + // final directory = await getApplicationDocumentsDirectory(); + // final directoryDir = Directory('${directory.path}/ExternalAudio'); + // if (!await directoryDir.exists()) { + // await directoryDir.create(recursive: true); + // } + // final String filePath = '${directory.path}/ExternalAudio/${file.name}'; + // final File localFile = File(filePath); + // await localFile.writeAsBytes(file.bytes!); + // return filePath; + // } // 修改标题 void editMeetingTitle(int id) async { @@ -589,8 +612,7 @@ class MeetingHomeController extends GetxController { SqfliteApi.deleteMeeting(id); _deleteFile(filepath); meetingUploadService.removeUpload(id, isDeleteTask: true); - meetingTaskService.removeTask(id, isDeleteTask: true); - meetingTaskService2.removeTask(id, isDeleteTask: true); + meetingTaskService.removeTask(id); } // 删除文件 diff --git a/lib/modules/meeting/views/bottomSheet/generate_bottom_sheet.dart b/lib/modules/meeting/views/bottomSheet/generate_bottom_sheet.dart index e8268169d..ae64f372a 100644 --- a/lib/modules/meeting/views/bottomSheet/generate_bottom_sheet.dart +++ b/lib/modules/meeting/views/bottomSheet/generate_bottom_sheet.dart @@ -26,8 +26,10 @@ class _GenerateBottomSheetState extends State { final GetStorage _storage = GetStorage(); bool _isSpeaker = true; - String _languageText = 'chineseSimplified'.tr; // 对应中文:中文普通话(简体) - String _language = 'zh-CN'; + String _formlanguageText = 'chineseSimplified'.tr; // 对应中文:中文普通话(简体) + String _formlanguage = 'zh-CN'; + String _tolanguageText = 'chineseSimplified'.tr; // 对应中文:中文普通话(简体) + String _tolanguage = 'zh-CN'; final Map _icons = { 'auto_stories': Icons.auto_stories, @@ -273,11 +275,34 @@ class _GenerateBottomSheetState extends State { ), _row( 'recordingLanguage'.tr, // 对应中文:录音语言 - value: _languageText, + value: _formlanguageText, + onTap: () { + Get.bottomSheet( + RecordLanguageBottomSheet(_formlanguage), + isScrollControlled: true, + backgroundColor: Colors.grey[100], + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + top: Radius.circular(12.r), + ), + ), + ).then((value) { + if (value != null) { + setState(() { + _formlanguageText = value['name']; + _formlanguage = value['value']; + }); + } + }); + }, + ), + _row( + 'targetLanguage'.tr, // 对应中文:目标语言 + value: _tolanguageText, isBorder: false, onTap: () { Get.bottomSheet( - RecordLanguageBottomSheet(_language), + RecordLanguageBottomSheet(_tolanguage), isScrollControlled: true, backgroundColor: Colors.grey[100], shape: RoundedRectangleBorder( @@ -288,8 +313,8 @@ class _GenerateBottomSheetState extends State { ).then((value) { if (value != null) { setState(() { - _languageText = value['name']; - _language = value['value']; + _tolanguageText = value['name']; + _tolanguage = value['value']; }); } }); @@ -318,16 +343,13 @@ class _GenerateBottomSheetState extends State { _storage.write("meeting_template_list", _templateList); Get.back(); if (widget.isSummarizeAgain) { - _controller.refreshSummary( - _templateData['prompt'], - _templateData['outline'], - ); + _controller.refreshSummary(_templateData['id']); } else { _controller.summaryCreate( + _formlanguage, + _tolanguage, _isSpeaker, - _language, - _templateData['prompt'], - _templateData['outline'], + _templateData['id'], ); } }, diff --git a/lib/modules/meeting/views/bottomSheet/more_bottom_sheet.dart b/lib/modules/meeting/views/bottomSheet/more_bottom_sheet.dart index 188ca945e..43335adfe 100644 --- a/lib/modules/meeting/views/bottomSheet/more_bottom_sheet.dart +++ b/lib/modules/meeting/views/bottomSheet/more_bottom_sheet.dart @@ -26,7 +26,7 @@ class _MoreBottomSheetState extends State { children: [ SizedBox(height: 10.w), _shareItem(Icons.refresh, 'reSummarize'.tr, //重新总结 - onTap: _detailsController.meetingDetails.value.tasktype >= 3 + onTap: _detailsController.meetingDetails.value.tasktype >= 5 ? () { Get.back(); Get.bottomSheet( diff --git a/lib/modules/meeting/views/bottomSheet/share_bottom_sheet.dart b/lib/modules/meeting/views/bottomSheet/share_bottom_sheet.dart index be5f16eb9..08a76eebf 100644 --- a/lib/modules/meeting/views/bottomSheet/share_bottom_sheet.dart +++ b/lib/modules/meeting/views/bottomSheet/share_bottom_sheet.dart @@ -13,7 +13,6 @@ import 'package:share_plus/share_plus.dart'; import 'package:webview_flutter/webview_flutter.dart'; -import '../../../../core/utils/synchrodata.dart'; import '../../../../data/services/version_update_service.dart'; import '../../../../data/services/network/dio_manager.dart'; import '../../controllers/meeting_details_controller.dart'; @@ -86,17 +85,9 @@ class _ShareBottomSheetState extends State { isBorder: false, onTap: () async { Get.back(); - Synchrodata.uploadOperationRecords(); final GetStorage storage = GetStorage(); String token = storage.read("logintoken") ?? ''; - String userId = ''; - Map? userInfo = storage.read("user_info"); - if (userInfo != null) { - userId = userInfo['user']['uid']; - } - - String id = - '${userId}_${_controller.meetingData.value.id}'; + int id = _controller.meetingData.value.id; // final thumbnail = await getAssetThumbnailFile( // 'assets/images/mine-background.png', @@ -126,7 +117,7 @@ class _ShareBottomSheetState extends State { ]), _box([ _shareItem(Icons.copy, 'copyTranscript'.tr, // 对应中文:复制转写 - onTap: _controller.meetingDetails.value.tasktype >= 2 + onTap: _controller.meetingDetails.value.tasktype >= 3 ? () { Get.back(); _copy(_getAllText()); @@ -134,7 +125,7 @@ class _ShareBottomSheetState extends State { : null), _shareItem(Icons.copy, 'copySummary'.tr, // 对应中文:复制总结 isBorder: false, - onTap: _controller.meetingDetails.value.tasktype >= 3 + onTap: _controller.meetingDetails.value.tasktype >= 5 ? () { Get.back(); _copy(_markdownToPlainText( @@ -151,7 +142,7 @@ class _ShareBottomSheetState extends State { 'shareMeetingAudio'.tr); // 对应中文:分享会议音频 }), _shareItem(Icons.message, 'exportTranscript'.tr, // 对应中文:导出转写 - onTap: _controller.meetingDetails.value.tasktype >= 2 + onTap: _controller.meetingDetails.value.tasktype >= 3 ? () async { Get.back(); final tempDir = await getTemporaryDirectory(); @@ -164,7 +155,7 @@ class _ShareBottomSheetState extends State { } : null), _shareItem(Icons.article, 'exportSummary'.tr, // 对应中文:导出总结 - onTap: _controller.meetingDetails.value.tasktype >= 3 + onTap: _controller.meetingDetails.value.tasktype >= 5 ? () async { Get.back(); Get.bottomSheet( @@ -214,7 +205,7 @@ class _ShareBottomSheetState extends State { _shareItem( Icons.save_alt, '保存思维导图', - onTap: _controller.meetingDetails.value.tasktype >= 3 + onTap: _controller.meetingDetails.value.tasktype >= 5 ? () { Get.back(); final mindMapController = @@ -242,7 +233,7 @@ class _ShareBottomSheetState extends State { Icons.lan, 'shareMindMap'.tr, // 对应中文:分享思维导图 isBorder: false, - onTap: _controller.meetingDetails.value.tasktype >= 3 + onTap: _controller.meetingDetails.value.tasktype >= 5 ? () { Get.back(); final mindMapController = diff --git a/lib/modules/meeting/views/bottomSheet/speaker_all_bottom_sheet.dart b/lib/modules/meeting/views/bottomSheet/speaker_all_bottom_sheet.dart index ba1708b90..2accd0bf3 100644 --- a/lib/modules/meeting/views/bottomSheet/speaker_all_bottom_sheet.dart +++ b/lib/modules/meeting/views/bottomSheet/speaker_all_bottom_sheet.dart @@ -8,7 +8,6 @@ import 'package:get/get.dart'; import 'package:get_storage/get_storage.dart'; import 'package:lottie/lottie.dart'; -import '../../../../core/utils/synchrodata.dart'; import '../../../../data/services/db/sqflite_api.dart'; import '../../controllers/meeting_details_controller.dart'; @@ -560,10 +559,6 @@ class _SpeakerAllBottomSheetState extends State { bool isEditSummary = false; String summaryText = _controller.meetingDetails.value.summary; _controller.textList.value = newTextList; - Synchrodata.saveOperationRecord('update', { - 'original': jsonEncode(_controller.textList), - 'id': _controller.meetingData.value.id, - }); for (var element in personnelList) { if (summaryText.contains('[${element['original']}]')) { summaryText = summaryText.replaceAll( diff --git a/lib/modules/meeting/views/bottomSheet/speaker_bottom_sheet.dart b/lib/modules/meeting/views/bottomSheet/speaker_bottom_sheet.dart index a93f9fa31..429cdb1ff 100644 --- a/lib/modules/meeting/views/bottomSheet/speaker_bottom_sheet.dart +++ b/lib/modules/meeting/views/bottomSheet/speaker_bottom_sheet.dart @@ -6,7 +6,6 @@ import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; import 'package:get_storage/get_storage.dart'; -import '../../../../core/utils/synchrodata.dart'; import '../../../../data/services/db/sqflite_api.dart'; import '../../controllers/meeting_details_controller.dart'; @@ -337,10 +336,6 @@ class _SpeakerBottomSheetState extends State { dataMap['speaker'] = _speakerController.text; _controller.textList[index] = dataMap; } - Synchrodata.saveOperationRecord('update', { - 'original': jsonEncode(_controller.textList), - 'id': _controller.meetingData.value.id, - }); _addHistorySpeaker(); Get.back(); } @@ -371,10 +366,6 @@ class _SpeakerBottomSheetState extends State { var result = await SqfliteApi.editMeetingSpeaker(editSpeakerList); if (result != null) { _controller.textList.value = newTextList; - Synchrodata.saveOperationRecord('update', { - 'original': jsonEncode(_controller.textList), - 'id': _controller.meetingData.value.id, - }); if (_controller.meetingDetails.value.summary .contains('[${widget.speakerName}]')) { String summaryText = _controller.meetingDetails.value.summary diff --git a/lib/modules/meeting/views/meeting_details_view.dart b/lib/modules/meeting/views/meeting_details_view.dart index cd21f85a5..1769e3045 100644 --- a/lib/modules/meeting/views/meeting_details_view.dart +++ b/lib/modules/meeting/views/meeting_details_view.dart @@ -331,35 +331,43 @@ class MeetingDetailsView extends GetView { Obx( () => controller.isLoading.value ? const SizedBox() - : controller.meetingDetails.value.tasktype == 1 || - controller.meetingDetails.value.tasktype == 2 - ? _meetingWait('analyzing'.tr) // 对应中文:正在分析... + : controller.meetingDetails.value.tasktype > 0 && + controller.meetingDetails.value.tasktype < 5 + ? _meetingWait( + controller.meetingDetails.value.tasktype > 0 && + controller.meetingDetails.value.tasktype < + 3 + ? 'transcribing'.tr // 对应中文:正在转写... + : 'analyzing'.tr) // 对应中文:正在分析... : const OverviewTab(), ), Obx( - () => controller.meetingDetails.value.tasktype == 1 + () => controller.meetingDetails.value.tasktype > 0 && + controller.meetingDetails.value.tasktype < 3 ? _meetingWait('transcribing'.tr) // 对应中文:正在转写... : const SpeechTab(), ), Obx( - () => controller.meetingDetails.value.tasktype == 1 || - controller.meetingDetails.value.tasktype == 2 + () => controller.meetingDetails.value.tasktype > 0 && + controller.meetingDetails.value.tasktype < 5 ? _meetingWait( - controller.meetingDetails.value.tasktype == 1 + controller.meetingDetails.value.tasktype > 0 && + controller.meetingDetails.value.tasktype < 3 ? 'transcribing'.tr // 对应中文:正在转写... - : 'summarizing'.tr) // 对应中文:正在总结... + : 'analyzing'.tr) // 对应中文:正在分析... : const SummaryTab(), ), - Obx( - () => controller.meetingDetails.value.tasktype >= 3 - ? const MindMapTab() - : controller.meetingDetails.value.tasktype == 0 - ? const SizedBox() - : _meetingWait( - controller.meetingDetails.value.tasktype == 1 - ? 'transcribing'.tr // 对应中文:正在转写... - : 'summarizing'.tr), // 对应中文:正在总结... - ), + Obx(() => controller.meetingDetails.value.tasktype >= 5 + ? const MindMapTab() + : controller.meetingDetails.value.tasktype == 0 + ? const SizedBox() + : _meetingWait(controller + .meetingDetails.value.tasktype > + 0 && + controller.meetingDetails.value.tasktype < 3 + ? 'transcribing'.tr // 对应中文:正在转写... + : 'analyzing'.tr) // 对应中文:正在分析... + ), ], ), ), diff --git a/lib/modules/meeting/views/meeting_home_view.dart b/lib/modules/meeting/views/meeting_home_view.dart index 36c5d610e..c15a3f6bf 100644 --- a/lib/modules/meeting/views/meeting_home_view.dart +++ b/lib/modules/meeting/views/meeting_home_view.dart @@ -247,6 +247,30 @@ class _MeetingHomeViewState extends State } Widget _item(BuildContext context, bool isDarkMode, MeetingModel data) { + String tasktypeText = ''; + switch (data.tasktype) { + case 0: + tasktypeText = ''; + break; + case 1: + tasktypeText = 'transcribing'.tr; // 对应中文:正在转写... + break; + case 2: + tasktypeText = 'transcribing'.tr; // 对应中文:正在转写... + break; + case 3: + tasktypeText = 'summarizing'.tr; // 对应中文:正在总结... + break; + case 4: + tasktypeText = 'summarizing'.tr; // 对应中文:正在总结... + break; + case 5: + tasktypeText = 'summaryComplete'.tr; // 对应中文:总结完成 + break; + default: + tasktypeText = ''; + } + return GestureDetector( onTap: () { Get.toNamed(Routes.meetingDetails, arguments: { @@ -374,29 +398,16 @@ class _MeetingHomeViewState extends State ) : const SizedBox(); }), - Obx(() { - Map taskData = - _controller.taskAggregator.taskFinishList.firstWhere( - (element) => element['id'] == data.id, - orElse: () => {}, - ); - return taskData.isNotEmpty - ? Padding( - padding: EdgeInsets.only(top: 5.w), - child: Text( - taskData['tasktype'] == 1 - ? 'transcribing'.tr // 对应中文:正在转写... - : taskData['tasktype'] == 2 - ? 'summarizing'.tr // 对应中文:正在总结... - : 'summaryComplete'.tr, // 对应中文:总结完成 - style: TextStyle( - color: Colors.blue, - fontSize: 10.sp, - ), - ), - ) - : const SizedBox(); - }), + Padding( + padding: EdgeInsets.only(top: 5.w), + child: Text( + tasktypeText, + style: TextStyle( + color: Colors.blue, + fontSize: 10.sp, + ), + ), + ), ], ), ], diff --git a/lib/modules/splash/controllers/splash_controller.dart b/lib/modules/splash/controllers/splash_controller.dart index eabfbbadb..e947e795f 100644 --- a/lib/modules/splash/controllers/splash_controller.dart +++ b/lib/modules/splash/controllers/splash_controller.dart @@ -10,9 +10,6 @@ import 'package:http/http.dart' as http; import '../../../data/models/appconfig.dart'; import '../../../data/models/appconfig_model.dart'; import '../../../data/models/user_Info.dart'; -import '../../../data/services/meeting/meeting_task_service.dart'; -import '../../../data/services/meeting/meeting_upload_service.dart'; -import '../../../data/services/meeting/summary_task_service.dart'; import '../../../routes/app_routes.dart'; import '../../../data/services/network/api.dart'; import '../../../data/services/version_update_service.dart'; @@ -269,13 +266,8 @@ class SplashController extends GetxController { final isAppConfigInitialized = AppConfig.isInitialized(); // 检查登录状态和配置初始化状态 if (token != null && token.isNotEmpty && isAppConfigInitialized) { - Get.find(); - Get.find(); - Get.find(); Get.offAllNamed(Routes.pairing); } else { - _storage.remove('meeting_task_list'); - _storage.remove('meeting_task_finish_list'); _storage.remove('meeting_upload_list'); _storage.remove('operation_records'); _storage.remove('operation_delids'); From 5a9f2e3550dac3fb4d28ca7609cb697014a18705 Mon Sep 17 00:00:00 2001 From: tanlongsheng <252620078@qq.com> Date: Sat, 10 Jan 2026 14:46:18 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E9=87=8D=E6=9E=84=E4=BC=9A=E8=AE=AE?= =?UTF-8?q?=E5=8A=A9=E6=89=8B=EF=BC=8C=E4=BD=BF=E7=94=A8=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E7=AB=AF=E8=BD=AC=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/core/bindings/initial_binding.dart | 4 ++-- lib/core/utils/synchrodata.dart | 4 ++-- .../{meeting_task_post.dart => meeting_task_service.dart} | 2 +- lib/data/services/meeting/meeting_upload_service.dart | 4 ++-- .../meeting/controllers/meeting_details_controller.dart | 4 ++-- lib/modules/meeting/controllers/meeting_home_controller.dart | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) rename lib/data/services/meeting/{meeting_task_post.dart => meeting_task_service.dart} (99%) diff --git a/lib/core/bindings/initial_binding.dart b/lib/core/bindings/initial_binding.dart index cc7b629c2..94c3ecef5 100644 --- a/lib/core/bindings/initial_binding.dart +++ b/lib/core/bindings/initial_binding.dart @@ -1,4 +1,4 @@ -import '../../data/services/meeting/meeting_task_post.dart'; +import '../../data/services/meeting/meeting_task_service.dart'; import '/data/services/pay_service.dart'; import '/data/services/qq_music.dart'; @@ -68,7 +68,7 @@ class InitialBinding extends Bindings { fenix: true); // 会议助手任务服务 - Get.lazyPut(() => MeetingTaskPost(), fenix: true); + Get.lazyPut(() => MeetingTaskService(), fenix: true); // 会议助手音频上传 Get.lazyPut(() => MeetingUploadService(), fenix: true); diff --git a/lib/core/utils/synchrodata.dart b/lib/core/utils/synchrodata.dart index e5940e025..b4c1da1eb 100644 --- a/lib/core/utils/synchrodata.dart +++ b/lib/core/utils/synchrodata.dart @@ -5,7 +5,7 @@ import 'package:get_storage/get_storage.dart'; import '../../data/services/db/database_helper.dart'; import '../../data/services/db/sqflite_api.dart'; -import '../../data/services/meeting/meeting_task_post.dart'; +import '../../data/services/meeting/meeting_task_service.dart'; import '../../data/services/network/api.dart'; class Synchrodata { @@ -118,7 +118,7 @@ class Synchrodata { var result = await Api.getOperationAllRecord(); List records = result['records'] ?? []; if (records.isEmpty) return; - final meetingTaskService = Get.find(); + final meetingTaskService = Get.find(); List taskList = []; final db = await DatabaseHelper().database; await db.transaction((txn) async { diff --git a/lib/data/services/meeting/meeting_task_post.dart b/lib/data/services/meeting/meeting_task_service.dart similarity index 99% rename from lib/data/services/meeting/meeting_task_post.dart rename to lib/data/services/meeting/meeting_task_service.dart index 4937cf7a8..c8d7bb9e7 100644 --- a/lib/data/services/meeting/meeting_task_post.dart +++ b/lib/data/services/meeting/meeting_task_service.dart @@ -6,7 +6,7 @@ import '../db/sqflite_api.dart'; import '../network/api.dart'; import 'meeting_upload_service.dart'; -class MeetingTaskPost extends GetxService { +class MeetingTaskService extends GetxService { MeetingUploadService get _uploadService => Get.find(); List _taskList = []; //待处理的会议任务列表 diff --git a/lib/data/services/meeting/meeting_upload_service.dart b/lib/data/services/meeting/meeting_upload_service.dart index c0c15a0cf..af54033a2 100644 --- a/lib/data/services/meeting/meeting_upload_service.dart +++ b/lib/data/services/meeting/meeting_upload_service.dart @@ -7,11 +7,11 @@ import '../../../core/utils/upload_oss.dart'; import '../network/api.dart'; import '../usage_stats_service.dart'; import '../db/sqflite_api.dart'; -import 'meeting_task_post.dart'; +import 'meeting_task_service.dart'; /// 管理会议音频文件上传服务 class MeetingUploadService extends GetxService { - final _taskService = Get.find(); + final _taskService = Get.find(); final GetStorage _storage = GetStorage(); diff --git a/lib/modules/meeting/controllers/meeting_details_controller.dart b/lib/modules/meeting/controllers/meeting_details_controller.dart index 6cfbbf1c1..5557e3038 100644 --- a/lib/modules/meeting/controllers/meeting_details_controller.dart +++ b/lib/modules/meeting/controllers/meeting_details_controller.dart @@ -12,7 +12,7 @@ import 'package:get/get.dart'; import 'package:path_provider/path_provider.dart'; import '../../../data/services/db/sqflite_api.dart'; -import '../../../data/services/meeting/meeting_task_post.dart'; +import '../../../data/services/meeting/meeting_task_service.dart'; import '../../../data/services/meeting/meeting_upload_service.dart'; import '../../../data/services/network/api.dart'; import '../model/meeting_details_model.dart'; @@ -29,7 +29,7 @@ class MeetingDetailsController extends GetxController late final Worker _tasktypeListener; final _meetingUploadService = Get.find(); - final _meetingTaskService = Get.find(); + final _meetingTaskService = Get.find(); final PlayerController playerController = PlayerController(); diff --git a/lib/modules/meeting/controllers/meeting_home_controller.dart b/lib/modules/meeting/controllers/meeting_home_controller.dart index d86ee674d..d1abd4066 100644 --- a/lib/modules/meeting/controllers/meeting_home_controller.dart +++ b/lib/modules/meeting/controllers/meeting_home_controller.dart @@ -18,7 +18,7 @@ import '../../../core/utils/permission_util.dart'; import 'package:share_plus/share_plus.dart'; -import '../../../data/services/meeting/meeting_task_post.dart'; +import '../../../data/services/meeting/meeting_task_service.dart'; import '../../../data/services/meeting/meeting_upload_service.dart'; import '../../../data/services/network/api.dart'; import '../../../routes/app_routes.dart'; @@ -28,7 +28,7 @@ import 'meeting_controller.dart'; class MeetingHomeController extends GetxController { final meetingController = Get.find(); final meetingUploadService = Get.find(); - final meetingTaskService = Get.find(); + final meetingTaskService = Get.find(); final AudioPlayer _audioPlayer = AudioPlayer();