主界面获取个人信息客户端方

主界面获取个人信息客户端方

前言

上一集我们完成了websocket身份验证的内容,那么这一集开始我们将要配合MockServer来完成主界面获取个人信息的内容。

需求分析

我们这边是完成客户端那方的内容,当客户端登录成功之后,我们就要从服务器获取到当前登录用户的基本信息。包括头像、昵称、id、电话、个性签名等。其中,我们的头像是要直接显示在我们的主界面上的。如下图:

对应proto文件

我们之前规定了这些前后端交互的接口的URL,所以我们就要去翻一下我们的proto文件。

我们先来看到我们的路径!!!

这就是我们的前后端交互接口的URL。

我们再来看到我们网络传输的请求和响应的proto文件。

这两个分别对应的是我们请求和响应的body。请求和响应我们都需要使用protobuf进行序列化

这里我们也是需要规定一下以下内容。

客户端和网关服务器进行通信的时候,需要填充sessionId属性,而不需要填充userId的属性,我们这里的这个规定是为了确认用户的身份信息,用于鉴权。

网关服务器和其他服务器进行通信的时候,我们就不需要填充sessionId属性,而一定要有userId属性。

getMyselfAsync

我们可以看到上面proto定义的内容,我们构造的请求里面需要有请求id,用户id以及会话id,这里我们需要传入DataCenter里保存的loginSessionId这个内容,请求id我们需要到NetClient中获取,这里的用户id我们是不需要传入的!

所以我们要在DataCenter当中添加以下方法

    //通过网络获取用户个人信息(异步)
    void getMyselfAsync();

看到这个函数的名称的末尾,我们就可以补充一个知识点:

同步与异步

async我们称之为异步,Sync称之为同步。

就举个例子解释一下。我们发送请求的时候,请求到返回一个响应的中间都是需要时间的。


我们的异步就是发送请求,就可以做其他的事情了,不会一直阻塞等待这个响应回来。

我们的同步就是与异步相反,我们发送请求后,我们需要阻塞去等这个响应回来接收到。


那么我们在代码中就不期望使用同步的方式来完成我们的功能,我们需要获取我们的个人信息的功能只要触发了,就能做其他事情去了,这个个人信息等他自己程序自己在另一个载体进行处理。


那么这个载体或者说接力棒,我们就可以很容易想到我们的信号槽。我们的程序的流程就是发送一个请求,把接力棒给到我们的信号槽,当响应回来了就触发我们的信号槽的信号,之后就针对响应的处理放到我们的槽函数。那样我们就不会在程序上卡住停滞不前了。


由于我们还需要进一步进行构造请求id,那么我们就要将loginSessionId这个参数带入到我们的NetClient,那么我们就继续创建一个方法在NetClient当中,接收我们的loginSessionId。那么我们先完成getMyselfAsync方法。

void DataCenter::getMyselfAsync()
{
    //这里注意!DataCenter只是处理数据,进行网络通信的是NetClient
    netClient.getMyself(loginSessionId);
}

其实就是给NetClient传入loginSessionId这个信息。

NetClient::getMyself

那么进一步我们就要去完成网络通信部分的getMyself的方法。

我们的这个方法一共三个步骤

构造http的body部分、构造http请求,信号槽处理响应。

构造http的body

这一个部分是不同的请求需要构造的成员都不一样,所以我们就不能封装成一个函数来构造http的body,那么就只能按照proto文件一步一步来构造。

//构造http的body部分
    bite_im::GetUserInfoReq req;
    req.setRequestId(makeRequestId()); 
    req.setSessionId(loginSessionId);
    QByteArray body = req.serialize(&serializer);
    LOG() << "[获取个人信息] 发送请求 requestId=" << req.requestId() << ", loginSessionId=" << loginSessionId;

这里我们需要body弄成二进制文件才能在网络上正常的传输。这个序列化器我们的NetClient里面也有,可以直接用上。

构造http请求

这一个部分就基本都是一个通用的操作了,我们就封装一个方法来实现吧。

我们先来看一下我们请求的一个格式

