From 0b86fa7beebfe00f6679f9f9b56b9df60db57a5a Mon Sep 17 00:00:00 2001 From: kuaifan Date: Fri, 25 Jul 2025 11:17:12 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20=E6=9C=BA=E5=99=A8=E4=BA=BA=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E6=96=B0=E4=BC=9A=E8=AF=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Controllers/Api/DialogController.php | 11 +--- app/Http/Controllers/Api/UsersController.php | 10 ++- app/Models/User.php | 14 ++-- app/Models/UserBot.php | 64 +++++++++++-------- app/Models/WebSocketDialog.php | 25 +++++++- app/Tasks/BotReceiveMsgTask.php | 4 +- .../DialogView/template/bot-api.vue | 1 + .../pages/manage/components/DialogWrapper.vue | 10 --- .../manage/components/Forwarder/confirm.vue | 8 +-- 9 files changed, 85 insertions(+), 62 deletions(-) diff --git a/app/Http/Controllers/Api/DialogController.php b/app/Http/Controllers/Api/DialogController.php index cd16421d2..599e8e832 100755 --- a/app/Http/Controllers/Api/DialogController.php +++ b/app/Http/Controllers/Api/DialogController.php @@ -3291,15 +3291,7 @@ class DialogController extends AbstractController $dialog = WebSocketDialog::checkDialog($dialog_id); } // - if ($dialog->type != 'user') { - return Base::retError('当前对话不支持'); - } - // - $hasAiUser = WebSocketDialogUser::join('users as u', 'web_socket_dialog_users.userid', '=', 'u.userid') - ->where('dialog_id', $dialog->id) - ->where('u.email', 'like', 'ai-%@bot.system') - ->exists(); - if (!$hasAiUser) { + if (!$dialog->isSessionDialog()) { return Base::retError('当前对话不支持'); } // @@ -3312,7 +3304,6 @@ class DialogController extends AbstractController // $session = WebSocketDialogSession::create([ 'dialog_id' => $dialog->id, - 'status' => 1, 'title' => '', ]); $session->save(); diff --git a/app/Http/Controllers/Api/UsersController.php b/app/Http/Controllers/Api/UsersController.php index 3e9444a49..f7cf0538d 100755 --- a/app/Http/Controllers/Api/UsersController.php +++ b/app/Http/Controllers/Api/UsersController.php @@ -376,7 +376,7 @@ class UsersController extends AbstractController public function info__departments() { $user = User::auth(); - + // 获取部门列表 $list = UserDepartment::select(['id', 'owner_userid', 'parent_id', 'name']) ->whereIn('id', $user->department) @@ -2103,6 +2103,11 @@ class UsersController extends AbstractController * @apiParam {Number} [id] 机器人ID(编辑时必填,留空为添加) * @apiParam {String} [name] 机器人名称 * @apiParam {String} [avatar] 机器人头像 + * @apiParam {Number} [session] 开启新会话功能(仅 我的机器人) + * - 1:开启、0:关闭, 默认:0 + * - 此参数仅在添加机器人时有效 + * - 开启后,机器人对话窗口会出现新会话菜单和历史会话菜单 + * - 开启后,webhook_url 消息会多一个 session_id 字段 * @apiParam {Number} [clear_day] 清理天数(仅 我的机器人) * @apiParam {String} [webhook_url] Webhook地址(仅 我的机器人) * @@ -2115,8 +2120,9 @@ class UsersController extends AbstractController $user = User::auth(); // $botId = intval(Request::input('id')); + $session = intval(Request::input('session')); if (empty($botId)) { - $res = UserBot::newbot($user->userid, trim(Request::input('name'))); + $res = UserBot::newBot($user->userid, trim(Request::input('name')), (bool)$session); if (Base::isError($res)) { return $res; } diff --git a/app/Models/User.php b/app/Models/User.php index 719cf2c8d..ad7694d42 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -174,10 +174,9 @@ class User extends AbstractModel return UserDepartment::where('owner_userid', $this->userid)->exists(); } - /** * 获取机器人所有者 - * @return int|mixed + * @return int */ public function getBotOwner() { @@ -782,18 +781,17 @@ class User extends AbstractModel /** * 是否机器人 * @param $userid - * @return bool|mixed + * @return bool */ public static function isBot($userid) { if (empty($userid)) { return false; } - $userid = intval($userid); - if (RequestContext::has("isBot_" . $userid)) { - return RequestContext::get("isBot_" . $userid); - } - return (bool)User::find($userid)?->bot; + // 这个不会有变化,所以可以使用永久缓存 + return (bool)Cache::rememberForever('is-bot-user-' . $userid, function () use ($userid) { + return (bool)User::find($userid)?->bot; + }); } /** diff --git a/app/Models/UserBot.php b/app/Models/UserBot.php index 4d07fd5e5..87623d927 100644 --- a/app/Models/UserBot.php +++ b/app/Models/UserBot.php @@ -180,28 +180,8 @@ class UserBot extends AbstractModel ]; default: - if (preg_match('/^ai-(.*?)@bot\.system$/', $email, $match)) { - if (!Base::judgeClientVersion('0.42.62')) { - return [ - 'key' => '%3A.clear', - 'label' => Doo::translate('清空上下文') - ]; - } - $aibotSetting = Base::setting('aibotSetting'); - $aibotModel = $aibotSetting[$match[1] . '_model']; - $aibotModels = Setting::AIModels2Array($aibotSetting[$match[1] . '_models']); - if (empty($aibotModels)) { - return []; - } - return [ - [ - 'key' => '~ai-model-select', - 'label' => Doo::translate('选择模型'), - 'config' => [ - 'model' => $aibotModel, - 'models' => $aibotModels - ] - ], + if (preg_match('/^(ai-|user-session-)(.*?)@bot\.system$/', $email, $match)) { + $menus = [ [ 'key' => '~ai-session-create', 'label' => Doo::translate('开启新会话'), @@ -211,6 +191,27 @@ class UserBot extends AbstractModel 'label' => Doo::translate('历史会话'), ] ]; + if ($match[1] === "ai-") { + $aibotSetting = Base::setting('aibotSetting'); + $aibotModel = $aibotSetting[$match[1] . '_model']; + $aibotModels = Setting::AIModels2Array($aibotSetting[$match[1] . '_models']); + if ($aibotModels) { + $menus = array_merge( + [ + [ + 'key' => '~ai-model-select', + 'label' => Doo::translate('选择模型'), + 'config' => [ + 'model' => $aibotModel, + 'models' => $aibotModels + ] + ] + ], + $menus + ); + } + } + return $menus; } return []; } @@ -445,11 +446,12 @@ class UserBot extends AbstractModel /** * 创建我的机器人 - * @param $userid - * @param $botName + * @param int $userid 创建人userid + * @param string $botName 机器人名称 + * @param bool $sessionSupported 是否支持会话 * @return array */ - public static function newbot($userid, $botName) + public static function newBot($userid, $botName, $sessionSupported = false) { if (User::select(['users.*']) ->join('user_bots', 'users.userid', '=', 'user_bots.bot_id') @@ -461,7 +463,8 @@ class UserBot extends AbstractModel if (strlen($botName) < 2 || strlen($botName) > 20) { return Base::retError("机器人名称由2-20个字符组成。"); } - $data = User::botGetOrCreate("user-" . Base::generatePassword(), [ + $botType = ($sessionSupported ? "user-session-" : "user-normal-") . Base::generatePassword(); + $data = User::botGetOrCreate($botType, [ 'nickname' => $botName ], $userid); if (empty($data)) { @@ -469,6 +472,15 @@ class UserBot extends AbstractModel } $dialog = WebSocketDialog::checkUserDialog($data, $userid); if ($dialog) { + if ($sessionSupported) { + $dialogSession = WebSocketDialogSession::create([ + 'dialog_id' => $dialog->id, + 'title' => 'Default', + ]); + $dialogSession->save(); + $dialog->session_id = $dialogSession->id; + $dialog->save(); + } WebSocketDialogMsg::sendMsg(null, $dialog->id, 'template', [ 'type' => '/hello', 'title' => '创建成功。', diff --git a/app/Models/WebSocketDialog.php b/app/Models/WebSocketDialog.php index 25b71f94d..779a64108 100644 --- a/app/Models/WebSocketDialog.php +++ b/app/Models/WebSocketDialog.php @@ -705,6 +705,27 @@ class WebSocketDialog extends AbstractModel return WebSocketDialogUser::whereDialogId($this->id)->where('userid', '>', 0)->count() === 1; } + /** + * 检查是否支持创建会话 + * @return bool + */ + public function isSessionDialog() + { + // 这个不会有变化,所以可以使用永久缓存 + return Cache::rememberForever('is-session-dialog-' . $this->id, function () { + if ($this->type !== 'user') { + return false; + } + $data = $this->dialogUserBuilder()->get(); + foreach ($data as $item) { + if (preg_match('/^(ai-|user-session-)(.*?)@bot\.system$/', $item->email)) { + return true; + } + } + return false; + }); + } + /** * 检查是否是AI对话 * @return bool @@ -799,6 +820,7 @@ class WebSocketDialog extends AbstractModel WebSocketDialogUser::createInstance([ 'dialog_id' => $dialog->id, 'userid' => $value, + 'bot' => User::isBot($value) ? 1 : 0, 'important' => !in_array($group_type, ['user', 'all']), 'last_at' => in_array($group_type, ['user', 'department', 'all']) ? Carbon::now() : null, ])->save(); @@ -835,16 +857,17 @@ class WebSocketDialog extends AbstractModel WebSocketDialogUser::createInstance([ 'dialog_id' => $dialog->id, 'userid' => $user->userid, + 'bot' => User::isBot($user->userid) ? 1 : 0, ])->save(); WebSocketDialogUser::createInstance([ 'dialog_id' => $dialog->id, 'userid' => $receiver, + 'bot' => User::isBot($receiver) ? 1 : 0, ])->save(); // if ($user->isAiBot() || User::find($receiver)?->isAiBot()) { $session = WebSocketDialogSession::create([ 'dialog_id' => $dialog->id, - 'status' => 1, 'title' => '', ]); $session->save(); diff --git a/app/Tasks/BotReceiveMsgTask.php b/app/Tasks/BotReceiveMsgTask.php index af85a5dad..5ab35edde 100644 --- a/app/Tasks/BotReceiveMsgTask.php +++ b/app/Tasks/BotReceiveMsgTask.php @@ -205,7 +205,7 @@ class BotReceiveMsgTask extends AbstractTask * 创建 */ case '/newbot': - $res = UserBot::newbot($msg->userid, $array[1]); + $res = UserBot::newBot($msg->userid, $array[1]); if (Base::isError($res)) { $content = $res['msg']; } else { @@ -381,6 +381,7 @@ class BotReceiveMsgTask extends AbstractTask default => '不支持的指令', }; if ($type == '/api') { + $msgData['email'] = $botUser->email; $msgData['version'] = Base::getVersion(); } elseif ($type == '/help') { $msgData['manager'] = $isManager; @@ -522,6 +523,7 @@ class BotReceiveMsgTask extends AbstractTask 'text' => $command, 'reply_text' => $replyText, 'token' => User::generateToken($botUser), + 'session_id' => $dialog->session_id, 'dialog_id' => $dialog->id, 'dialog_type' => $dialog->type, 'msg_id' => $msg->id, diff --git a/resources/assets/js/pages/manage/components/DialogView/template/bot-api.vue b/resources/assets/js/pages/manage/components/DialogView/template/bot-api.vue index 7f1839e5a..48a1f61df 100644 --- a/resources/assets/js/pages/manage/components/DialogView/template/bot-api.vue +++ b/resources/assets/js/pages/manage/components/DialogView/template/bot-api.vue @@ -20,6 +20,7 @@

