加载指定会话最近消息
加载指定会话最近消息
前言
上一集我们就把三个标签页的加载列表的任务给完成啦!那么我们这一集就来完成加载指定绘画最近消息的任务。
需求分析
我们点击了某个会话之后,我们就会去显示我们的会话的最近的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;
}
这里我们就完成了整个加载指定会话最近消息的内容了。
相关文章:

加载指定会话最近消息
加载指定会话最近消息 前言 上一集我们就把三个标签页的加载列表的任务给完成啦!那么我们这一集就来完成加载指定绘画最近消息的任务。 需求分析 我们点击了某个会话之后,我们就会去显示我们的会话的最近的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操作符一起来使用,用来检查在子查询中是否存在满足条件的行。 如果在子查询中当前的行不满足条件:返回 FALSE,继续在子查询中查找 如果在子查询中当前的行满足条件&…...

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

【C++】初始化列表、类型转换
目录: 一、const成员函数 二、初始化列表 三、类型转换 正文 一、const成员函数 (1)将const修饰的成员函数称之为const成员函数,const修饰成员函数放到成员函数参数列表的后⾯。至于为什么这么放是语法规定。 (2&a…...

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

vue3封装Element Plus table表格组件
支持绝大部分Element Plus原有设置属性,支持分页,支持动态适配高度 效果展示 组件代码: <template><div class"table-wrap" ref"tableWrap"><el-tableclass"w100 h100":data"tableInfo.…...
Qt之QWidget相关
Qt概述 Qt 是一个跨平台的 C 开发框架。 跨平台支持:可以用于开发 Windows、macOS、Linux、Android、iOS 等多种操作系统下的应用程序。这意味着开发者使用 Qt 编写的代码,在经过适当的编译和配置后,能够在不同平台上运行,减少了…...

用web前端写出一个高校官网
所实现的效果如链接: 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
版本号 文件路径:Project\gradle\libs.versions.toml 直接搜索versions.agp是找不到的,这是变量引用的写法,查询 agp版本可以直接查版本号。 [versions] agp "8.5.0-alpha08" junit "4.13.2" junitVersion "1.…...

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

glob三个函数的效果
glob 允许你给一个从dirname到basename pattern的整字符串路径,然后匹配一切符合要求的路径。 glob0 dirname和basename分开传,只返回basename。可见不支持pattern匹配。 glob1 dirname和basename pattern分开传,只返回basename。支持pa…...
FreeRTOS:事件标志组与任务通知
目录 一、事件标志组(Event Groups) 1、事件标志组的特点 2、事件标志组与队列、信号量的区别 3、关键API函数 4、示例代码 5、优缺点 6、总结 二、任务通知(Task Notifications) 1、任务通知的特点 2、关键API函数 3、…...
c++11的动态类型
c17引入了any 和 variant,可以将任意数据类型统一用any或variant类型表示,在开发中还是能够带来很多便利的。在c11版本中,可以用下面这个例子,仿照实现一个Any类型。 #include <iostream> #include <stdexcept> #inc…...

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

内网安全隧道搭建-ngrok-frp-nps-sapp
1.ngrok 建立内网主机与公网跳板机的连接: 内网主机为客户机: 下载客户端执行 2.frp (1)以下为内网穿透端口转发 frp服务端配置: bindPort 为frp运行端口 服务端运行 ./frps -c frps.ini frp客户端配置…...
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坐标. 算法第一步,查找字…...
《数学物理学报》
作者指南 一、目的与范围 《数学物理学报》以刊登数学与物理科学的边缘学科中具有创造性的代表学科水平的科研成果为主的综合性学术刊物。其目的旨在向专业读者(研究生水平以上)提供数理学科领域的重要的、独创的成果。 二、投稿 要求文章内容具有创新…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统
医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上,开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识,在 vs 2017 平台上,进行 ASP.NET 应用程序和简易网站的开发;初步熟悉开发一…...

中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试
作者:Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位:中南大学地球科学与信息物理学院论文标题:BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接:https://arxiv.…...

cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...

如何将联系人从 iPhone 转移到 Android
从 iPhone 换到 Android 手机时,你可能需要保留重要的数据,例如通讯录。好在,将通讯录从 iPhone 转移到 Android 手机非常简单,你可以从本文中学习 6 种可靠的方法,确保随时保持连接,不错过任何信息。 第 1…...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...
从面试角度回答Android中ContentProvider启动原理
Android中ContentProvider原理的面试角度解析,分为已启动和未启动两种场景: 一、ContentProvider已启动的情况 1. 核心流程 触发条件:当其他组件(如Activity、Service)通过ContentR…...

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement
Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement 1. LAB环境2. L2公告策略2.1 部署Death Star2.2 访问服务2.3 部署L2公告策略2.4 服务宣告 3. 可视化 ARP 流量3.1 部署新服务3.2 准备可视化3.3 再次请求 4. 自动IPAM4.1 IPAM Pool4.2 …...

协议转换利器,profinet转ethercat网关的两大派系,各有千秋
随着工业以太网的发展,其高效、便捷、协议开放、易于冗余等诸多优点,被越来越多的工业现场所采用。西门子SIMATIC S7-1200/1500系列PLC集成有Profinet接口,具有实时性、开放性,使用TCP/IP和IT标准,符合基于工业以太网的…...

sshd代码修改banner
sshd服务连接之后会收到字符串: SSH-2.0-OpenSSH_9.5 容易被hacker识别此服务为sshd服务。 是否可以通过修改此banner达到让人无法识别此服务的目的呢? 不能。因为这是写的SSH的协议中的。 也就是协议规定了banner必须这么写。 SSH- 开头,…...