// +---------------------------------------------------------------------- declare (strict_types = 1); namespace addon\wxchat\controller\api; use addon\wxchat\model\WxchatMessage; use addon\wxchat\model\WxchatUnreadCount; use think\facade\Db; use think\facade\Request; use ywxapp\controller\FrontendBase; use ywxapp\model\MemberUser as UserModel; use ywxapp\model\MemberProfile as ProfileModel; /** * Message 类 * * @author ywxapp */ class Message extends FrontendBase { protected $noNeedLogin = []; protected $noNeedVerify = ['*']; protected function initialize() {} /** * 发送消息(HTTP 落库,WebSocket 实时推送由 worker 处理) */ public function send() { $uid = $this->auth->model->uid; $receiverId = (int) Request::post('receiver_id', 0); $receiverType = (int) Request::post('receiver_type', 0); // 0-单聊 1-群聊 $content = trim((string) Request::post('content', '')); $contentType = (int) Request::post('content_type', 0); if (! $receiverId || $receiverId == $uid) { $this->error('接收者无效'); } if ($content === '' && $contentType == 0) { $this->error('消息内容不能为空'); } $msg = new WxchatMessage(); $msg->sender_id = $uid; $msg->receiver_type = $receiverType; $msg->receiver_id = $receiverId; $msg->content = $content; $msg->content_type = $contentType; $msg->save(); // 更新对方未读计数(单聊) if ($receiverType == 0) { WxchatUnreadCount::increment($receiverId, 0, $uid); } $this->success($msg->toArray(), '发送成功'); } /** * 会话历史(单聊 / 群聊) */ public function history() { $uid = $this->auth->uid; $contactId = (int) Request::get('contact_id', 0); $contactType = (int) Request::get('contact_type', 0); $limit = (int) Request::get('limit', 20); $page = (int) Request::get('page', 1); if (! $contactId) { $this->error('会话对象无效'); } $query = WxchatMessage::where('is_recalled', 0) ->where('receiver_type', $contactType); if ($contactType == 0) { // 单聊:取双方互发消息(修复原逻辑因外层 receiver_id 过滤而漏掉对方发来的消息) $query->where(function ($q) use ($uid, $contactId) { $q->where(function ($q) use ($uid, $contactId) { $q->where('sender_id', $uid)->where('receiver_id', $contactId); })->whereOr(function ($q) use ($uid, $contactId) { $q->where('sender_id', $contactId)->where('receiver_id', $uid); }); }); } else { $query->where('receiver_id', $contactId); } $list = $query->order('create_at', 'desc') ->paginate($limit, false, ['page' => $page]); // 单聊:读取后清零未读 if ($contactType == 0) { WxchatUnreadCount::reset($uid, 0, $contactId); } $this->success([ 'list' => $list->items(), 'total' => $list->total(), 'pages' => $list->lastPage(), ]); } /** * 未读消息列表 */ public function unread() { $uid = $this->auth->model->uid; $list = WxchatUnreadCount::where('user_id', $uid) ->where('unread_count', '>', 0) ->order('update_at', 'desc') ->select(); $this->success($list); } /** * 会话列表(聚合单聊最新一条消息 + 未读 + 对方资料/在线状态) */ public function conversations() { $uid = $this->auth->model->uid; $msgs = WxchatMessage::where('receiver_type', 0) ->where('is_recalled', 0) ->where(function ($q) use ($uid) { $q->where('sender_id', $uid)->whereOr('receiver_id', $uid); }) ->order('id', 'desc') ->limit(1000) ->select(); $convMap = []; $peerIds = []; foreach ($msgs as $m) { $sid = (int) $m->sender_id; $rid = (int) $m->receiver_id; $peer = ($sid == $uid) ? $rid : $sid; if (! isset($convMap[$peer])) { $convMap[$peer] = [ 'peer_id' => $peer, 'last_content' => $m->content, 'last_content_type' => (int) $m->content_type, 'last_time' => (int) $m->create_at, 'last_sender_id' => $sid, ]; $peerIds[] = $peer; } } if (empty($peerIds)) { $this->success(['list' => [], 'total' => 0]); return; } $users = UserModel::where('uid', 'in', $peerIds)->column('*', 'uid'); $unread = WxchatUnreadCount::where('user_id', $uid) ->where('contact_type', 0) ->where('contact_id', 'in', $peerIds) ->column('unread_count', 'contact_id'); $onlineMap = MemberProfile::where('uid', 'in', $peerIds)->column('online_status', 'uid'); $list = []; foreach ($convMap as $peer => $c) { $u = $users[$peer] ?? []; $list[] = [ 'peer_id' => $peer, 'nickname' => $u['nickname'] ?? '', 'avatar' => $u['avatar'] ?? '', 'online' => ((int) ($onlineMap[$peer] ?? 0)) == 1, 'last_content' => $c['last_content'], 'last_content_type' => $c['last_content_type'], 'last_time' => $c['last_time'], 'unread_count' => (int) ($unread[$peer] ?? 0), 'is_self' => $c['last_sender_id'] == $uid, ]; } usort($list, function ($a, $b) { return $b['last_time'] - $a['last_time']; }); $this->success(['list' => $list, 'total' => count($list)]); } /** * 撤回消息 */ public function recall() { $userId = $this->auth->model->uid; $messageId = (int) Request::post('message_id', 0); $message = WxchatMessage::find($messageId); if (! $message || $message->sender_id != $userId) { $this->error('无权撤回', 403); } $message->is_recalled = 1; $message->recalled_at = date('Y-m-d H:i:s'); $message->save(); $this->success([], '撤回成功'); } }