From 2df65195b0eeee16874bf19db9dbbc343fad7d67 Mon Sep 17 00:00:00 2001 From: tanlongsheng <252620078@qq.com> Date: Thu, 29 May 2025 19:56:49 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=9F=A5=E8=AF=A2=E7=8A=B6?= =?UTF-8?q?=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/data/services/db/database_helper.dart | 1 + lib/data/services/db/sqflite_api.dart | 13 +- .../controllers/meeting_controller.dart | 144 +++++++++++++++++- .../meeting_details_controller.dart | 41 ++++- lib/modules/meeting/model/meeting_model.dart | 3 + lib/modules/meeting/views/meeting_view.dart | 17 ++- 6 files changed, 200 insertions(+), 19 deletions(-) diff --git a/lib/data/services/db/database_helper.dart b/lib/data/services/db/database_helper.dart index 9c9794381..0b74feb2d 100644 --- a/lib/data/services/db/database_helper.dart +++ b/lib/data/services/db/database_helper.dart @@ -34,6 +34,7 @@ class DatabaseHelper { seconds INTEGER, filepath TEXT, audiourl TEXT, + tasktype INTEGER, creationtime INTEGER ) '''); diff --git a/lib/data/services/db/sqflite_api.dart b/lib/data/services/db/sqflite_api.dart index 3e13d639a..6d380d84b 100644 --- a/lib/data/services/db/sqflite_api.dart +++ b/lib/data/services/db/sqflite_api.dart @@ -5,13 +5,13 @@ import 'database_helper.dart'; class SqfliteApi { // 获取商务会议助手列表 - static Future getMeetingList(int page) async { + static Future getMeetingList(int offset) async { final db = await DatabaseHelper().database; List> dataList = []; List meetingList = await db.query( 'meeting', limit: 10, - offset: (page - 1) * 10, + offset: offset, orderBy: 'creationtime DESC', ); for (var element in meetingList) { @@ -22,7 +22,7 @@ class SqfliteApi { ); dataList.add({ ...element, - 'tasktype': detailsList.first['tasktype'], + 'taskid': detailsList.first['taskid'], }); } return dataList; @@ -40,7 +40,7 @@ class SqfliteApi { ); return { ...meetingList.first, - 'tasktype': detailsList.first['tasktype'], + 'taskid': detailsList.first['taskid'], 'details': detailsList.first }; } @@ -162,10 +162,9 @@ class SqfliteApi { } // 修改商务会议助手标题 - static Future editMeetingTitle(id, title) async { + static Future editMeetingTitle(id, Map meeting) async { final db = await DatabaseHelper().database; - await db.update('meeting', {'title': title}, - where: 'id = ?', whereArgs: [id]); + await db.update('meeting', meeting, where: 'id = ?', whereArgs: [id]); } // 删除商务会议助手 diff --git a/lib/modules/meeting/controllers/meeting_controller.dart b/lib/modules/meeting/controllers/meeting_controller.dart index 520225296..8322dc380 100644 --- a/lib/modules/meeting/controllers/meeting_controller.dart +++ b/lib/modules/meeting/controllers/meeting_controller.dart @@ -1,13 +1,16 @@ +import 'dart:convert'; import 'dart:io'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:get/get.dart'; +import 'package:http/http.dart' as http; import 'package:just_audio/just_audio.dart'; import 'package:path_provider/path_provider.dart'; import 'package:permission_handler/permission_handler.dart'; +import '../../../core/utils/logger.dart'; import '../../../core/utils/upload_oss.dart'; import '../../../data/services/db/sqflite_api.dart'; @@ -16,18 +19,33 @@ class MeetingController extends GetxController { final AudioPlayer _audioPlayer = AudioPlayer(); // 是否还有更多数据 - bool isLoadingMore = true; + bool isGetMore = true; + bool isLoading = false; int page = 1; + int addQuantity = 0; TextEditingController titleController = TextEditingController(); // 录音标题 RxList dataList = [].obs; + List dataListTask = []; + bool isDisposed = false; + + final String _appid = '2316081933'; + final String _token = 'mcJTh6OelBqU_L78lgGd77Nyrc_-5-En'; + final String _cluster = 'volc_auc_meeting_flash'; + final String _serviceUrl = 'https://openspeech.bytedance.com/api/v1/auc'; + Map _headers = {}; + @override void onInit() { super.onInit(); listController.addListener(_onScroll); Permission.storage.request(); + _headers = { + 'Authorization': 'Bearer; $_token', + 'Content-Type': 'application/json', + }; _getDataList(); } @@ -36,13 +54,16 @@ class MeetingController extends GetxController { listController.dispose(); titleController.dispose(); _audioPlayer.dispose(); + isDisposed = true; super.onClose(); } void _onScroll() { - if (isLoadingMore && + if (isGetMore && + !isLoading && listController.position.pixels >= listController.position.maxScrollExtent - 100) { + isLoading = true; page++; _getDataList(); } @@ -50,13 +71,16 @@ class MeetingController extends GetxController { // 请求本地数据 void _getDataList() async { - List list = await SqfliteApi.getMeetingList(page); + List list = await SqfliteApi.getMeetingList((page - 1) * 10 + addQuantity); dataList.addAll(list); + queryDataListTask(list); + addQuantity = 0; if (list.length < 10) { - isLoadingMore = false; + isGetMore = false; } else { - isLoadingMore = true; + isGetMore = true; } + isLoading = false; } void addAudio() async { @@ -79,15 +103,17 @@ class MeetingController extends GetxController { 'seconds': seconds, 'filepath': filePath, 'audiourl': audiourl, + 'tasktype': 0, 'creationtime': creationTime, }; final meetingId = await SqfliteApi.insertMeeting(data); if (meetingId != null) { dataList.insert(0, { 'id': meetingId, - 'tasktype': 0, + 'taskid': '', ...data, }); + addQuantity++; } } EasyLoading.dismiss(); @@ -104,7 +130,7 @@ class MeetingController extends GetxController { // 修改标题 void editMeetingTitle(int id, String title) async { if (title.isNotEmpty) { - SqfliteApi.editMeetingTitle(id, title); + SqfliteApi.editMeetingTitle(id, {'title': title}); int index = dataList.indexWhere((item) => item['id'] == id); if (index != -1) { final dataMap = Map.from(dataList[index]); @@ -121,6 +147,7 @@ class MeetingController extends GetxController { // 删除数据库中的记录 SqfliteApi.deleteMeeting(id); _deleteFile(filepath); + addQuantity--; } // 删除文件 @@ -131,4 +158,107 @@ class MeetingController extends GetxController { await file.delete(); } } + + // 查询转写状态 + void queryDataListTask(List list, {bool isAdd = true}) { + for (var element in List.from(list)) { + if (element['tasktype'] == 1) { + if (isAdd) { + dataListTask.add(element); + } + final taskId = element['taskid']; + _fileRecognize(element['id'], taskId); + } + } + } + + Future _fileRecognize(int id, String taskId) async { + try { + final startTime = DateTime.now().millisecondsSinceEpoch; + + if (isDisposed) return; + var respDic = await _queryTask(taskId); + var code = respDic['resp']['code'] as int; + + if (code == 1000) { + await SqfliteApi.editMeetingTitle(id, {'tasktype': 2}); + int index = dataList.indexWhere((item) => item['id'] == id); + if (index != -1) { + final dataMap = Map.from(dataList[index]); + dataMap['tasktype'] = 2; + dataList[index] = dataMap; + int taskIndex = dataListTask.indexWhere((item) => item['id'] == id); + if (taskIndex != -1) { + dataListTask.removeAt(taskIndex); + } + } + return; + } else if (code < 2000) { + Logger.debug("FileRecognize failed"); + return; + } + + while (!isDisposed) { + await Future.delayed(const Duration(seconds: 2)); + + if (isDisposed) return; + + respDic = await _queryTask(taskId); + code = respDic['resp']['code'] as int; + + if (code == 1000) { + await SqfliteApi.editMeetingTitle(id, {'tasktype': 2}); + int index = dataList.indexWhere((item) => item['id'] == id); + if (index != -1) { + final dataMap = Map.from(dataList[index]); + dataMap['tasktype'] = 2; + dataList[index] = dataMap; + int taskIndex = dataListTask.indexWhere((item) => item['id'] == id); + if (taskIndex != -1) { + dataListTask.removeAt(taskIndex); + } + } + return; + } else if (code < 2000) { + Logger.debug("FileRecognize failed"); + return; + } + + final currentTime = DateTime.now().millisecondsSinceEpoch; + if (currentTime - startTime > 300 * 1000) { + Logger.debug('wait time exceeds 300s'); + return; + } + } + } catch (e) { + Logger.error('Error occurred: ${e.toString()}'); + } + } + + Future> _queryTask(String taskId) async { + final queryBody = jsonEncode({ + "appid": _appid, + "token": _token, + "id": taskId, + "cluster": _cluster, + }); + + try { + final response = await http.post( + Uri.parse('$_serviceUrl/query'), + headers: _headers, + body: queryBody, + ); + + if (response.statusCode != 200) { + throw Exception('Query failed with status: ${response.statusCode}'); + } + + final respDic = jsonDecode(response.body); + return respDic; + } catch (e) { + Logger.error('Query error: ${e.toString()}'); + rethrow; + } + } } diff --git a/lib/modules/meeting/controllers/meeting_details_controller.dart b/lib/modules/meeting/controllers/meeting_details_controller.dart index ebec7e774..ea15ec38e 100644 --- a/lib/modules/meeting/controllers/meeting_details_controller.dart +++ b/lib/modules/meeting/controllers/meeting_details_controller.dart @@ -92,17 +92,40 @@ class MeetingDetailsController extends GetxController super.onClose(); } - void popInvokedWithResult(didPop, result) { + void popInvokedWithResult(didPop, result) async { if (meetingData.value.tasktype != meetingDetails.value.tasktype) { + await SqfliteApi.editMeetingTitle( + id, {'tasktype': meetingDetails.value.tasktype}); int index = _meetingController.dataList.indexWhere((item) => item['id'] == id); if (index != -1) { final dataMap = Map.from(_meetingController.dataList[index]); dataMap['tasktype'] = meetingDetails.value.tasktype; + if (dataMap['taskid'].isEmpty) { + dataMap['taskid'] = meetingDetails.value.taskid; + } _meetingController.dataList[index] = dataMap; + int taskIndex = _meetingController.dataListTask + .indexWhere((item) => item['id'] == id); + if (taskIndex != -1) { + if (meetingDetails.value.tasktype == 1) { + _meetingController.dataListTask[taskIndex] = dataMap; + } else { + _meetingController.dataListTask.removeAt(taskIndex); + } + } else { + if (meetingDetails.value.tasktype == 1) { + _meetingController.dataListTask.add(dataMap); + } + } } } + _meetingController.isDisposed = false; + _meetingController.queryDataListTask( + _meetingController.dataListTask, + isAdd: false, + ); } void _getAgent() async { @@ -327,13 +350,25 @@ class MeetingDetailsController extends GetxController try { final startTime = DateTime.now().millisecondsSinceEpoch; + if (_isDisposed) return; + var respDic = await _queryTask(taskId); + var code = respDic['resp']['code'] as int; + + if (code == 1000) { + await _addTextList(respDic['resp']['utterances']); + return; + } else if (code < 2000) { + Logger.debug("FileRecognize failed"); + return; + } + while (!_isDisposed) { await Future.delayed(const Duration(seconds: 2)); if (_isDisposed) return; - final respDic = await _queryTask(taskId); - final code = respDic['resp']['code'] as int; + respDic = await _queryTask(taskId); + code = respDic['resp']['code'] as int; if (code == 1000) { await _addTextList(respDic['resp']['utterances']); diff --git a/lib/modules/meeting/model/meeting_model.dart b/lib/modules/meeting/model/meeting_model.dart index 186237cb5..3e2fd0f40 100644 --- a/lib/modules/meeting/model/meeting_model.dart +++ b/lib/modules/meeting/model/meeting_model.dart @@ -7,6 +7,7 @@ class MeetingModel { late int seconds; late String filepath; late String audiourl; + late String taskid; late int tasktype; late int creationtime; @@ -17,6 +18,7 @@ class MeetingModel { ..seconds = map["seconds"] ..filepath = map["filepath"] ..audiourl = map["audiourl"] + ..taskid = map["taskid"] ..tasktype = map["tasktype"] ..creationtime = map["creationtime"]; @@ -28,6 +30,7 @@ class MeetingModel { data['seconds'] = seconds; data['filepath'] = filepath; data['audiourl'] = audiourl; + data['taskid'] = taskid; data['tasktype'] = tasktype; data['creationtime'] = creationtime; return data; diff --git a/lib/modules/meeting/views/meeting_view.dart b/lib/modules/meeting/views/meeting_view.dart index ae73e9ac8..fce0d4021 100644 --- a/lib/modules/meeting/views/meeting_view.dart +++ b/lib/modules/meeting/views/meeting_view.dart @@ -56,6 +56,7 @@ class MeetingView extends GetView { Widget _buildBody(BuildContext context, bool isDarkMode) { return Obx( () => ListView.builder( + controller: controller.listController, itemCount: controller.dataList.length, padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 5.w), itemBuilder: (BuildContext context, int index) { @@ -69,6 +70,7 @@ class MeetingView extends GetView { Widget _item(BuildContext context, bool isDarkMode, MeetingModel data) { return GestureDetector( onTap: () { + controller.isDisposed = true; Get.toNamed(Routes.meetingDetails, arguments: { 'id': data.id, }); @@ -158,11 +160,22 @@ class MeetingView extends GetView { ), ), ), - if (data.tasktype == 1 || data.tasktype == 2) + if (data.tasktype == 1) Padding( padding: EdgeInsets.only(top: 5.w), child: Text( - data.tasktype == 1 ? '正在生成...' : '已生成', + '正在生成...', + style: TextStyle( + color: Colors.blue, + fontSize: 10.sp, + ), + ), + ), + if (data.tasktype == 2) + Padding( + padding: EdgeInsets.only(top: 5.w), + child: Text( + '已生成', style: TextStyle( color: Colors.blue, fontSize: 10.sp,