当前位置: 首页 > news >正文

加载指定会话最近消息

加载指定会话最近消息

前言

上一集我们就把三个标签页的加载列表的任务给完成啦!那么我们这一集就来完成加载指定绘画最近消息的任务。

需求分析

我们点击了某个会话之后,我们就会去显示我们的会话的最近的N条消息。请看下图。

我们这里涉及到两个区域,一个是中间的区域,一个是右边的消息展示区域。

当然这里我们只展示N条消息记录,我们不多去展示,不然影响我们的程序的效率和用户的体验感。这里和我们的返回历史消息记录查询是完全不同的东西!

我们还是先来看一下我们的URL

再来看一眼我们的请求和响应的定义。

这就是我们这一集的任务要求!

客户端

常规操作,分两端,这里是客户端的代码操作!

loadRecentMessage

SessionItem::active

我们要考虑这个加载指定会话的最近消息要在哪里进行调用?

我们应该在这个会话被点击的时候就加载这个函数,那么我们就应该把这个函数放到我们SessionItem的active这个成员函数当中。

由于又要涉及到我们主界面的消息展示区,那么这个函数必须放置在我们的主界面的代码当中。

那么我们就要获取到MainWidget的实例,之后调用这个函数。

void SessionItem::active()
{LOG() << "点击 SessionItem 触发的逻辑 chatSessionId=" << chatSessionId;//加载会话历史消息会涉及到当前内存的数据操作,又会涉及到网络通信,还会涉及到界面的变更//使用到主窗口的方法进行调用MainWidget* mainWidget = MainWidget::getInstance();mainWidget->loadRecentMessage(chatSessionId);}

loadRecentMessage的具体实现

我们需要传入需要加载的会话id,因为我们不是加载整个最近消息,所以我们要去传入会话id。

那么我们还是分为了两个部分,一个是从内存进行加载,一个是从网络进行加载。

我们判定的是这个会话的最近消息列表,而不是整个最近的消息列表,所以我们应该去访问key为chatSessionId的最近消息列表。我们判定整个会话的最近消息列表是没有什么用的!

getRecentMessageList
QList<Message> *DataCenter::getRecentMessageList(const QString &chatSessionId)
{if(!recentMessage->contains(chatSessionId)){return nullptr;}return &(*recentMessage)[chatSessionId];
}

我们先完成这个判定的函数,这里我们要先去访问这个哈希表是否存在这个chatSessionId的列表,如果没有就直接返回一个nullptr即可,如果有,就返回这个列表的指针即可。

void MainWidget::loadRecentMessage(const QString &chatSessionId)
{//先判定本地是否存有数据DataCenter* dataCenter = DataCenter::getInstance();//判定的是这个会话的最近消息列表,不是整个最近消息列表if(dataCenter->getRecentMessageList(chatSessionId) != nullptr){//本地数据加载updateRecentMessage(chatSessionId);}else{//网络数据加载connect(dataCenter, &DataCenter::getRecentMessageListDone, this, &MainWidget::updateRecentMessage, Qt::UniqueConnection);dataCenter->getRecentMessageListAsync(chatSessionId);}}
本地数据加载
updateRecentMessage

这个代码也是分为两个部分,我们先讲这个本地数据加载的内容。

我们这里不要忘了,这个内容是展示到我们的消息展示区的,不是我们的好友列表上!

我们先从DataCenter中获取这个列表,之后清空我们整个消息展示区,之后头插我们的消息内容。我们判定我们的消息是靠左还是靠右,只需要去看这个消息的发送者的userId,和我们DataCenter里面保存的Myself的userId即可。

之后我们要设置我们的会话标题,再把我们当前选中的会话保存到我们的DataCenter当中。最后我们再来一个滚动条设置滑动到我们的消息的末尾即可。

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->getMyself()->userId;//用这个消息的发送者的用户id和数据中心的自身信息的用户id对比即可messageShowArea->addFrontMessage(isLeft, message);}//设置会话标题ChatSessionInfo* chatSessionInfo =  dataCenter->findChatSessionById(chatSessionId);if(chatSessionInfo != nullptr){//把会话的名称显示到界面上sessionTitleLabel->setText(chatSessionInfo->chatSessionName);}//保存当前选中的会话dataCenter->setCurrentChatSessionId(chatSessionId);//自动把滚动条滚动到末尾,就可以先看到最近的第一条消息messageShowArea->scrollToEnd();}

这里我们还要去是完成获取会话消息,保存当前选中会话,以及自动滑动滚动条到末尾的三个重要函数。

findChatSessionById

我们通过chatSessionId从DataCenter当中寻找会话列表中的指定会话的信息。

ChatSessionInfo *DataCenter::findChatSessionById(const QString &chatSessionId)
{//判断会话列表是否为空if(chatSessionList == nullptr){return nullptr;}for(auto& info : *chatSessionList){if(info.chatSessionId == chatSessionId){return &info;}}//没找到return nullptr;
}
set/getCurrentChatSessionId
void DataCenter::setCurrentChatSessionId(const QString &chatSessionId)
{this->currentChatSessionId = chatSessionId;
}const QString &DataCenter::getCurrentChatSessionId()
{return currentChatSessionId;
}
scrollToEnd
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);
}

