【Qt6聊天室项目】 主界面功能实现

1. 获取当前用户的个人信息

1.1 前后端逻辑分析(主界面功能)

主界面上所有的前后端交互逻辑相同,分析到加载会话列表后其余功能仅实现。

核心逻辑总结

异步请求-响应模型

  • 客户端发起请求,向服务器发送包含会话ID的请求
  • 服务端处理请求并响应,根据的会话ID查找用户信息,并返回结果
  • 客户端处理响应,解析服务器的响应并更新UI显式界面

前后端交互逻辑概述

前端流程

  • MainWidget::initData:前端入口函数,主要就是负责初始化数据,与此同时客户端向DataCenter发送异步请求以获取个人信息
  • DataCenter::getMyselfAsync:向NetClient发送请求,NetClient负责与服务器进行通信
  • NetClient::getMyself:这个函数则是通过HTTP请求向服务器发送用户登录会话ID(loginSessionId)来获取个人信息

后端流程

  • 服务器的HttpServer收到来自NetClient的请求后,然后解析请求数据(也就是会话ID)
  • HttpServer::getUserInfo:服务器根据会话ID,查找用户信息并构建响应,然后将用户信息返回给客户端

客户端处理响应

  • 服务器返回的响应信息由NetClient接收,然后通过DataCenter,最后在MainWidget中展示用户的头像信息

代码逻辑实现分析(前后端交互逻辑)

前端发起请求

MainWidget中通过初始化信号槽函数中,使用connect函数建立了一个连接,当DataCenter::getMyselfDone信号发出后,会执行一个回调函数,这个回调函数的作用就是获取用户信息(myself)然以后设置头像

dataCenter->getMyselfAsync():异步请求,也就是调用该函数进一步通过网络底层获取用户信息。

connect(dataCenter, &DataCenter::getMyselfDone, this, [=]() {
    const auto* myself = dataCenter->getMyself();
    this->userAvatar->setIcon(myself->avatar);
});
dataCenter->getMyselfAsync();

 异步请求逻辑

