diff --git a/src/addon/aikefu/api/controller/WebSocket.php b/src/addon/aikefu/api/controller/WebSocket.php index 32b08bfa1..8bf1e47d9 100644 --- a/src/addon/aikefu/api/controller/WebSocket.php +++ b/src/addon/aikefu/api/controller/WebSocket.php @@ -112,8 +112,15 @@ class WebSocket extends WebSocketBase return; } + // 处理文件预览 + if (isset($data['action']) && $data['action'] === 'file_preview') { + $this->handleFilePreview($conn, $data); + return; + } + $conn->send(json_encode(['type' => 'error', 'message' => 'Unknown action'])); + } catch (\Exception $e) { $conn->send(json_encode(['type' => 'error', 'message' => $e->getMessage(), 'line' => $e->getLine(), 'file' => $e->getFile(), 'trace' => $e->getTraceAsString()])); } @@ -268,7 +275,7 @@ class WebSocket extends WebSocketBase $enable_stream = $stream || $response_mode == 'streaming'; // 构建请求数据和请求头 - $requestData = $this->buildRequestData($query, $user_id, $conversation_id, $enable_stream); + $requestData = $this->buildRequestData($query, $user_id, $conversation_id, $enable_stream, $data); $headers = $this->buildRequestHeaders($config['api_key']); // 发送请求到Dify API @@ -299,7 +306,7 @@ class WebSocket extends WebSocketBase try { // 获取客户端信息 $clientInfo = $this->clientData[$conn->resourceId]; - + // 获取分片上传相关参数 $file_id = $data['file_id'] ?? ''; $file_name = $data['file_name'] ?? ''; @@ -358,7 +365,7 @@ class WebSocket extends WebSocketBase try { // 获取客户端信息 $clientInfo = $this->clientData[$conn->resourceId]; - + // 获取合并相关参数 $file_id = $data['file_id'] ?? ''; $file_name = $data['file_name'] ?? ''; @@ -485,17 +492,17 @@ class WebSocket extends WebSocketBase $this->cleanupTempFiles($temp_dir); } } - + // 解析错误信息 $errorMessage = $e->getMessage(); $errorCode = 500; $errorType = 'upload_failed'; - + // 提取HTTP错误码和Dify错误信息 if (preg_match('/HTTP请求失败,状态码:(\d+),响应:(.*)/', $errorMessage, $matches)) { - $errorCode = (int)$matches[1]; + $errorCode = (int) $matches[1]; $errorResponse = $matches[2]; - + try { $errorData = json_decode($errorResponse, true); if (isset($errorData['code'])) { @@ -508,7 +515,7 @@ class WebSocket extends WebSocketBase // 解析失败,使用原始错误信息 } } - + $conn->send(json_encode([ 'type' => 'error', 'code' => $errorCode, @@ -555,7 +562,7 @@ class WebSocket extends WebSocketBase try { // 获取客户端信息 $clientInfo = $this->clientData[$conn->resourceId]; - + // 获取检查相关参数 $file_id = $data['file_id'] ?? ''; $total_chunks = $data['total_chunks'] ?? 0; @@ -606,6 +613,144 @@ class WebSocket extends WebSocketBase } } + /** + * 处理文件预览 + * @param ConnectionInterface $conn + * @param array $data + */ + private function handleFilePreview(ConnectionInterface $conn, $data) + { + try { + // 获取客户端信息 + $clientInfo = $this->clientData[$conn->resourceId]; + + // 获取预览相关参数 + $file_id = $data['file_id'] ?? ''; + $as_attachment = $data['as_attachment'] ?? false; + $user_id = $data['user_id'] ?? $clientInfo['user_id']; + $site_id = $data['uniacid'] ?? $clientInfo['site_id']; + $token = $data['token'] ?? $clientInfo['token']; + + // 验证参数 + if (empty($file_id)) { + throw new \Exception('文件ID不能为空'); + } + + // 验证参数并获取配置,与 Kefu.php 保持一致 + $config = $this->validateAndGetConfig([ + 'file_id' => ['required' => true, 'message' => '文件ID不能为空', 'description' => '文件ID'], + 'user_id' => ['required' => true, 'message' => '请求参数 `user_id` 不能为空', 'description' => '用户ID'] + ], [ + 'file_id' => $file_id, + 'user_id' => $user_id, + 'uniacid' => $site_id, + 'token' => $token + ]); + + // 构建请求URL + $url = $config['base_url'] . '/files/' . $file_id . '/preview'; + if ($as_attachment) { + $url .= '?as_attachment=true'; + } + + // 构建请求头 + $headers = [ + 'Authorization: Bearer ' . $config['api_key'], + 'Accept: */*' + ]; + + // 发送请求到Dify API + $response = $this->curlGetFile($url, $headers); + + // 发送预览成功响应 + $conn->send(json_encode([ + 'type' => 'file_preview_success', + 'file_id' => $file_id, + 'file_url' => $url, + 'message' => '文件预览请求成功' + ])); + + $this->log('文件预览请求成功,文件ID:' . $file_id, 'info'); + + } catch (\Exception $e) { + // 解析错误信息 + $errorMessage = $e->getMessage(); + $errorCode = 500; + $errorType = 'preview_failed'; + + // 提取HTTP错误码和Dify错误信息 + if (preg_match('/HTTP请求失败,状态码:(\d+),响应:(.*)/', $errorMessage, $matches)) { + $errorCode = (int) $matches[1]; + $errorResponse = $matches[2]; + + try { + $errorData = json_decode($errorResponse, true); + if (isset($errorData['code'])) { + $errorType = $errorData['code']; + } + if (isset($errorData['message'])) { + $errorMessage = $errorData['message']; + } + } catch (\Exception $decodeEx) { + // 解析失败,使用原始错误信息 + } + } + + $conn->send(json_encode([ + 'type' => 'error', + 'code' => $errorCode, + 'error_type' => $errorType, + 'message' => '文件预览失败:' . $errorMessage + ])); + } + } + + /** + * 封装文件预览的curl请求方法 + * @param string $url 请求URL + * @param array $headers 请求头 + * @return string 响应内容 + */ + private function curlGetFile($url, $headers = []) + { + $ch = curl_init(); + + // 设置URL + curl_setopt($ch, CURLOPT_URL, $url); + + // 设置请求方法 + curl_setopt($ch, CURLOPT_HTTPGET, true); + + // 设置请求头 + if (!empty($headers)) { + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + } + + // 设置返回值 + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + curl_setopt($ch, CURLOPT_TIMEOUT, 60); + + // 执行请求 + $response = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + + // 关闭连接 + curl_close($ch); + + if ($response === false) { + throw new \Exception('Curl请求失败'); + } + + if ($httpCode >= 400) { + throw new \Exception('HTTP请求失败,状态码:' . $httpCode . ',响应:' . $response); + } + + return $response; + } + + /** * 封装文件上传的curl请求方法(适用于 Dify 1.9.0 版本) * @param string $url 请求URL @@ -696,7 +841,7 @@ class WebSocket extends WebSocketBase { try { // 记录开始处理流式请求 - $this->log('AI客服WebSocket流式请求开始处理,用户ID:' . $user_id . ',请求消息:' . $query, 'info'); + $this->log('AI客服WebSocket流式请求开始处理,用户ID:' . $user_id . ',请求内容:' . json_encode($requestData), 'info'); // 初始化模型 $kefu_conversation_model = new KefuConversationModel(); @@ -1316,9 +1461,10 @@ class WebSocket extends WebSocketBase * @param string $user_id 用户ID * @param string $conversation_id 会话ID * @param bool $stream 是否使用流式响应 + * @param array $origin_data 原始数据 * @return array */ - private function buildRequestData($message, $user_id, $conversation_id, $stream) + private function buildRequestData($message, $user_id, $conversation_id, $stream, $origin_data) { $requestData = [ 'inputs' => [], @@ -1330,6 +1476,12 @@ class WebSocket extends WebSocketBase // 如果有会话ID,添加到请求中 if (!empty($conversation_id)) { $requestData['conversation_id'] = $conversation_id; + + // ----- 只有会话ID的情况下,下列情况才添加相关的数据 + // 如果有files字段,添加到请求中 + if (!empty($origin_data['files']) && count($origin_data['files']) > 0) { + $requestData['files'] = $origin_data['files']; + } } return $requestData; diff --git a/src/addon/aikefu/docs/ws_multi_addon_test.html b/src/addon/aikefu/docs/ws_multi_addon_test.html index 70c50b3ff..549850c7b 100644 --- a/src/addon/aikefu/docs/ws_multi_addon_test.html +++ b/src/addon/aikefu/docs/ws_multi_addon_test.html @@ -10,7 +10,7 @@ - +