text: {{$L("消息文本")}}

reply_text: {{$L("回复/引用消息文本")}}

token: {{$L("机器人Token")}}

+

session_id: {{$L("会话ID")}}

dialog_id: {{$L("对话ID")}}

dialog_type: {{$L("对话类型")}}

msg_id: {{$L("消息ID")}}

diff --git a/resources/assets/js/pages/manage/components/DialogWrapper.vue b/resources/assets/js/pages/manage/components/DialogWrapper.vue index c8d1502a7..6c7f2f78b 100644 --- a/resources/assets/js/pages/manage/components/DialogWrapper.vue +++ b/resources/assets/js/pages/manage/components/DialogWrapper.vue @@ -1917,13 +1917,6 @@ export default { // 开启新会话 case "~ai-session-create": - if (!this.isAiBot) { - return - } - // 清理会话本地缓存 - this.$store.dispatch("clearDialogMsgs", { - id: this.dialogId - }); // 创建新会话 this.$store.dispatch("call", { url: 'dialog/session/create', @@ -1940,9 +1933,6 @@ export default { // 历史会话 case "~ai-session-history": - if (!this.isAiBot) { - return - } this.sessionHistoryData = { dialog_id: this.dialogId, name: this.dialogData.name, diff --git a/resources/assets/js/pages/manage/components/Forwarder/confirm.vue b/resources/assets/js/pages/manage/components/Forwarder/confirm.vue index 39975bd9b..fbadb3d08 100644 --- a/resources/assets/js/pages/manage/components/Forwarder/confirm.vue +++ b/resources/assets/js/pages/manage/components/Forwarder/confirm.vue @@ -250,10 +250,6 @@ export default { const processQueue = async () => { try { for (const user of this.aiUser) { - // 清理会话本地缓存 - this.$store.dispatch("clearDialogMsgs", { - id: this.dialogId - }); // 创建新会话 await this.$store.dispatch("call", { url: 'dialog/session/create', @@ -261,6 +257,10 @@ export default { userid: user.userid, }, }); + // 清理会话本地缓存 + await this.$store.dispatch("clearDialogMsgs", { + id: this.dialogId + }); } resolve(); } catch (error) {