在数据核心类中,调用getMyselfAsync(()函数,然后又会调用NetClient中的getMyselef函数,然后该函数会将登陆的sessionId传递给NetClient,最后由NetClient来处理网络通信

void DataCenter::getMyselfAsync() const {
    netClient.getMyself(loginSessionId);
}

 前后端通信的实现(NetClient与服务端)

  • 构造请求:使用proto构建请求体,该处使用的是GetUserInfoReq协议消息
  • 发送请求:sendHttpRequest方法,通过传递请求路劲和请求体,通过http客户端post请求,然后返回一个HTTP响应
  • 处理响应:在httpResp请求完成后,netClient会接收服务端返回的响应信息,通过httpleHttpResponse方法解析响应,解析成功后,将用户信息传递给DataCenter ,同时发出getMyselfDone信号,最后通知MainWidget更新UI
void NetClient::getMyself(const QString &loginSessionId) {
    // 1. 构造请求体
    bite_im::GetUserInfoReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    
    // 2. 发送 HTTP 请求
    QByteArray body = req.serialize(&serializer);
    LOG() << "[获取个人信息] requestId=" << req.requestId() << ", sessionId=" << loginSessionId;
    QNetworkReply* httpResp = this->sendHttpRequest("/service/user/get_user_info", body);

    // 3. 处理 HTTP 响应
    connect(httpResp, &QNetworkReply::finished, this, [=]() {
        auto userInfoRsp = this->handleHttpResponse<bite_im::GetUserInfoRsp>(httpResp);
        if (!userInfoRsp) {
            return;
        }
        // b) 设置 DataCenter 中的用户信息
        dataCenter->resetMyself(userInfoRsp);
        // c) 发出信号通知获取完成
        emit dataCenter->getMyselfDone();
    });
}

代码实现逻辑(服务端处理请求)

  • 解析请求:服务端首先解析客户端传过来的请求体,然后根据sessionID查找用户信息
  • 构建响应:通过构建UserInfo对象,填充用户的详细信息,将这些信息装入响应体GetUserInfoRsp,设置success为true就表明操作成功
  • 返回响应:将序列化后的响应体返回给客户端,客户端收到这个响应后会解析数据,然后更新页面
QHttpServerResponse HttpServer::getUserInfo(const QHttpServerRequest &req) {
    // 解析请求
    bite_im::GetUserInfoReq pbReq;
    pbReq.deserialize(&serializer, req.body());
    LOG() << "[REQ 获取用户信息] requestId=" << pbReq.requestId() << ", sessionId=" << pbReq.sessionId();

    // 根据 sessionId 获取用户信息
    bite_im::UserInfo userInfo;
    userInfo.setUserId("1234");
    userInfo.setNickname("张三");
    userInfo.setDescription("这是个性签名");
    userInfo.setPhone("18612345678");
    userInfo.setAvatar(loadImageToByteArray(":/image/defaultAvatar.png"));

    // 构造响应
    bite_im::GetUserInfoRsp pbRsp;
    pbRsp.setSuccess(true);
    pbRsp.setUserInfo(userInfo);

    // 序列化响应体并发送
    QByteArray body = pbRsp.serialize(&serializer);
    return QHttpServerResponse(body, QHttpServerResponse::StatusCode::Ok);
}

代码实现逻辑(客户端处理响应) 

客户端接收到服务端返回的响应后,NetClient然后进行相应的处理

  • 解析响应:首先通过handleHttpResponse解析其中的HTTP响应体,生成GetUserInfoRsp对象
  • 更新数据:更新核心数据类中的数据
  • 发出信号:最后发出getMyselfDone信号,通知前端MainWidget数据获取完成,从而实现更新用户界面
auto userInfoRsp = this->handleHttpResponse<bite_im::GetUserInfoRsp>(httpResp);
if (!userInfoRsp) {
    return;
}
// b) 设置 DataCenter 中的数据
dataCenter->resetMyself(userInfoRsp);
// c) 发出信号通知获取完成
emit dataCenter->getMyselfDone();

不同文件交互梳理

类似于外卖系统中的订单处理

  • 客户端下单:用户提交外卖订单,这也就相当于MainWidget发起了获取用户信息的请求
  • 订单传递给平台:订单会进入外卖平台的处理系统,相对于DataCenter调用NetClient来发起网络请求
  • 平台传递订单给餐厅:外卖平台将订单信息发送给餐厅,类似于NetClient发送HTTP请求给服务器
  • 餐厅处理并回传订单状态:此时餐厅已经准备食品,然后将订单完成状态返回给平台,这也就是服务器返回用户信息给客户端
  • 平台通知客户端:外卖平台将订单完成状态通知用户,客户端根据订单的状态更新用户的UI界面,这也就对应这UI更新用户信息

架构实现分析 

该模块功能的实现使用MVC架构,也就是将数据、试图、控制逻辑三者进行分离,从而使得代码结构清晰模块化。具体来说就是Model负责数据管理、View负责UI展示、Controller负责业务逻辑的处理。

客户端

当网络请求发生的时候,打开主窗口,然后可以获取用户头像以及个人信息,然后头像显示到主窗口上,个人信息显示到个人资料上。 

构建HTTP请求

获取和重置用户信息(在核心数据类中实现)

请求处理封装成模版类型

 网络通信内部实现逻辑

获取个人信息逻辑 

  • 主窗口启动,关联信号槽,发起请求
  • 构造HTTP请求,然后处理响应
  • 响应处理完成后保存到中心数据类中,最后告知响应已经处理完成

测试服务器

分析HTTP服务器处理流程

该测试代码的任务就是负责处理“获取用户信息”请求,然后返回一个Protobuf序列化响应,使用QHttpServerRequest解析客户端请求,返回一个带有用户信息的QHttpServerResponse响应

  • 请求解析:从HTTP请求的body中提取数据,然后使用Protobuf反序列化成为GetUserInFoReq对象,提取请求中包含的用户信息
  • 构建响应:生成包含用户信息的GetUserInfoRsp响应性响应,并将其序列化后作为HTTP响应的body
  • 发送响应:使用protobuf序列化后的数据构建HTTP响应,设置响应头,并将其发送给客户端

服务器正常功能测试

 功能实现分析

根据前后端接口编写客户端(界面---dataCenter---netClient---服务器---反显示后---调用datacenter---给出一个信号---界面)---编写服务器---测试

2. 加载好友列表

实现目标与实现思路分析

  • 实现目标:从服务器上获取好友列表然后显示到客户端界面上
  • 实现方式
    • 从核心数据类中获取好友列表数据
    • 判定数据核心类中是否已经有数据
      • 如果有数据则直接加载本地数据
      • 如果没有数据则需要从服务器中获取数据
    • 更新数据内容到客户端上
      • 如果内存中有数据的话,则直接将数据从内存中拿取出显示即可
  • 总体逻辑
    • 内存读取:首先从本地缓存DataCenter中获取好友数据,如果缓存中有则直接加载到界面
    • 网络请求:缓存中没有数据则需要进行网络请求,向服务器获取好友列表
    • 数据存储:服务器返回数据后,先调用数据中心类的接口,清空原有数据列表中的数据,然后将新的好友列表加载到内存中缓存,方便后续的快速读取
    • 界面更新:网络请求完成后,通过信号的方式通知主界面,然后根据获取的新好友列表更新界面

       

本地文件加载逻辑实现

 

网络请求获取好友列表数据

向服务器发送好友列表请求

  • Protobuf构造GetFriendListenReq请求消息,设置RequestId 和 SessionId
  • 向服务器发起请求,并指定URL(预先约定好)
  •  connect()函数负责等待服务器的响应到来,然后接收数据
  • 响应到达后,解析响应,获取friendListResp,也就是好友列表响应 

 网络请求成功后将好友列表存储在核心数据类中

测试服务器逻辑

  • 注册路由
    • 监听指定的URL地址,当请求到达的时候,则调用getChatSessionList函数进行处理
  • 解析请求
    • 将客户端的请求体反序列化为缓冲区的Protobuf对象,同时通过日志记录
  • 构造响应
    • 初始化响应对象,然后设置请求ID、成功状态、错误信息
  • 发送响应
    • 构建所有会话信息后,服务器将响应序列化发送给客户端

前后端总体实现逻辑总结

 

 

3. 加载会话列表

逻辑分析

4. 加载好友申请列表

实现逻辑汇总 

 

 

 

 

5. 加载会话的最近消息

初始化逻辑 

该功能的实现并非在程序启动的时候,而是当用户点击某个会话的时候才会触发,下面从选择好友开始梳理其逻辑

 

 

逻辑梳理

 

服务器处理

 

客户端处理服务器响应

细节问题处理  

保证滚动条每次直接到达的末尾位置

6. 处理点击好友列表项 

功能分析

点击好友列表后

  • 切换到会话列表
  • 选中对应的会话,根据点击好友的userid和会话列表中的userid匹配
  • 消息展示区中,加载出对应会话的最近消息

逻辑整理

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/902930.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

经纬恒润AUTOSAR成功适配芯钛科技Alioth TTA8车规级芯片

在汽车电子领域&#xff0c;功能安全扮演着守护者的角色&#xff0c;它确保了车辆在复杂多变的情况下保持稳定可靠的运行。随着汽车电子的复杂性增加&#xff0c;市场对产品功能安全的要求也日益提高。基于此背景&#xff0c;经纬恒润AUTOSAR基础软件产品INTEWORK-EAS-CP成功适…

10.22 软考初级网络管理员之局域网

局域网的基本概念&#xff0c;IE802标准 局域网是指将分散在一个局部地理范围的多台计算机通过传输媒体连接起来的通信网络。IEEE802&#xff0c;对应物理层和数据链路层 IE802标准 以太网技术&#xff08;局域网通信协议、数据传输方式&#xff09; 分值占比高 ‌‌以太网是…

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-23

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-23 目录 文章目录 计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-23目录1. Advancements in Visual Language Models for Remote Sensing: Datasets, Capabilities, and Enhancement Techniques摘…

高效集成小满OKKICRM与金蝶K3-WISE的成功案例

小满客户>k3wise销售站点&#xff1a;高效数据集成案例分享 在企业信息化建设中&#xff0c;数据的高效流动和准确对接是实现业务流程优化的关键。本文将聚焦于一个具体的系统对接集成案例&#xff1a;如何将小满OKKICRM的数据无缝集成到金蝶K3-WISE系统中&#xff0c;实现…

【十六进制数转十进制数 】

【十六进制数转十进制数 】 C语言版本C 版本Java版本Python版本 &#x1f490;The Begin&#x1f490;点点关注&#xff0c;收藏不迷路&#x1f490; 从键盘接收一个十六进制数&#xff0c;编程实现将其转换成十进制数。 输入 输入一个十六进制数 输出 输出一个十进制数 样…

Xcode 格式化代码快捷键

例如这段代码没有左对齐,看起来很乱 格式化快捷键: 全选需要格式化的代码,Xcode 工具栏--> Editor --> Structure --> Re-indent

Vxe UI vue vxe-table 虚拟树表格的使用,流畅的渲染万级数据树结构表格

Vxe UI vue vxe-table 虚拟树表格的使用&#xff0c;流畅的渲染万级数据树结构表格 代码 普通树表格&#xff0c;一般存数据库里都是平级数据&#xff0c;vxe-table 的树渲染这就非常友好了&#xff0c;只有带有父子id关联的数组&#xff0c;就可以自动渲染树表格。 <te…

面试阿里、字节全都一面挂,被面试官说我的水平还不如应届生

测试员可以先在大厂镀金&#xff0c;以后去中小厂毫无压力&#xff0c;基本不会被卡&#xff0c;事实果真如此吗&#xff1f;但是在我身上却是给了我很大一巴掌... 所谓大厂镀金只是不卡简历而已&#xff0c;如果面试答得稀烂&#xff0c;人家根本不会要你。况且要不是大厂出来…

微调大模型-4-合并基座模型

合并模型 新建文件夹,默认参数,开始导出,但是发现报错: 报错显示磁盘空间不足,这是由于AutoDL云,当前默认空间用的是系统盘,空间只有30G。 参考:https://blog.csdn.net/lwd19981223/article/details/130740905/ 将合并路径存储到50个G的数据盘,数据库路径是autodl-t…

《Linux系统编程篇》fork函数——基础篇

文章目录 引言fork() 函数概述父子进程 fork函数fork() 的常见问题fork() 的优势与限制 结论 命为志存。 —— 朱熹 引言 《Linux系统编程篇》——基础篇首页传送门 本节我们正式进入Linux的进程代码编写。 fork() 是 Unix 系统中一个重要的系统调用&#xff0c;用于创建一个…

基于KU115+ZU19EG+C6678 的高性能6U VPX 载板

基于KU115ZU19EGC6678 的高性能6U VPX 载板&#xff0c;板载 2 个 HPC 形式的FMC 连接器&#xff08;用于外部信号扩展&#xff09;。板卡选用了 1 片Xilinx 公司的Kintex UltraScale 系列 FPGA 家族中的XCKU115-2FLVA1517I 和 1 片 Zynq UltraScale MPSoC 家族的XCZU19EG-2FFV…

USB Type-C 受电端取电快充协议芯片,支持PD+QC+FCP+SCP+AFC快充协议

前言 随着科技的飞速发展&#xff0c;电子设备对于快速充电的需求日益增加。为了满足这一需求&#xff0c;市场上涌现出了众多快充技术和产品。其中&#xff0c;XSP08Q诱骗取电芯片以其卓越的性能和广泛的应用场景&#xff0c;成为了快充领域的一颗璀璨明星。本文将对XSP08Q P…

Flutter Row组件实战案例

In this section, we’ll continue our exploration by combining the Row and Container widgets to create more complex layouts. Let’s dive in! 在本节中&#xff0c;我们将继续探索&#xff0c;结合“Row”和“Container”小部件来创建更复杂的布局。让我们开始吧! Sc…

CMake 生成器表达式介绍

【写在前面】 生成器表达式在构建系统生成期间进行评估&#xff0c;以生成特定于每个构建配置的信息。它们的形式为 $<...>。例如&#xff1a; target_include_directories(tgt PRIVATE /opt/include/$<CXX_COMPILER_ID>) 这将扩展为 “/opt/include/GNU”、“/opt…

李宇皓现身第十届“文荣奖”,allblack造型帅气绅士引关注

近日&#xff0c;第十届“文荣奖”在众人的期待中拉开帷幕&#xff0c;与众多影视奖项不同&#xff0c;“文荣奖”始终关注年轻群体需求&#xff0c;致力于发掘和扶植影视新人新作&#xff0c;为热爱影视行业的新人提供宝贵机会与激励。今年的文荣奖评委阵容十分强大&#xff0…

深度学习:SGD的缺点

首先看下述函数&#xff1a; 最小值为x0&#xff0c;y0处 先了解下它的梯度特征。了理解其梯度特征&#xff0c;我们需要计算其梯度向量。 梯度向量 ∇f 是函数 f 在每个变量方向上的偏导数组成的向量。具体来说&#xff1a; ∇f(∂f/∂x,∂f∂/y) 首先&#xff0c;我们计算 f …

R语言机器学习算法实战系列(十二)线性判别分析分类算法 (Linear Discriminant Analysis)

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍LDA的原理LDA的步骤教程下载数据加载R包导入数据数据预处理数据描述数据切割构建模型预测测试数据评估模型模型准确性混淆矩阵模型评估指标ROC CurvePRC Curve保存模型总结优点:缺…

如何用猿大师办公助手实现OA系统中Word公文/合同在线编辑及流转?

在OA系统或者合同管理系统中&#xff0c;我们会经常遇到网页在线编辑Word文档形式的公文及合同的情况&#xff0c;并且需要上级对下级的公文进行批注等操作&#xff0c;或者不同部门的人需要签字审核&#xff0c;这就需要用到文档流转功能&#xff0c;如何用猿大师办公助手实现…

3DS MAX三维建模平面基础与修改工具(图形编辑与二维建模修改工具)

又是一年1024祝大家程序员节日快乐 3DS MAX三维建模平面基础与修改工具&#xff08;图形编辑与二维建模修改工具&#xff09; 欢迎大家来学习3DS MAX教程&#xff0c;在这里先说一下研究好3ds Max一定要一边看教程一边要自己学的操作才能更快的进步&#xff0c;预祝大家学习顺利…

医疗保健知识中台:引领医疗行业智能化转型的新篇章

前言 随着科技的迅猛进步&#xff0c;医疗保健领域正迎来一场深刻的智能化变革。在这场变革中&#xff0c;知识中台作为医疗行业智能化升级的重要基石&#xff0c;正逐步成为提升医疗服务质量和效率的关键驱动力。本文将深入剖析医疗保健知识中台的内容构成、应用场景以及更新…