使用延时操作,等会话里的消息都加载到界面上,我们再去获取到我们的具体滚动区域最大值,之后滑动即可。

这样我们就可以通过本地数据进行加载我们指定会话的最近消息列表了!

网络通信加载
getRecentMessageListAsync

我们不仅仅要传到NetClient的有loginSessionId,我们还有指定的chatSessionId。

void DataCenter::getRecentMessageListAsync(const QString &chatSessionId)
{netClient.getRecentMessageList(loginSessionId, chatSessionId);
}
getRecentMessageList

我们还是老步骤啊,构造请求body,序列化,之后发送请求到我们指定的URL,之后处理我们的响应,之后就是把我们的数据放置到我们的数据中心,之后发送信号,之后触发我们的信号槽从本地数据再获取数据即可!

void NetClient::getRecentMessageList(const QString &loginSessionId, const QString &chatSessionId)
{//构造请求bodybite_im::GetRecentMsgReq req;req.setRequestId(makeRequestId());req.setChatSessionId(chatSessionId);req.setMsgCount(50);//固定获取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<bite_im::GetRecentMsgRsp>(resp, &ok, &reason);//判定响应是否okif(!ok){LOG() << "[获取最近消息] 失败! requestId=" << req.requestId() << ", reason=" << reason;return;}//设置到DataCenter中dataCenter->resetRecentMessageList(chatSessionId, pbResp);//发送信号告知界面进行更新emit dataCenter->getRecentMessageListDone(chatSessionId);});
}
resetRecentMessageList

这里我们不需要判定是否为空,因为哈希的特性!我们不存在就会直接创建,我们记得清空一下这个指定会话的最近消息列表,之后通过遍历,我们把获得的数据设置到我们的recentMessage,记住一定要用引用!!!

void DataCenter::resetRecentMessageList(const QString &chatSessionId, std::shared_ptr<bite_im::GetRecentMsgRsp> resp)
{//哈希的特性存在就使用,不存在就创建新的,清空QList<Message>& messageList = (*recentMessage)[chatSessionId];messageList.clear();//遍历for(auto& m : resp->msgList()){Message message;message.load(m);messageList.push_back(message);}
}

之后触发我们的信号就可以返回到我们的主界面那边了。

 connect(dataCenter, &DataCenter::getRecentMessageListDone, this, &MainWidget::updateRecentMessage, Qt::UniqueConnection);

记得我们的第五个参数,是为了防止一个信号触发我们多个这个槽函数。

到这里我们的客户端就完成了,我们继续完成测试服务端!

测试服务端

配置路由

    httpServer.route("/service/message_storage/get_recent", [=](const QHttpServerRequest& req){return this->getRecent(req);});

基本操作,我们直接到getRecent函数当中!

getRecent

也是基本操作,我们构造了一堆假数据放置到我们的界面上。

QHttpServerResponse HttpServer::getRecent(const QHttpServerRequest &req)
{//解析请求bite_im::GetRecentMsgReq pbReq;pbReq.deserialize(&serializer, req.body());LOG() << "[REQ 获取最近消息列表] requestId=" << pbReq.requestId() << ", loginSessionId=" << pbReq.sessionId();//构造响应bite_im::GetRecentMsgRsp pbResp;pbResp.setRequestId(pbReq.requestId());pbResp.setSuccess(true);pbResp.setErrmsg("");QByteArray avatar = loadFileToByteArray(":/resource/image/defaultAvatar.png");for(int i = 0; i < 20; ++i){bite_im::MessageInfo messageInfo = makeTextMessageInfo(i, "2000", avatar);pbResp.msgList().push_back(messageInfo);}//序列化QByteArray body = pbResp.serialize(&serializer);//构造http响应对象QHttpServerResponse resp(body, QHttpServerResponse::StatusCode::Ok);resp.setHeader("Content-Type", "application/x-protobuf");return resp;
}

这里我们就完成了整个加载指定会话最近消息的内容了。

相关文章:

加载指定会话最近消息

加载指定会话最近消息 前言 上一集我们就把三个标签页的加载列表的任务给完成啦&#xff01;那么我们这一集就来完成加载指定绘画最近消息的任务。 需求分析 我们点击了某个会话之后&#xff0c;我们就会去显示我们的会话的最近的N条消息。请看下图。 我们这里涉及到两个区…...

