Browse Source

feat(会议任务): 引入任务聚合器统一管理任务状态与列表

重构会议任务服务架构,新增 MeetingTaskAggregator 服务聚合器,统一管理标准版与极速版任务服务的状态与列表数据:
1. 移除服务间直接状态同步逻辑,改为通过聚合器统一对外暴露状态
2. 新增聚合器服务并在初始绑定中注册
3. 修改各控制器使用聚合器替代直接服务引用
4. 优化极速版服务日志输出为中文
5. 清理冗余的状态同步代码
weicu
lxm 11 months ago
parent
commit
e226ac65d4
  1. 6
      lib/core/bindings/initial_binding.dart
  2. 105
      lib/data/services/meeting/meeting_task_aggregator.dart
  3. 228
      lib/data/services/meeting/meeting_task_service2.dart
  4. 3
      lib/modules/meeting/controllers/meeting_details_controller.dart
  5. 2
      lib/modules/meeting/controllers/meeting_home_controller.dart
  6. 4
      lib/modules/meeting/views/meeting_home_view.dart

6
lib/core/bindings/initial_binding.dart

@ -12,6 +12,7 @@ import '../../../data/services/speech_impl/azure_ast_service.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import '../../data/services/meeting/meeting_task_service.dart'; import '../../data/services/meeting/meeting_task_service.dart';
import '../../data/services/meeting/meeting_task_service2.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/meeting/meeting_upload_service.dart';
import '../../data/services/novice_guide_service.dart'; import '../../data/services/novice_guide_service.dart';
import '../../data/services/speech_factory.dart'; import '../../data/services/speech_factory.dart';
@ -71,8 +72,11 @@ class InitialBinding extends Bindings {
// 会议助手任务服务 // 会议助手任务服务
Get.lazyPut<MeetingTaskService>(() => MeetingTaskService(), fenix: true); Get.lazyPut<MeetingTaskService>(() => MeetingTaskService(), fenix: true);
// 会议助手任务服务(极速识别版) // 会议助手任务服务(极速识别版,2小时内音频)
Get.lazyPut<MeetingTaskService2>(() => MeetingTaskService2(), fenix: true); Get.lazyPut<MeetingTaskService2>(() => MeetingTaskService2(), fenix: true);
// 会议助手任务服务聚合器(统一列表与状态)
Get.lazyPut<MeetingTaskAggregator>(() => MeetingTaskAggregator(),
fenix: true);
// 会议助手总结任务服务 // 会议助手总结任务服务
Get.lazyPut<SummaryTaskService>(() => SummaryTaskService(), fenix: true); Get.lazyPut<SummaryTaskService>(() => SummaryTaskService(), fenix: true);

105
lib/data/services/meeting/meeting_task_aggregator.dart

@ -0,0 +1,105 @@
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<MeetingTaskService>();
final MeetingTaskService2 s2 = Get.find<MeetingTaskService2>();
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<int> 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;
}
}

228
lib/data/services/meeting/meeting_task_service2.dart

