398-添加好友业务代码和测试

news/2024/11/23 9:53:00/

我们在mysql数据库有Friend表
在这里插入图片描述
表中两个字段,一个是用户id,一个是用户的friend的id,这种关系不用写多次,所以用联合主键。我跟你是好友写一般就可以了。
我把你添加好友,服务器会给我返回好友列表信息,然后我根据列表上的用户的id号进行聊天。

添加好友业务代码

我们完善public.hpp

#ifndef PUBLIC_H
#define PUBLIC_H/*
server和client的公共文件
*/
enum EnMsgType
{LOGIN_MSG = 1,//登录消息LOGIN_MSG_ACK,//登录响应消息REG_MSG, //注册消息REG_MSG_ACK,//注册响应消息ONE_CHAT_MSG,//聊天消息ADD_FRIEND_MSG,//添加好友消息
};#endif

在这里插入图片描述
我们完善chatservice.hpp
新增代码

#include "friendmodel.hpp"
//添加好友业务
void addFriend(const TcpConnectionPtr &conn, json &js, Timestamp time);FriendModel _friendModel;

完整代码

#ifndef CHATSERVICE_H
#define CHATSERVICE_H#include <muduo/net/TcpConnection.h>
#include <unordered_map>//一个消息ID映射一个事件处理 
#include <functional>
#include <mutex>
using namespace std;
using namespace muduo;
using namespace muduo::net;#include "usermodel.hpp"
#include "offlinemessagemodel.hpp"
#include "friendmodel.hpp"
#include "json.hpp"
using json = nlohmann::json;//表示处理消息的事件回调方法类型,事件处理器,派发3个东西 
using MsgHandler = std::function<void(const TcpConnectionPtr &conn, json &js, Timestamp)>;//聊天服务器业务类
class ChatService
{
public://获取单例对象的接口函数static ChatService *instance();//处理登录业务void login(const TcpConnectionPtr &conn, json &js, Timestamp time);//处理注册业务void reg(const TcpConnectionPtr &conn, json &js, Timestamp time);//一对一聊天业务void oneChat(const TcpConnectionPtr &conn, json &js, Timestamp time);//添加好友业务void addFriend(const TcpConnectionPtr &conn, json &js, Timestamp time);//处理客户端异常退出void clientCloseException(const TcpConnectionPtr &conn);//服务器异常,业务重置方法void reset();//获取消息对应的处理器MsgHandler getHandler(int msgid);
private:ChatService();//单例 //存储消息id和其对应的业务处理方法,消息处理器的一个表,写消息id对应的处理操作 unordered_map<int, MsgHandler> _msgHandlerMap;//存储在线用户的通信连接,用户的id, TcpConnectionPtrunordered_map<int, TcpConnectionPtr> _userConnMap;//定义互斥锁,保证_userConnMap的线程安全mutex _connMutex;//数据操作类对象UserModel _userModel;OfflineMsgModel _offlineMsgModel;   FriendModel _friendModel;};#endif

我们完善chatservice.cpp
新增代码

_msgHandlerMap.insert({ADD_FRIEND_MSG, std::bind(&ChatService::addFriend, this, _1, _2, _3)});
//添加好友业务 格式: msgid id friendid
void ChatService::addFriend(const TcpConnectionPtr &conn, json &js, Timestamp time)
{int userid = js["id"].get<int>();//当前用户的id int friendid = js["friendid"].get<int>();//添加好友的id //存储好友信息_friendModel.insert(userid, friendid);
}
//处理登录业务  id  pwd   检测pwd
void ChatService::login(const TcpConnectionPtr &conn, json &js, Timestamp time)
{int id = js["id"].get<int>();//获取id号 string pwd = js["password"];//获取密码 User user = _userModel.query(id);//查找 if (user.getId() == id && user.getPwd() == pwd)//查出来了,登录成功 {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{//登录成功,记录用户连接信息,要考虑线程安全,因为多线程访问 {lock_guard<mutex> lock(_connMutex);_userConnMap.insert({id, conn});}//加个作用域,出了这个右括号就自动解锁//登录成功,更新用户状态信息 state offline=>onlineuser.setState("online");_userModel.updateState(user);json response;response["msgid"] = LOGIN_MSG_ACK;response["errno"] = 0;response["id"] = user.getId();response["name"] = user.getName();//查询该用户是否有离线消息vector<string> vec = _offlineMsgModel.query(id);//查当用户id if (!vec.empty())//不为空 {response["offlinemsg"] = vec;//json库可以和容器之间序列化和反序列化 //读取该用户的离线消息后,把该用户的所有离线消息删除掉_offlineMsgModel.remove(id);}//查询该用户的好友信息并返回vector<User> userVec = _friendModel.query(id);if (!userVec.empty()){vector<string> vec2;for (User &user : userVec){json js;js["id"] = user.getId();js["name"] = user.getName();js["state"] = user.getState();vec2.push_back(js.dump());}response["friends"] = vec2;}conn->send(response.dump());            }}else{//该用户不存在,用户存在但是密码错误,登录失败json response;response["msgid"] = LOGIN_MSG_ACK;response["errno"] = 1;response["errmsg"] = "id or password is invalid!";conn->send(response.dump());}
}

完整代码

#include "chatservice.hpp"
#include "public.hpp"
#include <muduo/base/Logging.h>//muduo的日志 
#include <vector>
using namespace std;
using namespace muduo;//获取单例对象的接口函数
ChatService *ChatService::instance()
{static ChatService service;return &service;
}//构造方法,注册消息以及对应的Handler回调操作
ChatService::ChatService()
{//用户基本业务管理相关事件处理回调注册_msgHandlerMap.insert({LOGIN_MSG, std::bind(&ChatService::login, this, _1, _2, _3)});_msgHandlerMap.insert({REG_MSG, std::bind(&ChatService::reg, this, _1, _2, _3)});_msgHandlerMap.insert({ONE_CHAT_MSG, std::bind(&ChatService::oneChat, this, _1, _2, _3)});   _msgHandlerMap.insert({ADD_FRIEND_MSG, std::bind(&ChatService::addFriend, this, _1, _2, _3)});
}//服务器异常,业务重置方法
void ChatService::reset()
{//把online状态的用户,设置成offline_userModel.resetState();
}//获取消息对应的处理器
MsgHandler ChatService::getHandler(int msgid)
{//记录错误日志,msgid没有对应的事件处理回调auto it = _msgHandlerMap.find(msgid);if (it == _msgHandlerMap.end())//找不到 {//返回一个默认的处理器,空操作,=按值获取 return [=](const TcpConnectionPtr &conn, json &js, Timestamp) {LOG_ERROR << "msgid:" << msgid << " can not find handler!";//muduo日志会自动输出endl };}else//成功的话 {return _msgHandlerMap[msgid];//返回这个处理器 }
}//处理登录业务  id  pwd   检测pwd
void ChatService::login(const TcpConnectionPtr &conn, json &js, Timestamp time)
{int id = js["id"].get<int>();//获取id号 string pwd = js["password"];//获取密码 User user = _userModel.query(id);//查找 if (user.getId() == id && user.getPwd() == pwd)//查出来了,登录成功 {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{//登录成功,记录用户连接信息,要考虑线程安全,因为多线程访问 {lock_guard<mutex> lock(_connMutex);_userConnMap.insert({id, conn});}//加个作用域,出了这个右括号就自动解锁//登录成功,更新用户状态信息 state offline=>onlineuser.setState("online");_userModel.updateState(user);json response;response["msgid"] = LOGIN_MSG_ACK;response["errno"] = 0;response["id"] = user.getId();response["name"] = user.getName();//查询该用户是否有离线消息vector<string> vec = _offlineMsgModel.query(id);//查当用户id if (!vec.empty())//不为空 {response["offlinemsg"] = vec;//json库可以和容器之间序列化和反序列化 //读取该用户的离线消息后,把该用户的所有离线消息删除掉_offlineMsgModel.remove(id);}//查询该用户的好友信息并返回vector<User> userVec = _friendModel.query(id);if (!userVec.empty()){vector<string> vec2;for (User &user : userVec){json js;js["id"] = user.getId();js["name"] = user.getName();js["state"] = user.getState();vec2.push_back(js.dump());}response["friends"] = vec2;}conn->send(response.dump());            }}else{//该用户不存在,用户存在但是密码错误,登录失败json response;response["msgid"] = LOGIN_MSG_ACK;response["errno"] = 1;response["errmsg"] = "id or password is invalid!";conn->send(response.dump());}
}//处理注册业务  name  password
void ChatService::reg(const TcpConnectionPtr &conn, json &js, Timestamp time)
{string name = js["name"];//获取名字 string pwd = js["password"];//获取密码 User user;//创建用户对象 user.setName(name);user.setPwd(pwd);bool state = _userModel.insert(user);//新用户的插入 if (state)//插入成功 {//注册成功json response;response["msgid"] = REG_MSG_ACK;response["errno"] = 0;response["id"] = user.getId();conn->send(response.dump());//回调 ,返回json字符串 }else//插入失败 {//注册失败json response;response["msgid"] = REG_MSG_ACK;response["errno"] = 1;conn->send(response.dump());//回调 ,返回json字符串 }
}//处理客户端异常退出
void ChatService::clientCloseException(const TcpConnectionPtr &conn)
{User user;{lock_guard<mutex> lock(_connMutex);for (auto it = _userConnMap.begin(); it != _userConnMap.end(); ++it)//用迭代器 {if (it->second == conn){//从map表删除用户的链接信息user.setId(it->first);_userConnMap.erase(it);break;}}}//更新用户的状态信息if (user.getId() != -1){user.setState("offline");_userModel.updateState(user);}
}//一对一聊天业务
void ChatService::oneChat(const TcpConnectionPtr &conn, json &js, Timestamp time)
{int toid = js["toid"].get<int>();//获取对方的id号 {lock_guard<mutex> lock(_connMutex);//访问连接信息表,要保证线程安全 auto it = _userConnMap.find(toid);//查找对方id号 if (it != _userConnMap.end())//找到了 {//toid在线,转发消息  服务器主动推送消息给toid用户it->second->send(js.dump());return;}}//toid不在线,存储离线消息_offlineMsgModel.insert(toid, js.dump());
}//添加好友业务 格式: msgid id friendid
void ChatService::addFriend(const TcpConnectionPtr &conn, json &js, Timestamp time)
{int userid = js["id"].get<int>();//当前用户的id int friendid = js["friendid"].get<int>();//添加好友的id //存储好友信息_friendModel.insert(userid, friendid);
}

我们在include的server的model下创建文件:friendmodel.hpp

#ifndef FRIENDMODEL_H
#define FRIENDMODEL_H#include "user.hpp"
#include <vector>
using namespace std;//维护好友信息的操作接口方法
class FriendModel
{
public://添加好友关系void insert(int userid, int friendid);//返回用户好友列表vector<User> query(int userid);
};#endif

我们做下面这两张表的联合查询就可以了。
在这里插入图片描述
我们在src下的server下的model下创建friendmodel.cpp

#include "friendmodel.hpp"
#include "db.hpp"//添加好友关系
void FriendModel::insert(int userid, int friendid)
{//1.组装sql语句char sql[1024] = {0};sprintf(sql, "insert into friend values(%d, %d)", userid, friendid);MySQL mysql;if (mysql.connect()){mysql.update(sql);}
}//返回用户好友列表
vector<User> FriendModel::query(int userid)
{// 1.组装sql语句char sql[1024] = {0};sprintf(sql, "select a.id,a.name,a.state from user a inner join friend b on b.friendid = a.id where b.userid=%d", userid);vector<User> vec;MySQL mysql;if (mysql.connect()){MYSQL_RES *res = mysql.query(sql);if (res != nullptr){//把userid的好友请求的用户信息都存入数组中 MYSQL_ROW row;while((row = mysql_fetch_row(res)) != nullptr){User user;user.setId(atoi(row[0]));user.setName(row[1]);user.setState(row[2]);vec.push_back(user);}mysql_free_result(res);return vec;}}return vec;
}

编译成功
在这里插入图片描述

测试

我们打开两个终端,启动
在这里插入图片描述
我们先看看mysql数据库的user表有什么?
在这里插入图片描述

我先登录上linzeyu用户
在这里插入图片描述
然后linzeyu添加zhangsan为好友

{"msgid":6,"id":1,"friendid":2}

在这里插入图片描述
我们打开mysql数据库看一下
在这里插入图片描述
然后现在linzeyu用户先退出,然后重新登录
在这里插入图片描述
显示出好友列表了!


http://www.ppmy.cn/news/546557.html

相关文章

Oralce系列十九:Oralce的体系结构

Oralce的体系结构 1. 物理结构2. 内存结构2.1 SGA2.2 后台进程 3. 逻辑结构 1. 物理结构 Oracle数据库的物理结构由参数文件、控制文件、数据文件和日志文件组成&#xff0c;用于存储和管理数据库的数据和元数据&#xff0c;每个文件都扮演着不可或缺的角色。 参数文件用于配…

Linux系统编程:进程的状态和进程优先级

目录 一. Linux操作系统进程的状态 二. 进程状态的标识 2.1 运行状态R 2.2 睡眠状态S 2.3 前台进程和后台进程 2.4 磁盘睡眠状态D 2.5 暂停状态和调试状态T 2.6 僵尸状态Z 2.7 终止状态X 2.8 孤儿进程 三. 进程优先级 3.1 什么是优先级 3.2 进程优先级的修改和进…

【人工智能】— 深度神经网络、卷积神经网络(CNN)、多卷积核、全连接、池化

【人工智能】— 深度神经网络、卷积神经网络&#xff08;CNN&#xff09;、多卷积核、全连接、池化 深度神经网络训练训练深度神经网络参数共享 卷积神经网络&#xff08;CNN&#xff09;卷积多卷积核卷积全连接最大池化卷积池化拉平向量激活函数优化小结 深度神经网络训练 Pr…

Redis 2023面试5题(六)

一、Redis集群为什么至少需要三个master节点 Redis集群至少需要三个master节点&#xff0c;这是因为新master的选举需要大于半数的集群master节点同意才能选举成功&#xff0c;如果只有两个master节点&#xff0c;当其中一个挂了&#xff0c;是达不到选举新master的条件的。此…

【C++】泛型编程——模板进阶

文章目录 前言1. 模板参数的分类2. 非类型模板参数2.1 非类型模板参数的概念2.2 铺垫2.2 非类型模板参数的使用2.4 注意2.5 array的了解 3. 模板的特化3.1 概念3.2 函数模板特化3.3 类模板特化3.3.1 全特化3.3.2 偏特化部分特化参数更进一步的限制 4. 模板分离编译4.1 什么是分…

计算机病毒是指______.,计算机病毒是指

语音内容&#xff1a; 大家好&#xff0c;我是时间财富网智能客服时间君&#xff0c;上述问题将由我为大家进行解答。 计算机病毒是编制者在计算机程序中插入的破坏计算机功能或者数据的代码&#xff0c;能影响计算机使用&#xff0c;能自我复制的一组计算机指令或者程序代码。…

关于计算机病毒

1.计算机病毒 编制或在计算机中插入的可破坏计算机功能&#xff0c;数据&#xff0c;并能够自我复制的计算机指令或程序代码。 2.蠕虫 蠕虫病毒是以计算机为载体&#xff0c;以网络为攻击对象的病毒。它在互联网环境下复制自身进行传播&#xff0c;通过共享文件夹&#xff0c;…

计算机病毒计算机中的程序是吗,计算机病毒是一种程序吗?

计算机病毒是一种程序&#xff0c;是一种工资编制的特别程序。计算机病毒是编制者在计算机程序中插进去的损坏计算机功用或许数据的代码&#xff0c;能影响计算机运用&#xff0c;能自我复制的一组计算机指令或许程序代码。 计算机病毒是工资制作的&#xff0c;有损坏性&#x…