params['uniacid'] ?? $this->site_id; // 使用 uniacid, 方便以后迁移,而且uniacid 是唯一的, site_id 不是,同时被params给过滤了 // 检查site_id 如果 < 1, 则返回错误 if ($site_id < 1) { return $this->response($this->error('缺少关键参数站点ID')); } // 准备事件数据 $event_data = [ 'check_id' => uniqid('health_', true), 'timestamp' => date('Y-m-d H:i:s'), 'site_id' => $site_id, 'check_type' => $this->params['check_type'] ?? 'full' // full, basic, ai_service ]; try { // 触发健康检查事件 $result = Event::trigger('KefuHealthCheck', $event_data); // 汇总检查结果 $health_summary = [ 'status' => 'healthy', 'check_id' => $event_data['check_id'], 'timestamp' => $event_data['timestamp'], 'total_checks' => 0, 'passed_checks' => 0, 'failed_checks' => 0, 'response_time_ms' => 0, 'components' => [], 'warnings' => [], 'errors' => [] ]; if (is_array($result) && !empty($result)) { foreach ($result as $check_result) { if (isset($check_result['component'])) { $health_summary['components'][$check_result['component']] = $check_result; $health_summary['total_checks']++; if ($check_result['status'] === 'healthy') { $health_summary['passed_checks']++; } else { $health_summary['failed_checks']++; if ($check_result['status'] === 'error') { $health_summary['errors'][] = $check_result['message'] ?? 'Unknown error'; $health_summary['status'] = 'unhealthy'; } elseif ($check_result['status'] === 'warning') { $health_summary['warnings'][] = $check_result['message'] ?? 'Unknown warning'; if ($health_summary['status'] === 'healthy') { $health_summary['status'] = 'warning'; } } } } } } // 计算总体响应时间 $health_summary['response_time_ms'] = round((microtime(true) - $start_time) * 1000, 2); // 如果没有任何检查结果,进行基础检查 if ($health_summary['total_checks'] === 0) { $health_summary['components']['basic'] = [ 'status' => 'healthy', 'message' => '基础服务正常', 'response_time_ms' => 0, 'details' => [ 'php_version' => PHP_VERSION, 'memory_usage' => round(memory_get_usage() / 1024 / 1024, 2) . ' MB', 'time_limit' => ini_get('max_execution_time') . 's' ] ]; $health_summary['total_checks'] = 1; $health_summary['passed_checks'] = 1; } // 根据检查结果确定HTTP状态码 $http_code = 200; if ($health_summary['status'] === 'unhealthy') { $http_code = 503; // Service Unavailable } elseif ($health_summary['status'] === 'warning') { $http_code = 200; // Still OK but with warnings } return $this->response([ 'code' => 0, 'message' => $health_summary['status'], 'data' => $health_summary ], $http_code); } catch (\Exception $e) { return $this->response([ 'code' => -1, 'message' => '健康检查失败', 'data' => [ 'status' => 'error', 'check_id' => $event_data['check_id'], 'timestamp' => $event_data['timestamp'], 'error' => $e->getMessage(), 'response_time_ms' => round((microtime(true) - $start_time) * 1000, 2) ] ], 500); } } /** * 清除会话历史 */ public function clearConversation() { // 获取请求参数 $conversation_id = $this->params['conversation_id'] ?? ''; $user_id = $this->params['user_id'] ?? $this->member_id; // (可选)获取站点ID和会员ID,可以通过事件数据传递 $site_id = $this->params['uniacid'] ?? $this->site_id; // 使用 uniacid, 方便以后迁移,而且uniacid 是唯一的, site_id 不是,同时被params给过滤了 $member_id = $this->params['member_id'] ?? $this->member_id; $token = $this->params['token'] ?? $this->token; // 验证参数 if (empty($conversation_id)) { return $this->response($this->error('会话ID不能为空')); } try { // 准备事件数据 $event_data = [ 'conversation_id' => $conversation_id, 'user_id' => $user_id, 'site_id' =>$site_id, 'member_id' => $member_id, 'token' => $token, ]; // 触发清除会话事件 $result = Event::trigger('KefuClearConversation', $event_data); // 处理事件结果 $response = [ 'code' => 0, 'message' => 'success', 'data' => [] ]; if (is_array($result) && !empty($result)) { foreach ($result as $res) { if (isset($res['code']) && $res['code'] < 0) { $response = $res; break; } if (isset($res['data'])) { $response['data'] = array_merge($response['data'], $res['data']); } } } return $this->response($response); } catch (\Exception $e) { return $this->response($e->getMessage()); } } /** * 智能客服聊天接口 * @return \think\response\Json */ public function chat() { // 获取请求参数 $message = $this->params['message'] ?? ''; $user_id = $this->params['user_id'] ?? $this->member_id; $conversation_id = $this->params['conversation_id'] ?? ''; $stream = $this->params['stream'] ?? false; // (可选)获取站点ID和会员ID,可以通过事件数据传递 $site_id = $this->params['uniacid'] ?? $this->site_id; // 使用 uniacid, 方便以后迁移,而且uniacid 是唯一的, site_id 不是,同时被params给过滤了 $member_id = $this->params['member_id'] ?? $this->member_id; $token = $this->params['token'] ?? $this->token; // 验证参数 if (empty($message)) { return $this->response($this->error('请输入消息内容')); } try { // 准备事件数据 $event_data = [ 'message' => $message, 'user_id' => $user_id, 'conversation_id' => $conversation_id, 'stream' => $stream, 'site_id' =>$site_id, 'member_id' => $member_id, 'token' => $token, ]; // 触发智能客服聊天事件 $result = Event::trigger('KefuChat', $event_data); // 处理事件结果 $response = [ 'code' => 0, 'message' => 'success', 'data' => [] ]; if (is_array($result) && !empty($result)) { foreach ($result as $res) { if (isset($res['code']) && $res['code'] < 0) { $response = $res; break; } if (isset($res['data'])) { $response['data'] = array_merge($response['data'], $res['data']); } } } return $this->response($response); } catch (\Exception $e) { return $this->response($this->error('请求失败:' . $e->getMessage())); } } /** * 智能客服聊天流式接口 */ public function chatStream() { // 获取请求参数 $message = $this->params['message'] ?? ''; $user_id = $this->params['user_id'] ?? $this->member_id; $conversation_id = $this->params['conversation_id'] ?? ''; $stream = true; // (可选)获取站点ID和会员ID,可以通过事件数据传递 $site_id = $this->params['uniacid'] ?? $this->site_id; // 使用 uniacid, 方便以后迁移,而且uniacid 是唯一的, site_id 不是,同时被params给过滤了 $member_id = $this->params['member_id'] ?? $this->member_id; $token = $this->params['token'] ?? $this->token; // 验证参数 if (empty($message)) { $this->sendStreamError('请输入消息内容'); return; } try { // 设置流式响应头 $this->setStreamHeaders(); // 准备事件数据 $event_data = [ 'message' => $message, 'user_id' => $user_id, 'conversation_id' => $conversation_id, 'stream' => $stream, 'site_id' =>$site_id, 'member_id' => $member_id, 'token' => $token, 'request_id' => uniqid('stream_', true), 'timestamp' => time(), ]; // 发送开始事件 $this->sendStreamEvent('start', [ 'request_id' => $event_data['request_id'], 'timestamp' => $event_data['timestamp'], 'message' => '开始处理请求' ]); // 触发流式聊天事件 $result = Event::trigger('KefuChatStream', $event_data); // 处理事件结果并流式输出 $this->processStreamResults($result, $event_data); } catch (\Exception $e) { $this->sendStreamError('请求失败:' . $e->getMessage()); } } /** * 设置流式响应头 */ private function setStreamHeaders() { header('Content-Type: text/event-stream'); header('Cache-Control: no-cache'); header('Connection: keep-alive'); header('Access-Control-Allow-Origin: *'); header('Access-Control-Allow-Headers: Cache-Control, Content-Type'); header('X-Accel-Buffering: no'); // 禁用nginx缓冲 if (function_exists('apache_setenv')) { apache_setenv('no-gzip', '1'); } } /** * 发送流式事件 */ private function sendStreamEvent($event_type, $data) { $payload = [ 'id' => uniqid(), 'event' => $event_type, 'timestamp' => time(), 'data' => $data ]; echo "event: {$event_type}\n"; echo "data: " . json_encode($payload, JSON_UNESCAPED_UNICODE) . "\n\n"; if (ob_get_level()) { ob_flush(); } flush(); } /** * 发送流式错误 */ private function sendStreamError($message) { if (!headers_sent()) { $this->setStreamHeaders(); } $this->sendStreamEvent('error', [ 'error' => $message, 'finished' => true ]); exit; } /** * 处理流式结果 */ private function processStreamResults($results, $event_data) { try { if (is_array($results) && !empty($results)) { foreach ($results as $result) { if (isset($result['type'])) { switch ($result['type']) { case 'chunk': // 发送内容块 $this->sendStreamEvent('message', [ 'content' => $result['content'] ?? '', 'conversation_id' => $result['conversation_id'] ?? $event_data['conversation_id'], 'message_id' => $result['message_id'] ?? '', 'finished' => $result['finished'] ?? false ]); break; case 'error': // 发送错误 $this->sendStreamEvent('error', [ 'error' => $result['message'] ?? '未知错误', 'conversation_id' => $result['conversation_id'] ?? $event_data['conversation_id'] ]); break; case 'complete': // 发送完成事件 $this->sendStreamEvent('complete', [ 'conversation_id' => $result['conversation_id'] ?? $event_data['conversation_id'], 'message_id' => $result['message_id'] ?? '', 'usage' => $result['usage'] ?? [], 'finish_reason' => $result['finish_reason'] ?? '' ]); break; } } } } else { // 如果没有事件处理器或结果为空,发送完成事件 $this->sendStreamEvent('complete', [ 'conversation_id' => $event_data['conversation_id'], 'message' => '暂无可用响应' ]); } // 发送结束事件 $this->sendStreamEvent('end', [ 'request_id' => $event_data['request_id'], 'status' => 'completed' ]); } catch (\Exception $e) { $this->sendStreamError('处理流式结果失败:' . $e->getMessage()); } } /** * 创建新会话 * @return \think\response\Json */ public function createConversation() { // 获取请求参数 $user_id = $this->params['user_id'] ?? $this->member_id; // (可选)获取站点ID和会员ID,可以通过事件数据传递 $site_id = $this->params['uniacid'] ?? $this->site_id; // 使用 uniacid, 方便以后迁移,而且uniacid 是唯一的, site_id 不是,同时被params给过滤了 $member_id = $this->params['member_id'] ?? $this->member_id; $token = $this->params['token'] ?? $this->token; try { // 准备事件数据 $event_data = [ 'user_id' => $user_id, 'site_id' =>$site_id, 'member_id' => $member_id, 'token' => $token, ]; // 触发创建会话事件 $result = Event::trigger('KefuCreateConversation', $event_data); // 处理事件结果 $response = [ 'code' => 0, 'message' => 'success', 'data' => [] ]; if (is_array($result) && !empty($result)) { foreach ($result as $res) { if (isset($res['code']) && $res['code'] < 0) { $response = $res; break; } if (isset($res['data'])) { $response['data'] = array_merge($response['data'], $res['data']); } } } return $this->response($response); } catch (\Exception $e) { return $this->response($this->error('请求失败:' . $e->getMessage())); } } /** * 获取会话历史 * @return \think\response\Json */ public function getHistory() { // 获取请求参数 $conversation_id = $this->params['conversation_id'] ?? ''; $user_id = $this->params['user_id'] ?? $this->member_id; $limit = $this->params['limit'] ?? 20; $offset = $this->params['offset'] ?? 0; // (可选)获取站点ID和会员ID,可以通过事件数据传递 $site_id = $this->params['uniacid'] ?? $this->site_id; // 使用 uniacid, 方便以后迁移,而且uniacid 是唯一的, site_id 不是,同时被params给过滤了 $member_id = $this->params['member_id'] ?? $this->member_id; $token = $this->params['token'] ?? $this->token; // 验证参数 if (empty($conversation_id)) { return $this->response($this->error('会话ID不能为空')); } try { // 准备事件数据 $event_data = [ 'conversation_id' => $conversation_id, 'user_id' => $user_id, 'limit' => $limit, 'offset' => $offset, 'site_id' =>$site_id, 'member_id' => $member_id, 'token' => $token, ]; // 触发获取历史消息事件 $result = Event::trigger('KefuGetHistory', $event_data); // 处理事件结果 $response = [ 'code' => 0, 'message' => 'success', 'data' => [] ]; if (is_array($result) && !empty($result)) { foreach ($result as $res) { if (isset($res['code']) && $res['code'] < 0) { $response = $res; break; } if (isset($res['data'])) { $response['data'] = array_merge($response['data'], $res['data']); } } } return $this->response($response); } catch (\Exception $e) { return $this->response($this->error('请求失败:' . $e->getMessage())); } } }