基于tensorflow使用VGG16实现猫狗识别

import tensorflow as tf import numpy as np from tensorflow.keras import layers, models, optimizers from tensorflow.keras.preprocessing.image import ImageDataGenerator# 定义 VGG16 模型 class VGG16(tf.keras.Model):def __init__(self, num_classes2):super(VGG16…...

第18章 EXISTS 与 NOT EXISTS 关键字

一、EXISTS 关键字介绍 关键字介绍EXISTS 关联子查询通常也会和 EXISTS操作符一起来使用&#xff0c;用来检查在子查询中是否存在满足条件的行。 如果在子查询中当前的行不满足条件&#xff1a;返回 FALSE&#xff0c;继续在子查询中查找 如果在子查询中当前的行满足条件&…...

Windows多JDK版本管理工具JVMs

Windows多JDK版本管理工具JVMs 官网安装使用手动下载jdk 官网 https://github.com/ystyle/jvms 下载 https://github.com/ystyle/jvms/releases 当前下载版本为v2.1.6 安装 下载后&#xff0c;解压到某个目录。 比如&#xff1a;D:\soft\JVMs\jvms_v2.1.6_amd64 把这个目录…...

【C++】初始化列表、类型转换

目录&#xff1a; 一、const成员函数 二、初始化列表 三、类型转换 正文 一、const成员函数 &#xff08;1&#xff09;将const修饰的成员函数称之为const成员函数&#xff0c;const修饰成员函数放到成员函数参数列表的后⾯。至于为什么这么放是语法规定。 &#xff08;2&a…...

创新设计,精准仿真|SOLIDWORKS Simulation 2025新功能

SOLIDWORKS Simulation 2025 带来了多项新功能&#xff0c;不仅提高了工作效率&#xff0c;还增强了仿真的精确度。以下是五大新功能的详细介绍&#xff0c;帮助您更好地利用这些新特性提升设计仿真能力。 1. 从分析中排除实体 在复杂的装配体仿真中&#xff0c;有时需要排除某…...

vue3封装Element Plus table表格组件

支持绝大部分Element Plus原有设置属性&#xff0c;支持分页&#xff0c;支持动态适配高度 效果展示 组件代码&#xff1a; <template><div class"table-wrap" ref"tableWrap"><el-tableclass"w100 h100":data"tableInfo.…...

Qt之QWidget相关

Qt概述 Qt 是一个跨平台的 C 开发框架。 跨平台支持&#xff1a;可以用于开发 Windows、macOS、Linux、Android、iOS 等多种操作系统下的应用程序。这意味着开发者使用 Qt 编写的代码&#xff0c;在经过适当的编译和配置后&#xff0c;能够在不同平台上运行&#xff0c;减少了…...

用web前端写出一个高校官网

所实现的效果如链接&#xff1a; http://127.0.0.1:5500/school.html <!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>xigongshang</title> <style> * {margin: 0;padding: 0;} a{ text-decoration: none…...

【笔记】Android Gradle Plugin配置文件相关说明-libs.versions.toml

版本号 文件路径&#xff1a;Project\gradle\libs.versions.toml 直接搜索versions.agp是找不到的&#xff0c;这是变量引用的写法&#xff0c;查询 agp版本可以直接查版本号。 [versions] agp "8.5.0-alpha08" junit "4.13.2" junitVersion "1.…...

如何修复WordPress卡在维护模式

当你管理WordPress网站时&#xff0c;没有什么比看到它卡在维护模式更令人沮丧的了。特别是在你进行重要更新或期望大量流量的时候&#xff0c;这种情况会更加令人不安。 维护模式可能由许多因素引起&#xff0c;从简单的文件损坏到更复杂的插件冲突或存在的.maintenance文件。…...

glob三个函数的效果

glob 允许你给一个从dirname到basename pattern的整字符串路径&#xff0c;然后匹配一切符合要求的路径。 glob0 dirname和basename分开传&#xff0c;只返回basename。可见不支持pattern匹配。 glob1 dirname和basename pattern分开传&#xff0c;只返回basename。支持pa…...

FreeRTOS:事件标志组与任务通知

目录 一、事件标志组&#xff08;Event Groups&#xff09; 1、事件标志组的特点 2、事件标志组与队列、信号量的区别 3、关键API函数 4、示例代码 5、优缺点 6、总结 二、任务通知&#xff08;Task Notifications&#xff09; 1、任务通知的特点 2、关键API函数 3、…...

c++11的动态类型

