diff --git a/Server/application/ai/controller/CozeAI.php b/Server/application/ai/controller/CozeAI.php index 9d9bf150..5208326f 100644 --- a/Server/application/ai/controller/CozeAI.php +++ b/Server/application/ai/controller/CozeAI.php @@ -76,7 +76,7 @@ class CozeAI extends Controller $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' => $result['code'], 'msg' => $result['msg'], 'data' => []]); } return json_encode(['code' => 200, 'msg' => '创建成功', 'data' => $result['data']]); @@ -136,9 +136,9 @@ class CozeAI extends Controller $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' => $result['code'], 'msg' => $result['msg'], 'data' => []]); } - return json_encode(['code' => 200, 'msg' => '获取成功']); + return json_encode(['code' => 200, 'msg' => '更新成功', 'data' => []]); } @@ -162,9 +162,9 @@ class CozeAI extends Controller $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' => $result['code'], 'msg' => $result['msg'], 'data' => []]); } - return json_encode(['code' => 200, 'msg' => '发布成功']); + return json_encode(['code' => 200, 'msg' => '发布成功', 'data' => []]); } @@ -191,7 +191,7 @@ class CozeAI extends Controller $result = json_decode($result, true); if ($result['code'] != 0) { - return errorJson($result['msg'], $result['code']); + return json_encode(['code' => $result['code'], 'msg' => $result['msg'], 'data' => []]); } return json_encode(['code' => 200, 'msg' => '创建成功','data' => $result['data']]); } @@ -231,7 +231,7 @@ class CozeAI extends Controller $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' => $result['code'], 'msg' => $result['msg'], 'data' => []]); } return json_encode(['code' => 200, 'msg' => '创建成功','data' => $result['document_infos']]); } @@ -254,9 +254,9 @@ class CozeAI extends Controller $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' => $result['code'], 'msg' => $result['msg'], 'data' => []]); } - return json_encode(['code' => 200, 'msg' => '删除成功']); + return json_encode(['code' => 200, 'msg' => '删除成功', 'data' => []]); } @@ -285,7 +285,7 @@ class CozeAI extends Controller $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' => $result['code'], 'msg' => $result['msg'], 'data' => []]); } return json_encode(['code' => 200, 'msg' => '创建成功','data' => $result['data']]); } @@ -306,15 +306,15 @@ class CozeAI extends Controller if(empty($bot_id)){ - return errorJson('智能体ID不能为空'); + return json_encode(['code' => 500, 'msg' => '智能体ID不能为空', 'data' => []]); } if(empty($conversation_id)){ - return errorJson('会话ID不能为空'); + return json_encode(['code' => 500, 'msg' => '会话ID不能为空', 'data' => []]); } if(empty($question)){ - return errorJson('问题不能为空'); + return json_encode(['code' => 500, 'msg' => '问题不能为空', 'data' => []]); } // 构建请求数据 @@ -330,16 +330,21 @@ class CozeAI extends Controller $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' => $result['code'], 'msg' => $result['msg'], 'data' => []]); } return json_encode(['code' => 200, 'msg' => '发送成功','data' => $result['data']]); } catch (\Exception $e) { - return errorJson('创建对话失败:' . $e->getMessage()); + return json_encode(['code' => 500, 'msg' => '创建对话失败:' . $e->getMessage(), 'data' => []]); } } + /** + * 查看对话详情 + * @param $data + * @return false|string|\think\response\Json + */ public function getConversationChat($data = []) { $conversation_id = !empty($data['conversation_id']) ? $data['conversation_id'] : ''; @@ -348,12 +353,17 @@ class CozeAI extends Controller $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' => $result['code'], 'msg' => $result['msg'], 'data' => []]); } - return json_encode(['code' => 200, 'msg' => '发送成功','data' => $result['data']]); + return json_encode(['code' => 200, 'msg' => '获取成功','data' => $result['data']]); } + /** + * 查看对话消息详情 + * @param $data + * @return false|string|\think\response\Json + */ public function listConversationMessage($data = []) { $conversation_id = !empty($data['conversation_id']) ? $data['conversation_id'] : ''; @@ -362,9 +372,35 @@ class CozeAI extends Controller $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' => $result['code'], 'msg' => $result['msg'], 'data' => []]); } - return json_encode(['code' => 200, 'msg' => '发送成功','data' => $result['data']]); + return json_encode(['code' => 200, 'msg' => '获取成功','data' => $result['data']]); + } + + + /** + * 取消进行中的对话 + * @param $data + * @return false|string|\think\response\Json + */ + public function cancelConversationChat($data = []) + { + $conversation_id = !empty($data['conversation_id']) ? $data['conversation_id'] : ''; + $chat_id = !empty($data['chat_id']) ? $data['chat_id'] : ''; + + // 构建请求数据 + $params = [ + 'conversation_id' => (string) $conversation_id, + 'chat_id' => (string) $chat_id + ]; + + $url = $this->apiUrl . '/v3/chat/cancel'; + $result = requestCurl($url, $params, 'POST', $this->headers, 'json'); + $result = json_decode($result, true); + if ($result['code'] != 0) { + return json_encode(['code' => $result['code'], 'msg' => $result['msg'], 'data' => []]); + } + return json_encode(['code' => 200, 'msg' => '取消成功', 'data' => []]); } } \ No newline at end of file diff --git a/Server/application/chukebao/config/route.php b/Server/application/chukebao/config/route.php index 9dd32c2c..491f16e4 100644 --- a/Server/application/chukebao/config/route.php +++ b/Server/application/chukebao/config/route.php @@ -12,10 +12,12 @@ Route::group('v1/', function () { //好友相关 Route::group('wechatFriend/', function () { Route::get('list', 'app\chukebao\controller\WechatFriendController@getList'); // 获取好友列表 + Route::get('detail', 'app\chukebao\controller\WechatFriendController@getDetail'); // 获取好友详情 }); //群相关 Route::group('wechatChatroom/', function () { Route::get('list', 'app\chukebao\controller\WechatChatroomController@getList'); // 获取好友列表 + Route::get('detail', 'app\chukebao\controller\WechatChatroomController@getDetail'); // 获取群详情 Route::post('aiAnnouncement', 'app\chukebao\controller\WechatChatroomController@aiAnnouncement'); // AI群公告 }); diff --git a/Server/application/chukebao/controller/AiChatController.php b/Server/application/chukebao/controller/AiChatController.php index 919204f0..29699262 100644 --- a/Server/application/chukebao/controller/AiChatController.php +++ b/Server/application/chukebao/controller/AiChatController.php @@ -11,6 +11,9 @@ use app\chukebao\model\FriendSettings; use app\chukebao\model\TokensCompany; use library\ResponseHelper; use think\Db; +use think\facade\Cache; +use think\facade\Log; + /** * AI聊天控制器 @@ -27,8 +30,16 @@ class AiChatController extends BaseController const STATUS_CANCELED = 'canceled'; // 对话已取消 // 轮询配置 - const MAX_RETRY_TIMES = 30; // 最大重试次数 - const RETRY_INTERVAL = 2; // 重试间隔(秒) + const MAX_RETRY_TIMES = 1000; // 最大重试次数 + const RETRY_INTERVAL = 500000; // 重试间隔(微秒,即500毫秒) + + // 并发控制 + const CACHE_EXPIRE = 30; // 缓存过期时间(秒) + + // 请求唯一标识符 + private $requestKey = ''; + private $requestId = ''; + private $currentStep = 0; /** * AI聊天主入口 @@ -40,59 +51,219 @@ class AiChatController extends BaseController try { // 1. 参数验证和初始化 $params = $this->validateAndInitParams(); + if ($params === false) { return ResponseHelper::error('参数验证失败'); } + + // 并发控制:检查并处理同一用户的重复请求 + $this->requestKey = "aichat_{$params['friendId']}_{$params['wechatAccountId']}"; + $this->requestId = uniqid('req_', true); + + $concurrentCheck = $this->handleConcurrentRequest($params); + if ($concurrentCheck !== true) { + return $concurrentCheck; // 返回错误响应 + } + + $this->currentStep = 1; // 2. 验证Tokens余额 - if (!$this->checkTokensBalance($params['companyId'])) { + $this->updateRequestStep(2); + if ($this->isRequestCanceled()) { + return ResponseHelper::error('该好友有新的AI对话请求正在处理中,当前请求已被取消'); + } + $hasBalance = $this->checkTokensBalance($params['companyId']); + + if (!$hasBalance) { + $this->clearRequestCache(); return ResponseHelper::error('Tokens余额不足,请充值后再试'); } // 3. 获取AI配置 + $this->updateRequestStep(3); + if ($this->isRequestCanceled()) { + return ResponseHelper::error('该好友有新的AI对话请求正在处理中,当前请求已被取消'); + } $setting = $this->getAiSettings($params['companyId']); + if (!$setting) { + $this->clearRequestCache(); return ResponseHelper::error('未找到AI配置信息,请先配置AI策略'); } // 4. 获取好友AI设置 + $this->updateRequestStep(4); + if ($this->isRequestCanceled()) { + return ResponseHelper::error('该好友有新的AI对话请求正在处理中,当前请求已被取消'); + } $friendSettings = $this->getFriendSettings($params['companyId'], $params['friendId']); + if (!$friendSettings) { + $this->clearRequestCache(); return ResponseHelper::error('该好友未配置或未开启AI功能'); } // 5. 确保会话存在 + $this->updateRequestStep(5); + if ($this->isRequestCanceled()) { + return ResponseHelper::error('该好友有新的AI对话请求正在处理中,当前请求已被取消'); + } $conversationId = $this->ensureConversation($friendSettings, $setting, $params); + if (!$conversationId) { + $this->clearRequestCache(); return ResponseHelper::error('创建会话失败'); } // 6. 获取历史消息 + $this->updateRequestStep(6); + if ($this->isRequestCanceled()) { + return ResponseHelper::error('该好友有新的AI对话请求正在处理中,当前请求已被取消'); + } $msgData = $this->getHistoryMessages($params['friendId'], $friendSettings); - // 7. 创建AI对话 + // 7. 创建AI对话(从这步开始需要保存对话ID以便取消) + $this->updateRequestStep(7); + if ($this->isRequestCanceled($conversationId, null)) { + return ResponseHelper::error('该好友有新的AI对话请求正在处理中,当前请求已被取消'); + } $chatId = $this->createAiChat($setting, $friendSettings, $msgData); + if (!$chatId) { + $this->clearRequestCache(); return ResponseHelper::error('创建对话失败'); } + + // 保存对话ID到缓存,以便新请求可以取消 + $this->updateRequestStep(7, $conversationId, $chatId); // 8. 等待AI处理完成(轮询) - $chatResult = $this->waitForChatCompletion($conversationId, $chatId); - if (!$chatResult) { - return ResponseHelper::error('AI处理超时或失败'); + $this->updateRequestStep(8, $conversationId, $chatId); + if ($this->isRequestCanceled($conversationId, $chatId)) { + return ResponseHelper::error('该好友有新的AI对话请求正在处理中,当前请求已被取消'); } + $chatResult = $this->waitForChatCompletion($conversationId, $chatId); + + if (!$chatResult['success']) { + $this->clearRequestCache(); + return ResponseHelper::error($chatResult['error']); + } + + $chatResult = $chatResult['data']; // 9. 扣除Tokens + $this->updateRequestStep(9, $conversationId, $chatId); + if ($this->isRequestCanceled($conversationId, $chatId)) { + return ResponseHelper::error('该好友有新的AI对话请求正在处理中,当前请求已被取消'); + } $this->consumeTokens($chatResult, $params, $friendSettings); // 10. 获取对话消息 + $this->updateRequestStep(10, $conversationId, $chatId); + if ($this->isRequestCanceled($conversationId, $chatId)) { + return ResponseHelper::error('该好友有新的AI对话请求正在处理中,当前请求已被取消'); + } $messages = $this->getChatMessages($conversationId, $chatId); + if (!$messages) { return ResponseHelper::error('获取对话消息失败'); } - return ResponseHelper::success($messages[1]['content'], '对话成功'); + + // 筛选type为answer的消息(AI回复的内容) + $answerContent = ''; + foreach ($messages as $msg) { + if (isset($msg['type']) && $msg['type'] === 'answer') { + $answerContent = $msg['content'] ?? ''; + break; + } + } + + if (empty($answerContent)) { + Log::warning('未找到AI回复内容,messages: ' . json_encode($messages)); + return ResponseHelper::error('未获取到AI回复内容'); + } + + // 清理请求缓存 + $this->clearRequestCache(); + + // 返回结果 + return ResponseHelper::success(['content' => $answerContent], '对话成功'); + } catch (\Exception $e) { - \think\facade\Log::error('AI聊天异常:' . $e->getMessage()); + Log::error('AI聊天异常:' . $e->getMessage()); + + // 清理请求缓存 + $this->clearRequestCache(); + + return ResponseHelper::error('系统异常:' . $e->getMessage()); + } + } + + /** + * 取消AI对话 + * 取消当前正在进行的AI对话请求 + * + * @return \think\response\Json + */ + public function cancel() + { + try { + // 获取参数 + $friendId = $this->request->param('friendId', ''); + $wechatAccountId = $this->request->param('wechatAccountId', ''); + + if (empty($wechatAccountId) || empty($friendId)) { + return ResponseHelper::error('参数缺失'); + } + + // 生成缓存键 + $requestKey = "aichat_{$friendId}_{$wechatAccountId}"; + + // 获取缓存数据 + $cacheData = Cache::get($requestKey); + + if (!$cacheData) { + return ResponseHelper::error('当前没有正在进行的AI对话'); + } + + $requestId = $cacheData['request_id'] ?? ''; + $step = $cacheData['step'] ?? 0; + $conversationId = $cacheData['conversation_id'] ?? ''; + $chatId = $cacheData['chat_id'] ?? ''; + + Log::info("手动取消AI对话 - 请求ID: {$requestId}, 步骤: {$step}"); + + // 如果已经到达步骤7或之后,需要调用取消API + if ($step >= 7 && !empty($conversationId) && !empty($chatId)) { + try { + $cozeAI = new CozeAI(); + $cancelResult = $cozeAI->cancelConversationChat([ + 'conversation_id' => $conversationId, + 'chat_id' => $chatId, + ]); + + $result = json_decode($cancelResult, true); + if ($result['code'] != 200) { + Log::error("调用取消API失败 - conversation_id: {$conversationId}, chat_id: {$chatId}, 错误: " . ($result['msg'] ?? '未知错误')); + } else { + Log::info("成功调用取消API - conversation_id: {$conversationId}, chat_id: {$chatId}"); + } + } catch (\Exception $e) { + Log::error("调用取消API异常:" . $e->getMessage()); + } + } + + // 清理缓存 + Cache::rm($requestKey); + Log::info("已清理AI对话缓存 - 请求ID: {$requestId}"); + + return ResponseHelper::success([ + 'canceled_request_id' => $requestId, + 'step' => $step + ], 'AI对话已取消'); + + } catch (\Exception $e) { + Log::error('取消AI对话异常:' . $e->getMessage()); return ResponseHelper::error('系统异常:' . $e->getMessage()); } } @@ -194,7 +365,7 @@ class AiChatController extends BaseController $res = json_decode($res, true); if ($res['code'] != 200) { - \think\facade\Log::error('创建会话失败:' . ($res['msg'] ?? '未知错误')); + Log::error('创建会话失败:' . ($res['msg'] ?? '未知错误')); return null; } @@ -292,7 +463,7 @@ class AiChatController extends BaseController $res = json_decode($res, true); if ($res['code'] != 200) { - \think\facade\Log::error('创建对话失败:' . ($res['msg'] ?? '未知错误')); + Log::error('创建对话失败:' . ($res['msg'] ?? '未知错误')); return null; } @@ -304,7 +475,7 @@ class AiChatController extends BaseController * * @param string $conversationId 会话ID * @param string $chatId 对话ID - * @return array|null + * @return array ['success' => bool, 'data' => array|null, 'error' => string] */ private function waitForChatCompletion($conversationId, $chatId) { @@ -320,8 +491,9 @@ class AiChatController extends BaseController $res = json_decode($res, true); if ($res['code'] != 200) { - \think\facade\Log::error('获取对话状态失败:' . ($res['msg'] ?? '未知错误')); - return null; + $errorMsg = 'AI接口调用失败:' . ($res['msg'] ?? '未知错误'); + Log::error($errorMsg); + return ['success' => false, 'data' => null, 'error' => $errorMsg]; } $status = $res['data']['status'] ?? ''; @@ -330,36 +502,41 @@ class AiChatController extends BaseController switch ($status) { case self::STATUS_COMPLETED: // 对话完成,返回结果 - return $res['data']; + return ['success' => true, 'data' => $res['data'], 'error' => '']; case self::STATUS_IN_PROGRESS: case self::STATUS_CREATED: // 继续等待 $retryCount++; - sleep(self::RETRY_INTERVAL); + usleep(self::RETRY_INTERVAL); break; case self::STATUS_FAILED: - \think\facade\Log::error('对话失败,chat_id: ' . $chatId); - return null; + $errorMsg = 'AI对话处理失败'; + Log::error($errorMsg . ',chat_id: ' . $chatId); + return ['success' => false, 'data' => null, 'error' => $errorMsg]; case self::STATUS_CANCELED: - \think\facade\Log::error('对话已取消,chat_id: ' . $chatId); - return null; + $errorMsg = 'AI对话已被取消'; + Log::error($errorMsg . ',chat_id: ' . $chatId); + return ['success' => false, 'data' => null, 'error' => $errorMsg]; case self::STATUS_REQUIRES_ACTION: - \think\facade\Log::warning('对话需要进一步处理,chat_id: ' . $chatId); - return null; + $errorMsg = 'AI对话需要进一步处理'; + Log::warning($errorMsg . ',chat_id: ' . $chatId); + return ['success' => false, 'data' => null, 'error' => $errorMsg]; default: - \think\facade\Log::error('未知状态:' . $status); - return null; + $errorMsg = 'AI返回未知状态:' . $status; + Log::error($errorMsg); + return ['success' => false, 'data' => null, 'error' => $errorMsg]; } } // 超时 - \think\facade\Log::error('对话处理超时,chat_id: ' . $chatId); - return null; + $errorMsg = 'AI对话处理超时,已等待' . (self::MAX_RETRY_TIMES * self::RETRY_INTERVAL / 1000000) . '秒'; + Log::error($errorMsg . ',chat_id: ' . $chatId); + return ['success' => false, 'data' => null, 'error' => $errorMsg]; } /** @@ -412,13 +589,154 @@ class AiChatController extends BaseController $res = json_decode($res, true); if ($res['code'] != 200) { - \think\facade\Log::error('获取对话消息失败:' . ($res['msg'] ?? '未知错误')); + Log::error('获取对话消息失败:' . ($res['msg'] ?? '未知错误')); return null; } return $res['data'] ?? []; } + /** + * 处理并发请求 + * 检查是否有同一用户的旧请求正在处理,如果有则取消旧请求 + * + * @param array $params 请求参数 + * @return true|\think\response\Json true表示可以继续,否则返回错误响应 + */ + private function handleConcurrentRequest($params) + { + $cacheData = Cache::get($this->requestKey); + + if ($cacheData) { + // 有旧请求正在处理 + $oldRequestId = $cacheData['request_id'] ?? ''; + $oldStep = $cacheData['step'] ?? 0; + $oldConversationId = $cacheData['conversation_id'] ?? ''; + $oldChatId = $cacheData['chat_id'] ?? ''; + + Log::info("检测到并发请求 - 旧请求: {$oldRequestId} (步骤{$oldStep}), 新请求: {$this->requestId}"); + + // 如果旧请求已经到达步骤7或之后,需要调用取消API + if ($oldStep >= 7 && !empty($oldConversationId) && !empty($oldChatId)) { + try { + $cozeAI = new CozeAI(); + $cancelResult = $cozeAI->cancelConversationChat([ + 'conversation_id' => $oldConversationId, + 'chat_id' => $oldChatId, + ]); + Log::info("已调用取消API取消旧请求的对话 - conversation_id: {$oldConversationId}, chat_id: {$oldChatId}"); + } catch (\Exception $e) { + Log::error("取消旧请求对话失败:" . $e->getMessage()); + } + } + + // 标记旧请求为已取消(通过更新缓存的 canceled 标志) + $cacheData['canceled'] = true; + $cacheData['canceled_by'] = $this->requestId; + Cache::set($this->requestKey, $cacheData, self::CACHE_EXPIRE); + } + + // 设置当前请求为活动请求 + $newCacheData = [ + 'request_id' => $this->requestId, + 'step' => 1, + 'start_time' => time(), + 'canceled' => false, + 'conversation_id' => '', + 'chat_id' => '', + ]; + Cache::set($this->requestKey, $newCacheData, self::CACHE_EXPIRE); + + return true; + } + + /** + * 检查当前请求是否被新请求取消 + * + * @param string $conversationId 会话ID(可选,用于取消对话) + * @param string $chatId 对话ID(可选,用于取消对话) + * @return bool + */ + private function isRequestCanceled($conversationId = '', $chatId = '') + { + $cacheData = Cache::get($this->requestKey); + + if (!$cacheData) { + // 缓存不存在,说明被清理或过期,视为被取消 + return true; + } + + $currentRequestId = $cacheData['request_id'] ?? ''; + $isCanceled = $cacheData['canceled'] ?? false; + + // 如果缓存中的请求ID与当前请求ID不一致,或者被标记为取消 + if ($currentRequestId !== $this->requestId || $isCanceled) { + Log::info("当前请求已被取消 - 请求ID: {$this->requestId}, 缓存请求ID: {$currentRequestId}, 取消标志: " . ($isCanceled ? 'true' : 'false')); + + // 如果提供了对话ID,尝试取消对话 + if (!empty($conversationId) && !empty($chatId) && $this->currentStep >= 7) { + try { + $cozeAI = new CozeAI(); + $cancelResult = $cozeAI->cancelConversationChat([ + 'conversation_id' => $conversationId, + 'chat_id' => $chatId, + ]); + Log::info("已取消当前请求的对话 - conversation_id: {$conversationId}, chat_id: {$chatId}"); + } catch (\Exception $e) { + Log::error("取消当前请求对话失败:" . $e->getMessage()); + } + } + + return true; + } + + return false; + } + + /** + * 更新请求步骤 + * + * @param int $step 当前步骤 + * @param string $conversationId 会话ID(可选) + * @param string $chatId 对话ID(可选) + */ + private function updateRequestStep($step, $conversationId = '', $chatId = '') + { + $this->currentStep = $step; + + $cacheData = Cache::get($this->requestKey); + + if ($cacheData && $cacheData['request_id'] === $this->requestId) { + $cacheData['step'] = $step; + $cacheData['update_time'] = time(); + + if (!empty($conversationId)) { + $cacheData['conversation_id'] = $conversationId; + } + if (!empty($chatId)) { + $cacheData['chat_id'] = $chatId; + } + + Cache::set($this->requestKey, $cacheData, self::CACHE_EXPIRE); + } + } + + /** + * 清理请求缓存 + */ + private function clearRequestCache() + { + if (!empty($this->requestKey)) { + $cacheData = Cache::get($this->requestKey); + + // 只有当前请求才能清理自己的缓存 + if ($cacheData && isset($cacheData['request_id']) && $cacheData['request_id'] === $this->requestId) { + Cache::rm($this->requestKey); + Log::info("已清理请求缓存 - 请求ID: {$this->requestId}"); + } + } + } + public function index2222() { diff --git a/Server/application/chukebao/controller/MessageController.php b/Server/application/chukebao/controller/MessageController.php index 5593eafb..1d44d14d 100644 --- a/Server/application/chukebao/controller/MessageController.php +++ b/Server/application/chukebao/controller/MessageController.php @@ -30,7 +30,7 @@ class MessageController extends BaseController // 优化后的查询:使用MySQL兼容的查询方式 $unionQuery = " - (SELECT m.id, m.content, m.wechatFriendId, m.wechatChatroomId, m.createTime, m.wechatTime, 2 as msgType, wc.nickname, wc.chatroomAvatar as avatar, wc.chatroomId + (SELECT m.id, m.content, m.wechatFriendId, m.wechatChatroomId, m.createTime, m.wechatTime,m.wechatAccountId, 2 as msgType, wc.nickname, wc.chatroomAvatar as avatar, wc.chatroomId FROM s2_wechat_chatroom wc INNER JOIN s2_wechat_message m ON wc.id = m.wechatChatroomId AND m.type = 2 INNER JOIN ( @@ -42,7 +42,7 @@ class MessageController extends BaseController WHERE wc.accountId = {$accountId} AND wc.isDeleted = 0 ) UNION ALL - (SELECT m.id, m.content, m.wechatFriendId, m.wechatChatroomId, m.createTime, m.wechatTime, 1 as msgType, 1 as nickname, 1 as avatar, 1 as chatroomId + (SELECT m.id, m.content, m.wechatFriendId, m.wechatChatroomId, m.createTime, m.wechatTime, 1 as msgType, 1 as nickname, 1 as avatar, 1 as chatroomId, 1 as wechatAccountId FROM s2_wechat_message m INNER JOIN ( SELECT wechatFriendId, MAX(wechatTime) as maxTime, MAX(id) as maxId diff --git a/Server/application/chukebao/controller/WechatChatroomController.php b/Server/application/chukebao/controller/WechatChatroomController.php index 76edb7e0..bd07bb31 100644 --- a/Server/application/chukebao/controller/WechatChatroomController.php +++ b/Server/application/chukebao/controller/WechatChatroomController.php @@ -83,7 +83,51 @@ class WechatChatroomController extends BaseController return ResponseHelper::success(['list'=>$list,'total'=>$total]); } + public function getDetail(){ + $id = input('id', 0); + + if (!$id) { + return ResponseHelper::error('聊天室ID不能为空'); + } + $accountId = $this->getUserInfo('s2_accountId'); + if (empty($accountId)){ + return ResponseHelper::error('请先登录'); + } + + $detail = Db::table('s2_wechat_chatroom') + ->where(['accountId' => $accountId, 'id' => $id, 'isDeleted' => 0]) + ->find(); + + if (!$detail) { + return ResponseHelper::error('聊天室不存在或无权限访问'); + } + + // 处理时间格式 + $detail['createTime'] = !empty($detail['createTime']) ? date('Y-m-d H:i:s', $detail['createTime']) : ''; + $detail['updateTime'] = !empty($detail['updateTime']) ? date('Y-m-d H:i:s', $detail['updateTime']) : ''; + + // 查询未读消息数量 + $unreadCount = Db::table('s2_wechat_message') + ->where('wechatChatroomId', $id) + ->where('isRead', 0) + ->count(); + + // 查询最新消息 + $latestMessage = Db::table('s2_wechat_message') + ->where('wechatChatroomId', $id) + ->order('id desc') + ->find(); + + $config = [ + 'unreadCount' => $unreadCount, + 'chat' => !empty($latestMessage), + 'msgTime' => isset($latestMessage['wechatTime']) ? $latestMessage['wechatTime'] : 0 + ]; + $detail['config'] = $config; + + return ResponseHelper::success($detail); + } public function aiAnnouncement() { diff --git a/Server/application/chukebao/controller/WechatFriendController.php b/Server/application/chukebao/controller/WechatFriendController.php index d94a0ebb..573e6811 100644 --- a/Server/application/chukebao/controller/WechatFriendController.php +++ b/Server/application/chukebao/controller/WechatFriendController.php @@ -101,4 +101,44 @@ class WechatFriendController extends BaseController return ResponseHelper::success(['list' => $list, 'total' => $total]); } + + /** + * 获取单个好友详情 + * @return \think\response\Json + */ + public function getDetail() + { + $friendId = $this->request->param('id'); + $accountId = $this->getUserInfo('s2_accountId'); + + if (empty($accountId)) { + return ResponseHelper::error('请先登录'); + } + + if (empty($friendId)) { + return ResponseHelper::error('好友ID不能为空'); + } + + // 查询好友详情 + $friend = Db::table('s2_wechat_friend') + ->where(['id' => $friendId, 'isDeleted' => 0]) + ->find(); + + if (empty($friend)) { + return ResponseHelper::error('好友不存在'); + } + + // 处理好友数据 + $friend['labels'] = json_decode($friend['labels'], true); + $friend['siteLabels'] = json_decode($friend['siteLabels'], true); + $friend['createTime'] = !empty($friend['createTime']) ? date('Y-m-d H:i:s', $friend['createTime']) : ''; + $friend['updateTime'] = !empty($friend['updateTime']) ? date('Y-m-d H:i:s', $friend['updateTime']) : ''; + $friend['passTime'] = !empty($friend['passTime']) ? date('Y-m-d H:i:s', $friend['passTime']) : ''; + + // 获取AI类型设置 + $aiTypeSetting = FriendSettings::where('friendId', $friendId)->find(); + $friend['aiType'] = $aiTypeSetting ? $aiTypeSetting['type'] : 0; + + return ResponseHelper::success(['detail' => $friend]); + } } \ No newline at end of file