|
|
|
@ -10,29 +10,35 @@ import '../db/sqflite_api.dart'; |
|
|
|
import '../deapsound_ai_service.dart'; |
|
|
|
import 'meeting_upload_service.dart'; |
|
|
|
|
|
|
|
///会议任务服务类,负责管理会议音频的转写任务处理流程,包括任务提交、状态轮询、结果处理和AI分析等功能。 |
|
|
|
class MeetingTaskService extends GetxService { |
|
|
|
final GetStorage _storage = GetStorage(); |
|
|
|
|
|
|
|
MeetingUploadService get _uploadService => Get.find<MeetingUploadService>(); |
|
|
|
|
|
|
|
List _taskList = []; |
|
|
|
RxList taskFinishList = [].obs; |
|
|
|
List _taskList = []; //待处理的会议任务列表 |
|
|
|
RxList taskFinishList = [].obs; //已处理的会议任务列表 |
|
|
|
|
|
|
|
bool _isTask = false; |
|
|
|
bool _isTask = false; //是否有正在处理的任务 |
|
|
|
|
|
|
|
String _userId = ''; |
|
|
|
|
|
|
|
//字节跳动(火山引擎)语音服务配置 |
|
|
|
final String _appid = '2316081933'; |
|
|
|
final String _token = 'mcJTh6OelBqU_L78lgGd77Nyrc_-5-En'; |
|
|
|
final String _cluster = 'volc_auc_tele_flash'; |
|
|
|
final String _serviceUrl = 'https://openspeech.bytedance.com/api/v1/auc'; |
|
|
|
final String _cluster = 'volc_auc_video_flash'; |
|
|
|
final String _cluster1 = 'volc.bigasr.auc'; |
|
|
|
final String _serviceUrl = 'https://openspeech.bytedance.com/api/v1/auc'; // |
|
|
|
final String _serviceUrl1 = |
|
|
|
'https://openspeech.bytedance.com/api/v3/auc/bigmodel'; //大模型录音文件识别api地址 |
|
|
|
Map<String, String> _headers = {}; |
|
|
|
Map<String, String> _headers1 = {}; |
|
|
|
|
|
|
|
int meetingId = 0; |
|
|
|
RxInt tasktype = 1.obs; |
|
|
|
List textList = []; |
|
|
|
String summary = ''; |
|
|
|
String overview = ''; |
|
|
|
RxInt tasktype = 1.obs; //任务类型,1-转写中,2-总结中,3-完成 |
|
|
|
List textList = []; //转写结果列表 |
|
|
|
String summary = ''; //AI总结内容 |
|
|
|
String overview = ''; //AI概览内容 |
|
|
|
|
|
|
|
@override |
|
|
|
void onInit() { |
|
|
|
@ -41,6 +47,14 @@ class MeetingTaskService extends GetxService { |
|
|
|
'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': DateTime.now().millisecondsSinceEpoch.toString(), |
|
|
|
// 'X-Api-Sequence': '-1', |
|
|
|
// }; |
|
|
|
|
|
|
|
_getAgent(); |
|
|
|
_getTaskList(); |
|
|
|
} |
|
|
|
@ -56,6 +70,14 @@ class MeetingTaskService extends GetxService { |
|
|
|
_executeTask(); |
|
|
|
} |
|
|
|
|
|
|
|
///添加会议任务 |
|
|
|
///[id] 会议ID |
|
|
|
///[audiourl] 音频URL |
|
|
|
///[taskid] 任务ID |
|
|
|
///[isSpeaker] 是否为发言人 |
|
|
|
///[language] 语言 |
|
|
|
///[prompt] 提示词 |
|
|
|
///[outline] 大纲 |
|
|
|
Future<void> addTask(int id, String audiourl, String taskid, bool isSpeaker, |
|
|
|
String language, String prompt, String outline) async { |
|
|
|
if (audiourl.isEmpty) { |
|
|
|
@ -81,6 +103,14 @@ class MeetingTaskService extends GetxService { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
///添加会议任务列表 |
|
|
|
///[id] 会议ID |
|
|
|
///[audiourl] 音频URL |
|
|
|
///[taskId] 任务ID |
|
|
|
///[isSpeaker] 是否为发言人 |
|
|
|
///[language] 语言 |
|
|
|
///[prompt] 提示词 |
|
|
|
///[outline] 大纲 |
|
|
|
Future<void> addTaskList(int id, String audiourl, String taskId, |
|
|
|
bool isSpeaker, String language, String prompt, String outline) async { |
|
|
|
if (taskId.isEmpty) { |
|
|
|
@ -103,14 +133,15 @@ class MeetingTaskService extends GetxService { |
|
|
|
_executeTask(); |
|
|
|
} |
|
|
|
|
|
|
|
///执行会议任务 |
|
|
|
void _executeTask() async { |
|
|
|
if (_isTask) return; |
|
|
|
_isTask = true; |
|
|
|
while (_taskList.isNotEmpty) { |
|
|
|
await Future.delayed(const Duration(seconds: 2)); |
|
|
|
|
|
|
|
for (var element in List.from(_taskList)) { |
|
|
|
await _fileRecognize(element); |
|
|
|
Logger.info("lxm--轮询---会议任务列表$_taskList"); |
|
|
|
for (var task in List.from(_taskList)) { |
|
|
|
await _fileRecognize(task); |
|
|
|
} |
|
|
|
} |
|
|
|
_isTask = false; |
|
|
|
@ -133,9 +164,46 @@ class MeetingTaskService extends GetxService { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// |
|
|
|
Future<String> _submitTask( |
|
|
|
String audiourl, bool isSpeaker, String language) async { |
|
|
|
String format = _getFileType(audiourl); |
|
|
|
final taskId = DateTime.now().millisecondsSinceEpoch.toString(); |
|
|
|
|
|
|
|
_headers1 = { |
|
|
|
'X-Api-App-Key': _appid, |
|
|
|
'X-Api-Access-Key': _token, |
|
|
|
'X-Api-Resource-Id': _cluster1, |
|
|
|
'X-Api-Request-Id': taskId, |
|
|
|
'X-Api-Sequence': '-1', |
|
|
|
}; |
|
|
|
print("lxm--大模型asr请求头$_headers1"); |
|
|
|
|
|
|
|
final requestBody1 = jsonEncode({ |
|
|
|
"user": { |
|
|
|
"uid": _userId, |
|
|
|
}, |
|
|
|
"audio": { |
|
|
|
"url": audiourl, |
|
|
|
"language": language, |
|
|
|
"format": format, |
|
|
|
}, |
|
|
|
"request": { |
|
|
|
"model_name": "bigmodel", // 模型名称,目前只有bigmodel |
|
|
|
"model_version": "400", // 模型版本,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, // 启用语种识别 |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
print("lxm--大模型asr请求体$requestBody1"); |
|
|
|
|
|
|
|
final requestBody = jsonEncode({ |
|
|
|
"app": { |
|
|
|
@ -158,18 +226,23 @@ class MeetingTaskService extends GetxService { |
|
|
|
|
|
|
|
try { |
|
|
|
final response = await http.post( |
|
|
|
Uri.parse('$_serviceUrl/submit'), |
|
|
|
headers: _headers, |
|
|
|
body: requestBody, |
|
|
|
Uri.parse('$_serviceUrl1/submit'), |
|
|
|
headers: _headers1, |
|
|
|
body: requestBody1, |
|
|
|
); |
|
|
|
|
|
|
|
print("lxm--大模型asr响应体${response.body}"); |
|
|
|
print("lxm--大模型asr上传响应头${response.headers['x-tt-logid']}"); |
|
|
|
print("lxm--大模型asr上传服务端返回的状态码${response.headers['x-api-status-code']}"); |
|
|
|
print("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'); |
|
|
|
//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()}'); |
|
|
|
@ -182,6 +255,8 @@ class MeetingTaskService extends GetxService { |
|
|
|
return array[array.length - 1]; |
|
|
|
} |
|
|
|
|
|
|
|
///轮询语音转文字任务状态 |
|
|
|
///[taskId] 任务ID |
|
|
|
Future<Map<String, dynamic>> _queryTask(String taskId) async { |
|
|
|
final queryBody = jsonEncode({ |
|
|
|
"appid": _appid, |
|
|
|
@ -192,11 +267,18 @@ class MeetingTaskService extends GetxService { |
|
|
|
|
|
|
|
try { |
|
|
|
final response = await http.post( |
|
|
|
Uri.parse('$_serviceUrl/query'), |
|
|
|
headers: _headers, |
|
|
|
body: queryBody, |
|
|
|
Uri.parse('$_serviceUrl1/query'), |
|
|
|
headers: _headers1, |
|
|
|
body: jsonEncode({}), |
|
|
|
); |
|
|
|
|
|
|
|
// print("lxm---大模型asr响应体${response.body}"); |
|
|
|
// print("lxm---大模型asr响应头${response.headers}"); |
|
|
|
// print("lxm---大模型asr响应状态码${response.statusCode}"); |
|
|
|
// print("lxm---大模型asr响应体logid${response.headers['x-tt-logid']}"); |
|
|
|
// print("lxm---大模型asr服务端返回的状态码${response.headers['x-api-status-code']}"); |
|
|
|
// print("lxm---大模型asr服务端返回的消息${response.headers['x-api-message']}"); |
|
|
|
|
|
|
|
if (response.statusCode != 200) { |
|
|
|
throw Exception('Query failed with status: ${response.statusCode}'); |
|
|
|
} |
|
|
|
@ -209,20 +291,24 @@ class MeetingTaskService extends GetxService { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
///轮询语音转文字任务状态,若任务完成则处理转写结果 |
|
|
|
///[item] 包含任务ID、会议ID、AI总结提示词、AI概览提示词的Map |
|
|
|
Future<void> _fileRecognize(Map item) async { |
|
|
|
try { |
|
|
|
var respDic = await _queryTask(item['taskid']); |
|
|
|
var code = respDic['resp']['code'] as int; |
|
|
|
//var code = respDic['resp']['code'] as int; |
|
|
|
|
|
|
|
print("lxm---大模型asr响应体结果$respDic['result']['text']"); |
|
|
|
|
|
|
|
if (code == 1000) { |
|
|
|
if (respDic['result']['text'] != null) { |
|
|
|
await _addTextList( |
|
|
|
item['id'], |
|
|
|
respDic['resp']['utterances'], |
|
|
|
item['prompt'], |
|
|
|
item['outline'], |
|
|
|
item['id'], //会议ID |
|
|
|
respDic['result']['utterances'], //语音转文字结果 |
|
|
|
item['prompt'], //AI总结提示词 |
|
|
|
item['outline'], //AI概览提示词 |
|
|
|
); |
|
|
|
return; |
|
|
|
} else if (code < 2000) { |
|
|
|
} else { |
|
|
|
Logger.debug("FileRecognize failed"); |
|
|
|
return; |
|
|
|
} |
|
|
|
@ -231,11 +317,18 @@ class MeetingTaskService extends GetxService { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
///处理语音识别完成后的转写结果,包括数据库存储、状态更新和触发AI分析 |
|
|
|
///[id] 会议ID |
|
|
|
///[utterances] 语音转文字结果 |
|
|
|
///[prompt] AI总结提示词 |
|
|
|
///[outline] AI概览提示词 |
|
|
|
Future<void> _addTextList( |
|
|
|
int id, List utterances, String prompt, String outline) async { |
|
|
|
String text = ''; |
|
|
|
String personnel = ''; |
|
|
|
List<Map<String, Object?>> list = []; |
|
|
|
|
|
|
|
// 处理转写结果,将每个句子的开始时间、结束时间、发言人和内容存储到列表中 |
|
|
|
for (var element in utterances) { |
|
|
|
String speaker = ''; |
|
|
|
if (element['additions'].isNotEmpty) { |
|
|
|
@ -254,8 +347,10 @@ class MeetingTaskService extends GetxService { |
|
|
|
}); |
|
|
|
text += '${element['text']}\n'; |
|
|
|
} |
|
|
|
// 存储处理后的转写结果到数据库 |
|
|
|
var result = await SqfliteApi.insertMeetingSpeaker(list); |
|
|
|
if (result != null) { |
|
|
|
// 更新会议状态为“总结中” |
|
|
|
await SqfliteApi.editMeeting( |
|
|
|
id, |
|
|
|
{'tasktype': 2, 'personnel': personnel}, |
|
|
|
@ -282,9 +377,16 @@ class MeetingTaskService extends GetxService { |
|
|
|
_storage.write("meeting_task_list", _taskList); |
|
|
|
_storage.write("meeting_task_finish_list", taskFinishList); |
|
|
|
} |
|
|
|
|
|
|
|
// 触发AI分析,分别请求总结和概览 |
|
|
|
aiServiceMessage(id, text, prompt, outline); |
|
|
|
} |
|
|
|
|
|
|
|
///触发AI分析,分别请求总结和概览 |
|
|
|
///[id] 会议ID |
|
|
|
///[text] 语音转文字结果 |
|
|
|
///[prompt] AI总结提示词 |
|
|
|
///[outline] AI概览提示词 |
|
|
|
void aiServiceMessage(int id, String text, String prompt, String outline) { |
|
|
|
int awaitQuantity = 0; |
|
|
|
String summaryText = ''; |
|
|
|
@ -321,6 +423,10 @@ class MeetingTaskService extends GetxService { |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
///处理AI分析完成后的结果,包括数据库存储和状态更新 |
|
|
|
///[id] 会议ID |
|
|
|
///[summaryText] 总结结果 |
|
|
|
///[overviewText] 概览结果 |
|
|
|
void _aiServiceValue(int id, String summaryText, String overviewText) { |
|
|
|
SqfliteApi.editMeeting(id, { |
|
|
|
'tasktype': 3, |
|
|
|
|