From aa3c07a0b236599ac07138d58f92d4356119a38c Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Sat, 25 Oct 2025 17:41:20 +0800 Subject: [PATCH] =?UTF-8?q?ai=E5=AF=B9=E8=AF=9D=E5=8A=9F=E8=83=BD=E6=8F=90?= =?UTF-8?q?=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Server/application/ai/config/route.php | 4 - Server/application/ai/controller/CozeAI.php | 370 ++++++++++++ Server/application/ai/controller/OpenAi.php | 2 +- .../chukebao/controller/AiChatController.php | 449 +++++++++++++- .../chukebao/model/AiKnowledgeBase.php | 52 ++ .../chukebao/model/AiKnowledgeBaseType.php | 57 ++ .../application/chukebao/model/AiSettings.php | 17 + Server/application/cunkebao/config/route.php | 20 + .../controller/AiKnowledgeBaseController.php | 570 ++++++++++++++++++ .../controller/AiSettingsController.php | 344 +++++++++++ .../cunkebao/model/AiKnowledgeBase.php | 11 + .../cunkebao/model/AiKnowledgeBaseType.php | 11 + 12 files changed, 1883 insertions(+), 24 deletions(-) create mode 100644 Server/application/ai/controller/CozeAI.php create mode 100644 Server/application/chukebao/model/AiKnowledgeBase.php create mode 100644 Server/application/chukebao/model/AiKnowledgeBaseType.php create mode 100644 Server/application/chukebao/model/AiSettings.php create mode 100644 Server/application/cunkebao/controller/AiKnowledgeBaseController.php create mode 100644 Server/application/cunkebao/controller/AiSettingsController.php create mode 100644 Server/application/cunkebao/model/AiKnowledgeBase.php create mode 100644 Server/application/cunkebao/model/AiKnowledgeBaseType.php diff --git a/Server/application/ai/config/route.php b/Server/application/ai/config/route.php index 286b7c24..4fd9f4f6 100644 --- a/Server/application/ai/config/route.php +++ b/Server/application/ai/config/route.php @@ -15,8 +15,4 @@ Route::group('v1/ai', function () { Route::group('doubao', function () { Route::post('text', 'app\ai\controller\DouBaoAI@text'); }); - - - - })->middleware(['jwt']); \ No newline at end of file diff --git a/Server/application/ai/controller/CozeAI.php b/Server/application/ai/controller/CozeAI.php new file mode 100644 index 00000000..9d9bf150 --- /dev/null +++ b/Server/application/ai/controller/CozeAI.php @@ -0,0 +1,370 @@ +apiUrl = Env::get('cozeAi.api_url'); + $this->accessToken = Env::get('cozeAi.token'); + + if (empty($this->accessToken) || empty($this->apiUrl)) { + return json_encode(['code' => 500, 'msg' => '参数缺失']); + } + + // 设置请求头 + $this->headers = [ + 'Authorization: Bearer ' . $this->accessToken, + 'Content-Type: application/json' + ]; + } + + + /** + * 创建智能体 + * @param $data + * @return false|string|\think\response\Json + */ + public function createBot($data = []) + { + $space_id = Env::get('cozeAi.space_id'); + $name = !empty($data['name']) ? $data['name'] : ''; + $model_id = !empty($data['model_id']) ? $data['model_id'] : ''; + $prompt_info = !empty($data['prompt_info']) ? $data['prompt_info'] : ''; + $plugin_id_list = [ + 'id_list' => [ + ['api_id' => '7362852017859035163', 'plugin_id' => '7362852017859018779'], + ['api_id' => '7472045461050851367', 'plugin_id' => '7472045461050834983'], + ] + ]; + if (empty($name)) { + return json_encode(['code' => 500, 'msg' => '参数缺失']); + } + + $model_info_config = [ + 'model_id' => (string)$model_id, + ]; + + $params = [ + 'space_id' => $space_id, + 'name' => $name, + 'model_info_config' => (object)$model_info_config, + 'plugin_id_list' => (object)$plugin_id_list + ]; + + if (!empty($prompt_info)){ + $new_prompt_info = [ + 'prompt' => $prompt_info + ]; + $params['prompt_info'] = (object) $new_prompt_info; + } + + $result = requestCurl($this->apiUrl . '/v1/bot/create', $params, 'POST', $this->headers, 'json'); + $result = json_decode($result, true); + if ($result['code'] != 0) { + return errorJson($result['msg'], $result['code']); + } + + return json_encode(['code' => 200, 'msg' => '创建成功', 'data' => $result['data']]); + } + + + /** + * 创建智能体 + * @param $data + * @return false|string|\think\response\Json + */ + public function updateBot($data = []) + { + $space_id = Env::get('cozeAi.space_id'); + $bot_id = !empty($data['bot_id']) ? $data['bot_id'] : ''; + $name = !empty($data['name']) ? $data['name'] : ''; + $model_id = !empty($data['model_id']) ? $data['model_id'] : ''; + $prompt_info = !empty($data['prompt_info']) ? $data['prompt_info'] : ''; + $dataset_ids = !empty($data['dataset_ids']) ? $data['dataset_ids'] : ''; + $plugin_id_list = [ + 'id_list' => [ + ['api_id' => '7362852017859035163', 'plugin_id' => '7362852017859018779'], + ['api_id' => '7472045461050851367', 'plugin_id' => '7472045461050834983'], + ] + ]; + if (empty($name) || empty($bot_id)) { + return json_encode(['code' => 500, 'msg' => '参数缺失']); + } + + $model_info_config = [ + 'model_id' => (string)$model_id, + ]; + + $params = [ + 'bot_id' => $bot_id, + 'space_id' => $space_id, + 'name' => $name, + 'model_info_config' => (object)$model_info_config, + 'plugin_id_list' => (object)$plugin_id_list + ]; + + + if (!empty($prompt_info)){ + $new_prompt_info = [ + 'prompt' => $prompt_info + ]; + $params['prompt_info'] = (object) $new_prompt_info; + } + + if (!empty($dataset_ids)){ + $knowledge = [ + 'dataset_ids' => $dataset_ids + ]; + $params['knowledge'] = (object) $knowledge; + } + + $result = requestCurl($this->apiUrl . '/v1/bot/update', $params, 'POST', $this->headers, 'json'); + $result = json_decode($result, true); + if ($result['code'] != 0) { + return errorJson($result['msg'], $result['code']); + } + return json_encode(['code' => 200, 'msg' => '获取成功']); + } + + + /** + * 发布智能体 + * @param $data + * @return false|string|\think\response\Json + */ + public function botPublish($data = []) + { + $bot_id = !empty($data['bot_id']) ? $data['bot_id'] : ''; + $connector_ids = ['1024', '999']; + if (empty($bot_id) || empty($connector_ids)) { + return json_encode(['code' => 500, 'msg' => '参数缺失']); + } + + $params = [ + 'bot_id' => $bot_id, + 'connector_ids' => $connector_ids, + ]; + $result = requestCurl($this->apiUrl . '/v1/bot/publish', $params, 'POST', $this->headers, 'json'); + $result = json_decode($result, true); + if ($result['code'] != 0) { + return errorJson($result['msg'], $result['code']); + } + return json_encode(['code' => 200, 'msg' => '发布成功']); + } + + + /** + * 创建知识库 + * @param $data + * @return false|string|\think\response\Json + */ + public function createKnowledge($data = []) + { + + $space_id = Env::get('cozeAi.space_id'); + $name = !empty($data['name']) ? $data['name'] : ''; + if (empty($space_id) || empty($name)) { + return json_encode(['code' => 500, 'msg' => '参数缺失']); + } + + $params = [ + 'space_id' => $space_id, + 'format_type' => 0, + 'name' => $name, + ]; + $result = requestCurl($this->apiUrl . '/v1/datasets', $params, 'POST', $this->headers, 'json'); + $result = json_decode($result, true); + + if ($result['code'] != 0) { + return errorJson($result['msg'], $result['code']); + } + return json_encode(['code' => 200, 'msg' => '创建成功','data' => $result['data']]); + } + + + public function createDocument($data = []) + { + // 文件路径 + $filePath = !empty($data['filePath']) ? $data['filePath'] : ''; + $fileName = !empty($data['fileName']) ? $data['fileName'] : ''; + if (empty($filePath)) { + return json_encode(['code' => 500, 'msg' => '参数缺失']); + } + // 读取文件内容 + $fileContent = file_get_contents($filePath); + // 将文件内容编码为Base64 + $base64EncodedContent = base64_encode($fileContent); + + + $dataset_id = !empty($data['dataset_id']) ? $data['dataset_id'] : ''; + + $document_bases = [ + ['name' => $fileName,'source_info' => ['file_base64' => $base64EncodedContent]] + ]; + + $chunk_strategy = [ + 'chunk_type' => 0, + 'remove_extra_spaces' => true + ]; + $params = [ + 'dataset_id' => (string) $dataset_id, + 'document_bases' => $document_bases, + 'chunk_strategy' => (object) $chunk_strategy, + 'format_type' => 0 + ]; + $headers = array_merge($this->headers, ['Agw-Js-Conv: str']); + $result = requestCurl($this->apiUrl . '/open_api/knowledge/document/create', $params, 'POST', $headers, 'json'); + $result = json_decode($result, true); + if ($result['code'] != 0) { + return errorJson($result['msg'], $result['code']); + } + return json_encode(['code' => 200, 'msg' => '创建成功','data' => $result['document_infos']]); + } + + + /** + * 删除知识库文件 + * @param $data + * @return false|string|\think\response\Json + */ + public function deleteDocument($data = []) + { + if (empty($data)) { + return json_encode(['code' => 500, 'msg' => '参数缺失']); + } + $params = [ + 'document_ids' => $data, + ]; + $headers = array_merge($this->headers, ['Agw-Js-Conv: str']); + $result = requestCurl($this->apiUrl . '/open_api/knowledge/document/delete', $params, 'POST', $headers, 'json'); + $result = json_decode($result, true); + if ($result['code'] != 0) { + return errorJson($result['msg'], $result['code']); + } + return json_encode(['code' => 200, 'msg' => '删除成功']); + + } + + + /** + * 创建会话 + * @param $data + * @return false|string|\think\response\Json + */ + public function createConversation($data = []) + { + $bot_id = !empty($data['bot_id']) ? $data['bot_id'] : ''; + $name = !empty($data['name']) ? $data['name'] : ''; + $meta_data = !empty($data['meta_data']) ? $data['meta_data'] : []; + + if (empty($bot_id) || empty($name)) { + return json_encode(['code' => 500, 'msg' => '参数缺失']); + } + $params = [ + 'bot_id' => $bot_id, + 'name' => $name, + ]; + if (!empty($meta_data)){ + $params['meta_data'] = $meta_data; + } + $result = requestCurl($this->apiUrl . '/v1/conversation/create', $params, 'POST', $this->headers, 'json'); + $result = json_decode($result, true); + if ($result['code'] != 0) { + return errorJson($result['msg'], $result['code']); + } + return json_encode(['code' => 200, 'msg' => '创建成功','data' => $result['data']]); + } + + + /** + * 开始对话 + * @param $data + * @return false|string|\think\response\Json + */ + public function createChat($data = []) + { + try { + $bot_id = !empty($data['bot_id']) ? $data['bot_id'] : ''; + $uid = !empty($data['uid']) ? $data['uid'] : ''; + $conversation_id = !empty($data['conversation_id']) ? $data['conversation_id'] : ''; + $question = !empty($data['question']) ? $data['question'] : []; + + + if(empty($bot_id)){ + return errorJson('智能体ID不能为空'); + } + + if(empty($conversation_id)){ + return errorJson('会话ID不能为空'); + } + + if(empty($question)){ + return errorJson('问题不能为空'); + } + + // 构建请求数据 + $params = [ + 'bot_id' => strval($bot_id), + 'user_id' => strval($uid), + 'additional_messages' => $question, + 'stream' => false, + 'auto_save_history' => true + ]; + + $url = $this->apiUrl . '/v3/chat?conversation_id='.$conversation_id; + $result = requestCurl($url, $params, 'POST', $this->headers, 'json'); + $result = json_decode($result, true); + if ($result['code'] != 0) { + return errorJson($result['msg'], $result['code']); + } + return json_encode(['code' => 200, 'msg' => '发送成功','data' => $result['data']]); + + } catch (\Exception $e) { + return errorJson('创建对话失败:' . $e->getMessage()); + } + } + + + public function getConversationChat($data = []) + { + $conversation_id = !empty($data['conversation_id']) ? $data['conversation_id'] : ''; + $chat_id = !empty($data['chat_id']) ? $data['chat_id'] : ''; + $url = $this->apiUrl . '/v3/chat/retrieve?conversation_id='.$conversation_id.'&chat_id='.$chat_id; + $result = requestCurl($url, [], 'GET', $this->headers, 'json'); + $result = json_decode($result, true); + if ($result['code'] != 0) { + return errorJson($result['msg'], $result['code']); + } + return json_encode(['code' => 200, 'msg' => '发送成功','data' => $result['data']]); + } + + + public function listConversationMessage($data = []) + { + $conversation_id = !empty($data['conversation_id']) ? $data['conversation_id'] : ''; + $chat_id = !empty($data['chat_id']) ? $data['chat_id'] : ''; + $url = $this->apiUrl . '/v3/chat/message/list?conversation_id='.$conversation_id.'&chat_id='.$chat_id; + $result = requestCurl($url, [], 'GET', $this->headers, 'json'); + $result = json_decode($result, true); + if ($result['code'] != 0) { + return errorJson($result['msg'], $result['code']); + } + return json_encode(['code' => 200, 'msg' => '发送成功','data' => $result['data']]); + } + +} \ No newline at end of file diff --git a/Server/application/ai/controller/OpenAi.php b/Server/application/ai/controller/OpenAi.php index 3c4f3db4..b71491f6 100644 --- a/Server/application/ai/controller/OpenAi.php +++ b/Server/application/ai/controller/OpenAi.php @@ -3,7 +3,7 @@ namespace app\ai\controller; use think\facade\Env; -class OpenAi +class OpenAI { protected $apiUrl; protected $apiKey; diff --git a/Server/application/chukebao/controller/AiChatController.php b/Server/application/chukebao/controller/AiChatController.php index 75d0416e..919204f0 100644 --- a/Server/application/chukebao/controller/AiChatController.php +++ b/Server/application/chukebao/controller/AiChatController.php @@ -2,37 +2,448 @@ namespace app\chukebao\controller; +use app\ai\controller\CozeAI; use app\ai\controller\DouBaoAI; +use app\api\model\WechatFriendModel; use app\chukebao\controller\TokensRecordController as tokensRecord; +use app\chukebao\model\AiSettings; +use app\chukebao\model\FriendSettings; use app\chukebao\model\TokensCompany; use library\ResponseHelper; use think\Db; +/** + * AI聊天控制器 + * 负责处理与好友的AI对话功能 + */ class AiChatController extends BaseController { - public function index(){ + // 对话状态常量 + const STATUS_CREATED = 'created'; // 对话已创建 + const STATUS_IN_PROGRESS = 'in_progress'; // 智能体正在处理中 + const STATUS_COMPLETED = 'completed'; // 智能体已完成处理 + const STATUS_FAILED = 'failed'; // 对话失败 + const STATUS_REQUIRES_ACTION = 'requires_action'; // 对话中断,需要进一步处理 + const STATUS_CANCELED = 'canceled'; // 对话已取消 + + // 轮询配置 + const MAX_RETRY_TIMES = 30; // 最大重试次数 + const RETRY_INTERVAL = 2; // 重试间隔(秒) + + /** + * AI聊天主入口 + * + * @return \think\response\Json + */ + public function index() + { + try { + // 1. 参数验证和初始化 + $params = $this->validateAndInitParams(); + if ($params === false) { + return ResponseHelper::error('参数验证失败'); + } + + // 2. 验证Tokens余额 + if (!$this->checkTokensBalance($params['companyId'])) { + return ResponseHelper::error('Tokens余额不足,请充值后再试'); + } + + // 3. 获取AI配置 + $setting = $this->getAiSettings($params['companyId']); + if (!$setting) { + return ResponseHelper::error('未找到AI配置信息,请先配置AI策略'); + } + + // 4. 获取好友AI设置 + $friendSettings = $this->getFriendSettings($params['companyId'], $params['friendId']); + if (!$friendSettings) { + return ResponseHelper::error('该好友未配置或未开启AI功能'); + } + + // 5. 确保会话存在 + $conversationId = $this->ensureConversation($friendSettings, $setting, $params); + if (!$conversationId) { + return ResponseHelper::error('创建会话失败'); + } + + // 6. 获取历史消息 + $msgData = $this->getHistoryMessages($params['friendId'], $friendSettings); + + // 7. 创建AI对话 + $chatId = $this->createAiChat($setting, $friendSettings, $msgData); + if (!$chatId) { + return ResponseHelper::error('创建对话失败'); + } + + // 8. 等待AI处理完成(轮询) + $chatResult = $this->waitForChatCompletion($conversationId, $chatId); + if (!$chatResult) { + return ResponseHelper::error('AI处理超时或失败'); + } + + // 9. 扣除Tokens + $this->consumeTokens($chatResult, $params, $friendSettings); + + // 10. 获取对话消息 + $messages = $this->getChatMessages($conversationId, $chatId); + if (!$messages) { + return ResponseHelper::error('获取对话消息失败'); + } + return ResponseHelper::success($messages[1]['content'], '对话成功'); + } catch (\Exception $e) { + \think\facade\Log::error('AI聊天异常:' . $e->getMessage()); + return ResponseHelper::error('系统异常:' . $e->getMessage()); + } + } + + /** + * 验证和初始化参数 + * + * @return array|false + */ + private function validateAndInitParams() + { $userId = $this->getUserInfo('id'); $companyId = $this->getUserInfo('companyId'); - $friendId = $this->request->param('friendId', ''); - $wechatAccountId = $this->request->param('wechatAccountId', ''); - $content = $this->request->param('content', ''); + $friendId = $this->request->param('friendId', ''); + $wechatAccountId = $this->request->param('wechatAccountId', ''); - if (empty($wechatAccountId) || empty($friendId)){ + if (empty($wechatAccountId) || empty($friendId)) { + return false; + } + + return [ + 'userId' => $userId, + 'companyId' => $companyId, + 'friendId' => $friendId, + 'wechatAccountId' => $wechatAccountId + ]; + } + + /** + * 检查Tokens余额 + * + * @param int $companyId 公司ID + * @return bool + */ + private function checkTokensBalance($companyId) + { + $tokens = TokensCompany::where(['companyId' => $companyId])->value('tokens'); + return !empty($tokens) && $tokens > 1000; + } + + /** + * 获取AI配置 + * + * @param int $companyId 公司ID + * @return AiSettings|null + */ + private function getAiSettings($companyId) + { + return AiSettings::where(['companyId' => $companyId])->find(); + } + + /** + * 获取好友AI设置 + * + * @param int $companyId 公司ID + * @param string $friendId 好友ID + * @return FriendSettings|null + */ + private function getFriendSettings($companyId, $friendId) + { + $friendSettings = FriendSettings::where([ + 'companyId' => $companyId, + 'friendId' => $friendId + ])->find(); + + if (empty($friendSettings) || $friendSettings->type == 0) { + return null; + } + + return $friendSettings; + } + + /** + * 确保会话存在 + * + * @param FriendSettings $friendSettings 好友设置 + * @param AiSettings $setting AI设置 + * @param array $params 参数 + * @return string|null 会话ID + */ + private function ensureConversation($friendSettings, $setting, $params) + { + if (!empty($friendSettings->conversationId)) { + return $friendSettings->conversationId; + } + + // 创建新会话 + $cozeAI = new CozeAI(); + $data = [ + 'bot_id' => $setting->botId, + 'name' => '与好友' . $params['friendId'] . '的对话', + 'meta_data' => [ + 'friendId' => (string)$friendSettings->friendId, + 'wechatAccountId' => (string)$params['wechatAccountId'], + ], + ]; + + $res = $cozeAI->createConversation($data); + $res = json_decode($res, true); + + if ($res['code'] != 200) { + \think\facade\Log::error('创建会话失败:' . ($res['msg'] ?? '未知错误')); + return null; + } + + // 保存会话ID + $conversationId = $res['data']['id']; + $friendSettings->conversationId = $conversationId; + $friendSettings->conversationTime = time(); + $friendSettings->save(); + + return $conversationId; + } + + /** + * 获取历史消息 + * + * @param string $friendId 好友ID + * @param FriendSettings $friendSettings 好友设置 + * @return array + */ + private function getHistoryMessages($friendId, $friendSettings) + { + $msgData = []; + + // 会话创建时间小于1分钟,加载最近10条消息 + if ($friendSettings->conversationTime >= time() - 60) { + $messages = Db::table('s2_wechat_message') + ->where('wechatFriendId', $friendId) + ->where('msgType', '<', 50) + ->order('wechatTime desc') + ->field('id,content,msgType,isSend,wechatTime') + ->limit(10) + ->select(); + + // 按时间正序排列 + usort($messages, function ($a, $b) { + return $a['wechatTime'] <=> $b['wechatTime']; + }); + + // 处理聊天数据 + foreach ($messages as $val) { + if (empty($val['content'])) { + continue; + } + + $msg = [ + 'role' => empty($val['isSend']) ? 'user' : 'assistant', + 'content' => $val['content'], + 'type' => empty($val['isSend']) ? 'question' : 'answer', + 'content_type' => 'text' + ]; + $msgData[] = $msg; + } + } else { + // 只加载最新一条用户消息 + $message = Db::table('s2_wechat_message') + ->where('wechatFriendId', $friendId) + ->where('msgType', '<', 50) + ->where('isSend', 0) + ->order('wechatTime desc') + ->field('id,content,msgType,isSend,wechatTime') + ->find(); + + if (!empty($message) && !empty($message['content'])) { + $msgData[] = [ + 'role' => 'user', + 'content' => $message['content'], + 'type' => 'question', + 'content_type' => 'text' + ]; + } + } + + return $msgData; + } + + /** + * 创建AI对话 + * + * @param AiSettings $setting AI设置 + * @param FriendSettings $friendSettings 好友设置 + * @param array $msgData 消息数据 + * @return string|null 对话ID + */ + private function createAiChat($setting, $friendSettings, $msgData) + { + $cozeAI = new CozeAI(); + $data = [ + 'bot_id' => $setting->botId, + 'uid' => $friendSettings->friendId, + 'conversation_id' => $friendSettings->conversationId, + 'question' => $msgData, + ]; + + $res = $cozeAI->createChat($data); + $res = json_decode($res, true); + + if ($res['code'] != 200) { + \think\facade\Log::error('创建对话失败:' . ($res['msg'] ?? '未知错误')); + return null; + } + + return $res['data']['id']; + } + + /** + * 等待AI处理完成(轮询机制) + * + * @param string $conversationId 会话ID + * @param string $chatId 对话ID + * @return array|null + */ + private function waitForChatCompletion($conversationId, $chatId) + { + $cozeAI = new CozeAI(); + $retryCount = 0; + + while ($retryCount < self::MAX_RETRY_TIMES) { + // 获取对话状态 + $res = $cozeAI->getConversationChat([ + 'conversation_id' => $conversationId, + 'chat_id' => $chatId, + ]); + $res = json_decode($res, true); + + if ($res['code'] != 200) { + \think\facade\Log::error('获取对话状态失败:' . ($res['msg'] ?? '未知错误')); + return null; + } + + $status = $res['data']['status'] ?? ''; + + // 处理不同的状态 + switch ($status) { + case self::STATUS_COMPLETED: + // 对话完成,返回结果 + return $res['data']; + + case self::STATUS_IN_PROGRESS: + case self::STATUS_CREATED: + // 继续等待 + $retryCount++; + sleep(self::RETRY_INTERVAL); + break; + + case self::STATUS_FAILED: + \think\facade\Log::error('对话失败,chat_id: ' . $chatId); + return null; + + case self::STATUS_CANCELED: + \think\facade\Log::error('对话已取消,chat_id: ' . $chatId); + return null; + + case self::STATUS_REQUIRES_ACTION: + \think\facade\Log::warning('对话需要进一步处理,chat_id: ' . $chatId); + return null; + + default: + \think\facade\Log::error('未知状态:' . $status); + return null; + } + } + + // 超时 + \think\facade\Log::error('对话处理超时,chat_id: ' . $chatId); + return null; + } + + /** + * 扣除Tokens + * + * @param array $chatResult 对话结果 + * @param array $params 参数 + * @param FriendSettings $friendSettings 好友设置 + */ + private function consumeTokens($chatResult, $params, $friendSettings) + { + $tokenCount = $chatResult['usage']['token_count'] ?? 0; + + if (empty($tokenCount)) { + return; + } + + // 获取好友昵称 + $nickname = WechatFriendModel::where('id', $friendSettings->friendId)->value('nickname'); + $remarks = !empty($nickname) ? '与好友【' . $nickname . '】聊天' : '与好友聊天'; + + // 扣除Tokens + $tokensRecord = new tokensRecord(); + $data = [ + 'tokens' => $tokenCount * 20, + 'type' => 0, + 'form' => 1, + 'wechatAccountId' => $params['wechatAccountId'], + 'friendIdOrGroupId' => $params['friendId'], + 'remarks' => $remarks, + ]; + + $tokensRecord->consumeTokens($data); + } + + /** + * 获取对话消息 + * + * @param string $conversationId 会话ID + * @param string $chatId 对话ID + * @return array|null + */ + private function getChatMessages($conversationId, $chatId) + { + $cozeAI = new CozeAI(); + $res = $cozeAI->listConversationMessage([ + 'conversation_id' => $conversationId, + 'chat_id' => $chatId, + ]); + $res = json_decode($res, true); + + if ($res['code'] != 200) { + \think\facade\Log::error('获取对话消息失败:' . ($res['msg'] ?? '未知错误')); + return null; + } + + return $res['data'] ?? []; + } + + + public function index2222() + { + $userId = $this->getUserInfo('id'); + $companyId = $this->getUserInfo('companyId'); + $friendId = $this->request->param('friendId', ''); + $wechatAccountId = $this->request->param('wechatAccountId', ''); + $content = $this->request->param('content', ''); + + if (empty($wechatAccountId) || empty($friendId)) { return ResponseHelper::error('参数缺失'); } $tokens = TokensCompany::where(['companyId' => $companyId])->value('tokens'); - if (empty($tokens) || $tokens <= 0){ + if (empty($tokens) || $tokens <= 0) { return ResponseHelper::error('用户Tokens余额不足'); } //读取AI配置 - $setting = Db::name('ai_settings')->where(['companyId' => $companyId,'userId' => $userId])->find(); - if(empty($setting)){ + $setting = Db::name('ai_settings')->where(['companyId' => $companyId, 'userId' => $userId])->find(); + if (empty($setting)) { return ResponseHelper::error('未找到配置信息,请先配置AI策略'); } - $config = json_decode($setting['config'],true); + $config = json_decode($setting['config'], true); $modelSetting = $config['modelSetting']; $round = isset($config['round']) ? $config['round'] : 10; @@ -45,20 +456,20 @@ class AiChatController extends BaseController ->limit($round) ->select(); - usort($messages, function($a, $b) { + usort($messages, function ($a, $b) { return $a['wechatTime'] <=> $b['wechatTime']; }); //处理聊天数据 $msg = []; - foreach ($messages as $val){ - if (empty($val['content'])){ + foreach ($messages as $val) { + if (empty($val['content'])) { continue; } - if (!empty($val['isSend'])){ - $msg[] = '客服:' . $val['content']; - }else{ - $msg[] = '用户:' . $val['content']; + if (!empty($val['isSend'])) { + $msg[] = '客服:' . $val['content']; + } else { + $msg[] = '用户:' . $val['content']; } } $content = implode("\n", $msg); @@ -78,13 +489,13 @@ class AiChatController extends BaseController //AI处理 $ai = new DouBaoAI(); $res = $ai->text($params); - $res = json_decode($res,true); + $res = json_decode($res, true); if ($res['code'] == 200) { //扣除Tokens $tokensRecord = new tokensRecord(); $nickname = Db::table('s2_wechat_friend')->where(['id' => $friendId])->value('nickname'); - $remarks = !empty($nickname) ? '与好友【'.$nickname.'】聊天' : '与好友聊天'; + $remarks = !empty($nickname) ? '与好友【' . $nickname . '】聊天' : '与好友聊天'; $data = [ 'tokens' => $res['data']['token'], 'type' => 0, @@ -95,7 +506,7 @@ class AiChatController extends BaseController ]; $tokensRecord->consumeTokens($data); return ResponseHelper::success($res['data']['content']); - }else{ + } else { return ResponseHelper::error($res['msg']); } diff --git a/Server/application/chukebao/model/AiKnowledgeBase.php b/Server/application/chukebao/model/AiKnowledgeBase.php new file mode 100644 index 00000000..2e08c8f7 --- /dev/null +++ b/Server/application/chukebao/model/AiKnowledgeBase.php @@ -0,0 +1,52 @@ +belongsTo(AiKnowledgeBaseType::class, 'typeId', 'id'); + } + + /** + * 获取有效的知识库列表(未删除) + */ + public static function getValidList($companyId, $typeId = null) + { + $where = [ + ['isDel', '=', 0], + ['companyId', '=', $companyId] + ]; + + if ($typeId !== null) { + $where[] = ['typeId', '=', $typeId]; + } + + return self::where($where) + ->with(['type']) + ->order('createTime', 'desc') + ->select(); + } +} + diff --git a/Server/application/chukebao/model/AiKnowledgeBaseType.php b/Server/application/chukebao/model/AiKnowledgeBaseType.php new file mode 100644 index 00000000..80fa9f2d --- /dev/null +++ b/Server/application/chukebao/model/AiKnowledgeBaseType.php @@ -0,0 +1,57 @@ +order('createTime', 'desc') + ->select(); + } + + /** + * 检查是否为系统类型 + */ + public function isSystemType() + { + return $this->type == self::TYPE_SYSTEM; + } +} + diff --git a/Server/application/chukebao/model/AiSettings.php b/Server/application/chukebao/model/AiSettings.php new file mode 100644 index 00000000..7808bd14 --- /dev/null +++ b/Server/application/chukebao/model/AiSettings.php @@ -0,0 +1,17 @@ +middleware(['jwt']); diff --git a/Server/application/cunkebao/controller/AiKnowledgeBaseController.php b/Server/application/cunkebao/controller/AiKnowledgeBaseController.php new file mode 100644 index 00000000..7ab048a9 --- /dev/null +++ b/Server/application/cunkebao/controller/AiKnowledgeBaseController.php @@ -0,0 +1,570 @@ +getUserInfo('companyId'); + if (empty($companyId)) { + return ResponseHelper::error('公司信息获取失败'); + } + + // 获取分页参数 + $page = $this->request->param('page', 1); + $pageSize = $this->request->param('pageSize', 20); + $includeSystem = $this->request->param('includeSystem', 1); // 是否包含系统类型 + + // 构建查询条件 + $where = [['isDel', '=', 0]]; + + if ($includeSystem == 1) { + // 包含系统类型和本公司创建的类型 + $where[] = ['type', '=', AiKnowledgeBaseType::TYPE_SYSTEM]; + $where[] = ['companyId|type', 'in', [$companyId, 0]]; + } else { + // 只显示本公司创建的类型 + $where[] = ['companyId', '=', $companyId]; + $where[] = ['type', '=', AiKnowledgeBaseType::TYPE_USER]; + } + + // 查询数据 + $list = AiKnowledgeBaseType::where($where) + ->order('type', 'asc') // 系统类型排在前面 + ->order('createTime', 'desc') + ->paginate($pageSize, false, ['page' => $page]); + + return ResponseHelper::success($list, '获取成功'); + + } catch (\Exception $e) { + return ResponseHelper::error('系统异常:' . $e->getMessage()); + } + } + + /** + * 添加知识库类型 + * + * @return \think\response\Json + */ + public function addType() + { + try { + $userId = $this->getUserInfo('id'); + $companyId = $this->getUserInfo('companyId'); + + if (empty($companyId)) { + return ResponseHelper::error('公司信息获取失败'); + } + + // 获取参数 + $name = $this->request->param('name', ''); + $description = $this->request->param('description', ''); + $label = $this->request->param('label', []); + $prompt = $this->request->param('prompt', ''); + + // 参数验证 + if (empty($name)) { + return ResponseHelper::error('类型名称不能为空'); + } + + // 检查名称是否重复 + $exists = AiKnowledgeBaseType::where([ + ['companyId', '=', $companyId], + ['name', '=', $name], + ['isDel', '=', 0] + ])->find(); + + if ($exists) { + return ResponseHelper::error('该类型名称已存在'); + } + + // 创建类型 + $typeModel = new AiKnowledgeBaseType(); + $data = [ + 'type' => AiKnowledgeBaseType::TYPE_USER, + 'name' => $name, + 'description' => $description, + 'label' => json_decode($label,256), + 'prompt' => $prompt, + 'companyId' => $companyId, + 'userId' => $userId, + 'createTime' => time(), + 'updateTime' => time(), + 'isDel' => 0 + ]; + + if ($typeModel->save($data)) { + return ResponseHelper::success(['id' => $typeModel->id], '添加成功'); + } else { + return ResponseHelper::error('添加失败'); + } + + } catch (\Exception $e) { + return ResponseHelper::error('系统异常:' . $e->getMessage()); + } + } + + /** + * 编辑知识库类型 + * + * @return \think\response\Json + */ + public function editType() + { + try { + $companyId = $this->getUserInfo('companyId'); + if (empty($companyId)) { + return ResponseHelper::error('公司信息获取失败'); + } + + // 获取参数 + $id = $this->request->param('id', 0); + $name = $this->request->param('name', ''); + $description = $this->request->param('description', ''); + $label = $this->request->param('label', []); + $prompt = $this->request->param('prompt', ''); + + // 参数验证 + if (empty($id)) { + return ResponseHelper::error('类型ID不能为空'); + } + + if (empty($name)) { + return ResponseHelper::error('类型名称不能为空'); + } + + // 查找类型 + $typeModel = AiKnowledgeBaseType::where([ + ['id', '=', $id], + ['isDel', '=', 0] + ])->find(); + + if (!$typeModel) { + return ResponseHelper::error('类型不存在'); + } + + // 检查是否为系统类型 + if ($typeModel->isSystemType()) { + return ResponseHelper::error('系统类型不允许编辑'); + } + + // 检查权限(只能编辑本公司的类型) + if ($typeModel->companyId != $companyId) { + return ResponseHelper::error('无权限编辑该类型'); + } + + // 检查名称是否重复(排除自己) + $exists = AiKnowledgeBaseType::where([ + ['companyId', '=', $companyId], + ['name', '=', $name], + ['id', '<>', $id], + ['isDel', '=', 0] + ])->find(); + + if ($exists) { + return ResponseHelper::error('该类型名称已存在'); + } + + // 更新数据 + $typeModel->name = $name; + $typeModel->description = $description; + $typeModel->label = $label; + $typeModel->prompt = $prompt; + $typeModel->updateTime = time(); + + if ($typeModel->save()) { + return ResponseHelper::success([], '更新成功'); + } else { + return ResponseHelper::error('更新失败'); + } + + } catch (\Exception $e) { + return ResponseHelper::error('系统异常:' . $e->getMessage()); + } + } + + /** + * 删除知识库类型 + * + * @return \think\response\Json + */ + public function deleteType() + { + try { + $companyId = $this->getUserInfo('companyId'); + if (empty($companyId)) { + return ResponseHelper::error('公司信息获取失败'); + } + + // 获取参数 + $id = $this->request->param('id', 0); + + // 参数验证 + if (empty($id)) { + return ResponseHelper::error('类型ID不能为空'); + } + + // 查找类型 + $typeModel = AiKnowledgeBaseType::where([ + ['id', '=', $id], + ['isDel', '=', 0] + ])->find(); + + if (!$typeModel) { + return ResponseHelper::error('类型不存在'); + } + + // 检查是否为系统类型 + if ($typeModel->isSystemType()) { + return ResponseHelper::error('系统类型不允许删除'); + } + + // 检查权限(只能删除本公司的类型) + if ($typeModel->companyId != $companyId) { + return ResponseHelper::error('无权限删除该类型'); + } + + // 检查是否有关联的知识库 + $hasKnowledge = AiKnowledgeBase::where([ + ['typeId', '=', $id], + ['isDel', '=', 0] + ])->count(); + + if ($hasKnowledge > 0) { + return ResponseHelper::error('该类型下存在知识库,无法删除'); + } + + // 软删除 + $typeModel->isDel = 1; + $typeModel->delTime = time(); + + if ($typeModel->save()) { + return ResponseHelper::success([], '删除成功'); + } else { + return ResponseHelper::error('删除失败'); + } + + } catch (\Exception $e) { + return ResponseHelper::error('系统异常:' . $e->getMessage()); + } + } + + // ==================== 知识库管理 ==================== + + /** + * 获取知识库列表 + * + * @return \think\response\Json + */ + public function getList() + { + try { + $companyId = $this->getUserInfo('companyId'); + if (empty($companyId)) { + return ResponseHelper::error('公司信息获取失败'); + } + + // 获取分页参数 + $page = $this->request->param('page', 1); + $pageSize = $this->request->param('pageSize', 20); + $typeId = $this->request->param('typeId', 0); // 类型筛选 + $keyword = $this->request->param('keyword', ''); // 关键词搜索 + + // 构建查询条件 + $where = [ + ['isDel', '=', 0], + ['companyId', '=', $companyId] + ]; + + if ($typeId > 0) { + $where[] = ['typeId', '=', $typeId]; + } + + if (!empty($keyword)) { + $where[] = ['name', 'like', '%' . $keyword . '%']; + } + + // 查询数据 + $list = AiKnowledgeBase::where($where) + ->with(['type']) + ->order('createTime', 'desc') + ->paginate($pageSize, false, ['page' => $page]); + + return ResponseHelper::success($list, '获取成功'); + + } catch (\Exception $e) { + return ResponseHelper::error('系统异常:' . $e->getMessage()); + } + } + + /** + * 添加知识库 + * + * @return \think\response\Json + */ + public function add() + { + try { + $userId = $this->getUserInfo('id'); + $companyId = $this->getUserInfo('companyId'); + + if (empty($companyId)) { + return ResponseHelper::error('公司信息获取失败'); + } + + $datasetId = AiSettingsModel::where(['companyId' => $companyId])->value('datasetId'); + + + // 获取参数 + $typeId = $this->request->param('typeId', 0); + $name = $this->request->param('name', ''); + $label = $this->request->param('label', []); + $fileUrl = $this->request->param('fileUrl', ''); + + // 参数验证 + if (empty($typeId)) { + return ResponseHelper::error('请选择知识库类型'); + } + + if (empty($name)) { + return ResponseHelper::error('知识库名称不能为空'); + } + + if (empty($fileUrl)) { + return ResponseHelper::error('文件地址不能为空'); + } + + // 检查类型是否存在 + $typeExists = AiKnowledgeBaseType::where([ + ['id', '=', $typeId], + ['isDel', '=', 0] + ])->find(); + + if (!$typeExists) { + return ResponseHelper::error('知识库类型不存在'); + } + + // 创建知识库 + $knowledgeModel = new AiKnowledgeBase(); + $data = [ + 'typeId' => $typeId, + 'name' => $name, + 'label' => json_encode($label, 256), + 'fileUrl' => $fileUrl, + 'companyId' => $companyId, + 'userId' => $userId, + 'createTime' => time(), + 'updateTime' => time(), + 'isDel' => 0 + ]; + + if ($knowledgeModel->save($data)) { + if (!empty($datasetId)) { + $createDocumentData = [ + 'filePath' => $fileUrl, + 'fileName' => $name, + 'dataset_id' => $datasetId + ]; + $cozeAI = new CozeAI(); + $result = $cozeAI->createDocument($createDocumentData); + $result = json_decode($result, true); + if ($result['code'] == 200) { + $documentId = $result['data'][0]['document_id']; + AiKnowledgeBase::where('id', $knowledgeModel->id)->update(['documentId' => $documentId, 'updateTime' => time()]); + AiSettingsModel::where(['companyId' => $companyId])->update(['isRelease' => 0,'updateTime' => time()]); + } + } + return ResponseHelper::success(['id' => $knowledgeModel->id], '添加成功'); + } else { + return ResponseHelper::error('添加失败'); + } + + } catch (\Exception $e) { + return ResponseHelper::error('系统异常:' . $e->getMessage()); + } + } + + /** + * 编辑知识库 + * + * @return \think\response\Json + */ + public function edit() + { + try { + $companyId = $this->getUserInfo('companyId'); + if (empty($companyId)) { + return ResponseHelper::error('公司信息获取失败'); + } + + // 获取参数 + $id = $this->request->param('id', 0); + $typeId = $this->request->param('typeId', 0); + $name = $this->request->param('name', ''); + $label = $this->request->param('label', []); + $fileUrl = $this->request->param('fileUrl', ''); + + // 参数验证 + if (empty($id)) { + return ResponseHelper::error('知识库ID不能为空'); + } + + if (empty($typeId)) { + return ResponseHelper::error('请选择知识库类型'); + } + + if (empty($name)) { + return ResponseHelper::error('知识库名称不能为空'); + } + + // 查找知识库 + $knowledgeModel = AiKnowledgeBase::where([ + ['id', '=', $id], + ['companyId', '=', $companyId], + ['isDel', '=', 0] + ])->find(); + + if (!$knowledgeModel) { + return ResponseHelper::error('知识库不存在或无权限编辑'); + } + + // 检查类型是否存在 + $typeExists = AiKnowledgeBaseType::where([ + ['id', '=', $typeId], + ['isDel', '=', 0] + ])->find(); + + if (!$typeExists) { + return ResponseHelper::error('知识库类型不存在'); + } + + // 更新数据 + $knowledgeModel->typeId = $typeId; + $knowledgeModel->name = $name; + $knowledgeModel->label = json_encode($label, 256); + if (!empty($fileUrl)) { + $knowledgeModel->fileUrl = $fileUrl; + } + $knowledgeModel->updateTime = time(); + + if ($knowledgeModel->save()) { + return ResponseHelper::success([], '更新成功'); + } else { + return ResponseHelper::error('更新失败'); + } + + } catch (\Exception $e) { + return ResponseHelper::error('系统异常:' . $e->getMessage()); + } + } + + /** + * 删除知识库 + * + * @return \think\response\Json + */ + public function delete() + { + try { + $companyId = $this->getUserInfo('companyId'); + if (empty($companyId)) { + return ResponseHelper::error('公司信息获取失败'); + } + + // 获取参数 + $id = $this->request->param('id', 0); + + // 参数验证 + if (empty($id)) { + return ResponseHelper::error('知识库ID不能为空'); + } + + // 查找知识库 + $knowledgeModel = AiKnowledgeBase::where([ + ['id', '=', $id], + ['companyId', '=', $companyId], + ['isDel', '=', 0] + ])->find(); + + if (!$knowledgeModel) { + return ResponseHelper::error('知识库不存在或无权限删除'); + } + + // 软删除 + $knowledgeModel->isDel = 1; + $knowledgeModel->delTime = time(); + + if ($knowledgeModel->save()) { + if (!empty($knowledgeModel->documentId)){ + $cozeAI = new CozeAI(); + $cozeAI->deleteDocument([$knowledgeModel->documentId]); + AiSettingsModel::where(['companyId' => $companyId])->update(['isRelease' => 0,'updateTime' => time()]); + } + return ResponseHelper::success([], '删除成功'); + } else { + return ResponseHelper::error('删除失败'); + } + + } catch (\Exception $e) { + return ResponseHelper::error('系统异常:' . $e->getMessage()); + } + } + + /** + * 获取知识库详情 + * + * @return \think\response\Json + */ + public function detail() + { + try { + $companyId = $this->getUserInfo('companyId'); + if (empty($companyId)) { + return ResponseHelper::error('公司信息获取失败'); + } + + // 获取参数 + $id = $this->request->param('id', 0); + + // 参数验证 + if (empty($id)) { + return ResponseHelper::error('知识库ID不能为空'); + } + + // 查找知识库 + $knowledge = AiKnowledgeBase::where([ + ['id', '=', $id], + ['companyId', '=', $companyId], + ['isDel', '=', 0] + ])->with(['type'])->find(); + + if (!$knowledge) { + return ResponseHelper::error('知识库不存在或无权限查看'); + } + + return ResponseHelper::success($knowledge, '获取成功'); + + } catch (\Exception $e) { + return ResponseHelper::error('系统异常:' . $e->getMessage()); + } + } +} \ No newline at end of file diff --git a/Server/application/cunkebao/controller/AiSettingsController.php b/Server/application/cunkebao/controller/AiSettingsController.php new file mode 100644 index 00000000..8eb41423 --- /dev/null +++ b/Server/application/cunkebao/controller/AiSettingsController.php @@ -0,0 +1,344 @@ +getUserInfo('id'); + $companyId = $this->getUserInfo('companyId'); + + if (empty($companyId)) { + return ResponseHelper::error('公司信息获取失败'); + } + + // 查找公司AI设置 + $settings = $this->getOrCreateAiSettings($companyId, $userId); + + if (!$settings) { + return ResponseHelper::error('AI设置初始化失败'); + } + + // 确保智能体已创建 + if (empty($settings->botId)) { + $botCreated = $this->createBot($settings); + if (!$botCreated) { + return ResponseHelper::error('智能体创建失败'); + } + } + + // 确保知识库已创建 + if (empty($settings->datasetId)) { + $knowledgeCreated = $this->createKnowledge($settings); + if (!$knowledgeCreated) { + return ResponseHelper::error('知识库创建失败'); + } + } + if (!empty($settings->botId) && !empty($settings->datasetId)) { + $cozeAI = new CozeAI(); + $config = json_decode($settings->config,true); + $config['bot_id'] = $settings->botId; + $config['dataset_ids'] = [$settings->datasetId]; + $cozeAI->updateBot($config); + } + + // 解析配置信息 + $settings->config = json_decode($settings->config, true); + + return ResponseHelper::success($settings, 'AI设置初始化成功'); + + } catch (\Exception $e) { + return ResponseHelper::error('系统异常:' . $e->getMessage()); + } + } + + /** + * 获取或创建AI设置 + * + * @param int $companyId 公司ID + * @param int $userId 用户ID + * @return AiSettingsModel|false + */ + private function getOrCreateAiSettings($companyId, $userId) + { + // 查找现有设置 + $settings = AiSettingsModel::where(['companyId' => $companyId])->find(); + + if (empty($settings)) { + // 获取公司信息 + $company = CompanyModel::where('id', $companyId)->find(); + if (empty($company)) { + return false; + } + + // 创建默认配置 + $config = $this->getDefaultConfig($company['name']); + + // 保存AI设置 + $settings = $this->saveAiSettings($companyId, $userId, $config); + } + + return $settings; + } + + /** + * 获取默认AI配置 + * + * @param string $companyName 公司名称 + * @return array + */ + private function getDefaultConfig($companyName) + { + return [ + 'name' => $companyName, + 'model_id' => '1737521813', // 默认模型ID + 'prompt_info' => $this->getDefaultPrompt() + ]; + } + + /** + * 获取默认提示词 + * + * @return string + */ + private function getDefaultPrompt() + { + return '# 角色 +你是一位全能知识客服,作为专业的客服智能体,具备全面的知识储备,能够回答用户提出的各类问题。在回答问题前,会仔细查阅知识库内容,并且始终严格遵守中国法律法规。 + +## 技能 +### 技能 1: 回答用户问题 +1. 当用户提出问题时,首先在知识库中进行搜索查找相关信息。 +2. 依据知识库中的内容,为用户提供准确、清晰、完整的回答。 + +## 限制 +- 仅依据知识库内容回答问题,对于知识库中没有的信息,如实告知用户无法回答。 +- 回答必须严格遵循中国法律法规,不得出现任何违法违规内容。 +- 回答需简洁明了,避免冗长复杂的表述。'; + } + + /** + * 保存AI设置到数据库 + * + * @param int $companyId 公司ID + * @param int $userId 用户ID + * @param array $config 配置信息 + * @return AiSettingsModel|false + */ + private function saveAiSettings($companyId, $userId, $config) + { + $data = [ + 'companyId' => $companyId, + 'userId' => $userId, + 'config' => json_encode($config, JSON_UNESCAPED_UNICODE), + 'createTime' => time(), + 'updateTime' => time(), + 'botId' => 0, + 'datasetId' => 0, + ]; + + $aiSettingsModel = new AiSettingsModel(); + $result = $aiSettingsModel->save($data); + + if ($result) { + return AiSettingsModel::where(['companyId' => $companyId])->find(); + } + + return false; + } + + /** + * 创建AI智能体 + * + * @param AiSettingsModel $settings AI设置对象 + * @return bool + */ + private function createBot($settings) + { + if (empty($settings)) { + return false; + } + + try { + $config = json_decode($settings->config, true); + if (empty($config)) { + return false; + } + + // 调用CozeAI创建智能体 + $cozeAI = new CozeAI(); + $result = $cozeAI->createBot($config); + $result = json_decode($result, true); + + if ($result['code'] != 200) { + \think\facade\Log::error('智能体创建失败:' . ($result['msg'] ?? '未知错误')); + return false; + } + + // 更新智能体ID + $settings->botId = $result['data']['bot_id']; + $settings->updateTime = time(); + + return $settings->save(); + + } catch (\Exception $e) { + \think\facade\Log::error('创建智能体异常:' . $e->getMessage()); + return false; + } + } + + /** + * 创建知识库 + * + * @param AiSettingsModel $settings AI设置对象 + * @return bool + */ + private function createKnowledge($settings) + { + if (empty($settings)) { + return false; + } + + try { + $config = json_decode($settings->config, true); + if (empty($config)) { + return false; + } + + // 调用CozeAI创建知识库 + $cozeAI = new CozeAI(); + $result = $cozeAI->createKnowledge(['name' => $config['name']]); + $result = json_decode($result, true); + + if ($result['code'] != 200) { + \think\facade\Log::error('知识库创建失败:' . ($result['msg'] ?? '未知错误')); + return false; + } + + // 更新知识库ID + $settings->datasetId = $result['data']['dataset_id']; + $settings->updateTime = time(); + + return $settings->save(); + + } catch (\Exception $e) { + \think\facade\Log::error('创建知识库异常:' . $e->getMessage()); + return false; + } + } + + /** + * 更新AI配置 + * + * @return \think\response\Json + */ + public function updateConfig() + { + try { + $companyId = $this->getUserInfo('companyId'); + if (empty($companyId)) { + return ResponseHelper::error('公司信息获取失败'); + } + + // 获取请求参数 + $config = $this->request->param('config', []); + if (empty($config)) { + return ResponseHelper::error('配置参数不能为空'); + } + + // 查找现有设置 + $settings = AiSettingsModel::where(['companyId' => $companyId])->find(); + if (empty($settings)) { + return ResponseHelper::error('AI设置不存在,请先初始化'); + } + + // 更新配置 + $settings->config = json_encode($config, JSON_UNESCAPED_UNICODE); + $settings->updateTime = time(); + + if ($settings->save()) { + return ResponseHelper::success([], '配置更新成功'); + } else { + return ResponseHelper::error('配置更新失败'); + } + + } catch (\Exception $e) { + return ResponseHelper::error('系统异常:' . $e->getMessage()); + } + } + + /** + * 获取AI设置详情 + * + * @return \think\response\Json + */ + public function getSettings() + { + try { + $companyId = $this->getUserInfo('companyId'); + if (empty($companyId)) { + return ResponseHelper::error('公司信息获取失败'); + } + + $settings = AiSettingsModel::where(['companyId' => $companyId])->find(); + if (empty($settings)) { + return ResponseHelper::error('AI设置不存在'); + } + + // 解析配置信息 + $settings->config = json_decode($settings->config, true); + + return ResponseHelper::success($settings, '获取成功'); + + } catch (\Exception $e) { + return ResponseHelper::error('系统异常:' . $e->getMessage()); + } + } + + + /** + * 发布智能体 + * @return \think\response\Json + * @throws \Exception + */ + public function release() + { + $companyId = $this->getUserInfo('companyId'); + $settings = AiSettingsModel::where(['companyId' => $companyId])->find(); + if (!empty($settings->isRelease)) { + return ResponseHelper::success('', '已发布,无需重复发布'); + } + + $cozeAI = new CozeAI(); + $res = $cozeAI->botPublish(['bot_id' => $settings->botId]); + $res = json_decode($res, true); + + if ($res['code'] != 200) { + $msg = '发布失败失败:' . ($res['msg'] ?? '未知错误'); + return ResponseHelper::error($msg); + } + $settings->isRelease = 1; + $settings->releaseTime = time(); + $settings->save(); + return ResponseHelper::success('', '发布成功'); + } +} \ No newline at end of file diff --git a/Server/application/cunkebao/model/AiKnowledgeBase.php b/Server/application/cunkebao/model/AiKnowledgeBase.php new file mode 100644 index 00000000..fbf0664c --- /dev/null +++ b/Server/application/cunkebao/model/AiKnowledgeBase.php @@ -0,0 +1,11 @@ +