Browse Source

Merge branch 'liwei' of https://github.com/deepcloud2048/deep_voice into liwei

weicu
liwei1dao 11 months ago
parent
commit
21d2a6429d
  1. 7
      lib/core/bindings/initial_binding.dart
  2. 105
      lib/data/services/meeting/meeting_task_aggregator.dart
  3. 1
      lib/data/services/meeting/meeting_task_service.dart
  4. 913
      lib/data/services/meeting/meeting_task_service2.dart
  5. 40
      lib/data/services/meeting/meeting_upload_service.dart
  6. 27
      lib/data/services/meeting/summary_task_service.dart
  7. 77
      lib/modules/meeting/controllers/meeting_details_controller.dart
  8. 5
      lib/modules/meeting/controllers/meeting_home_controller.dart
  9. 4
      lib/modules/meeting/views/meeting_home_view.dart

7
lib/core/bindings/initial_binding.dart

@ -11,6 +11,8 @@ import '../../../data/services/ast_service.dart';
import '../../../data/services/speech_impl/azure_ast_service.dart'; 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_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';
@ -70,6 +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<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;
}
}

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

@ -15,6 +15,7 @@ import '../../../data/models/appconfig.dart';
import 'summary_task_service.dart'; import 'summary_task_service.dart';
///会议任务服务类,负责管理会议音频的转写任务处理流程,包括任务提交、状态轮询、结果处理和AI分析等功能。 ///会议任务服务类,负责管理会议音频的转写任务处理流程,包括任务提交、状态轮询、结果处理和AI分析等功能。
///火山引擎录音文件识别大模型-标准版
class MeetingTaskService extends GetxService { class MeetingTaskService extends GetxService {
final GetStorage _storage = GetStorage(); final GetStorage _storage = GetStorage();

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

@ -0,0 +1,913 @@
import 'dart:convert';
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 '../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<MeetingUploadService>();
SummaryTaskService get _summaryTaskService => Get.find<SummaryTaskService>();
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<void> 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<void> addTaskList(
int id,
String audiourl, {
bool isSpeaker = false,
String language = '',
String targetLanguage = '',
String prompt = '',
String outline = '',
}) async {
await SqfliteApi.editMeeting(id, {'tasktype': 1});
_taskList.add({
'id': id,
'audiourl': audiourl,
'tasktype': 1,
'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<Map<String, dynamic>> _recognizeAudio(
String audiourl, {
bool enableSpeakerInfo = true,
String language = '',
}) async {
String requestId = DateTime.now().millisecondsSinceEpoch.toString();
// 为每次请求生成新的headers - 适配大模型极速版API
Map<String, String> 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<String, dynamic> 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<String> 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<void> _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,
);
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<String, dynamic> 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<void> _addTextList(int id, List utterances,
[Map? templateData]) async {
Logger.debug('处理会议 $id 的 ${utterances.length} 条语句');
String text = '';
String personnel = '';
List<Map<String, Object?>> 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 + '输出。');
_summaryTaskService.addTaskList(
id, summarySourceText, promptToUse, outlineToUse);
}
}
Future<void> _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<dynamic> 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 (_) {}
}
}
}

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