@ -10,7 +10,7 @@ import '../../../core/utils/logger.dart';
import '../../../core/utils/synchrodata.dart'; import '../../../core/utils/synchrodata.dart';
import '../deapsound_ai_service.dart'; import '../deapsound_ai_service.dart';
import 'meeting_upload_service.dart'; import 'meeting_upload_service.dart';
import 'meeting_task_service.dart'; // 移除标准版服务桥接,改为通过聚合器对外暴露统一状态
import 'summary_task_service.dart'; import 'summary_task_service.dart';
//火山引擎录音文件识别大模型-极速版 //火山引擎录音文件识别大模型-极速版
@ -140,43 +140,31 @@ class MeetingTaskService2 extends GetxService {
taskFinishList.add({'id': id, 'tasktype': 1}); taskFinishList.add({'id': id, 'tasktype': 1});
_storage.write("meeting_task_list_v2", _taskList); _storage.write("meeting_task_list_v2", _taskList);
_storage.write("meeting_task_finish_list_v2", taskFinishList); _storage.write("meeting_task_finish_list_v2", taskFinishList);
try { // 由聚合器统一对外呈现状态,不直接写入标准版列表
final s1 = Get.find<MeetingTaskService>();
Map taskFinishData1 = s1.taskFinishList.firstWhere(
(element) => element['id'] == id,
orElse: () => {},
);
if (taskFinishData1.isNotEmpty) {
taskFinishData1['tasktype'] = 3;
s1.taskFinishList.refresh();
}
_storage.write("meeting_task_finish_list", s1.taskFinishList);
} catch (_) {}
_executeTask(); _executeTask();
} }
void _executeTask() async { void _executeTask() async {
if (_isTask) return; if (_isTask) return;
_isTask = true; _isTask = true;
Logger.debug('Starting task execution. Task count: ${_taskList.length}'); Logger.debug('开始执行任务,任务数量:${_taskList.length}');
while (_taskList.isNotEmpty) { while (_taskList.isNotEmpty) {
Logger.debug('Task execution loop. Remaining tasks: ${_taskList.length}'); Logger.debug('任务循环执行,剩余任务:${_taskList.length}');
await Future.delayed(const Duration(seconds: 2)); await Future.delayed(const Duration(seconds: 2));
// 创建任务列表的副本,避免在迭代过程中修改原列表 // 创建任务列表的副本,避免在迭代过程中修改原列表
List tasksCopy = List.from(_taskList); List tasksCopy = List.from(_taskList);
for (var element in tasksCopy) { for (var element in tasksCopy) {
Logger.debug( Logger.debug('处理任务:${element['id']},音频:${element['audiourl']}');
'Processing task: ${element['id']} with audio: ${element['audiourl']}');
// 检查任务是否仍在列表中(可能已被其他地方移除) // 检查任务是否仍在列表中(可能已被其他地方移除)
if (_taskList.any((task) => task['id'] == element['id'])) { if (_taskList.any((task) => task['id'] == element['id'])) {
try { try {
await _fileRecognize(element); await _fileRecognize(element);
} catch (e) { } catch (e) {
Logger.error('Failed to process task ${element['id']}: $e'); Logger.error('处理任务失败:${element['id']},错误:$e');
// 确保失败的任务被移除,避免无限循环 // 确保失败的任务被移除,避免无限循环
_taskList.removeWhere((task) => task['id'] == element['id']); _taskList.removeWhere((task) => task['id'] == element['id']);
_storage.write("meeting_task_list_v2", _taskList); _storage.write("meeting_task_list_v2", _taskList);
@ -185,22 +173,19 @@ class MeetingTaskService2 extends GetxService {
await SqfliteApi.editMeeting(element['id'], {'tasktype': 0}); await SqfliteApi.editMeeting(element['id'], {'tasktype': 0});
} }
} else { } else {
Logger.debug( Logger.debug('任务 ${element['id']} 已从任务列表移除');
'Task ${element['id']} was already removed from task list');
} }
} }
// 如果没有任务被处理,避免无限循环 // 如果没有任务被处理,避免无限循环
if (_taskList.length == tasksCopy.length) { if (_taskList.length == tasksCopy.length) {
Logger.warning( Logger.warning('本次循环未处理任何任务,为避免无限执行终止循环');
'No tasks were processed in this iteration. Breaking loop to avoid infinite execution.');
break; break;
} }
} }
_isTask = false; _isTask = false;
Logger.debug( Logger.debug('任务执行完成,剩余任务:${_taskList.length}');
'Task execution completed. Remaining tasks: ${_taskList.length}');
} }
void removeTask(int id, {bool isDeleteTask = false}) { void removeTask(int id, {bool isDeleteTask = false}) {
@ -260,9 +245,9 @@ class MeetingTaskService2 extends GetxService {
try { try {
// 记录请求详情 // 记录请求详情
Logger.debug('API Request URL: $_serviceUrl'); Logger.debug('API 请求地址:$_serviceUrl');
Logger.debug('API Request Headers: $requestHeaders'); Logger.debug('API 请求头:$requestHeaders');
Logger.debug('API Request Body: $requestBody'); Logger.debug('API 请求体:$requestBody');
final response = await http.post( final response = await http.post(
Uri.parse(_serviceUrl), Uri.parse(_serviceUrl),
@ -270,30 +255,27 @@ class MeetingTaskService2 extends GetxService {
body: requestBody, body: requestBody,
); );
Logger.debug('API Response Status: ${response.statusCode}'); Logger.debug('API 响应状态码:${response.statusCode}');
Logger.debug('API Response Headers: ${response.headers}'); Logger.debug('API 响应头:${response.headers}');
if (response.statusCode != 200) { if (response.statusCode != 200) {
Logger.error( Logger.error('识别失败,状态码:${response.statusCode},响应体:${response.body}');
'Recognition failed with status: ${response.statusCode}, body: ${response.body}'); throw Exception('识别失败,状态码:${response.statusCode}');
throw Exception(
'Recognition failed with status: ${response.statusCode}');
} }
final respDic = jsonDecode(response.body); final respDic = jsonDecode(response.body);
Logger.debug('Recognition response: $respDic'); Logger.debug('识别接口响应:$respDic');
// 检查响应中是否包含资源相关信息 // 检查响应中是否包含资源相关信息
if (respDic.containsKey('resource_id') || respDic.containsKey('model')) { if (respDic.containsKey('resource_id') || respDic.containsKey('model')) {
Logger.debug('资源校验 - 使用的资源ID:volc.bigasr.auc_turbo');
Logger.debug( Logger.debug(
'Resource verification - Resource ID used: volc.bigasr.auc_turbo'); '响应中的资源信息:${respDic['resource_id'] ?? respDic['model'] ?? '响应未包含'}');
Logger.debug(
'Response resource info: ${respDic['resource_id'] ?? respDic['model'] ?? 'Not specified in response'}');
} }
return respDic; return respDic;
} catch (e) { } catch (e) {
Logger.error('Recognition error: ${e.toString()}'); Logger.error('识别请求异常:${e.toString()}');
rethrow; rethrow;
} }
} }
@ -315,19 +297,18 @@ class MeetingTaskService2 extends GetxService {
double parsed = double.parse(timeValue); double parsed = double.parse(timeValue);
return parsed.round(); return parsed.round();
} catch (e) { } catch (e) {
Logger.error('Failed to parse time value: $timeValue'); Logger.error('时间值解析失败:$timeValue');
return 0; return 0;
} }
} else { } else {
Logger.error( Logger.error('未知的时间值类型:${timeValue.runtimeType},值:$timeValue');
'Unknown time value type: ${timeValue.runtimeType}, value: $timeValue');
return 0; return 0;
} }
} }
Future<void> _fileRecognize(Map item) async { Future<void> _fileRecognize(Map item) async {
try { try {
Logger.debug('Starting recognition for audio: ${item['audiourl']}'); Logger.debug('开始识别音频:${item['audiourl']}');
// 识别语言选取:若设置了目标输出语言且与录音语言一致,优先让ASR自动识别,避免错配 // 识别语言选取:若设置了目标输出语言且与录音语言一致,优先让ASR自动识别,避免错配
final String tplLang = final String tplLang =
(((item['template'] ?? {})['language']) ?? '').toString(); (((item['template'] ?? {})['language']) ?? '').toString();
@ -344,8 +325,8 @@ class MeetingTaskService2 extends GetxService {
language: asrLanguage, language: asrLanguage,
); );
Logger.debug('Full API response structure: ${respDic.toString()}'); Logger.debug('API 全量响应结构:${respDic.toString()}');
Logger.debug('Response keys: ${respDic.keys.toList()}'); Logger.debug('响应包含的键:${respDic.keys.toList()}');
// 检查响应格式并提取utterances // 检查响应格式并提取utterances
List utterances = []; List utterances = [];
@ -354,77 +335,67 @@ class MeetingTaskService2 extends GetxService {
if (respDic['result'] != null && if (respDic['result'] != null &&
respDic['result']['utterances'] != null) { respDic['result']['utterances'] != null) {
utterances = respDic['result']['utterances']; utterances = respDic['result']['utterances'];
Logger.debug( Logger.debug('在 result.utterances 中找到 ${utterances.length} 条语句');
'Found utterances in result.utterances: ${utterances.length} items');
} else if (respDic['resp'] != null && } else if (respDic['resp'] != null &&
respDic['resp']['utterances'] != null) { respDic['resp']['utterances'] != null) {
utterances = respDic['resp']['utterances']; utterances = respDic['resp']['utterances'];
Logger.debug( Logger.debug('在 resp.utterances 中找到 ${utterances.length} 条语句');
'Found utterances in resp.utterances: ${utterances.length} items');
} else if (respDic['utterances'] != null) { } else if (respDic['utterances'] != null) {
utterances = respDic['utterances']; utterances = respDic['utterances'];
Logger.debug( Logger.debug('在根级 utterances 中找到 ${utterances.length} 条语句');
'Found utterances in root.utterances: ${utterances.length} items');
} else if (respDic['data'] != null && } else if (respDic['data'] != null &&
respDic['data']['utterances'] != null) { respDic['data']['utterances'] != null) {
utterances = respDic['data']['utterances']; utterances = respDic['data']['utterances'];
Logger.debug( Logger.debug('在 data.utterances 中找到 ${utterances.length} 条语句');
'Found utterances in data.utterances: ${utterances.length} items');
} else if (respDic['response'] != null && } else if (respDic['response'] != null &&
respDic['response']['utterances'] != null) { respDic['response']['utterances'] != null) {
utterances = respDic['response']['utterances']; utterances = respDic['response']['utterances'];
Logger.debug( Logger.debug('在 response.utterances 中找到 ${utterances.length} 条语句');
'Found utterances in response.utterances: ${utterances.length} items');
} else { } else {
// 检查是否有错误信息 // 检查是否有错误信息
if (respDic['error'] != null) { if (respDic['error'] != null) {
Logger.error('API returned error: ${respDic['error']}'); Logger.error('接口返回错误:${respDic['error']}');
} else if (respDic['message'] != null) { } else if (respDic['message'] != null) {
Logger.error('API returned message: ${respDic['message']}'); Logger.error('接口返回消息:${respDic['message']}');
} else { } else {
Logger.error( Logger.error('未知的响应格式。可用键:${respDic.keys.toList()}');
'Unknown response format. Available keys: ${respDic.keys.toList()}');
// 尝试查找任何包含文本的字段 // 尝试查找任何包含文本的字段
respDic.forEach((key, value) { respDic.forEach((key, value) {
Logger.debug( Logger.debug('键:$key,值类型:${value.runtimeType},值:$value');
'Key: $key, Value type: ${value.runtimeType}, Value: $value');
}); });
} }
} }
if (utterances.isNotEmpty) { if (utterances.isNotEmpty) {
Logger.debug('Processing ${utterances.length} utterances'); Logger.debug('开始处理 ${utterances.length} 条语句');
await _addTextList(item['id'], utterances, item['template']); await _addTextList(item['id'], utterances, item['template']);
// 从任务列表中移除已完成的任务 // 从任务列表中移除已完成的任务
_taskList.removeWhere((element) => element['id'] == item['id']); _taskList.removeWhere((element) => element['id'] == item['id']);
_storage.write("meeting_task_list_v2", _taskList); _storage.write("meeting_task_list_v2", _taskList);
Logger.debug( Logger.debug('音频识别成功,会议ID:${item['id']}');
'Recognition completed successfully for item ${item['id']}');
// 更新UI状态 - 如果是当前会议,触发UI更新 // 更新UI状态 - 如果是当前会议,触发UI更新
if (item['id'] == meetingId) { if (item['id'] == meetingId) {
tasktype.value = 2; // 转写完成,进入分析阶段 tasktype.value = 2; // 转写完成,进入分析阶段
} }
} else { } else {
Logger.error( Logger.error('响应中未发现语句。完整响应:$respDic');
"No utterances found in response. Full response: $respDic"); Logger.debug('未检测到语句,开始执行无声音频检查');
print( Logger.debug('无声音频检测调试信息');
'🔥🔥🔥 NO UTTERANCES FOUND - STARTING SILENT AUDIO CHECK 🔥🔥🔥'); Logger.debug('响应键列表:${respDic.keys.toList()}');
print('=== DEBUGGING SILENT AUDIO DETECTION ==='); Logger.debug('响应代码:${respDic['code']}');
print('Response keys: ${respDic.keys.toList()}'); Logger.debug('响应消息:${respDic['message']}');
print('Response code: ${respDic['code']}');
print('Response message: ${respDic['message']}');
// 检查是否为无声音频 // 检查是否为无声音频
bool isSilentAudio = _checkIfSilentAudio(respDic); bool isSilentAudio = _checkIfSilentAudio(respDic);
print('Silent audio detection result: $isSilentAudio'); Logger.debug('无声检测结果:$isSilentAudio');
if (isSilentAudio) { if (isSilentAudio) {
print('=== SILENT AUDIO DETECTED - SETTING TASKTYPE TO -1 ==='); Logger.debug('检测到无声音频,设置任务类型为 -1');
// 设置无声音频状态 (使用特殊值 -1) // 设置无声音频状态 (使用特殊值 -1)
await SqfliteApi.editMeeting(item['id'], {'tasktype': -1}); await SqfliteApi.editMeeting(item['id'], {'tasktype': -1});
if (item['id'] == meetingId) { if (item['id'] == meetingId) {
print('Setting tasktype.value to -1 for current meeting'); Logger.debug('当前会议设置 tasktype.value 为 -1');
tasktype.value = -1; // 无声音频 tasktype.value = -1; // 无声音频
} }
@ -438,32 +409,10 @@ class MeetingTaskService2 extends GetxService {
taskFinishList.refresh(); taskFinishList.refresh();
} }
_storage.write("meeting_task_finish_list_v2", taskFinishList); _storage.write("meeting_task_finish_list_v2", taskFinishList);
try { // 由聚合器统一对外呈现状态,不直接写入标准版列表
final s1 = Get.find<MeetingTaskService>(); // 由聚合器统一对外呈现状态,不直接写入标准版列表
Map taskFinishData1 = s1.taskFinishList.firstWhere(
(element) => element['id'] == item['id'],
orElse: () => {},
);
if (taskFinishData1.isNotEmpty) {
taskFinishData1['tasktype'] = 0;
s1.taskFinishList.refresh();
}
_storage.write("meeting_task_finish_list", s1.taskFinishList);
} catch (_) {}
try {
final s1 = Get.find<MeetingTaskService>();
Map taskFinishData1 = s1.taskFinishList.firstWhere(
(element) => element['id'] == item['id'],
orElse: () => {},
);
if (taskFinishData1.isNotEmpty) {
taskFinishData1['tasktype'] = -1;
s1.taskFinishList.refresh();
}
_storage.write("meeting_task_finish_list", s1.taskFinishList);
} catch (_) {}
} else { } else {
Logger.error('Recognition failed for unknown reason'); Logger.error('识别失败,原因未知');
// 识别失败,更新数据库状态为失败 // 识别失败,更新数据库状态为失败
await SqfliteApi.editMeeting(item['id'], {'tasktype': 0}); await SqfliteApi.editMeeting(item['id'], {'tasktype': 0});
if (item['id'] == meetingId) { if (item['id'] == meetingId) {
@ -487,14 +436,14 @@ class MeetingTaskService2 extends GetxService {
_storage.write("meeting_task_list_v2", _taskList); _storage.write("meeting_task_list_v2", _taskList);
} }
} catch (e) { } catch (e) {
Logger.error('Error occurred in _fileRecognize: ${e.toString()}'); Logger.error('文件识别流程发生异常:${e.toString()}');
Logger.error('Stack trace: ${StackTrace.current}'); Logger.error('堆栈信息:${StackTrace.current}');
// 更新数据库状态为失败 // 更新数据库状态为失败
try { try {
await SqfliteApi.editMeeting(item['id'], {'tasktype': 0}); await SqfliteApi.editMeeting(item['id'], {'tasktype': 0});
} catch (dbError) { } catch (dbError) {
Logger.error('Failed to update meeting status in database: $dbError'); Logger.error('数据库更新会议状态失败:$dbError');
} }
// 出错时也要从任务列表中移除,避免无限重试 // 出错时也要从任务列表中移除,避免无限重试
@ -510,7 +459,7 @@ class MeetingTaskService2 extends GetxService {
/// 检查是否为无声音频 /// 检查是否为无声音频
bool _checkIfSilentAudio(Map<String, dynamic> response) { bool _checkIfSilentAudio(Map<String, dynamic> response) {
print('Checking if silent audio, response: ${response.toString()}'); Logger.debug('检查是否为无声音频,响应:${response.toString()}');
// 检查API响应中的错误信息或状态码 // 检查API响应中的错误信息或状态码
// 根据实际API响应格式调整检测逻辑 // 根据实际API响应格式调整检测逻辑
@ -518,7 +467,7 @@ class MeetingTaskService2 extends GetxService {
// 检查是否有错误码表示无声音频 // 检查是否有错误码表示无声音频
if (response['code'] != null) { if (response['code'] != null) {
int code = response['code']; int code = response['code'];
print('Found response code: $code'); Logger.debug('检测到响应代码:$code');
// 常见的无声音频错误码 // 常见的无声音频错误码
if (code == 40001 || if (code == 40001 ||
code == 40002 || code == 40002 ||
@ -526,7 +475,7 @@ class MeetingTaskService2 extends GetxService {
code == 50001 || code == 50001 ||
code == 50002 || code == 50002 ||
code == 50003) { code == 50003) {
print('Detected silent audio by error code: $code'); Logger.debug('根据错误码判断为无声音频:$code');
return true; return true;
} }
} }
@ -534,7 +483,7 @@ class MeetingTaskService2 extends GetxService {
// 检查错误消息 // 检查错误消息
if (response['message'] != null) { if (response['message'] != null) {
String message = response['message'].toString().toLowerCase(); String message = response['message'].toString().toLowerCase();
print('Found response message: $message'); Logger.debug('检测到响应消息:$message');
if (message.contains('silent') || if (message.contains('silent') ||
message.contains('no audio') || message.contains('no audio') ||
message.contains('无声') || message.contains('无声') ||
@ -543,7 +492,7 @@ class MeetingTaskService2 extends GetxService {
message.contains('no speech') || message.contains('no speech') ||
message.contains('audio too short') || message.contains('audio too short') ||
message.contains('insufficient audio')) { message.contains('insufficient audio')) {
print('Detected silent audio by message: $message'); Logger.debug('根据消息判断为无声音频:$message');
return true; return true;
} }
} }
@ -551,7 +500,7 @@ class MeetingTaskService2 extends GetxService {
// 检查result中的错误信息 // 检查result中的错误信息
if (response['result'] != null && response['result']['error'] != null) { if (response['result'] != null && response['result']['error'] != null) {
String error = response['result']['error'].toString().toLowerCase(); String error = response['result']['error'].toString().toLowerCase();
print('Found result error: $error'); Logger.debug('检测到 result.error:$error');
if (error.contains('silent') || if (error.contains('silent') ||
error.contains('no audio') || error.contains('no audio') ||
error.contains('无声') || error.contains('无声') ||
@ -559,7 +508,7 @@ class MeetingTaskService2 extends GetxService {
error.contains('empty audio') || error.contains('empty audio') ||
error.contains('no speech') || error.contains('no speech') ||
error.contains('audio too short')) { error.contains('audio too short')) {
print('Detected silent audio by result error: $error'); Logger.debug('根据 result.error 判断为无声音频:$error');
return true; return true;
} }
} }
@ -567,10 +516,10 @@ class MeetingTaskService2 extends GetxService {
// 检查是否有音频时长信息且时长为0或极短 // 检查是否有音频时长信息且时长为0或极短
if (response['result'] != null && response['result']['duration'] != null) { if (response['result'] != null && response['result']['duration'] != null) {
double duration = (response['result']['duration'] as num).toDouble(); double duration = (response['result']['duration'] as num).toDouble();
print('Found audio duration: $duration seconds'); Logger.debug('检测到音频时长:$duration 秒');
if (duration <= 0.5) { if (duration <= 0.5) {
// 音频时长小于0.5秒认为是无声 // 音频时长小于0.5秒认为是无声
print('Detected silent audio by short duration: $duration'); Logger.debug('根据时长过短判断为无声音频:$duration');
return true; return true;
} }
} }
@ -618,10 +567,10 @@ class MeetingTaskService2 extends GetxService {
1000; // 转换为秒 1000; // 转换为秒
} }
print('Found empty text content, audio duration: $audioDuration seconds'); Logger.debug('文本内容为空,音频时长:$audioDuration 秒');
if (audioDuration > 1.0) { if (audioDuration > 1.0) {
// 音频时长大于1秒但没有文本内容,认为是无声音频 // 音频时长大于1秒但没有文本内容,认为是无声音频
print('Detected silent audio: empty text with sufficient duration'); Logger.debug('判断为无声音频:文本为空且时长充足');
return true; return true;
} }
} }
@ -637,25 +586,25 @@ class MeetingTaskService2 extends GetxService {
// 如果有成功状态码但没有utterances,很可能是无声音频 // 如果有成功状态码但没有utterances,很可能是无声音频
if (hasSuccessCode) { if (hasSuccessCode) {
print('Detected silent audio: success code but no utterances'); Logger.debug('判断为无声音频:成功状态码但无语句');
return true; return true;
} }
} }
print('Not detected as silent audio'); Logger.debug('未判定为无声音频');
return false; return false;
} }
Future<void> _addTextList(int id, List utterances, Future<void> _addTextList(int id, List utterances,
[Map? templateData]) async { [Map? templateData]) async {
Logger.debug('Processing ${utterances.length} utterances for meeting $id'); Logger.debug('处理会议 $id 的 ${utterances.length} 条语句');
String text = ''; String text = '';
String personnel = ''; String personnel = '';
List<Map<String, Object?>> list = []; List<Map<String, Object?>> list = [];
for (int i = 0; i < utterances.length; i++) { for (int i = 0; i < utterances.length; i++) {
var element = utterances[i]; var element = utterances[i];
Logger.debug('Processing utterance $i: ${element.toString()}'); Logger.debug('处理第 $i 条语句:${element.toString()}');
String speaker = ''; String speaker = '';
String content = ''; String content = '';
@ -681,7 +630,7 @@ class MeetingTaskService2 extends GetxService {
} else if (element['transcript'] != null) { } else if (element['transcript'] != null) {
content = element['transcript'].toString(); content = element['transcript'].toString();
} else { } else {
Logger.error('No text content found in utterance: $element'); Logger.error('语句中未找到文本内容:$element');
continue; continue;
} }
@ -718,11 +667,10 @@ class MeetingTaskService2 extends GetxService {
}); });
text += '$content\n'; text += '$content\n';
Logger.debug( Logger.debug('已添加语句:说话人=$speaker,内容=$content,开始=$startTime,结束=$endTime');
'Added utterance: speaker=$speaker, content=$content, start=$startTime, end=$endTime');
} }
Logger.debug('Total processed utterances: ${list.length}'); Logger.debug('处理完成的语句总数:${list.length}');
var result = await SqfliteApi.insertMeetingSpeaker(list); var result = await SqfliteApi.insertMeetingSpeaker(list);
if (result != null) { if (result != null) {
// 初始用于总结/概览的文本,先用原始拼接文本作为默认值 // 初始用于总结/概览的文本,先用原始拼接文本作为默认值
@ -748,7 +696,7 @@ class MeetingTaskService2 extends GetxService {
// 保持在“正在转写”状态,先进行翻译为目标语言 // 保持在“正在转写”状态,先进行翻译为目标语言
await _translateTextList(id, targetLanguage); await _translateTextList(id, targetLanguage);
} catch (e) { } catch (e) {
Logger.error('Translate transcript failed: ${e.toString()}'); Logger.error('转写文本翻译失败:${e.toString()}');
} }
// 翻译完成后再写入 personnel、设置已完成并刷新 UI // 翻译完成后再写入 personnel、设置已完成并刷新 UI
await SqfliteApi.editMeeting( await SqfliteApi.editMeeting(
@ -763,38 +711,8 @@ class MeetingTaskService2 extends GetxService {
textList = newSpeakerList; textList = newSpeakerList;
tasktype.value = 2; tasktype.value = 2;
} }
try { // 由聚合器统一对外呈现状态,不直接写入标准版列表
final s1 = Get.find<MeetingTaskService>(); // 由聚合器统一对外呈现状态,不直接写入标准版列表
if (id == s1.meetingId) {
s1.textList = newSpeakerList;
s1.tasktype.value = 2;
}
Map taskFinishData1 = s1.taskFinishList.firstWhere(
(element) => element['id'] == id,
orElse: () => {},
);
if (taskFinishData1.isNotEmpty) {
taskFinishData1['tasktype'] = 2;
s1.taskFinishList.refresh();
}
_storage.write("meeting_task_finish_list", s1.taskFinishList);
} catch (_) {}
try {
final s1 = Get.find<MeetingTaskService>();
if (id == s1.meetingId) {
s1.textList = newSpeakerList;
s1.tasktype.value = 2;
}
Map taskFinishData1 = s1.taskFinishList.firstWhere(
(element) => element['id'] == id,
orElse: () => {},
);
if (taskFinishData1.isNotEmpty) {
taskFinishData1['tasktype'] = 2;
s1.taskFinishList.refresh();
}
_storage.write("meeting_task_finish_list", s1.taskFinishList);
} catch (_) {}
// 用最新(已翻译)的文本重建总结/概览输入 // 用最新(已翻译)的文本重建总结/概览输入
summarySourceText = ''; summarySourceText = '';
for (var element in newSpeakerList) { for (var element in newSpeakerList) {
@ -967,7 +885,7 @@ class MeetingTaskService2 extends GetxService {
final obj = jsonDecode(translated); final obj = jsonDecode(translated);
arr = (obj is Map && obj['data'] is List) ? (obj['data'] as List) : []; arr = (obj is Map && obj['data'] is List) ? (obj['data'] as List) : [];
} catch (e) { } catch (e) {
Logger.error('Parse translation JSON failed: ${e.toString()}'); Logger.error('翻译结果 JSON 解析失败:${e.toString()}');
return; return;
} }
} }

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

@ -18,6 +18,7 @@ import '../../../data/services/meeting/meeting_task_service.dart';
import '../../../data/services/meeting/meeting_task_service2.dart'; import '../../../data/services/meeting/meeting_task_service2.dart';
import '../../../data/services/meeting/meeting_upload_service.dart'; import '../../../data/services/meeting/meeting_upload_service.dart';
import '../../../data/services/meeting/summary_task_service.dart'; import '../../../data/services/meeting/summary_task_service.dart';
import '../../../data/services/meeting/meeting_task_aggregator.dart';
import '../model/meeting_details_model.dart'; import '../model/meeting_details_model.dart';
import '../model/meeting_model.dart'; import '../model/meeting_model.dart';
@ -100,6 +101,7 @@ class MeetingDetailsController extends GetxController
final bool isFast = svc == 'fast'; final bool isFast = svc == 'fast';
_meetingTaskService.meetingId = id; _meetingTaskService.meetingId = id;
_meetingTaskService2.meetingId = id; _meetingTaskService2.meetingId = id;
Get.find<MeetingTaskAggregator>().setMeeting(id);
_getDataDetails(); _getDataDetails();
_loadJS(); _loadJS();
_tasktypeListener = ever(_meetingTaskService.tasktype, (value) { _tasktypeListener = ever(_meetingTaskService.tasktype, (value) {
@ -141,6 +143,7 @@ class MeetingDetailsController extends GetxController
tabController.dispose(); tabController.dispose();
playerController.dispose(); playerController.dispose();
lottieController.dispose(); lottieController.dispose();
Get.find<MeetingTaskAggregator>().clearMeeting();
super.onClose(); super.onClose();
} }

2
lib/modules/meeting/controllers/meeting_home_controller.dart

@ -20,6 +20,7 @@ import 'package:share_plus/share_plus.dart';
import '../../../data/services/meeting/meeting_task_service.dart'; import '../../../data/services/meeting/meeting_task_service.dart';
import '../../../data/services/meeting/meeting_task_service2.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/meeting/meeting_upload_service.dart';
import '../../../data/services/meeting/summary_task_service.dart'; import '../../../data/services/meeting/summary_task_service.dart';
import '../../../routes/app_routes.dart'; import '../../../routes/app_routes.dart';
@ -31,6 +32,7 @@ class MeetingHomeController extends GetxController {
final meetingUploadService = Get.find<MeetingUploadService>(); final meetingUploadService = Get.find<MeetingUploadService>();
final meetingTaskService = Get.find<MeetingTaskService>(); final meetingTaskService = Get.find<MeetingTaskService>();
final meetingTaskService2 = Get.find<MeetingTaskService2>(); final meetingTaskService2 = Get.find<MeetingTaskService2>();
final taskAggregator = Get.find<MeetingTaskAggregator>();
final summaryTaskService = Get.find<SummaryTaskService>(); final summaryTaskService = Get.find<SummaryTaskService>();
final AudioPlayer _audioPlayer = AudioPlayer(); final AudioPlayer _audioPlayer = AudioPlayer();

4
lib/modules/meeting/views/meeting_home_view.dart

@ -320,8 +320,8 @@ class _MeetingHomeViewState extends State<MeetingHomeView>
: const SizedBox(); : const SizedBox();
}), }),
Obx(() { Obx(() {
Map taskData = Map taskData = _controller.taskAggregator.taskFinishList
_controller.meetingTaskService.taskFinishList.firstWhere( .firstWhere(
(element) => element['id'] == data.id, (element) => element['id'] == data.id,
orElse: () => {}, orElse: () => {},
); );

Loading…
Cancel
Save