From 0c64cf0546bdfc31c43084dc55bacb8e94bbdb81 Mon Sep 17 00:00:00 2001 From: kuaifan Date: Sun, 27 Oct 2024 07:44:32 +0800 Subject: [PATCH] =?UTF-8?q?faet:=20=E6=96=B0=E5=A2=9E=E6=96=87=E6=9C=AC?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E9=95=BF=E6=8C=89=E7=BF=BB=E8=AF=91=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Controllers/Api/DialogController.php | 74 +++++++++++++++++- app/Http/Controllers/Api/SystemController.php | 7 +- app/Models/WebSocketDialogMsgTranslate.php | 36 +++++++++ app/Module/Extranet.php | 53 +++++++++++++ ...web_socket_dialog_msg_translates_table.php | 35 +++++++++ .../manage/components/DialogView/index.vue | 4 +- .../manage/components/DialogView/record.vue | 32 +++++++- .../manage/components/DialogView/text.vue | 23 ++++++ .../pages/manage/components/DialogWrapper.vue | 43 +++++++++- .../setting/components/SystemSetting.vue | 8 ++ resources/assets/js/store/actions.js | 26 +++++- resources/assets/js/store/state.js | 5 +- .../sass/pages/components/dialog-wrapper.scss | 36 +++++---- .../public/css/fonts/taskfont/iconfont.ttf | Bin 93732 -> 92856 bytes .../public/css/fonts/taskfont/iconfont.woff | Bin 50764 -> 50420 bytes .../public/css/fonts/taskfont/iconfont.woff2 | Bin 42748 -> 42488 bytes 16 files changed, 352 insertions(+), 30 deletions(-) create mode 100644 app/Models/WebSocketDialogMsgTranslate.php create mode 100644 database/migrations/2024_10_27_070707_create_web_socket_dialog_msg_translates_table.php diff --git a/app/Http/Controllers/Api/DialogController.php b/app/Http/Controllers/Api/DialogController.php index 355a50d56..0a9f3ec6c 100755 --- a/app/Http/Controllers/Api/DialogController.php +++ b/app/Http/Controllers/Api/DialogController.php @@ -2,12 +2,11 @@ namespace App\Http\Controllers\Api; -use App\Tasks\PushTask; use DB; -use Hhxsv5\LaravelS\Swoole\Task\Task; use Request; use Redirect; use Carbon\Carbon; +use App\Tasks\PushTask; use App\Models\File; use App\Models\User; use App\Module\Base; @@ -20,6 +19,8 @@ use App\Models\WebSocketDialogMsg; use App\Models\WebSocketDialogUser; use App\Models\WebSocketDialogMsgRead; use App\Models\WebSocketDialogMsgTodo; +use App\Models\WebSocketDialogMsgTranslate; +use Hhxsv5\LaravelS\Swoole\Task\Task; /** * @apiDefine dialog @@ -1539,6 +1540,75 @@ class DialogController extends AbstractController return Base::retSuccess("success", $msg); } + /** + * @api {get} api/dialog/msg/translation 31. 翻译消息 + * + * @apiDescription 将文本消息翻译成当前语言,需要token身份 + * @apiVersion 1.0.0 + * @apiGroup dialog + * @apiName msg__translation + * + * @apiParam {Number} msg_id 消息ID + * + * @apiSuccess {Number} ret 返回状态码(1正确、0错误) + * @apiSuccess {String} msg 返回信息(错误描述) + * @apiSuccess {Object} data 返回数据 + */ + public function msg__translation() + { + User::auth(); + // + $msg_id = intval(Request::input("msg_id")); + $language = Base::headerOrInput('language'); + $targetLanguage = match ($language) { + "zh" => "简体中文", + "zh-CHT" => "繁体中文", + "en" => "英语", + "ko" => "韩语", + "ja" => "日语", + "de" => "德语", + "fr" => "法语", + "id" => "印度尼西亚语", + "ru" => "俄语", + default => '', + }; + // + if (empty($targetLanguage)) { + return Base::retError("参数错误"); + } + $msg = WebSocketDialogMsg::whereId($msg_id)->first(); + if (empty($msg)) { + return Base::retError("消息不存在或已被删除"); + } + if (!in_array($msg->type, ['text', 'record'])) { + return Base::retError("此消息不支持翻译"); + } + WebSocketDialog::checkDialog($msg->dialog_id); + // + $row = WebSocketDialogMsgTranslate::whereMsgId($msg_id)->whereLanguage($language)->first(); + if ($row) { + return Base::retSuccess("success", $row->only(['msg_id', 'language', 'content'])); + } + // + $msgData = Base::json2array($msg->getRawOriginal('msg')); + if (empty($msgData['text'])) { + return Base::retError("消息内容为空"); + } + $res = Extranet::openAItranslations($msgData['text'], $targetLanguage); + if (Base::isError($res)) { + return $res; + } + $row = WebSocketDialogMsgTranslate::createInstance([ + 'dialog_id' => $msg->dialog_id, + 'msg_id' => $msg_id, + 'language' => $language, + 'content' => $res['data'], + ]); + $row->save(); + // + return Base::retSuccess("success", $row->only(['msg_id', 'language', 'content'])); + } + /** * @api {get} api/dialog/msg/mark 32. 消息标记操作 * diff --git a/app/Http/Controllers/Api/SystemController.php b/app/Http/Controllers/Api/SystemController.php index cafee868c..fa18026e6 100755 --- a/app/Http/Controllers/Api/SystemController.php +++ b/app/Http/Controllers/Api/SystemController.php @@ -40,7 +40,7 @@ class SystemController extends AbstractController * @apiParam {String} type * - get: 获取(默认) * - all: 获取所有(需要管理员权限) - * - save: 保存设置(参数:['reg', 'reg_identity', 'reg_invite', 'temp_account_alias', 'login_code', 'password_policy', 'project_invite', 'chat_information', 'anon_message', 'voice2text', 'e2e_message', 'auto_archived', 'archived_day', 'task_visible', 'task_default_time', 'all_group_mute', 'all_group_autoin', 'user_private_chat_mute', 'user_group_chat_mute', 'image_compress', 'image_save_local', 'start_home']) + * - save: 保存设置(参数:['reg', 'reg_identity', 'reg_invite', 'temp_account_alias', 'login_code', 'password_policy', 'project_invite', 'chat_information', 'anon_message', 'voice2text', 'translation', 'e2e_message', 'auto_archived', 'archived_day', 'task_visible', 'task_default_time', 'all_group_mute', 'all_group_autoin', 'user_private_chat_mute', 'user_group_chat_mute', 'image_compress', 'image_save_local', 'start_home']) * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) @@ -67,6 +67,7 @@ class SystemController extends AbstractController 'chat_information', 'anon_message', 'voice2text', + 'translation', 'e2e_message', 'auto_archived', 'archived_day', @@ -97,6 +98,9 @@ class SystemController extends AbstractController if ($all['voice2text'] == 'open' && empty(Base::settingFind('aibotSetting', 'openai_key'))) { return Base::retError('开启语音转文字功能需要在应用中开启 ChatGPT AI 机器人。'); } + if ($all['translation'] == 'open' && empty(Base::settingFind('aibotSetting', 'openai_key'))) { + return Base::retError('开启翻译功能需要在应用中开启 ChatGPT AI 机器人。'); + } $setting = Base::setting('system', Base::newTrim($all)); } else { $setting = Base::setting('system'); @@ -118,6 +122,7 @@ class SystemController extends AbstractController $setting['chat_information'] = $setting['chat_information'] ?: 'optional'; $setting['anon_message'] = $setting['anon_message'] ?: 'open'; $setting['voice2text'] = $setting['voice2text'] ?: 'close'; + $setting['translation'] = $setting['translation'] ?: 'close'; $setting['e2e_message'] = $setting['e2e_message'] ?: 'close'; $setting['auto_archived'] = $setting['auto_archived'] ?: 'close'; $setting['archived_day'] = floatval($setting['archived_day']) ?: 7; diff --git a/app/Models/WebSocketDialogMsgTranslate.php b/app/Models/WebSocketDialogMsgTranslate.php new file mode 100644 index 000000000..c9c03e359 --- /dev/null +++ b/app/Models/WebSocketDialogMsgTranslate.php @@ -0,0 +1,36 @@ +timestamps = false; + } +} diff --git a/app/Module/Extranet.php b/app/Module/Extranet.php index 1ac39f22f..f1ff7518c 100644 --- a/app/Module/Extranet.php +++ b/app/Module/Extranet.php @@ -55,6 +55,59 @@ class Extranet return Base::retSuccess("success", $resData['text']); } + /** + * 通过 openAI 翻译 + * @param $text + * @param $targetLanguage + * @return array + */ + public static function openAItranslations($text, $targetLanguage) + { + $systemSetting = Base::setting('system'); + $aibotSetting = Base::setting('aibotSetting'); + if ($systemSetting['translation'] !== 'open' || empty($aibotSetting['openai_key'])) { + return Base::retError("翻译功能未开启"); + } + $extra = [ + 'Content-Type' => 'application/json', + 'Authorization' => 'Bearer ' . $aibotSetting['openai_key'], + ]; + if ($aibotSetting['openai_agency']) { + $extra['CURLOPT_PROXY'] = $aibotSetting['openai_agency']; + if (str_contains($aibotSetting['openai_agency'], 'socks')) { + $extra['CURLOPT_PROXYTYPE'] = CURLPROXY_SOCKS5; + } else { + $extra['CURLOPT_PROXYTYPE'] = CURLPROXY_HTTP; + } + } + $res = Ihttp::ihttp_request('https://api.openai.com/v1/chat/completions', json_encode([ + "model" => "gpt-3.5-turbo", + "messages" => [ + [ + "role" => "system", + "content" => "你是一个专业的翻译器,翻译的结果尽量符合“项目任务管理系统”的使用,并且翻译的结果不用额外添加换行尽量保持原格式,将提供的文本翻译成“{$targetLanguage}”语言。" + ], + [ + "role" => "user", + "content" => $text + ] + ] + ]), $extra, 15); + if (Base::isError($res)) { + return Base::retError("翻译失败", $res); + } + $resData = Base::json2array($res['data']); + if (empty($resData['choices'])) { + return Base::retError("翻译失败", $resData); + } + $result = $resData['choices'][0]['message']['content']; + $result = preg_replace('/^\"|\"$/', '', $result); + if (empty($result)) { + return Base::retError("翻译失败", $result); + } + return Base::retSuccess("success", $result); + } + /** * 获取IP地址经纬度 * @param string $ip diff --git a/database/migrations/2024_10_27_070707_create_web_socket_dialog_msg_translates_table.php b/database/migrations/2024_10_27_070707_create_web_socket_dialog_msg_translates_table.php new file mode 100644 index 000000000..feef35d32 --- /dev/null +++ b/database/migrations/2024_10_27_070707_create_web_socket_dialog_msg_translates_table.php @@ -0,0 +1,35 @@ +bigIncrements('id'); + $table->bigInteger('dialog_id')->nullable()->default(0)->comment('对话ID'); + $table->bigInteger('msg_id')->nullable()->default(0)->comment('消息ID'); + $table->string('language', 50)->nullable()->default('')->comment('语言'); + $table->longText('content')->nullable()->comment('翻译内容'); + $table->index(['msg_id', 'language']); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('web_socket_dialog_msg_translates'); + } +} diff --git a/resources/assets/js/pages/manage/components/DialogView/index.vue b/resources/assets/js/pages/manage/components/DialogView/index.vue index 1de76c3a5..7f29e6f94 100644 --- a/resources/assets/js/pages/manage/components/DialogView/index.vue +++ b/resources/assets/js/pages/manage/components/DialogView/index.vue @@ -26,11 +26,11 @@
- + - + diff --git a/resources/assets/js/pages/manage/components/DialogView/record.vue b/resources/assets/js/pages/manage/components/DialogView/record.vue index 6e4b03db8..d561b60ef 100644 --- a/resources/assets/js/pages/manage/components/DialogView/record.vue +++ b/resources/assets/js/pages/manage/components/DialogView/record.vue @@ -4,21 +4,45 @@
{{recordDuration(msg.duration)}}
-
- {{msg.text}} -
+ + + +