baseUrl = Env::get('api.wechat_url'); $this->authorization = AuthService::getSystemAuthorization(); } public function getList() { $page = $this->request->param('page', 1); $limit = $this->request->param('limit', 10); $accountId = $this->getUserInfo('s2_accountId'); if (empty($accountId)) { return ResponseHelper::error('请先登录'); } $friends = Db::table('s2_wechat_friend') ->where(['accountId' => $accountId, 'isDeleted' => 0]) ->column('id,nickname,avatar,conRemark,labels,groupId,wechatAccountId,wechatId,extendFields,phone,region,isTop'); // 构建好友子查询 $friendSubQuery = Db::table('s2_wechat_friend') ->where(['accountId' => $accountId, 'isDeleted' => 0]) ->field('id') ->buildSql(); // 优化后的查询:使用MySQL兼容的查询方式 $unionQuery = " (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, wc.isTop FROM s2_wechat_chatroom wc INNER JOIN s2_wechat_message m ON wc.id = m.wechatChatroomId AND m.type = 2 INNER JOIN ( SELECT wechatChatroomId, MAX(wechatTime) as maxTime, MAX(id) as maxId FROM s2_wechat_message WHERE type = 2 GROUP BY wechatChatroomId ) latest ON m.wechatChatroomId = latest.wechatChatroomId AND m.wechatTime = latest.maxTime AND m.id = latest.maxId 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, 1 as wechatAccountId, 0 as isTop FROM s2_wechat_message m INNER JOIN ( SELECT wechatFriendId, MAX(wechatTime) as maxTime, MAX(id) as maxId FROM s2_wechat_message WHERE type = 1 AND wechatFriendId IN {$friendSubQuery} GROUP BY wechatFriendId ) latest ON m.wechatFriendId = latest.wechatFriendId AND m.wechatTime = latest.maxTime AND m.id = latest.maxId WHERE m.type = 1 AND m.wechatFriendId IN {$friendSubQuery} ) ORDER BY wechatTime DESC LIMIT " . (($page - 1) * $limit) . ", {$limit} "; $list = Db::query($unionQuery); // 对分页后的结果进行排序(按wechatTime降序) usort($list, function ($a, $b) { return $b['wechatTime'] <=> $a['wechatTime']; }); // 批量统计未读数量(isRead=0),按好友/群聊分别聚合 $friendIds = []; $chatroomIds = []; foreach ($list as $row) { if (!empty($row['wechatFriendId'])) { $friendIds[] = $row['wechatFriendId']; } if (!empty($row['wechatChatroomId'])) { $chatroomIds[] = $row['wechatChatroomId']; } } $friendIds = array_values(array_unique(array_filter($friendIds))); $chatroomIds = array_values(array_unique(array_filter($chatroomIds))); $friendUnreadMap = []; if (!empty($friendIds)) { // 获取未读消息数量 $friendUnreadMap = Db::table('s2_wechat_message') ->where(['isRead' => 0]) ->whereIn('wechatFriendId', $friendIds) ->group('wechatFriendId') ->column('COUNT(*) AS cnt', 'wechatFriendId'); } $chatroomUnreadMap = []; if (!empty($chatroomIds)) { // 获取未读消息数量 $chatroomUnreadMap = Db::table('s2_wechat_message') ->where(['isRead' => 0]) ->whereIn('wechatChatroomId', $chatroomIds) ->group('wechatChatroomId') ->column('COUNT(*) AS cnt', 'wechatChatroomId'); } $aiTypeData = []; if (!empty($friendIds)) { $aiTypeData = FriendSettings::where('friendId', 'in', $friendIds)->column('friendId,type'); } foreach ($list as $k => &$v) { $createTime = !empty($v['createTime']) ? date('Y-m-d H:i:s', $v['createTime']) : ''; $wechatTime = !empty($v['wechatTime']) ? date('Y-m-d H:i:s', $v['wechatTime']) : ''; $unreadCount = 0; $v['aiType'] = 0; if (!empty($v['wechatFriendId'])) { $v['nickname'] = !empty($friends[$v['wechatFriendId']]) ? $friends[$v['wechatFriendId']]['nickname'] : ''; $v['avatar'] = !empty($friends[$v['wechatFriendId']]) ? $friends[$v['wechatFriendId']]['avatar'] : ''; $v['conRemark'] = !empty($friends[$v['wechatFriendId']]) ? $friends[$v['wechatFriendId']]['conRemark'] : ''; $v['groupId'] = !empty($friends[$v['wechatFriendId']]) ? $friends[$v['wechatFriendId']]['groupId'] : ''; $v['wechatAccountId'] = !empty($friends[$v['wechatFriendId']]) ? $friends[$v['wechatFriendId']]['wechatAccountId'] : ''; $v['wechatId'] = !empty($friends[$v['wechatFriendId']]) ? $friends[$v['wechatFriendId']]['wechatId'] : ''; $v['extendFields'] = !empty($friends[$v['wechatFriendId']]) ? $friends[$v['wechatFriendId']]['extendFields'] : []; $v['region'] = !empty($friends[$v['wechatFriendId']]) ? $friends[$v['wechatFriendId']]['region'] : ''; $v['phone'] = !empty($friends[$v['wechatFriendId']]) ? $friends[$v['wechatFriendId']]['phone'] : ''; $v['isTop'] = !empty($friends[$v['wechatFriendId']]) ? $friends[$v['wechatFriendId']]['isTop'] : 0; $v['labels'] = !empty($friends[$v['wechatFriendId']]) ? json_decode($friends[$v['wechatFriendId']]['labels'], true) : []; $unreadCount = isset($friendUnreadMap[$v['wechatFriendId']]) ? (int)$friendUnreadMap[$v['wechatFriendId']] : 0; $v['aiType'] = isset($aiTypeData[$v['wechatFriendId']]) ? $aiTypeData[$v['wechatFriendId']] : 0; unset($v['chatroomId']); } if (!empty($v['wechatChatroomId'])) { $v['conRemark'] = ''; $unreadCount = isset($chatroomUnreadMap[$v['wechatChatroomId']]) ? (int)$chatroomUnreadMap[$v['wechatChatroomId']] : 0; } $v['id'] = !empty($v['wechatFriendId']) ? $v['wechatFriendId'] : $v['wechatChatroomId']; $v['config'] = [ 'top' => !empty($v['isTop']) ? true : false, 'unreadCount' => $unreadCount, 'chat' => true, 'msgTime' => $v['wechatTime'], ]; $v['createTime'] = $createTime; $v['lastUpdateTime'] = $wechatTime; // 最新消息内容已经在UNION查询中获取,直接使用 $v['latestMessage'] = [ 'content' => $v['content'], 'wechatTime' => $wechatTime ]; unset($v['wechatFriendId'], $v['wechatChatroomId'],$v['isTop']); } unset($v); return ResponseHelper::success($list); } public function readMessage() { $wechatFriendId = $this->request->param('wechatFriendId', ''); $wechatChatroomId = $this->request->param('wechatChatroomId', ''); $accountId = $this->getUserInfo('s2_accountId'); if (empty($accountId)) { return ResponseHelper::error('请先登录'); } if (empty($wechatChatroomId) && empty($wechatFriendId)) { return ResponseHelper::error('参数缺失'); } $where = []; if (!empty($wechatChatroomId)) { $where[] = ['wechatChatroomId', '=', $wechatChatroomId]; } if (!empty($wechatFriendId)) { $where[] = ['wechatFriendId', '=', $wechatFriendId]; } Db::table('s2_wechat_message')->where($where)->update(['isRead' => 1]); return ResponseHelper::success([]); } /** * 获取单条消息发送状态(带轮询功能) * @return \think\response\Json */ public function getMessageStatus() { $messageId = $this->request->param('messageId', 0); $wechatAccountId = $this->request->param('wechatAccountId', ''); $accountId = $this->getUserInfo('s2_accountId'); $wechatFriendId = $this->request->param('wechatFriendId', ''); $wechatChatroomId = $this->request->param('wechatChatroomId', ''); if (empty($accountId)) { return ResponseHelper::error('请先登录'); } if (empty($messageId)) { return ResponseHelper::error('消息ID不能为空'); } if(empty($wechatFriendId) && empty($wechatChatroomId)) { return ResponseHelper::error('消息类型不能为空'); } // 查询单条消息的基本信息(只需要发送状态相关字段) $message = Db::table('s2_wechat_message') ->where('id', $messageId) ->field('id,wechatAccountId,wechatFriendId,wechatChatroomId,sendStatus') ->find(); if (empty($message)) { $message = [ 'id' => $messageId, 'wechatAccountId' => $wechatAccountId, 'wechatFriendId' => $wechatFriendId, 'wechatChatroomId' => $wechatChatroomId, 'sendStatus' => 0, ]; } $sendStatus = isset($message['sendStatus']) ? (int)$message['sendStatus'] : 0; $isUpdated = false; $pollCount = 0; $maxPollCount = 10; // 最多轮询10次 // 如果sendStatus不为0,开始轮询 if ($sendStatus != 0) { $messageRequest = [ 'id' => $message['id'], 'wechatAccountId' => !empty($wechatAccountId) ? $wechatAccountId : $message['wechatAccountId'], 'wechatFriendId' => !empty($message['wechatFriendId']) ? $message['wechatFriendId'] : '', 'wechatChatroomId' => !empty($message['wechatChatroomId']) ? $message['wechatChatroomId'] : '', 'from' => '', 'to' => '', ]; // 轮询逻辑:最多10次 while ($pollCount < $maxPollCount && $sendStatus != 0) { $pollCount++; // 请求线上接口获取最新状态 $newData = $this->fetchLatestMessageFromApi($messageRequest); if (!empty($newData)) { // 重新查询消息状态(可能已更新) $updatedMessage = Db::table('s2_wechat_message') ->where('id', $messageId) ->field('sendStatus') ->find(); if (!empty($updatedMessage)) { $newSendStatus = isset($updatedMessage['sendStatus']) ? (int)$updatedMessage['sendStatus'] : 0; // 如果状态已更新为0(已发送),停止轮询 if ($newSendStatus == 0) { $sendStatus = 0; $isUpdated = true; break; } // 如果状态仍然是1,继续轮询(但需要等待一下,避免请求过快) if ($newSendStatus != 0 && $pollCount < $maxPollCount) { // 每次轮询间隔500毫秒(0.5秒) usleep(500000); } } } else { // 如果请求失败,等待后继续尝试 if ($pollCount < $maxPollCount) { usleep(500000); } } } } // 返回发送状态信息 return ResponseHelper::success([ 'messageId' => $messageId, 'sendStatus' => $sendStatus, 'statusText' => $sendStatus == 0 ? '已发送' : '发送中' ]); } public function details() { $wechatFriendId = $this->request->param('wechatFriendId', ''); $wechatChatroomId = $this->request->param('wechatChatroomId', ''); $wechatAccountId = $this->request->param('wechatAccountId', ''); $page = $this->request->param('page', 1); $limit = $this->request->param('limit', 10); $from = $this->request->param('From', ''); $to = $this->request->param('To', ''); $olderData = $this->request->param('olderData', false); $accountId = $this->getUserInfo('s2_accountId'); if (empty($accountId)) { return ResponseHelper::error('请先登录'); } if (empty($wechatChatroomId) && empty($wechatFriendId)) { return ResponseHelper::error('参数缺失'); } $where = []; if (!empty($wechatChatroomId)) { $where[] = ['wechatChatroomId', '=', $wechatChatroomId]; } if (!empty($wechatFriendId)) { $where[] = ['wechatFriendId', '=', $wechatFriendId]; } if (!empty($From) && !empty($To)) { $where[] = ['wechatTime', 'between', [$from, $to]]; } $total = Db::table('s2_wechat_message')->where($where)->count(); $list = Db::table('s2_wechat_message')->where($where)->page($page, $limit)->order('id DESC')->select(); // 检查消息是否有sendStatus字段,如果有且不为0,则请求线上最新接口 foreach ($list as $k => &$item) { // 检查是否存在sendStatus字段且不为0(0表示已发送成功) if (isset($item['sendStatus']) && $item['sendStatus'] != 0) { // 需要请求新的数据 $messageRequest = [ 'id' => $item['id'], 'wechatAccountId' => $wechatAccountId, 'wechatFriendId' => $wechatFriendId, 'wechatChatroomId' => $wechatChatroomId, 'from' => '', 'to' => '', ]; $newData = $this->fetchLatestMessageFromApi($messageRequest); if (!empty($newData)){ $item['sendStatus'] = 0; } } // 格式化时间 $item['wechatTime'] = !empty($item['wechatTime']) ? date('Y-m-d H:i:s', $item['wechatTime']) : ''; } unset($item); return ResponseHelper::success(['total' => $total, 'list' => $list]); } /** * 从线上接口获取最新消息 * @param array $messageRequest 消息项(包含wechatAccountId、wechatFriendId或wechatChatroomId、id等) * @return array|null 最新消息数据,失败返回null */ private function fetchLatestMessageFromApi($messageRequest) { if (empty($this->baseUrl) || empty($this->authorization)) { return null; } try { // 设置请求头 $headerData = ['client:system']; $header = setHeader($headerData, $this->authorization, 'json'); // 判断是好友消息还是群聊消息 if (!empty($messageRequest['wechatFriendId'])) { // 好友消息接口 $params = [ 'keyword' => '', 'msgType' => '', 'accountId' => '', 'count' => 20, // 获取多条消息以便找到对应的消息 'messageId' => isset($messageRequest['id']) ? $messageRequest['id'] : '', 'olderData' => true, 'wechatAccountId' => $messageRequest['wechatAccountId'], 'wechatFriendId' => $messageRequest['wechatFriendId'], 'from' => $messageRequest['from'], 'to' => $messageRequest['to'], 'searchFrom' => 'admin' ]; $result = requestCurl($this->baseUrl . 'api/FriendMessage/searchMessage', $params, 'GET', $header, 'json'); $response = handleApiResponse($result); // 查找对应的消息 if (!empty($response) && is_array($response)) { $data = $response[0]; if ($data['sendStatus'] == 0){ WechatMessageModel::where(['id' => $data['id']])->update(['sendStatus' => 0]); return true; } } return false; } elseif (!empty($messageRequest['wechatChatroomId'])) { // 群聊消息接口 $params = [ 'keyword' => '', 'msgType' => '', 'accountId' => '', 'count' => 20, // 获取多条消息以便找到对应的消息 'messageId' => isset($messageRequest['id']) ? $messageRequest['id'] : '', 'olderData' => true, 'wechatId' => '', 'wechatAccountId' => $messageRequest['wechatAccountId'], 'wechatChatroomId' => $messageRequest['wechatChatroomId'], 'from' => $messageRequest['from'], 'to' => $messageRequest['to'], 'searchFrom' => 'admin' ]; $result = requestCurl($this->baseUrl . 'api/ChatroomMessage/searchMessage', $params, 'GET', $header, 'json'); $response = handleApiResponse($result); // 查找对应的消息 if (!empty($response) && is_array($response)) { $data = $response[0]; if ($data['sendStatus'] == 0){ WechatMessageModel::where(['id' => $data['id']])->update(['sendStatus' => 0]); return true; } } return false; } } catch (\Exception $e) { // 记录错误日志,但不影响主流程 \think\facade\Log::error('获取线上最新消息失败:' . $e->getMessage()); } return null; } /** * 更新数据库中的消息 * @param array $latestMessage 线上获取的最新消息 * @param array $oldMessage 旧消息数据 */ private function updateMessageInDatabase($latestMessage, $oldMessage) { try { // 使用API模块的MessageController来保存消息 $apiMessageController = new \app\api\controller\MessageController(); // 判断是好友消息还是群聊消息 if (!empty($oldMessage['wechatFriendId'])) { // 保存好友消息 $apiMessageController->saveMessage($latestMessage); } elseif (!empty($oldMessage['wechatChatroomId'])) { // 保存群聊消息 $apiMessageController->saveChatroomMessage($latestMessage); } } catch (\Exception $e) { // 记录错误日志,但不影响主流程 \think\facade\Log::error('更新数据库消息失败:' . $e->getMessage()); } } }