@ -5,6 +5,7 @@ import '../../../core/utils/logger.dart';
import '../../../core/utils/upload_oss.dart'; import '../../../core/utils/upload_oss.dart';
import '../db/sqflite_api.dart'; import '../db/sqflite_api.dart';
import 'meeting_task_service.dart'; import 'meeting_task_service.dart';
import 'meeting_task_service2.dart';
class MeetingUploadService extends GetxService { class MeetingUploadService extends GetxService {
final _taskService = Get.find<MeetingTaskService>(); final _taskService = Get.find<MeetingTaskService>();
@ -43,6 +44,13 @@ class MeetingUploadService extends GetxService {
String rootDir, { String rootDir, {
bool isUpload = true, bool isUpload = true,
}) async { }) async {
bool flash = false;
try {
final details = await SqfliteApi.getMeetingDetails(id);
final int seconds = (details['seconds'] ?? 0) as int;
flash = seconds > 0 && seconds <= 7200;
} catch (_) {}
_storage.write("meeting_service_type_$id", flash ? 'fast' : 'standard');
uploadList.add({ uploadList.add({
'id': id, 'id': id,
'filepath': filePath, 'filepath': filePath,
@ -53,6 +61,7 @@ class MeetingUploadService extends GetxService {
'language': '', 'language': '',
'prompt': '', 'prompt': '',
'outline': '', 'outline': '',
'useFlashASR': flash,
}); });
_storage.write("meeting_upload_list", uploadList); _storage.write("meeting_upload_list", uploadList);
if (isUpload) { if (isUpload) {
@ -82,15 +91,28 @@ class MeetingUploadService extends GetxService {
{'audiourl': audiourl}, {'audiourl': audiourl},
); );
if (element['isTask']) { if (element['isTask']) {
await _taskService.addTaskList( if (element['useFlashASR'] == true) {
element['id'], final service2 = Get.find<MeetingTaskService2>();
audiourl, await service2.addTaskList(
'', element['id'],
element['isSpeaker'], audiourl,
element['language'], isSpeaker: element['isSpeaker'],
element['prompt'], language: element['language'],
element['outline'], targetLanguage: '',
); prompt: element['prompt'],
outline: element['outline'],
);
} else {
await _taskService.addTaskList(
element['id'],
audiourl,
'',
element['isSpeaker'],
element['language'],
element['prompt'],
element['outline'],
);
}
} }
element['audiourl'] = audiourl; element['audiourl'] = audiourl;
uploadProgress.value = 0.0; uploadProgress.value = 0.0;

27
lib/data/services/meeting/summary_task_service.dart

@ -5,9 +5,11 @@ import '../../../core/utils/logger.dart';
import '../db/sqflite_api.dart'; import '../db/sqflite_api.dart';
import '../deapsound_ai_service.dart'; import '../deapsound_ai_service.dart';
import 'meeting_task_service.dart'; import 'meeting_task_service.dart';
import 'meeting_task_service2.dart';
class SummaryTaskService extends GetxService { class SummaryTaskService extends GetxService {
final _taskService = Get.find<MeetingTaskService>(); final _taskService = Get.find<MeetingTaskService>();
final _taskService2 = Get.find<MeetingTaskService2>();
final GetStorage _storage = GetStorage(); final GetStorage _storage = GetStorage();
@ -137,11 +139,24 @@ class SummaryTaskService extends GetxService {
'summary': summaryText, 'summary': summaryText,
'overview': overviewText, 'overview': overviewText,
}); });
// 更新 V1 Service 状态
if (id == _taskService.meetingId) { if (id == _taskService.meetingId) {
summary = summaryText; summary = summaryText;
overview = overviewText; overview = overviewText;
_taskService.tasktype.value = 3; _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( Map taskFinishData = _taskService.taskFinishList.firstWhere(
(element) => element['id'] == id, (element) => element['id'] == id,
orElse: () => {}, orElse: () => {},
@ -151,6 +166,18 @@ class SummaryTaskService extends GetxService {
_taskService.taskFinishList.refresh(); _taskService.taskFinishList.refresh();
} }
_storage.write("meeting_task_finish_list", _taskService.taskFinishList); _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); _taskList.removeWhere((element) => element['id'] == id);
_storage.write("summary_task_list", _taskList); _storage.write("summary_task_list", _taskList);
_isTask = false; _isTask = false;

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

@ -9,13 +9,16 @@ import 'package:flutter/services.dart' show rootBundle;
import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import '../../../core/utils/synchrodata.dart'; import '../../../core/utils/synchrodata.dart';
import '../../../data/services/db/sqflite_api.dart'; import '../../../data/services/db/sqflite_api.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_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';
@ -28,9 +31,11 @@ class MeetingDetailsController extends GetxController
late AnimationController lottieController; late AnimationController lottieController;
late final Worker _tasktypeListener; late final Worker _tasktypeListener;
late final Worker _tasktypeListenerV2;
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 _summaryTaskService = Get.find<SummaryTaskService>(); final _summaryTaskService = Get.find<SummaryTaskService>();
final PlayerController playerController = PlayerController(); final PlayerController playerController = PlayerController();
@ -90,13 +95,20 @@ class MeetingDetailsController extends GetxController
..duration = const Duration(seconds: 2); ..duration = const Duration(seconds: 2);
final Map<String, dynamic> args = Get.arguments ?? {}; final Map<String, dynamic> args = Get.arguments ?? {};
id = args['id']; id = args['id'];
final storage = GetStorage();
final String svc =
(storage.read("meeting_service_type_$id") ?? '').toString();
final bool isFast = svc == 'fast';
_meetingTaskService.meetingId = id; _meetingTaskService.meetingId = id;
_meetingTaskService2.meetingId = id;
Get.find<MeetingTaskAggregator>().setMeeting(id);
_getDataDetails(); _getDataDetails();
_loadJS(); _loadJS();
_tasktypeListener = ever(_meetingTaskService.tasktype, (value) { _tasktypeListener = ever(_meetingTaskService.tasktype, (value) {
if (isFast && value != 3) return;
meetingDetails.value.tasktype = value; meetingDetails.value.tasktype = value;
if (_everType <= value) { if (_everType <= value) {
if (value == 2) { if (!isFast && value == 2) {
textList.value = _meetingTaskService.textList; textList.value = _meetingTaskService.textList;
} else if (value == 3) { } else if (value == 3) {
meetingDetails.value.summary = _summaryTaskService.summary; meetingDetails.value.summary = _summaryTaskService.summary;
@ -106,23 +118,41 @@ class MeetingDetailsController extends GetxController
} }
meetingDetails.refresh(); 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;
summaryNumberModifications.value++;
}
}
meetingDetails.refresh();
});
} }
@override @override
void onClose() { void onClose() {
_tasktypeListener.dispose(); _tasktypeListener.dispose();
_tasktypeListenerV2.dispose();
_currentDurationSubscription?.cancel(); _currentDurationSubscription?.cancel();
_playerStateSubscription?.cancel(); _playerStateSubscription?.cancel();
tabController.dispose(); tabController.dispose();
playerController.dispose(); playerController.dispose();
lottieController.dispose(); lottieController.dispose();
Get.find<MeetingTaskAggregator>().clearMeeting();
super.onClose(); super.onClose();
} }
void popInvokedWithResult(didPop, result) async { void popInvokedWithResult(didPop, result) async {
_meetingUploadService.removeUpload(id); _meetingUploadService.removeUpload(id);
_meetingTaskService.removeTask(id); _meetingTaskService.removeTask(id);
_meetingTaskService2.removeTask(id);
_meetingTaskService.meetingId = 0; _meetingTaskService.meetingId = 0;
_meetingTaskService2.meetingId = 0;
} }
void _getDataDetails() async { void _getDataDetails() async {
@ -257,10 +287,33 @@ class MeetingDetailsController extends GetxController
bool isSpeaker, String language, String prompt, String outline) async { bool isSpeaker, String language, String prompt, String outline) async {
EasyLoading.show(); EasyLoading.show();
_everType = 2; _everType = 2;
String audiourl = meetingData.value.audiourl; final String audiourl = meetingData.value.audiourl;
String taskId = meetingDetails.value.taskid; final int seconds = meetingData.value.seconds;
await _meetingTaskService.addTask( final storage = GetStorage();
id, audiourl, taskId, isSpeaker, language, prompt, outline); 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,
);
}
meetingDetails.value.tasktype = 1; meetingDetails.value.tasktype = 1;
meetingDetails.refresh(); meetingDetails.refresh();
EasyLoading.dismiss(); EasyLoading.dismiss();
@ -275,8 +328,20 @@ class MeetingDetailsController extends GetxController
text += '${element['content']}\n'; text += '${element['content']}\n';
} }
_everType = 3; _everType = 3;
EasyLoading.show();
await SqfliteApi.editMeeting(id, {'tasktype': 2}); await SqfliteApi.editMeeting(id, {'tasktype': 2});
_meetingTaskService.tasktype.value = 2; meetingDetails.value.tasktype = 2;
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); _summaryTaskService.addTaskList(id, text, prompt, outline);
EasyLoading.dismiss();
} }
} }

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

