123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342 |
- import 'dart:async';
- import 'dart:io';
- import 'package:flutter_sound/flutter_sound.dart';
- import 'package:get/get.dart';
- import 'package:path_provider/path_provider.dart';
- import '../../../../common/api/index.dart';
- import '../../../../common/models/index.dart';
- import '../../../../common/routers/index.dart';
- import '../../../../common/services/index.dart';
- import '../../../../common/utils/index.dart';
- import '../../../apps/meeting/index.dart';
- import '../../../apps/process/process_picker/index.dart';
- import '../../../common/create_form/index.dart';
- import '../../../common/portal/index.dart';
- import 'index.dart';
- class SpeechAssistantChatController extends GetxController {
- SpeechAssistantChatController();
- final state = SpeechAssistantChatState();
- /// 录音
- FlutterSoundRecorder? _myRecorder = FlutterSoundRecorder();
- bool _mRecorderIsInited = false;
- String _mPath = "";
- final _maxLength = 60; // 最长录音时间60秒
- int _recordSecond = 0;
- StreamSubscription? _mRecordingDataSubscription;
- bool _ttsOpen = false;
- /// 在 onInit() 之后调用 1 帧。这是进入的理想场所
- @override
- void onReady() {
- state.btnTitle = 'im_chat_tools_voice'.tr;
- _loadInitCommandList();
- initTheRecorder();
- _ttsOpen = ProgramCenterService.to.extendParam()['needTTS'] ?? false;
- super.onReady();
- }
- @override
- void onClose() {
- _myRecorder?.closeRecorder();
- _myRecorder = null;
- if (_ttsOpen) {
- FlutterTTSHelper.instance.stop();
- }
- super.onClose();
- }
- /// 初始化录音功能
- Future<void> initTheRecorder() async {
- // 检查录音权限
- var isPermission = await O2Utils.microphonePermission();
- if (!isPermission) {
- OLogger.e('没有权限,无法录音');
- Loading.showError('im_chat_error_need_record_permission'.tr);
- return;
- }
- // 开启录音工具
- await _myRecorder?.openRecorder();
- //设置订阅计时器
- await _myRecorder
- ?.setSubscriptionDuration(const Duration(milliseconds: 10));
- // 初始化录音路径
- var tempDir = await getTemporaryDirectory();
- _mPath = '${tempDir.path}/o2_speech_assistant.pcm'; // pcm 格式录音 baidu 语音需要
- _mRecorderIsInited = true;
- }
- /// 开始录音
- Future<void> startRecorder() async {
- if (!_mRecorderIsInited) {
- Loading.showError('im_chat_error_record_not_init'.tr);
- return;
- }
- if (state.status.value != SpeechStatus.idle) {
- OLogger.i('正在数据加载中!');
- return;
- }
- if (_ttsOpen) {
- FlutterTTSHelper.instance.stop();
- }
- state.status.value = SpeechStatus.speaking;
- // 初始化数字
- _recordSecond = 0;
- // 初始化文件
- var outputFile = File(_mPath);
- if (outputFile.existsSync()) {
- await outputFile.delete();
- }
- _myRecorder?.startRecorder(
- toFile: _mPath,
- codec: Codec.pcm16,
- );
- /// 监听录音
- _mRecordingDataSubscription = _myRecorder?.onProgress?.listen((e) {
- final date = DateTime.fromMillisecondsSinceEpoch(
- e.duration.inMilliseconds,
- isUtc: true);
- // var txt = date.hms();
- _recordSecond = date.second; //
- //设置了最大录音时长
- if (date.second >= _maxLength) {
- stopRecorder();
- return;
- }
- //更新录音时长
- // state.btnTitle = txt;
- });
- }
- Future<void> stopRecorder() async {
- if (state.status.value != SpeechStatus.speaking) {
- OLogger.i('正在数据加载中!');
- return;
- }
- await _myRecorder!.stopRecorder();
- _cancelRecorderSubscriptions();
- OLogger.d("record time $_recordSecond");
- if (_recordSecond < 1) {
- Loading.showError('im_chat_error_record_time_too_short'.tr);
- state.status.value = SpeechStatus.idle;
- } else {
- _sendFileToServer();
- }
- OLogger.d('stop end ......');
- }
- /// 取消录音监听
- void _cancelRecorderSubscriptions() {
- if (_mRecordingDataSubscription != null) {
- _mRecordingDataSubscription?.cancel();
- _mRecordingDataSubscription = null;
- }
- }
- /// 发送到后台
- Future<void> _sendFileToServer() async {
- state.status.value = SpeechStatus.thinking;
- File file = File(_mPath);
- final ids = await FileAssembleService.to.uploadFileForSpeechAssistant(file);
- if (ids != null) {
- OLogger.d('文件 id ${ids.id}');
- _thinking(fileId: ids.id);
- } else {
- state.status.value = SpeechStatus.idle;
- OLogger.e('上传文件失败!');
- }
- }
- /// 随机命令列表,初始化展示用
- Future<void> _loadInitCommandList() async {
- Map<String, dynamic> body = <String, dynamic>{};
- body['type'] = 'init';
- final result =
- await ProgramCenterService.to.executeScript(ProgramCenterService.to.speechScript(), body);
- var list = result == null ? [] : result as List;
- state.initCommandList.addAll(list.map((e) => e as String).toList());
- }
- Future<void> _thinking({String? fileId, String? inputText}) async {
- Map<String, dynamic> body = <String, dynamic>{};
- body['personId'] = O2ApiManager.instance.o2User?.id ?? '';
- if (fileId != null && fileId.isNotEmpty) {
- body['speechFileId'] = fileId;
- } else if (inputText != null && inputText.isNotEmpty) {
- body['inputText'] = inputText;
- }
- final result =
- await ProgramCenterService.to.executeScript(ProgramCenterService.to.speechScript(), body);
- if (result != null) {
- ImSpeechAssistantResponse res =
- ImSpeechAssistantResponse.fromJson(result);
- if (res.msg == null || res.msg!.isEmpty == true) {
- _answerMsg(res.err!);
- } else {
- res.createTime = DateTime.now();
- res.side = ImSpeechAssistantResponse.rightSide;
- state.chatList.add(res);
- final command = res.command;
- if (command == null) {
- _answerMsg('im_chat_speech_assistant_msg_unknown_command'.tr);
- } else {
- final answer = command.answer ?? '';
- final processId = command.processId ?? '';
- final portalId = command.portalId ?? '';
- final commandName = command.name ?? '';
- if (answer.isNotEmpty) {
- // 有答案 直接回复
- _answerMsg(answer);
- } else if (processId.isNotEmpty) {
- // 有流程 就启动流程工作
- _answerMsg(
- 'im_chat_speech_assistant_msg_open_start_level_process'.tr);
- _startWorkByProcess(processId);
- } else if (portalId.isNotEmpty) {
- _answerMsg('im_chat_speech_assistant_msg_open_app'
- .trArgs([res.msg ?? '']));
- _openPortal(portalId, command.pageId);
- } else if (commandName.isNotEmpty) {
- // 有命令执行对应的命令
- _doCommand(commandName);
- } else if (res.err != null && res.err!.isNotEmpty == true) {
- // 有错误,比如不认识的命令 直接回复
- _answerMsg(res.err!);
- } else {
- _answerMsg('im_chat_speech_assistant_msg_unknown_command'.tr);
- }
- }
- }
- } else {
- Loading.toast('request_error'.tr);
- }
- state.status.value = SpeechStatus.idle;
- OLogger.d('_thinking 完成!');
- }
- // 执行特殊命令,一般是打开某个应用
- _doCommand(String command) {
- switch (command) {
- case 'task':
- _answerMsg('im_chat_speech_assistant_msg_open_app'
- .trArgs([O2NativeAppEnum.task.name]));
- Get.toNamed(O2OARoutes.appTask);
- break;
- case 'taskCompleted':
- _answerMsg('im_chat_speech_assistant_msg_open_app'
- .trArgs([O2NativeAppEnum.taskcompleted.name]));
- Get.toNamed(O2OARoutes.appTaskcompleted);
- break;
- case 'read':
- _answerMsg('im_chat_speech_assistant_msg_open_app'
- .trArgs([O2NativeAppEnum.read.name]));
- Get.toNamed(O2OARoutes.appRead);
- break;
- case 'readCompleted':
- _answerMsg('im_chat_speech_assistant_msg_open_app'
- .trArgs([O2NativeAppEnum.readcompleted.name]));
- Get.toNamed(O2OARoutes.appReadcompleted);
- break;
- case 'startProcess':
- _answerMsg('im_chat_speech_assistant_msg_open_start_process'.tr);
- // 打开启动流程工具
- _startProcess();
- break;
- case 'meeting':
- _answerMsg('im_chat_speech_assistant_msg_open_app'
- .trArgs([O2NativeAppEnum.meeting.name]));
- // 打开会议管理
- MeetingPage.startMeetingApp();
- break;
- case 'createMeeting':
- _answerMsg('im_chat_speech_assistant_msg_open_meeting_create'.tr);
- // 打开创建会议
- MeetingPage.startMeetingApp(openCreate: true);
- break;
- case 'attendance':
- _answerMsg('im_chat_speech_assistant_msg_open_app'
- .trArgs([O2NativeAppEnum.attendance.name]));
- // 打开考勤打卡
- FastCheckInService.instance.stop(); // 进入考勤 先关闭极速打卡
- Get.toNamed(O2OARoutes.appAttendance);
- break;
- case 'bbs': // 论坛
- _answerMsg('im_chat_speech_assistant_msg_open_app'
- .trArgs([O2NativeAppEnum.bbs.name]));
- Get.toNamed(O2OARoutes.appBBS);
- break;
- case 'yunpan': // 网盘
- _answerMsg('im_chat_speech_assistant_msg_open_app'
- .trArgs([O2NativeAppEnum.yunpan.name]));
- Get.toNamed(FileAssembleService.to.isV3()
- ? O2OARoutes.appCloudDiskV3
- : O2OARoutes.appYunpan);
- break;
- case 'cms': // 信息中心
- _answerMsg('im_chat_speech_assistant_msg_open_app'
- .trArgs([O2NativeAppEnum.cms.name]));
- Get.toNamed(O2OARoutes.appCms);
- break;
- case 'calendar': // 信息中心
- _answerMsg('im_chat_speech_assistant_msg_open_app'
- .trArgs([O2NativeAppEnum.calendar.name]));
- Get.toNamed(O2OARoutes.appCalendar);
- break;
- case 'mindMap': // 信息中心
- _answerMsg('im_chat_speech_assistant_msg_open_app'
- .trArgs([O2NativeAppEnum.mindMap.name]));
- Get.toNamed(O2OARoutes.appMindMap);
- break;
- default:
- _answerMsg('im_chat_speech_assistant_msg_unknown_command'.tr);
- break;
- }
- }
- /// 回复信息
- _answerMsg(String msg) {
- state.chatList.add(ImSpeechAssistantResponse(
- msg: msg,
- side: ImSpeechAssistantResponse.leftSide,
- createTime: DateTime.now()));
- if (_ttsOpen) {
- FlutterTTSHelper.instance.speak(msg);
- }
- }
- /// 新建工作
- Future<void> _startProcess() async {
- // 选择启动流程
- var result = await ProcessPickerPage.startPicker(ProcessPickerMode.process);
- if (result != null && result is ProcessData) {
- CreateFormPage.startProcess(true, process: result);
- }
- }
- /// 启动流程,创建工作
- /// 先查询请假流程对象,然后启动流程
- Future<void> _startWorkByProcess(String processId) async {
- if (processId.isEmpty) {
- _answerMsg('im_chat_speech_assistant_msg_not_found_level_process'.tr);
- return;
- }
- final processInfo = await ProcessSurfaceService.to.getProcess(processId);
- if (processInfo == null) {
- _answerMsg('im_chat_speech_assistant_msg_not_found_level_process'.tr);
- return;
- }
- CreateFormPage.startProcess(true, process: processInfo);
- }
- /// 打开门户
- void _openPortal(String portalId, String? pageId) {
- PortalPage.open(portalId, pageId: pageId);
- }
- }
|