c17引入了any 和 variant&#xff0c;可以将任意数据类型统一用any或variant类型表示&#xff0c;在开发中还是能够带来很多便利的。在c11版本中&#xff0c;可以用下面这个例子&#xff0c;仿照实现一个Any类型。 #include <iostream> #include <stdexcept> #inc…...

付费会员渗透难,腾讯音乐的触顶挑战

腾讯音乐付费用户增长背后&#xff0c;月活跃用户数下滑3%&#xff0c;超级会员渗透率仅1.8%。 转载|原创新熵 作者丨晓伊 编辑丨蕨影 腾讯音乐此次营收同比正增长的到来&#xff0c;殊为不易。要知道&#xff0c;此前已连续四个季度出现营收同比下滑的态势。 11月12日&#x…...

内网安全隧道搭建-ngrok-frp-nps-sapp

1.ngrok 建立内网主机与公网跳板机的连接&#xff1a; 内网主机为客户机&#xff1a; 下载客户端执行 2.frp &#xff08;1&#xff09;以下为内网穿透端口转发 frp服务端配置&#xff1a; bindPort 为frp运行端口 服务端运行 ./frps -c frps.ini frp客户端配置&#xf…...

Load-Balanced-Online-OJ(负载均衡式在线OJ)

负载均衡式在线OJ 1. 项目介绍2. 项目说明4. 项目代码5. 项目演示 1. 项目介绍 2. 项目说明 4. 项目代码 5. 项目演示...

Postman之变量操作

系列文章目录 1.Postman之安装及汉化基本使用介绍 2.Postman之变量操作 3.Postman之数据提取 4.Postman之pm.test断言操作 5.Postman之newman Postman之变量操作 1.pm.globals全局变量2.pm.environment环境变量3.pm.collectionVariables集合变量4.pm.variables5.提取数据-设置变…...

查找字符串中某个字符返回字符位置

当然有正则表达式就非常简单,没有的话也不用担心,我们自己写算法完成这个功能. 早期的vs版本不支持vs,当然也可以下载boost来实现,关键还是不想下载,那么就自己写吧. 1.要求,查找字符串中同一个字符,并找出字符的位置. 2.根据字符位置,计算出对应的x,y坐标. 算法第一步,查找字…...

《数学物理学报》

作者指南 一、目的与范围 《数学物理学报》以刊登数学与物理科学的边缘学科中具有创造性的代表学科水平的科研成果为主的综合性学术刊物。其目的旨在向专业读者&#xff08;研究生水平以上&#xff09;提供数理学科领域的重要的、独创的成果。 二、投稿 要求文章内容具有创新…...

OpenLayers 可视化之热力图

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 热力图&#xff08;Heatmap&#xff09;又叫热点图&#xff0c;是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...

7.4.分块查找

一.分块查找的算法思想&#xff1a; 1.实例&#xff1a; 以上述图片的顺序表为例&#xff0c; 该顺序表的数据元素从整体来看是乱序的&#xff0c;但如果把这些数据元素分成一块一块的小区间&#xff0c; 第一个区间[0,1]索引上的数据元素都是小于等于10的&#xff0c; 第二…...

C++:std::is_convertible

C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...

React Native 开发环境搭建(全平台详解)

React Native 开发环境搭建&#xff08;全平台详解&#xff09; 在开始使用 React Native 开发移动应用之前&#xff0c;正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南&#xff0c;涵盖 macOS 和 Windows 平台的配置步骤&#xff0c;如何在 Android 和 iOS…...

QMC5883L的驱动

简介 本篇文章的代码已经上传到了github上面&#xff0c;开源代码 作为一个电子罗盘模块&#xff0c;我们可以通过I2C从中获取偏航角yaw&#xff0c;相对于六轴陀螺仪的yaw&#xff0c;qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

在rocky linux 9.5上在线安装 docker

前面是指南&#xff0c;后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)

概述 在 Swift 开发语言中&#xff0c;各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过&#xff0c;在涉及到多个子类派生于基类进行多态模拟的场景下&#xff0c;…...

【位运算】消失的两个数字(hard)

消失的两个数字&#xff08;hard&#xff09; 题⽬描述&#xff1a;解法&#xff08;位运算&#xff09;&#xff1a;Java 算法代码&#xff1a;更简便代码 题⽬链接&#xff1a;⾯试题 17.19. 消失的两个数字 题⽬描述&#xff1a; 给定⼀个数组&#xff0c;包含从 1 到 N 所有…...

Go 语言接口详解

Go 语言接口详解 核心概念 接口定义 在 Go 语言中&#xff0c;接口是一种抽象类型&#xff0c;它定义了一组方法的集合&#xff1a; // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的&#xff1a; // 矩形结构体…...

Netty从入门到进阶(二)

二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架&#xff0c;用于…...