这个看上去还是十分的简单的。我们需要设置的就是我们是需要使用post方法,以及这个请求的路径,之后是content-type。当然我们最后要用一个QNetworkReply进行接收响应,并且返回这个响应的变量。

我们这里还是看到那个URL

由于我们的发送的不同的请求的内容只有URL和body是不同的,那么我们就要从getMyself那里传入我们需要发送的请求路径以及构造好的请求body即可。

QNetworkReply *NetClient::sendHttpRequest(const QString &apiPath, const QByteArray &body)
{
    QNetworkRequest httpReq;
    httpReq.setUrl(QUrl(HTTP_URL + apiPath));
    httpReq.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-protobuf");

    QNetworkReply* httpResp = httpClient.post(httpReq,body);
    return httpResp;
}

信号槽处理响应

那么到这里,我们就已经把请求发送出去了,那么我们是要把接力棒交给我们的信号槽,我们就能让这个程序异步处理我们的请求了。

处理响应之前,我们要等到响应回来我们才能正式处理响应,所以我们的信号就是这个响应是finished的状态就可以开始我们的处理。

那么我们处理响应的时候是需要先判断我们的响应的业务上是否出错,HTTP上是否有出错。

如果我们的响应没有错误,我们就可以根据不同的响应做出不同的处理了。我们这里的需要把获取到的个人信息给我们保存到DataCenter当中,毕竟我们一开始在DataCenter里面我们的这个内容是nullptr的!


那么处理我们的业务上的逻辑是否出错我们也可以封装成一个方法。

但是这个函数需要能够处理不同的返回结果的对象,所以我们就要引入我们的模板,我们要去使用泛型编程。

返回值方面,我们虽然可以使用直接使用T来返回,但是!拷贝的开销是很大的。那么我们可以引入智能指针,在函数的内部,通过new的方式创建出对象,并返回指针,为了让这个对象能够在合适的时机进行释放,我们选用智能指针就是最好的办法!

