• (六)用户登录模块讲解


    登录验证

    json对象中获取用户ID和密码,并在数据库中查询获取用户信息。

    int id = js["id"].get<int>();
    std::string password = js["password"];
    

    如果用户已经登录过,即state == "online",则返回错误信息。

    if (user.getId() == id && user.getPassword() == password)
    {
    	if (user.getState() == "online")
    	{
    		// 该用户已经登录,不能重复登录
    		json response;
    		response["msgid"] = LOGIN_MSG_ACK;
    		response["errno"] = 2;
    		response["errmsg"] = "this account is using, input another!";
    		conn->send(response.dump());
    	}
    	else
    	{
    		// ...
    	}
    }
    

    登录成功后,需要在用户表_userConnMap中记录新登录的用户。考虑到多线程对此map进行操作,我们需要使用互斥锁。

    // 登录成功,记录用户连接信息
    // 需要考虑线程安全问题 onMessage会在不同线程中被调用
    {
    	lock_guard<mutex> lock(_connMutex);
    	_userConnMap.insert({id, conn});
    }
    

    lock_guard遵守RALL手法,初始化即上锁,销毁即解锁。这里用临时作用域让锁得颗粒度变小,保证性能。

    查询离线消息

    我们还需要查询该用户离线时是否有离线消息,如果有离线消息是要取出放到vector容器中得。我们可以使用 JSON 直接与 STL 容器进行操作。

    // 查询该用户是否有离线消息
    std::vector<std::string> vec = _offlineMsgModel.query(id);
    if (!vec.empty())
    {
    	response["offlinemsg"] = vec;
    	// 读取该用户的离线消息后,将该用户离线消息删除掉
    	_offlineMsgModel.remove(id);
    }
    

    显示好友列表

    我们还需要显示这个登录用户得好友列表,在 MySQL 中记录着每个用户 ID 的好友名单。我们需要取出来,并且加入到 JSON 对象中。

    std::vector<User> userVec = _friendModel.query(id);
    if (!userVec.empty())
    {
    	std::vector<std::string> vec;
    	for (auto& user : userVec)
    	{
    		json js;
    		js["id"] = user.getId();
    		js["name"] = user.getName();
    		js["state"] = user.getState();
    		vec.push_back(js.dump());
    	}
    	response["friends"] = vec;
    }
    
    conn->send(response.dump());
    

    登录模块完整代码

    #include "chatservice.hpp"
    #include "public.hpp"
    #include 
    
    /**
     * 登录业务
     * 从json得到用户id
     * 从数据中获取此id的用户,判断此用户的密码是否等于json获取到的密码
     * 判断用户是否重复登录
     * {"msgid":1,"id":13,"password":"123456"}
     * {"errmsg":"this account is using, input another!","errno":2,"msgid":2}
     */
    void ChatService::loginHandler(const TcpConnectionPtr &conn, json &js, Timestamp time)
    {
        LOG_DEBUG << "do login service!";
    
        int id = js["id"].get<int>();
        std::string password = js["password"];
    
        User user = _userModel.query(id);
        if (user.getId() == id && user.getPassword() == password)
        {
            if (user.getState() == "online")
            {
                // 该用户已经登录,不能重复登录
                json response;
                response["msgid"] = LOGIN_MSG_ACK;
                response["errno"] = 2;
                response["errmsg"] = "this account is using, input another!";
                conn->send(response.dump());
            }
            else
            {
                // 登录成功,记录用户连接信息
                // 需要考虑线程安全问题 onMessage会在不同线程中被调用
                {
                    lock_guard<mutex> lock(_connMutex);
                    _userConnMap.insert({id, conn});
                }
    
                // id用户登录成功后,向redis订阅channel(id)
                // _redis.subscribe(id); 
    
                // 登录成功,更新用户状态信息 state offline => online
                user.setState("online");
                _userModel.updateState(user);
    
                json response;
                response["msgid"] = LOGIN_MSG_ACK;
                response["errno"] = 0;
                response["id"] = user.getId();
                response["name"] = user.getName();
    
                // 查询该用户是否有离线消息
                std::vector<std::string> vec = _offlineMsgModel.query(id);
                if (!vec.empty())
                {
                    response["offlinemsg"] = vec;
                    // 读取该用户的离线消息后,将该用户离线消息删除掉
                    _offlineMsgModel.remove(id);
                }
                else
                {
                    LOG_INFO << "无离线消息";
                }
    
                std::vector<User> userVec = _friendModel.query(id);
                if (!userVec.empty())
                {
                    std::vector<std::string> vec;
                    for (auto& user : userVec)
                    {
                        json js;
                        js["id"] = user.getId();
                        js["name"] = user.getName();
                        js["state"] = user.getState();
                        vec.push_back(js.dump());
                    }
                    response["friends"] = vec;
                }
    
                conn->send(response.dump());
            }
        }
        
    }
    
    

    用户登录模块测试

    可以看到返回的错误码:errno = 0表示成功,并在里面返回了好友列表的JSON信息。

    在这里插入图片描述

    数据库查看:可以看到zhang san是登录的状态了

    在这里插入图片描述

  • 相关阅读:
    Linux CentOS7设置时区
    GBase8s数据库对 SET COLLATION 的限制
    Babylonjs学习笔记(五)——创建PBR材质
    JAVA数组案例!
    k8s pod访问策略及使用service访问集群中的pod
    StringBuilder 线程不安全,到底哪里不安全?
    在AgilePLM项目中使用积木报表
    MySQL 的数据目录
    详解API基础知识
    软件测试基础理论复习
  • 原文地址:https://blog.csdn.net/weixin_46272577/article/details/127093506