@ -19,6 +19,8 @@ import '../../../core/utils/permission_util.dart';
import 'package:share_plus/share_plus.dart'; import 'package:share_plus/share_plus.dart';
import '../../../data/services/meeting/meeting_task_service.dart'; import '../../../data/services/meeting/meeting_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/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';
@ -29,6 +31,8 @@ class MeetingHomeController extends GetxController {
final meetingController = Get.find<MeetingController>(); final meetingController = Get.find<MeetingController>();
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 taskAggregator = Get.find<MeetingTaskAggregator>();
final summaryTaskService = Get.find<SummaryTaskService>(); final summaryTaskService = Get.find<SummaryTaskService>();
final AudioPlayer _audioPlayer = AudioPlayer(); final AudioPlayer _audioPlayer = AudioPlayer();
@ -585,6 +589,7 @@ class MeetingHomeController extends GetxController {
_deleteFile(filepath); _deleteFile(filepath);
meetingUploadService.removeUpload(id, isDeleteTask: true); meetingUploadService.removeUpload(id, isDeleteTask: true);
meetingTaskService.removeTask(id, isDeleteTask: true); meetingTaskService.removeTask(id, isDeleteTask: true);
meetingTaskService2.removeTask(id, isDeleteTask: true);
} }
// 删除文件 // 删除文件

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