那么判断这个业务逻辑是否有问题,我们就传入这个http的响应以及传入两个指针,一个ok用于判定是否有问题,一个reason用于接收错误原因。

 connect(httpResp, &QNetworkReply::finished, this, [=](){
        //返回值能获取一个反序列好的对象,并且判定业务上正确的
        bool ok = true;
        QString reason;
        auto resp = handleHttpResponse<bite_im::GetUserInfoRsp>(httpResp, &ok, &reason);

        if(!ok){
            LOG() << "[获取个人信息] 出错!requestId=" << req.requestId() <<"reason=" << reason;
            return;
        }
}

那么我们就要进去完成我们的handleHttpResponse的内容。

handleHttpResponse

我们这个函数要先判断http是否出错,我们就要去看是否NoError

之后返回一个空的智能指针即可。要记得delete以下我们的响应对象。

之后就要获取我们响应的body,之后进行反序列化

    template <typename T>
    std::shared_ptr<T> handleHttpResponse(QNetworkReply* httpResp, bool* ok, QString* reason){
        //判定http是否出错
        if(httpResp->error() != QNetworkReply::NoError){
            *ok = false;
            *reason = httpResp->errorString();
            httpResp->deleteLater();
            return std::shared_ptr<T>();
        }

        //获取响应body
        QByteArray respBody = httpResp->readAll();

        //body反序列化
        std::shared_ptr<T> respObj = std::make_shared<T>();
        respObj->deserialize(&serializer, respBody);

        //判定业务上的逻辑
        if(!respObj->success()){
            *ok = false;
            *reason = respObj->errmsg();
            httpResp->deleteLater();
            return std::shared_ptr<T>();
        }

        //释放对象
        httpResp->deleteLater();

        return respObj;

    }

我们反序列化后能够得到以下的内容

我们就可以知道我们的业务逻辑是否是成功的,如果成功了我们就可以直接返回我们的respObj了,我们接收就可以在外面接收我们的respObj了。里面就包含了我们的userInfo!当然一定要记得我们的响应是已经使用完了,不需要他了,我们就要手动给他延迟delete!


C++模板

我们先不回到信号槽的代码!我们来谈一谈C++模板的问题

在C++中,模板的使用确实有一些特殊的考虑,特别是关于模板的声明和定义。你提到的“分离编译模型必须把声明和定义写在一起,不能分开写”的问题,实际上是由于C++模板的编译机制导致的。

模板的编译机制

C++模板是一种在编译时进行处理的泛型编程工具。编译器需要在编译时看到模板的完整定义,以便对模板进行实例化。这意味着,如果你只在头文件中声明模板而不提供定义,那么在其他.cpp文件中使用这个模板时,编译器将无法找到模板的定义,从而导致编译错误。

所以我们上面的那个函数就需要放到我们的头文件当中!


我们回到我们的信号槽部分,接下来我们就剩下把respObj里面的userInfo给放到我们的DataCenter当中。

也是十分的简单,请看下面的代码

我们一开始给myself就是一个nullptr,我们需要new一个新对象出来,再把userInfo给到我们的myself即可,这里也用到我们之前使用的load方法。

void DataCenter::resetMyself(std::shared_ptr<bite_im::GetUserInfoRsp> resp)
{
    if(myself == nullptr){
        myself = new UserInfo();
    }
    const bite_im::UserInfo& userInfo = resp->userInfo();
    myself->load(userInfo);
}

这里我们调用了这个函数就能把userInfo的内容放到我们的DataCenter里的myself了。


主窗口信号槽

我们只要进入主窗口就必须让这个头像和我们userInfo的内容就能够加载出来了,所以我们就要在整个触发连接信号槽的地方直接让他调用我们的getMyselfAsync的方法。只要一登录我们就能够从客户端发送请求到服务端获取我们的个人信息。

但是!展示在我们主页面的内容是也要显示正确才行啊,我们的头像还得渲染到我们的主页面的个人头像上,所以我们还得弄一个信号槽函数,用于接收一下信号,信号我们可以自定义一个,我们当把DataCenter的内容给弄上去之后就可以触发这个信号,信号被触发之后我们就会把头像的内容放到我们的主界面的userAvatar上。

    //获取个人信息
    connect(dataCenter, &DataCenter::getMyselfDone, this,[=](){
        //从DataCenter中拿到响应结果的myself,把里面的头像拿出来,放置在界面上
        auto myself = dataCenter->getMyself();
        userAvatar->setIcon(myself->avatar);
    });
    dataCenter->getMyselfAsync();
signals:
    //自定义信号
    void getMyselfDone();
//通过网络获取用户个人信息
void NetClient::getMyself(const QString &loginSessionId)
{
    //构造http的body部分
    bite_im::GetUserInfoReq req;
    req.setRequestId(makeRequestId()); 
    req.setSessionId(loginSessionId);
    QByteArray body = req.serialize(&serializer);
    LOG() << "[获取个人信息] 发送请求 requestId=" << req.requestId() << ", loginSessionId=" << loginSessionId;

    //构造http请求
    QNetworkReply* httpResp = sendHttpRequest("/service/user/get_user_info", body);

    //通过信号槽,获取到当前的响应
    connect(httpResp, &QNetworkReply::finished, this, [=](){
        //返回值能获取一个反序列好的对象,并且判定业务上正确的
        bool ok = true;
        QString reason;
        auto resp = handleHttpResponse<bite_im::GetUserInfoRsp>(httpResp, &ok, &reason);

        if(!ok){
            LOG() << "[获取个人信息] 出错!requestId=" << req.requestId() <<"reason=" << reason;
            return;
        }

        //响应数据保存到DataCenter中,一开始个人信息是nullptr的
        dataCenter->resetMyself(resp);

        //通知调用逻辑,响应处理完毕
        emit dataCenter->getMyselfDone();

        //打印日志
        LOG() << "[获取个人信息] 响应处理完毕!requestId=" << req.requestId();


    });

}

我们程序的链路就是:

  1. 主窗口调用DataCenter的getMyselfAsync
  2. 把DataCenter的loginSessionId传给NetClient,调用getMyself
  3. 进入getMyself,发送请求,返回并处理请求,保存myself,触发信号
  4. 触发信号,主窗口渲染头像即可。

这样我们客户端这边就暂时告一段落了,可以准备去完成测试服务器那边的内容。

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

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

相关文章

Git 分⽀规范 Git Flow 模型

前言 GitFlow 是一种流行的 Git 分支管理策略&#xff0c;由 Vincent Driessen 在 2010 年提出。它提供了一种结构化的方法来管理项目的开发、发布和维护&#xff0c;特别适合大型和复杂的项目。GitFlow 定义了一套明确的分支模型和工作流程&#xff0c;使得团队成员可以更有效…

极氪交付与整车营收双创新高,极氪汽车怎么做的?

在当前的新能源汽车市场上&#xff0c;新能源汽车的竞争已经白热化&#xff0c;各家新能源车企都面临巨大的压力&#xff0c;就在最近极氪的财报公布&#xff0c;交付与整车营收双创新高&#xff0c;极氪汽车是怎么做到的&#xff1f;极氪的未来我们又该怎么分析&#xff1f; 一…

HarmonyOS ArkUI(基于ArkTS) 开发布局 (上)

一 ArkUI(基于ArkTS)概述 基于ArkTS的声明式开发范式的方舟开发框架是一套开发极简、高性能、支持跨设备的UI开发框架&#xff0c;提供了构建应用UI所必需的能力 点击详情 特点 开发效率高&#xff0c;开发体验好 代码简洁&#xff1a;通过接近自然语义的方式描述UI&#x…

UE5 材质里面画圆锯齿严重的问题

直接这么画圆会带来锯齿&#xff0c;我们对锯齿位置进行模糊 可以用smoothstep&#xff0c;做值的平滑过渡&#xff08;虽然不是模糊&#xff0c;但是类似&#xff09;

[C++] 智能指针

文章目录 智能指针的使用原因及场景分析为什么需要智能指针&#xff1f;异常抛出导致的资源泄漏问题分析 智能指针与RAIIC常用智能指针 使用智能指针优化代码优化后的代码优化点分析 析构函数中的异常问题解决方法 RAII 和智能指针的设计思路详解什么是 RAII&#xff1f;RAII 的…

Python学习笔记(1)装饰器、异常检测、标准库概览、面向对象

1 装饰器 装饰器&#xff08;decorators&#xff09;是 Python 中的一种高级功能&#xff0c;它允许你动态地修改函数或类的行为。 装饰器是一种函数&#xff0c;它接受一个函数作为参数&#xff0c;并返回一个新的函数或修改原来的函数。 语法使用 decorator_name 来应用在…

为什么 Vue3 封装 Table 组件丢失 expose 方法呢?

在实际开发中&#xff0c;我们通常会将某些常见组件进行二次封装&#xff0c;以便更好地实现特定的业务需求。然而&#xff0c;在封装 Table 组件时&#xff0c;遇到一个问题&#xff1a;Table 内部暴露的方法&#xff0c;在封装之后的组件获取不到。 代码展示为&#xff1a; …

Spring boot + Vue2小项目基本模板

Spring boot Vue2小项目基本模板 基本介绍基本环境安装项目搭建最终效果展示 基本介绍 项目来源哔哩哔哩的青戈&#xff0c;跟着学习搭建自己的简单vue小项目&#xff1b;看别人的项目总觉得看不懂&#xff0c;需要慢慢打磨 这里目前只简单的搭建了菜单导航和表格页面&#x…

nacos-operator在k8s集群上部署nacos-server2.4.3版本踩坑实录

文章目录 操作步骤1. 拉取仓库代码2. 安装nacos-operator3. 安装nacos-server 坑点一坑点二nacos-ui页面访问同一集群环境下微服务连接nacos地址配置待办参考文档 操作步骤 1. 拉取仓库代码 &#xff08;这一步主要用到代码中的相关yml文件&#xff0c;稍加修改用于部署容器&…

论文概览 |《IJAEOG》2024.09 Vol.133(下)

本次给大家整理的是《International Journal of Applied Earth Observation and Geoinformation》杂志2024年09月第133期的论文的题目和摘要&#xff0c;一共包括61篇SCI论文&#xff01;由于论文过多&#xff0c;我们将通过两篇文章进行介绍&#xff0c;本篇文章介绍第31--第6…

Unity类银河战士恶魔城学习总结(P129 Craft UI 合成面板UI)

【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili 教程源地址&#xff1a;https://www.udemy.com/course/2d-rpg-alexdev/ 本章节实现了合成面板的UI设置 UI_CraftWindow.cs 字段作用&#xff1a; UI 组件&#xff1a; itemName / itemDescription / icon&#…

【WPF】Prism学习(三)

Prism Commands 1.复合命令&#xff08;Composite Commanding&#xff09; 这段内容主要介绍了在应用程序中如何使用复合命令&#xff08;Composite Commands&#xff09;来实现多个视图模型&#xff08;ViewModels&#xff09;上的命令。以下是对这段内容的解释&#xff1a; …

模型压缩相关技术概念澄清(量化/剪枝/知识蒸馏)

1.模型压缩背景 随着深度学习技术的不断发展&#xff0c;模型的规模和复杂度也随之增加。大型模型往往具有更高的精度和更强的泛化能力&#xff0c;但在实际应用中&#xff0c;模型的大小却成为了一个制约因素。模型体积过大会导致存储、传输和推理速度等方面的瓶颈&#xff0…

‌EAC(Estimate at Completion)和ETC(Estimate to Complete)

‌EAC 预计完工成本ETC 预计尚需成本Estimate at CompletionEstimate to Complete完成预估完工时尚需成本估算 EAC ETC ACETC EAC – AC 预测项目总成本&#xff0c;包含了到目前为止实际发生的成本&#xff08;AC&#xff09;和预计将发生的成本。如果EAC大于BAC&#xf…

计算机网络 (6)物理层的基本概念

前言 计算机网络物理层是OSI模型&#xff08;开放式系统互联模型&#xff09;中的第一层&#xff0c;也是七层中的最底层&#xff0c;它涉及到计算机网络中数据的物理传输。 一、物理层的主要任务和功能 物理层的主要任务是处理物理传输介质上的原始比特流&#xff0c;确保数据…

探索大规模语言模型(LLM)在心理健康护理领域中的应用与潜力

概述 心理健康是公共卫生最重要的领域之一。根据美国国家精神卫生研究所&#xff08;NIMH&#xff09;的数据&#xff0c;到 2021 年&#xff0c;22.8% 的美国成年人将患上某种形式的精神疾病。在全球范围内&#xff0c;精神疾病占非致命性疾病负担的 30%&#xff0c;并被世界…

深度学习之GAN应用

1 GAN的应用&#xff08;文本生成&#xff09; 1.1 GAN为什么不适合文本任务&#xff1f; ​ GAN在2014年被提出之后&#xff0c;在图像生成领域取得了广泛的研究应用。然后在文本领域却一直没有很惊艳的效果。主要在于文本数据是离散数据&#xff0c;而GAN在应用于离散数据时…

15分钟学 Go 实战项目五 :简单电子商务网站(3W字完整例子)

简单的电子商务网站开发实战 项目概述 目标 实现用户注册登录功能开发商品浏览和搜索功能实现购物车管理完成订单处理流程 技术栈 类别技术选择说明Web框架Gin高性能HTTP框架数据库MySQL存储用户和商品信息缓存Redis购物车和会话管理ORMGORM数据库操作认证JWT用户身份验证…

C++- 基于多设计模式下的同步异步日志系统

第一个项目:13万字,带源代码和详细步骤 目录 第一个项目:13万字,带源代码和详细步骤 1. 项目介绍 2. 核心技术 3. 日志系统介绍 3.1 为什么需要⽇志系统 3.2 ⽇志系统技术实现 3.2.1 同步写⽇志 3.2.2 异步写⽇志 4.知识点和单词补充 4.1单词补充 4.2知识点补充…

【AlphaFold3】开源本地的安装及使用

文章目录 安装安装DockerInstalling Docker on Host启用Rootless Docker 安装 GPU 支持安装 NVIDIA 驱动程序安装 NVIDIA 对 Docker 的支持 获取 AlphaFold 3 源代码获取基因数据库获取模型参数构建将运行 AlphaFold 3 的 Docker 容器 参考 AlphaFold3: https://github.com/goo…