From c50f574eed84ec41bee2daabccf3a7519bfb8388 Mon Sep 17 00:00:00 2001 From: xyz <2050965275@qq.com> Date: Tue, 17 Jun 2025 18:40:02 +0800 Subject: [PATCH] add recent session message --- mainwidget.cpp | 148 ++++++++++++++++++++++++++++++++++++++++-- mainwidget.h | 9 +++ messageshowarea.cpp | 20 ++++++ messageshowarea.h | 3 + model/datacenter.cpp | 102 +++++++++++++++++++++++++++++ model/datacenter.h | 26 +++++++- network/netclient.cpp | 110 ++++++++++++++++++++++++++++++- network/netclient.h | 5 +- sessionfriendarea.cpp | 7 ++ 9 files changed, 423 insertions(+), 7 deletions(-) diff --git a/mainwidget.cpp b/mainwidget.cpp index fa4d557..f795dd3 100644 --- a/mainwidget.cpp +++ b/mainwidget.cpp @@ -188,7 +188,7 @@ void MainWidget::initRightWindow() hlayout->setContentsMargins(10, 0, 10, 0); titleWidget->setLayout(hlayout); - QLabel* sessionTitleLabel = new QLabel(); + sessionTitleLabel = new QLabel(); sessionTitleLabel->setStyleSheet("QLabel { font-size: 22px; border-bottom: 1px solid rgb(230, 230, 230); }"); #if TEST_UI @@ -298,6 +298,16 @@ void MainWidget::initSignalSlot() // 获取好友列表 ///////////////////////////////////// loadFriendList(); + + ///////////////////////////////////// + // 获取会话列表 + ///////////////////////////////////// + loadSessionList(); + + ///////////////////////////////////// + // 获取好友申请的列表 + ///////////////////////////////////// + loadApplyList(); } void MainWidget::switchTabToSession() @@ -339,7 +349,20 @@ void MainWidget::switchTabToApply() //加载会话列表 void MainWidget::loadSessionList() { - //TODO + //会判定会话是否存在本地的DataCenter中,若存在,则直接构造界面 + //若不存在则从服务器获取数据 + DataCenter* dataCenter = DataCenter::getInstance(); + if (dataCenter->getChatSessionList() != nullptr) { + //本地内存中获取数据 + LOG() << "本地已存在会话列表数据,更新加载本地会话"; + updateChatSessionList(); + } + else { + // 从网路中加载数据 + LOG() << "本地会话列表数据不存在,正在从网络获取..."; + connect(dataCenter, &DataCenter::getChatSessionListDone, this, &MainWidget::updateChatSessionList, Qt::UniqueConnection); + dataCenter->getChatSessionListAsync(); + } } //加载好友列表 @@ -351,7 +374,7 @@ void MainWidget::loadFriendList() DataCenter* dataCenter = DataCenter::getInstance(); if (dataCenter->getFriendList() != nullptr) { //从内存中加载数据 - LOG() << "本地已存在好友列表数据,更新加载本地列表"; + LOG() << "本地已存在好友列表数据,更新加载本地好友"; updateFriendList(); } else { @@ -366,7 +389,21 @@ void MainWidget::loadFriendList() //加载申请列表 void MainWidget::loadApplyList() { - //TODO + //好友申请列表在DataCenter中存储 + //同理首先需要判定DataCenter中是否已经有数据了,如果有,则直接加载本地数据 + //如果没有,则从服务器读取 + DataCenter* dataCenter = DataCenter::getInstance(); + if (dataCenter->getApplyList() != nullptr) { + //从内存中加载数据 + LOG() << "本地已存在用户申请列表数据,更新加载申请列表"; + updateApplyList(); + } + else { + //通过网络加载数据 + LOG() << "用户申请列表数据不存在,正在从网络获取..."; + connect(dataCenter, &DataCenter::getApplyListDone, this, &MainWidget::updateApplyList, Qt::UniqueConnection); + dataCenter->getApplyListAsync(); + } } void MainWidget::updateFriendList() @@ -389,4 +426,107 @@ void MainWidget::updateFriendList() } } +void MainWidget::updateChatSessionList() +{ + if (activeTab != SESSION_LIST) { + //当前的标签页不是会话列表,不渲染任何数据到界面上 + return; + } + DataCenter* dataCenter = DataCenter::getInstance(); + QList* chatSessionList = dataCenter->getChatSessionList(); + + sessionFriendArea->clear(); + + for (const auto& c : *chatSessionList) { + switch (c.lastMessage.messageType) + { + case TEXT_TYPE: + sessionFriendArea->addItem(SessionItemType, c.chatSessionId, c.avatar, c.chatSessionName, c.lastMessage.content); + break; + case IMAGE_TYPE: + sessionFriendArea->addItem(SessionItemType, c.chatSessionId, c.avatar, c.chatSessionName, "[图片]"); + break; + case FILE_TYPE: + sessionFriendArea->addItem(SessionItemType, c.chatSessionId, c.avatar, c.chatSessionName, "[文件]"); + break; + case SPEECH_TYPE: + sessionFriendArea->addItem(SessionItemType, c.chatSessionId, c.avatar, c.chatSessionName, "[语音]"); + break; + default: + LOG() << "updateSessionList 收到了错误的“最后一条消息的类型”"; + + break; + } + } +} + +void MainWidget::updateApplyList() +{ + if (activeTab != APPLY_LIST) { + //当前的标签页不是“好友申请列表” + return; + } + DataCenter* dataCenter = DataCenter::getInstance(); + QList* applyList = dataCenter->getApplyList(); + + sessionFriendArea->clear(); + + for (const auto& u : *applyList) { + //直接把这个UserInfo对象添加到界面之上,不涉及PB和自己结构的转换 + //此处的UserInfo的desc不需要填写进来,好友的申请列表中, 不显示用户的标签 + sessionFriendArea->addItem(ApplyItemType, u.userId, u.avatar, u.nickname, ""); + } +} + +void MainWidget::loadRecentMessage(const QString& chatSessionId) +{ + //也是先判定,本地内存中是否已经存在对应的消息列表数据 + //有的话直接显示到界面之上,没有的话从网络中获取 + DataCenter* dataCenter = DataCenter::getInstance(); + + if (dataCenter->getRecentMessageList(chatSessionId) != nullptr) { + //直接拿着本地数据更新界面 + LOG() << "本地已存在好友聊天数据,更新加载好友聊天数据"; + updateRecentMessage(chatSessionId); + } + else { + //本地没有数据,从网络加载 + LOG() << "好友聊天数据不存在,正在从网络获取..."; + connect(dataCenter, &DataCenter::getRecentMessageListDone, this, &MainWidget::updateRecentMessage, Qt::UniqueConnection); + dataCenter->getRecnetMessageListAsync(chatSessionId); + } +} + +void MainWidget::updateRecentMessage(const QString& chatSessionId) +{ + //拿到该会话最近的消息列表 + DataCenter* dataCenter = DataCenter::getInstance(); + auto* recentMessageList = dataCenter->getRecentMessageList(chatSessionId); + + //清空原有界面上的消息列表 + messageShowArea->clear(); + + //根据当前拿到的消息列表,显示到界面上 + //此处把数据显示到界面上,可以使用头插也可以尾插 + //对于消息列表来说,用户最先看到的应该是最近的消息,也就是末尾的消息 + for (int i = recentMessageList->size() - 1; i >= 0; i--) { + const Message& message = recentMessageList->at(i); + bool isLeft = message.sender.userId != dataCenter->getMyselfsync()->userId; + messageShowArea->addFrontMessage(isLeft, message); + } + + //设置会话标题 + ChatSessionInfo* chatSessionInfo = dataCenter->findChatSessionById(chatSessionId); + if (chatSessionInfo != nullptr) { + //把会话名称显示到界面之上 + sessionTitleLabel->setText(chatSessionInfo->chatSessionName); + } + + //保存当前选中的会话是哪个 + dataCenter->setCurrentChatSessionId(chatSessionId); + + //自动把滚动条滚动到末尾 + messageShowArea->scrollToEnd(); +} + diff --git a/mainwidget.h b/mainwidget.h index 3b77715..21e72ba 100644 --- a/mainwidget.h +++ b/mainwidget.h @@ -62,6 +62,9 @@ private: // SessionFriendArea* sessionFriendArea; + //显示会话标题 + QLabel* sessionTitleLabel; + //显示会话详情按钮 QPushButton* extraBtn; @@ -79,6 +82,7 @@ private: ActiveTab activeTab = SESSION_LIST; +public: void initMainWindow(); void initLeftWindow(); void initMidWindow(); @@ -95,5 +99,10 @@ private: void loadApplyList(); void updateFriendList(); + void updateChatSessionList(); + void updateApplyList(); + + void loadRecentMessage(const QString& chatSessionId); + void updateRecentMessage(const QString& chatSessionId); }; #endif // MAINWIDGET_H diff --git a/messageshowarea.cpp b/messageshowarea.cpp index e4adba4..fb792c9 100644 --- a/messageshowarea.cpp +++ b/messageshowarea.cpp @@ -2,6 +2,7 @@ #include "mainwidget.h" #include "userinfowidget.h" +#include MessageShowArea::MessageShowArea() { //初始化基本属性 @@ -79,6 +80,25 @@ void MessageShowArea::clear() } } +void MessageShowArea::scrollToEnd() +{ + //拿到滚动区域的滚动条 + //获取到滚动条的最大值并设置 + //为了使滚动到正确的位置,即能够在界面绘制好后进行滚动设置 + //这里选择给滚动条加个延时 + QTimer* timer = new QTimer(); + connect(timer, &QTimer::timeout, this, [=]() { + //获取到垂直滚动条的最大值 + int maxValue = this->verticalScrollBar()->maximum(); + //设置滚动条滚动的位置 + this->verticalScrollBar()->setValue(maxValue); + + timer->stop(); + timer->deleteLater(); + }); + timer->start(500); +} + //////////////////////////////////////////// /// 表示一个消息元素 //////////////////////////////////////////// diff --git a/messageshowarea.h b/messageshowarea.h index 6448ed2..8b6e55d 100644 --- a/messageshowarea.h +++ b/messageshowarea.h @@ -35,6 +35,9 @@ public: //清空 void clear(); + //滚动到末尾 + void scrollToEnd(); + private: QWidget* container; }; diff --git a/model/datacenter.cpp b/model/datacenter.cpp index 9ee2944..a9c98c9 100644 --- a/model/datacenter.cpp +++ b/model/datacenter.cpp @@ -189,4 +189,106 @@ namespace model } + QList* DataCenter::getChatSessionList() + { + return chatSessionList; + } + + void DataCenter::getChatSessionListAsync() + { + netClient.getChatSessionList(loginSessionId); + } + + void DataCenter::resetChatSessionList(std::shared_ptr resp) + { + if (chatSessionList == nullptr) { + chatSessionList = new QList(); + } + chatSessionList->clear(); + + auto& chatSessionListPB = resp->chatSessionInfoList(); + for (auto& c : chatSessionListPB) { + ChatSessionInfo chatSessionInfo; + chatSessionInfo.load(const_cast(c)); + chatSessionList->push_back(chatSessionInfo); + } + } + + QList* DataCenter::getApplyList() + { + return applyList; + } + + void DataCenter::getApplyListAsync() + { + netClient.getApplyList(loginSessionId); + } + + void DataCenter::resetApplyList(std::shared_ptr resp) + { + if (applyList == nullptr) { + applyList = new QList(); + } + applyList->clear(); + + auto& eventList = resp->event(); + for (auto& event : eventList) { + UserInfo userInfo; + userInfo.load(event.sender()); + applyList->push_back(userInfo); + } + } + + void DataCenter::getRecnetMessageListAsync(const QString& chatSessionId) + { + netClient.getRecentMessageList(loginSessionId, chatSessionId); + } + + QList* DataCenter::getRecentMessageList(const QString& chatSessionId) + { + if (!recentMessages->contains(chatSessionId)) { + return nullptr; + } + return &(*recentMessages)[chatSessionId]; + } + + void DataCenter::resetRecentMessageList(const QString& chatSessionId, std::shared_ptr resp) + { + //拿到chatSessionId 对应的消息列表,并清空 + //注意此处务必使用的是引用类型,才是修改哈希表内部的内容 + QList& messageList = (*recentMessages)[chatSessionId]; + messageList.clear(); + + //遍历响应结果列表 + for (auto& m : resp->msgList()) { + Message message; + message.load(m); + + messageList.push_back(message); + } + } + + ChatSessionInfo* DataCenter::findChatSessionById(const QString& chatSessionId) + { + if (chatSessionList == nullptr) { + return nullptr; + } + for (auto& info : *chatSessionList) { + if (info.chatSessionId == chatSessionId) { + return &info; + } + } + return nullptr; + } + + void DataCenter::setCurrentChatSessionId(const QString& chatSessionId) + { + this->currentChatSessionId = chatSessionId; + } + + const QString& DataCenter::getCurrentSessionId() + { + return currentChatSessionId; + } + } //end namespace model diff --git a/model/datacenter.h b/model/datacenter.h index 5c9047a..6be2d20 100644 --- a/model/datacenter.h +++ b/model/datacenter.h @@ -49,7 +49,7 @@ namespace model QList* chatSessionList = nullptr; //记录当前选中的会话是哪个 - QString currentChatSession = ""; + QString currentChatSessionId = ""; //记录每个会话中,都有哪些成员 QHash>* memberList = nullptr;//unordered_map @@ -107,9 +107,33 @@ namespace model void getFriendListAsync(); void resetFriendList(std::shared_ptr resp); + //获取会话列表 + QList* getChatSessionList(); + void getChatSessionListAsync(); + void resetChatSessionList(std::shared_ptr resp); + + //获取好友申请列表 + QList* getApplyList(); + void getApplyListAsync(); + void resetApplyList(std::shared_ptr resp); + + //获取最近的消息列表 + void getRecnetMessageListAsync(const QString& chatSessionId); + QList* getRecentMessageList(const QString& chatSessionId); + void resetRecentMessageList(const QString& chatSessionId, std::shared_ptr resp); + + //根据会话id查询会话信息 + ChatSessionInfo* findChatSessionById(const QString& chatSessionId); + + //设置/获取当前选中的会话 + void setCurrentChatSessionId(const QString& chatSessionId); + const QString& getCurrentSessionId(); signals: //自定义信号 void getMyselfDone(); void getFriendListDone(); + void getChatSessionListDone(); + void getApplyListDone(); + void getRecentMessageListDone(const QString& chatSessionId); }; } //end namespace model diff --git a/network/netclient.cpp b/network/netclient.cpp index e5ac427..3f8d5a4 100644 --- a/network/netclient.cpp +++ b/network/netclient.cpp @@ -144,7 +144,7 @@ namespace network { }); } - void NetClient::getFriendList(const QString loginSessionId) + void NetClient::getFriendList(const QString& loginSessionId) { //通过protobuf构造body bite_im::GetFriendListReq req; @@ -175,4 +175,112 @@ namespace network { emit dataCenter->getFriendListDone(); }); } + + void NetClient::getChatSessionList(const QString& loginSessionId) + { + //通过protobuf构造body + bite_im::GetChatSessionListReq req; + req.setRequestId(makeRequestId()); + req.setSessionId(loginSessionId); + QByteArray body = req.serialize(&serializer); + + LOG() << "获取会话列表->发送请求 requestId= " << req.requestId() << ", loginSessionId= " << loginSessionId; + + //发送HTTP请求 + QNetworkReply* resp = this->sendHttpRequest("/service/friend/get_chat_session_list", body); + + //针对响应进行处理 + connect(resp, &QNetworkReply::finished, this, [=]() { + //解析响应 + bool ok = false; + QString reason; + auto pbResp = this->handleHttpResponse(resp, &ok, &reason); + + //判断响应是否正确 + if (!ok) { + LOG() << "获取会话列表->失败 reason= " << reason; + return; + } + + //到这里说明没有问题,把得到的数据,写入到DataCenter中 + dataCenter->resetChatSessionList(pbResp); + + //通知调用者,响应已经处理完毕了 + emit dataCenter->getChatSessionListDone(); + + //打印日志 + LOG() << "获取会话列表->处理响应完毕 requestId= " << pbResp->requestId(); + }); + } + + void NetClient::getApplyList(const QString& loginSessionId) + { + //通过protobuf构造body + bite_im::GetPendingFriendEventListReq req; + req.setRequestId(makeRequestId()); + req.setSessionId(loginSessionId); + QByteArray body = req.serialize(&serializer); + LOG() << "获取好友申请列表->发送请求 reason= " << req.requestId() << ", loginSessonId= " << loginSessionId; + + //发送HTTP请求 + QNetworkReply* resp = this->sendHttpRequest("/service/friend/get_pending_friend_events", body); + + //处理响应 + connect(resp, &QNetworkReply::finished, this, [=]() { + //解析响应 + bool ok = false; + QString reason; + auto pbResp = this->handleHttpResponse(resp, &ok, &reason); + //判定结果是否正确 + if (!ok) { + LOG() << "获取好友申请列表->失败 reason= " << reason; + return; + } + + //拿到的数据,写入到DataCenter中 + dataCenter->resetApplyList(pbResp); + + //发送信号通知界面,已经处理完毕 + emit dataCenter->getApplyListDone(); + + LOG() << "获取好友申请列表->处理响应完成 requestId= " << req.requestId(); + + }); + + } + + void NetClient::getRecentMessageList(const QString& loginSessionId, const QString& chatSessionId) + { + //通过protobuf构造请求body + bite_im::GetRecentMsgReq req; + req.setRequestId(makeRequestId()); + req.setChatSessionId(chatSessionId); + req.setMsgCount(50); + req.setSessionId(loginSessionId); + QByteArray body = req.serialize(&serializer); + LOG() << "获取最近消息->发送请求 requestId= " << req.requestId() << ", loginSessionId= " << loginSessionId; + + //发送http请求 + QNetworkReply* resp = this->sendHttpRequest("/service/message_storage/get_recent", body); + + //处理响应 + connect(resp, &QNetworkReply::finished, this, [=]() { + //解析响应,反序列化 + bool ok = false; + QString reason; + auto pbResp = this->handleHttpResponse(resp, &ok, &reason); + + //判定响应是否出错 + if (!ok) { + LOG() << "获取最近消息->失败 reason= " << reason; + return; + } + + //把拿到的数据,设置到DataCenter中 + dataCenter->resetRecentMessageList(chatSessionId, pbResp); + + //发送信号,告知界面进行更新 + emit dataCenter->getRecentMessageListDone(chatSessionId); + }); + } } //end namespace network \ No newline at end of file diff --git a/network/netclient.h b/network/netclient.h index 5851547..f8656a5 100644 --- a/network/netclient.h +++ b/network/netclient.h @@ -80,7 +80,10 @@ namespace network { } void getMyself(const QString& loginSessionId); - void getFriendList(const QString loginSessionId); + void getFriendList(const QString& loginSessionId); + void getChatSessionList(const QString& loginSessionId); + void getApplyList(const QString& loginSessionId); + void getRecentMessageList(const QString& loginSessionId, const QString& chatSessionId); private: model::DataCenter* dataCenter; diff --git a/sessionfriendarea.cpp b/sessionfriendarea.cpp index 4a522c3..79524ed 100644 --- a/sessionfriendarea.cpp +++ b/sessionfriendarea.cpp @@ -3,6 +3,7 @@ #include "debug.h" #include "model/data.h" +#include "mainwidget.h" SessionFriendArea::SessionFriendArea(QWidget *parent) : QScrollArea {parent} @@ -269,6 +270,10 @@ void SessionItem::active() { //点击之后,要加载会话历史消息的列表 LOG() << "click SessionItem... chatSessionId: " << chatSessionId; + + //加载会话历史消息,会涉及到当前内存数据的操作,又会涉及到网络通信,以及UI界面的变更 + MainWidget* mainWidget = MainWidget::getInstance(); + mainWidget->loadRecentMessage(chatSessionId); } //////////////////////////////////////// @@ -302,8 +307,10 @@ ApplyItem::ApplyItem(QWidget *owner, const QString &userId, const QIcon &avatar, //创建两个按钮 QPushButton* acceptBtn = new QPushButton(); + acceptBtn->setStyleSheet("QPushButton {color: rgb(0, 0, 0); } "); acceptBtn->setText("同意"); QPushButton* rejectBtn = new QPushButton(); + rejectBtn->setStyleSheet("QPushButton {color: rgb(0, 0, 0); } "); rejectBtn->setText("拒绝"); //添加到布局管理器中