三明市网站建设_网站建设公司_色彩搭配_seo优化
2025/12/24 23:24:34 网站建设 项目流程

Chap19-NotificationsAndConfirm

这一节要实现的比较多,包括前端的列表加载,确认,接受/拒绝。后端的加载,修改,查询,存储,发送等。

为了简化,这里关于后端要说的一句话就是,如果要发送信息的对象在Redis,也就是在线,那么直接查找uip所在服务器发送,否则就存入数据库。在这个用户登陆的时候,加载Notifications的列表。

数据库

由于为了修改添加了很多的操作,这里一一列出

// MysqlManager.h
/*** @brief 添加好友申请** @param fromUid* @param toUid* @return true* @return false*/
bool AddFriendApply(const std::string& fromUid, const std::string& toUid);
/*** @brief 获取用户信息,精确匹配** @param uid* @return std::shared_ptr<UserInfo>*/
std::shared_ptr<UserInfo> GetUser(int uid);
/*** @brief 获取用户信息,模糊查询** @return std::vector<std::shared_ptr<UserInfo>>*/
std::vector<std::shared_ptr<UserInfo>> GetUser(const std::string&);
/*** @brief 获取好友申请列表** @param uid* @param applyList* @return true* @return false*/
bool GetFriendApplyList(const std::string& uid, std::vector<std::shared_ptr<UserInfo>>& applyList);
/*** @brief 改变好友申请状态,1同意0拒绝** @param fromUid* @param toUid* @param status* @return true* @return false*/
bool ChangeApplyStatus(const std::string& fromUid, const std::string& toUid, int status);
/*** @brief 改变消息状态,1已读0未读** @param fromUid* @param toUid* @param status* @return true* @return false*/
bool ChangeMessageStatus(const std::string& uid, int status);
/*** @brief 建立好友关系** @param fromUid* @param toUid* @return true* @return false*/
bool MakeFriends(const std::string& fromUid, const std::string& toUid);
/*** @brief 检查是否是好友关系** @param fromUid* @param toUid* @return true* @return false*/
bool CheckIsFriend(const std::string& fromUid, const std::string& toUid);
/*** @brief 添加通知** @param uid* @param type* @param message* @return true* @return false*/
bool AddNotification(const std::string& uid, int type, const std::string& message);
/*** @brief 获取通知列表** @param uid* @param notificationList* @return true* @return false*/
bool GetNotificationList(const std::string& uid, std::vector<std::shared_ptr<UserInfo>>& notificationList);
// MysqlDao.cpp
bool MysqlDao::AddFriendApply(const std::string& fromUid, const std::string& toUid)
{if (fromUid == toUid) {return false; // 不允许自己添加自己}auto conn = _pool->GetConnection();if (!conn) {SPDLOG_ERROR("Failed to get connection from pool");return false;}Defer defer([this, &conn]() {_pool->ReturnConnection(std::move(conn));});try {mysqlpp::Query query = conn->query();query << "INSERT IGNORE INTO friend_apply (from_uid,to_uid) VALUES(%0,%1) ";query.parse();mysqlpp::SimpleResult res = query.execute(std::stoi(fromUid), std::stoi(toUid));int rowCount = res.rows();return rowCount >= 0;} catch (const mysqlpp::Exception& e) {SPDLOG_ERROR("MySQL++ exception: {}", e.what());return false;}return true;
}std::shared_ptr<UserInfo> MysqlDao::GetUser(int uid)
{auto conn = _pool->GetConnection();if (!conn) {SPDLOG_ERROR("Failed to get connection from pool");return nullptr;}try {mysqlpp::Query query = conn->query();query << "SELECT * FROM user WHERE uid = %0q";query.parse();mysqlpp::StoreQueryResult res = query.store(uid);/**jj["uid"] = uid;jj["name"] = user_info->name;jj["email"] = user_info->email;jj["nick"] = user_info->nick;jj["sex"] = user_info->sex;jj["desc"] = user_info->desc;jj["icon"] = user_info->icon;jj["token"] = token;**/if (res && res.num_rows() == 1) {auto user_info = std::make_shared<UserInfo>();user_info->uid = uid;user_info->sex = res[0]["sex"];user_info->status = res[0]["status"];user_info->name = std::string(res[0]["name"]);user_info->email = std::string(res[0]["email"]);user_info->icon = std::string(res[0]["icon"]);user_info->desc = std::string(res[0]["desc"]);// user_info->nick = std::string(res[0]["nick"]);_pool->ReturnConnection(std::move(conn));return user_info;} else {_pool->ReturnConnection(std::move(conn));SPDLOG_DEBUG("User not found with uid: {}", uid);return nullptr;}} catch (const mysqlpp::Exception& e) {SPDLOG_ERROR("MySQL++ exception: {}", e.what());if (conn)_pool->ReturnConnection(std::move(conn));return nullptr;}
}std::vector<std::shared_ptr<UserInfo>> MysqlDao::GetUser(const std::string& name)
{auto conn = _pool->GetConnection();if (!conn) {SPDLOG_ERROR("Failed to get connection from pool");return {};}Defer defer([this, &conn]() {_pool->ReturnConnection(std::move(conn));});try {mysqlpp::Query query = conn->query();// 使用预处理语句进行模糊查询query << "SELECT * FROM user WHERE name LIKE " << mysqlpp::quote << ("%" + name + "%");mysqlpp::StoreQueryResult res = query.store();std::vector<std::shared_ptr<UserInfo>> users;if (res) {users.reserve(res.num_rows()); // 预分配内存for (size_t i = 0; i < res.num_rows(); ++i) {auto user_info = std::make_shared<UserInfo>();user_info->uid = res[i]["uid"];user_info->sex = res[i]["sex"];user_info->status = res[i]["status"];user_info->name = std::string(res[i]["name"]);user_info->email = std::string(res[i]["email"]);user_info->icon = std::string(res[0]["icon"]);user_info->desc = std::string(res[0]["desc"]);// user_info->nick = std::string(res[0]["nick"]);users.push_back(user_info);}SPDLOG_DEBUG("Found {} users matching pattern: '{}'", users.size(), search_pattern);}return users;} catch (const mysqlpp::Exception& e) {SPDLOG_ERROR("MySQL++ exception: {}", e.what());return {};}
}bool MysqlDao::GetFriendApplyList(const std::string& uid, std::vector<std::shared_ptr<UserInfo>>& applyList)
{auto conn = _pool->GetConnection();if (!conn) {SPDLOG_ERROR("Failed to get connection from pool");return false;}Defer defer([this, &conn]() {_pool->ReturnConnection(std::move(conn));});try {mysqlpp::Query query = conn->query();query << "SELECT u.uid, u.name, u.icon, u.desc, u.sex, fa.time "<< "FROM user u, friend_apply fa "<< "WHERE u.uid = fa.from_uid " // 获取申请发送者的信息<< "AND fa.to_uid = %0q " // 当前用户是接收方<< "AND fa.status = 0 " // status = 0 表示等待处理<< "ORDER BY fa.time desc";query.parse();mysqlpp::StoreQueryResult res = query.store(uid);if (res && res.num_rows() > 0) {applyList.reserve(res.num_rows()); // 预分配内存for (size_t i = 0; i < res.num_rows(); ++i) {auto user_info = std::make_shared<UserInfo>();user_info->uid = res[i]["uid"];user_info->sex = res[i]["sex"];user_info->name = std::string(res[i]["name"]);user_info->icon = std::string(res[i]["icon"]);user_info->desc = std::string(res[i]["desc"]);user_info->back = std::string(res[i]["time"]);applyList.push_back(user_info);}return true;}return false;} catch (const mysqlpp::Exception& e) {SPDLOG_ERROR("MySQL++ exception: {}", e.what());return false;} catch (const std::exception& e) {SPDLOG_ERROR("Exception: {}", e.what());return false;}
}bool MysqlDao::CheckApplied(const std::string& fromUid, const std::string& toUid)
{auto conn = _pool->GetConnection();if (!conn) {SPDLOG_ERROR("Failed to get connection from pool");return false;}Defer defer([this, &conn]() {_pool->ReturnConnection(std::move(conn));});try {mysqlpp::Query query = conn->query();query << "SELECT COUNT(*) as cnt from friend_apply where from_uid = %0q and to_uid = %1q";query.parse();mysqlpp::StoreQueryResult res = query.store(std::stoi(fromUid), std::stoi(toUid));if (res && res.num_rows() == 1) {int count = res[0]["cnt"];return count > 0;}return false;} catch (const mysqlpp::Exception& e) {SPDLOG_ERROR("MySQL++ exception: {}", e.what());return false;} catch (const std::exception& e) {SPDLOG_ERROR("Exception: {}", e.what());return false;}
}bool MysqlDao::ChangeMessageStatus(const std::string& uid, int status)
{auto conn = _pool->GetConnection();if (!conn) {SPDLOG_ERROR("Failed to get connection from pool");return false;}Defer defer([this, &conn]() {_pool->ReturnConnection(std::move(conn));});try {mysqlpp::Query query = conn->query();query << "UPDATE notifications SET status = %0q WHERE uid = %1q";query.parse();mysqlpp::SimpleResult res = query.execute(status, std::stoi(uid));if (res) {int affected_rows = res.rows();if (affected_rows > 0) {SPDLOG_INFO("Message status changed successfully for uid: {}, status: {}", uid, status);return true;} else {SPDLOG_WARN("No message found with uid: {}", uid);return false;}} else {SPDLOG_ERROR("Failed to change message status: {}", query.error());return false;}} catch (const mysqlpp::Exception& e) {SPDLOG_ERROR("MySQL++ exception: {}", e.what());return false;} catch (const std::exception& e) {SPDLOG_ERROR("Exception: {}", e.what());return false;}
}bool MysqlDao::ChangeApplyStatus(const std::string& fromUid, const std::string& toUid, int status)
{if (!status || fromUid == toUid) {return false;}auto conn = _pool->GetConnection();if (!conn) {SPDLOG_ERROR("Failed to get connection from pool");return false;}Defer defer([this, &conn]() {_pool->ReturnConnection(std::move(conn));});try {mysqlpp::Query query = conn->query();query << "UPDATE friend_apply SET status = %0q WHERE from_uid = %1q AND to_uid = %2q";query.parse();mysqlpp::SimpleResult res = query.execute(status, std::stoi(fromUid), std::stoi(toUid));if (res) {int affected_rows = res.rows();if (affected_rows > 0) {SPDLOG_INFO("Apply status changed successfully for from_uid: {}, to_uid: {}, status: {}", fromUid, toUid, status);return true;} else {SPDLOG_WARN("No apply found with from_uid: {}, to_uid: {}", fromUid, toUid);return false;}} else {SPDLOG_ERROR("Failed to change apply status: {}", query.error());return false;}} catch (const mysqlpp::Exception& e) {SPDLOG_ERROR("MySQL++ exception: {}", e.what());return false;} catch (const std::exception& e) {SPDLOG_ERROR("Exception: {}", e.what());return false;}
}bool MysqlDao::CheckIsFriend(const std::string& fromUid, const std::string& toUid)
{if (fromUid == toUid) {return true;}auto conn = _pool->GetConnection();if (!conn) {SPDLOG_ERROR("Failed to get connection from pool");return false;}Defer defer([this, &conn]() {_pool->ReturnConnection(std::move(conn));});try {mysqlpp::Query query = conn->query();query << "SELECT COUNT(*) as cnt from friends where (self_id = %0q and friend_id = %1q)"<< "OR (self_id = %1q and friend_id = %0q)";query.parse();mysqlpp::StoreQueryResult res = query.store(std::stoi(fromUid), std::stoi(toUid));if (res && !res.empty()) {int count = res[0]["cnt"];return count == 2;}return false;} catch (const mysqlpp::Exception& e) {SPDLOG_ERROR("MySQL++ exception: {}", e.what());return false;} catch (const std::exception& e) {SPDLOG_ERROR("Exception: {}", e.what());return false;}
}bool MysqlDao::AddNotification(const std::string& uid, int type, const std::string& message)
{auto conn = _pool->GetConnection();if (!conn) {SPDLOG_ERROR("Failed to get connection from pool");return false;}Defer defer([this, &conn]() {_pool->ReturnConnection(std::move(conn));});try {mysqlpp::Query query = conn->query();query << "INSERT INTO notifications (uid,type,message) VALUES(%0q,%1q,%2q)";query.parse();mysqlpp::SimpleResult res = query.execute(std::stoi(uid), type, message);if (res) {int affected_rows = res.rows();if (affected_rows > 0) {SPDLOG_INFO("Notification added successfully for uid: {}, type: {}, message: {}", uid, type, message);return true;} else {SPDLOG_WARN("Failed to add notification for uid: {}, type: {}, message: {}", uid, type, message);return false;}} else {SPDLOG_ERROR("Failed to add notification: {}", query.error());return false;}} catch (const mysqlpp::Exception& e) {SPDLOG_ERROR("MySQL++ exception: {}", e.what());return false;} catch (const std::exception& e) {SPDLOG_ERROR("Exception: {}", e.what());return false;}
}bool MysqlDao::GetNotificationList(const std::string& uid, std::vector<std::shared_ptr<UserInfo>>& notificationList)
{auto conn = _pool->GetConnection();if (!conn) {SPDLOG_ERROR("Failed to get connection from pool");return false;}Defer defer([this, &conn]() {_pool->ReturnConnection(std::move(conn));});try {mysqlpp::Query query = conn->query();query << "SELECT u.uid,u.name,u.icon,u.sex,n.type,n.message,n.time from"<< " user u, notifications n"<< " WHERE u.uid = n.uid AND u.uid = %0q"<< " AND n.status = 0"<< " ORDER BY n.time DESC";query.parse();mysqlpp::StoreQueryResult res = query.store(std::stoi(uid));int count = res.num_rows();if (res && res.num_rows() > 0) {notificationList.reserve(res.num_rows()); // 预分配内存for (size_t i = 0; i < res.num_rows(); ++i) {auto user_info = std::make_shared<UserInfo>();user_info->uid = res[i]["uid"];user_info->status = res[i]["type"];user_info->desc = std::string(res[i]["message"]);user_info->back = std::string(res[i]["time"]);user_info->sex = res[i]["sex"];user_info->name = std::string(res[i]["name"]);user_info->icon = std::string(res[i]["icon"]);notificationList.push_back(user_info);}return true;}return false;} catch (const mysqlpp::Exception& e) {SPDLOG_ERROR("MySQL++ exception: {}", e.what());return false;} catch (const std::exception& e) {SPDLOG_ERROR("Exception: {}", e.what());return false;}
}bool MysqlDao::MakeFriends(const std::string& fromUid, const std::string& toUid)
{if (fromUid == toUid) {return false;}auto conn = _pool->GetConnection();if (!conn) {SPDLOG_ERROR("Failed to get connection from pool");return false;}Defer defer([this, &conn]() {_pool->ReturnConnection(std::move(conn));});try {mysqlpp::Transaction trans(*conn);// 添加好友应该是双向的,所以需要插入两条记录mysqlpp::Query query1 = conn->query();query1 << "INSERT INTO friends (self_id,friend_id) VALUES(%0q,%1q),"<< "(%1q,%0q)";query1.parse();mysqlpp::SimpleResult res1 = query1.execute(std::stoi(fromUid), std::stoi(toUid));if (res1) {int affected_rows1 = res1.rows();if (affected_rows1 > 0) {trans.commit();SPDLOG_INFO("Friends added successfully for from_uid: {}, to_uid: {}", fromUid, toUid);return true;} else {trans.rollback();SPDLOG_WARN("Failed to add friends for from_uid: {}, to_uid: {}", fromUid, toUid);return false;}} else {trans.rollback();SPDLOG_ERROR("Failed to add friends for from_uid: {}, to_uid: {}", fromUid, toUid);return false;}} catch (const mysqlpp::Exception& e) {SPDLOG_ERROR("MySQL++ exception: {}", e.what());return false;} catch (const std::exception& e) {SPDLOG_ERROR("Exception: {}", e.what());return false;}
}

前端

我们对于系统和好友消息的展示主要是通过一个“滑动的列表实现的”。

就是以程序的主窗口(根窗口)为父亲,创建一个高度略小于主窗口高度的QWidget,当点击消息按钮的时候,这个组件从右侧滑出,点击其他的位置,组件fade out.

这个通知栏分为三部分:

  • 最上侧的标题
  • 中间的好友信息列表
  • 下侧的系统通知列表

每部分列表都是一个QListWidget,但是我们自定义了一个QWidget,插入的时候指定我们创建QWidget,自定义显示。

目前我们主要完成好友信息列表的前后端通信。

下面是通知栏的代码:

// h
#ifndef NOTIFICATIONPANEL_H
#define NOTIFICATIONPANEL_H#include <QWidget>
#include <QObject>
#include <QGraphicsOpacityEffect>class QListWidgetItem;
class QPropertyAnimation;
class QListWidget;
class FriendsNewsItem;
class SystemNewsItem;
struct UserInfo;
class NotificationPanel:public QWidget
{Q_OBJECT
public:explicit NotificationPanel(QWidget*parent = nullptr);~NotificationPanel() = default;void addFriendNews(bool isReply,int uid,int sex,const QString &iconPath, const QString &name, const QString &content);void addSystemNews(bool isReply,int uid,const QString &iconPath, const QString &name, const QString &content);void showPanel();void hidePanel();void setupUI();void setupConnections();void checkIsEmpty();
signals:void on_unshow_red_dot();  // to TopChatArea::do_unshow_red_news();void on_show_red_dot(); // to TopChatArea::do_show_red_news();
public slots:void do_friend_accept(QListWidgetItem*item);void do_friend_reject(QListWidgetItem*item);void do_friend_confirm_clicked(QListWidgetItem*item);void do_system_accept(QListWidgetItem*item);void do_system_reject(QListWidgetItem*item);void do_system_confirm_clicked(QListWidgetItem*item);void do_get_apply_list(const std::vector<std::shared_ptr<UserInfo>>&list); // from TcpManager::on_get_apply_listvoid do_add_friend(const UserInfo&info); // from TcpManager::on_add_friend();void do_auth_friend(std::shared_ptr<UserInfo>info); // from TcpManager::on_auth_friendvoid do_message_to_list(const std::vector<std::shared_ptr<UserInfo>>&list);void do_notify_friend(std::shared_ptr<UserInfo>info,bool accept);   // from TcpManager::on_notify_friendvoid do_notify_friend2(std::shared_ptr<UserInfo>info,bool accept);   // from TcpManager::on_notify_friend2
private:QListWidget *friendsNews;QListWidget *systemNews;QPropertyAnimation *slideAnimation;QPropertyAnimation *fadeAnimation;QGraphicsOpacityEffect *opacityEffect;// QWidget interface
protected:void closeEvent(QCloseEvent *event);// QWidget interface
protected:void focusOutEvent(QFocusEvent *event);
};#endif // NOTIFICATIONPANEL_H// cpp
#include "notificationpanel.h"
#include "systemnewsitem.h"
#include "friendsnewsitem.h"
#include "../../../../Properties/global.h"
#include "../../../../tcpmanager.h"#include <QLabel>
#include <QVBoxLayout>
#include <QPropertyAnimation>
#include <QListWidget>
#include <QCloseEvent>
#include <QApplication>NotificationPanel::NotificationPanel(QWidget *parent): QWidget(parent)
{setParent(window());setWindowFlags(Qt::FramelessWindowHint | Qt::Popup);setAttribute(Qt::WA_StyledBackground, true);if (parent) {setAttribute(Qt::WA_ShowWithoutActivating);}setupUI();setupConnections();
}void NotificationPanel::addFriendNews(bool isReply, int uid, int sex,const QString &iconPath, const QString &name, const QString &content)
{emit on_show_red_dot();qDebug() << "iconPath" << iconPath;QString icon = (iconPath.isNull() || iconPath.isEmpty())?  ":/Resources/main/header-default.png":iconPath;FriendsNewsItem *itemWidget = new FriendsNewsItem(isReply,uid,sex,icon, name, content);QListWidgetItem*item = new QListWidgetItem;item->setSizeHint(itemWidget->sizeHint());friendsNews->addItem(item);friendsNews->setItemWidget(item,itemWidget);friendsNews->update();connect(itemWidget, &FriendsNewsItem::on_accepted_clicked, [this, item]() {do_friend_accept(item);});connect(itemWidget, &FriendsNewsItem::on_rejected_clicked, [this, item]() {do_friend_reject(item);});connect(itemWidget, &FriendsNewsItem::on_confirm_clicked, [this, item]() {do_friend_confirm_clicked(item);});}void NotificationPanel::addSystemNews(bool isReply, int uid, const QString &iconPath, const QString &name, const QString &content)
{emit on_show_red_dot();SystemNewsItem *itemWidget = new SystemNewsItem(isReply,uid,iconPath, name, content);QListWidgetItem*item = new QListWidgetItem;item->setSizeHint(itemWidget->sizeHint());systemNews->addItem(item);systemNews->setItemWidget(item,itemWidget);systemNews->update();connect(itemWidget, &SystemNewsItem::on_accepted_clicked, [this, item]() {do_system_accept(item);});connect(itemWidget, &SystemNewsItem::on_rejected_clicked, [this, item]() {do_system_reject(item);});connect(itemWidget, &SystemNewsItem::on_confirm_clicked, [this, item]() {do_system_confirm_clicked(item);});
}void NotificationPanel::showPanel()
{if (parentWidget() != nullptr){QWidget*mainWindow = parentWidget();QRect parentR = mainWindow->geometry();QRect startRect = QRect(parentR.width(),10,width(),mainWindow->height() - 20);qDebug() << startRect;QRect endRect(parentR.width() - width() - 10,10,width(),mainWindow->height() - 20);setGeometry(startRect);friendsNews->setFixedHeight(height()/2-80);show();raise();slideAnimation->setStartValue(startRect);slideAnimation->setEndValue(endRect);slideAnimation->start();}
}void NotificationPanel::hidePanel()
{fadeAnimation->setStartValue(1.0);fadeAnimation->setEndValue(0.0);fadeAnimation->start();connect(fadeAnimation, &QPropertyAnimation::finished, this, [this](){hide();});
}void NotificationPanel::setupUI()
{QVBoxLayout *main_vlay = new QVBoxLayout(this);main_vlay->setContentsMargins(0,0,0,0);main_vlay->setSpacing(10);QLabel *title = new QLabel;title->setText("通知");QPalette palette = title->palette();palette.setColor(QPalette::WindowText,Qt::black);title->setPalette(palette);QFont font = title->font();font.setPointSize(16);font.setBold(true);title->setFont(font);title->setFixedHeight(40);title->setAlignment(Qt::AlignCenter);// 好友通知标签QLabel *friendLabel = new QLabel("好友请求");friendLabel->setFixedHeight(30);// 好友通知列表friendsNews = new QListWidget;friendsNews->setSpacing(1);// 系统通知标签QLabel *systemLabel = new QLabel("系统通知");systemLabel->setFixedHeight(30);// 系统通知列表systemNews = new QListWidget;systemNews->setSpacing(1);main_vlay->addWidget(title);main_vlay->addWidget(friendLabel);main_vlay->addWidget(friendsNews);main_vlay->addWidget(systemLabel);main_vlay->addWidget(systemNews);// 创建动画slideAnimation = new QPropertyAnimation(this,"geometry",this);slideAnimation->setEasingCurve(QEasingCurve::InOutCubic);slideAnimation->setDuration(100);opacityEffect = new QGraphicsOpacityEffect(this);opacityEffect->setOpacity(1.0);setGraphicsEffect(opacityEffect);   // 接管绘制fadeAnimation = new QPropertyAnimation(this);fadeAnimation->setEasingCurve(QEasingCurve::Linear);fadeAnimation->setTargetObject(opacityEffect);fadeAnimation->setDuration(100);}void NotificationPanel::setupConnections()
{connect(TcpManager::GetInstance().get(),&TcpManager::on_get_apply_list,this,&NotificationPanel::do_get_apply_list);connect(TcpManager::GetInstance().get(),&TcpManager::on_auth_friend,this,&NotificationPanel::do_auth_friend);connect(TcpManager::GetInstance().get(),&TcpManager::on_add_friend,this,&NotificationPanel::do_add_friend);connect(TcpManager::GetInstance().get(),&TcpManager::on_notify_friend,this,&NotificationPanel::do_notify_friend);connect(TcpManager::GetInstance().get(),&TcpManager::on_notify_friend2,this,&NotificationPanel::do_notify_friend2);connect(TcpManager::GetInstance().get(),&TcpManager::on_message_to_list,this,&NotificationPanel::do_message_to_list);
}void NotificationPanel::checkIsEmpty()
{if (!friendsNews->count()&&!systemNews->count()){emit on_unshow_red_dot();}
}void NotificationPanel::do_friend_accept(QListWidgetItem *item)
{int row = friendsNews->row(item);friendsNews->takeItem(row);delete item;checkIsEmpty();
}void NotificationPanel::do_friend_reject(QListWidgetItem *item)
{int row = friendsNews->row(item);friendsNews->takeItem(row);delete item;checkIsEmpty();
}void NotificationPanel::do_friend_confirm_clicked(QListWidgetItem *item)
{int row = friendsNews->row(item);friendsNews->takeItem(row);delete item;checkIsEmpty();
}void NotificationPanel::do_system_accept(QListWidgetItem *item)
{int row = systemNews->row(item);systemNews->takeItem(row);delete item;checkIsEmpty();
}void NotificationPanel::do_system_reject(QListWidgetItem *item)
{int row = systemNews->row(item);systemNews->takeItem(row);delete item;checkIsEmpty();
}void NotificationPanel::do_system_confirm_clicked(QListWidgetItem *item)
{int row = systemNews->row(item);systemNews->takeItem(row);delete item;checkIsEmpty();
}void NotificationPanel::do_auth_friend(std::shared_ptr<UserInfo> info)
{addFriendNews(false,info->id,info->sex,info->avatar,info->name,"😄向您发来好友申请😄");
}void NotificationPanel::do_message_to_list(const std::vector<std::shared_ptr<UserInfo> > &list)
{for(auto&item:list){addFriendNews(true,item->id,-1,item->avatar,"时间"+item->back,item->desc);}
}void NotificationPanel::do_notify_friend(std::shared_ptr<UserInfo> info, bool accept)
{if (accept)addFriendNews(true,info->id,info->sex,info->avatar,info->name,"😄双方已建立好友关系!😄");
}void NotificationPanel::do_notify_friend2(std::shared_ptr<UserInfo> info, bool accept)
{if (accept)addFriendNews(true,info->id,info->sex,info->avatar,info->name,"😄对方同意了您的好友申请😄");elseaddFriendNews(true,info->id,info->sex,info->avatar,info->name,"😢对方拒绝了您的好友请求😢");
}void NotificationPanel::do_get_apply_list(const std::vector<std::shared_ptr<UserInfo>>&list)
{for(const auto&apply:list){addFriendNews(false,apply->id,apply->sex,apply->avatar,apply->name,"😄向您发来好友申请😄");}
}void NotificationPanel::do_add_friend(const UserInfo &info)
{/*addFriendNews(bool isReply, int uid, int sex,* const QString &iconPath, const QString &name, const QString &content) */addFriendNews(true,info.id,-1,":/Resources/main/header-default.png","好友申请通知",QString("😄向用户 %1 的请求已发出😄").arg(info.id));
}void NotificationPanel::closeEvent(QCloseEvent *event)
{hidePanel();event->ignore();
}void NotificationPanel::focusOutEvent(QFocusEvent *event)
{Q_UNUSED(event);
}

下面是FriendsNewsItem的实现,根据消息的类别,如果是纯通知类型的(isReply == true)就只有一个按钮(借用AcceptButton),如果是请求类型的(isReply == false),两个按钮,接受和拒绝。两种不同的情况,AcceptButton绑定不同的槽函数:

// h
#ifndef FRIENDSNEWSITEM_H
#define FRIENDSNEWSITEM_H#include <QObject>
#include <QWidget>class QLabel;
class QPushButton;class FriendsNewsItem : public QWidget
{Q_OBJECT
public:explicit FriendsNewsItem(bool isReply,int uid,int sex,const QString&iconPath,const QString&name,const QString&content,QWidget *parent = nullptr);
private:void setupUI();void setConnections();
private:QLabel *iconLabel;QLabel *nameLabel;QLabel *contentLabel;QPushButton *acceptButton;QPushButton *rejectButton;int _uid;bool _isRely; // 判断是好友申请还是申请回复,申请需要两个按钮,回复只需要一个按钮:使用acceptButton代替确认int _sex;QString _icon;
private slots:void do_accept_clicked();void do_reject_clcked();signals:void on_accepted_clicked();void on_rejected_clicked();void on_confirm_clicked();
};#endif // FRIENDSNEWSITEM_H// cpp
#include "friendsnewsitem.h"
#include <QJsonObject>
#include <QLabel>
#include <QPainter>
#include <QPainterPath>
#include <QPushButton>
#include <QVBoxLayout>
#include "../../../../usermanager.h"
#include "../../../../Properties/sourcemanager.h"
#include "../../../../tcpmanager.h"FriendsNewsItem::FriendsNewsItem(bool isReply,int uid,int sex,const QString &iconPath, const QString &name, const QString &content, QWidget *parent): QWidget(parent), _uid(uid), _isRely(isReply), _sex(sex), _icon(iconPath){setupUI();setConnections();nameLabel->setText(name);contentLabel->setText(content);QPixmap originalPixmap;// 创建带边框的圆形图片if (_icon.startsWith(":/")){originalPixmap = QPixmap(_icon);}else{QByteArray imageData = QByteArray::fromBase64(_icon.toUtf8());originalPixmap.loadFromData(imageData);}QPixmap finalPixmap(44, 44);finalPixmap.fill(Qt::transparent);QPainter painter(&finalPixmap);painter.setRenderHint(QPainter::Antialiasing);painter.setRenderHint(QPainter::SmoothPixmapTransform);// 1. 先绘制边框QColor borderColor = (_sex == 1) ? QColor("#00F5FF") : QColor("#FF69B4");painter.setBrush(borderColor);painter.setPen(Qt::NoPen);painter.drawEllipse(0, 0, 44, 44);// 2. 绘制背景painter.setBrush(QColor("#E3F2FD"));painter.drawEllipse(2, 2, 40, 40);  // 边框内部// 3. 裁剪并绘制头像QPainterPath clipPath;clipPath.addEllipse(2, 2, 40, 40);  // 头像区域painter.setClipPath(clipPath);painter.drawPixmap(2, 2, 40, 40, originalPixmap.scaled(40, 40, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation));iconLabel->setPixmap(finalPixmap);
}void FriendsNewsItem::setupUI()
{setFixedSize({200,100});QVBoxLayout*main_vlay = new QVBoxLayout(this);main_vlay->setContentsMargins(0,10,0,10);main_vlay->setSpacing(0);iconLabel = new QLabel;iconLabel->setFixedSize(44,44);iconLabel->setAlignment(Qt::AlignCenter);// 名称+内容QVBoxLayout*text_vlay = new QVBoxLayout;text_vlay->setContentsMargins(0,0,0,0);nameLabel = new QLabel;nameLabel->setObjectName("FriendsNewItem_NameLabel");contentLabel = new QLabel;contentLabel->setObjectName("FriendsNewsItem_ContentLabel");text_vlay->addWidget(nameLabel);text_vlay->addWidget(contentLabel);QHBoxLayout*top_hlay = new QHBoxLayout;top_hlay->setContentsMargins(0,0,0,0);top_hlay->addWidget(iconLabel);top_hlay->addLayout(text_vlay);QHBoxLayout*button_hlay = new QHBoxLayout;button_hlay->setContentsMargins(0,0,0,0);button_hlay->setSpacing(10);if (_isRely){acceptButton = new QPushButton;acceptButton->setObjectName("FriendsNewsItem_AcceptButton");acceptButton->setFixedSize(90,30);acceptButton->setText("确认");button_hlay->addWidget(acceptButton,Qt::AlignCenter);button_hlay->addStretch();}else{acceptButton = new QPushButton;acceptButton->setObjectName("FriendsNewsItem_AcceptButton");acceptButton->setFixedSize(90,30);acceptButton->setText("接受");rejectButton = new QPushButton;rejectButton->setObjectName("FriendsNewsItem_RejectButton");rejectButton->setFixedSize(90,30);rejectButton->setText("拒绝");button_hlay->addWidget(acceptButton);button_hlay->addWidget(rejectButton);}main_vlay->addLayout(top_hlay);main_vlay->addLayout(button_hlay);
}void FriendsNewsItem::setConnections()
{if (_isRely){connect(acceptButton,&QPushButton::clicked,this,&FriendsNewsItem::do_accept_clicked);}else{connect(acceptButton,&QPushButton::clicked,this,&FriendsNewsItem::do_accept_clicked);connect(rejectButton,&QPushButton::clicked,this,&FriendsNewsItem::do_reject_clcked);}
}void FriendsNewsItem::do_accept_clicked()
{if (_isRely){emit on_confirm_clicked();QJsonObject jsonObj;jsonObj["from_uid"] = UserManager::GetInstance()->GetUid();jsonObj["reply"] = true;QJsonDocument doc(jsonObj);TcpManager::GetInstance()->do_send_data(RequestType::ID_AUTH_FRIEND_REQ,doc.toJson(QJsonDocument::Compact));}else{emit on_accepted_clicked(); // 提示消除item// 下面回复请求为接受QJsonObject jsonObj;jsonObj["from_uid"] = UserManager::GetInstance()->GetUid();jsonObj["to_uid"] = _uid;jsonObj["from_name"] = UserManager::GetInstance()->GetName();jsonObj["from_sex"] = UserManager::GetInstance()->GetSex();jsonObj["from_icon"] =UserManager::GetInstance()->GetIcon();jsonObj["accept"] = true;QJsonDocument doc(jsonObj);TcpManager::GetInstance()->do_send_data(RequestType::ID_AUTH_FRIEND_REQ,doc.toJson(QJsonDocument::Compact));}
}void FriendsNewsItem::do_reject_clcked()
{emit on_rejected_clicked();// 下面回复请求为拒绝QJsonObject jsonObj;jsonObj["from_uid"] = UserManager::GetInstance()->GetUid();jsonObj["to_uid"] = _uid;jsonObj["from_name"] = UserManager::GetInstance()->GetName();jsonObj["from_sex"] = UserManager::GetInstance()->GetSex();jsonObj["accept"] = false;QJsonDocument doc(jsonObj);TcpManager::GetInstance()->do_send_data(RequestType::ID_AUTH_FRIEND_REQ,doc.toJson(QJsonDocument::Compact));
}

SystemNewsItem同理:

// h
#ifndef SYSTEMNEWSITEM_H
#define SYSTEMNEWSITEM_H#include <QObject>
#include <QWidget>class QLabel;
class QPushButton;class SystemNewsItem : public QWidget
{Q_OBJECT
public:explicit SystemNewsItem(bool isReply,int uid,const QString&iconPath,const QString&name,const QString&content,QWidget *parent = nullptr);
private:void setupUI();void setConnections();
private:QLabel *iconLabel;QLabel *nameLabel;QLabel *contentLabel;QPushButton *acceptButton;QPushButton *rejectButton;int _uid;bool _isRely;
private slots:void do_accept_clicked();void do_reject_clcked();signals:void on_accepted_clicked();void on_rejected_clicked();void on_confirm_clicked();
};#endif // SYSTEMNEWSITEM_H// cpp
#include "systemnewsitem.h"
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
#include "../../../../Properties/sourcemanager.h"SystemNewsItem::SystemNewsItem(bool isReply,int uid,const QString &iconPath, const QString &name, const QString &content, QWidget *parent): QWidget(parent), _uid(uid), _isRely(isReply){setupUI();setConnections();nameLabel->setText(name);contentLabel->setText(content);iconLabel->setPixmap(SourceManager::GetInstance()->getPixmap(iconPath).scaled(40,40));
}void SystemNewsItem::setupUI()
{setFixedSize({200,100});QVBoxLayout*main_vlay = new QVBoxLayout(this);main_vlay->setContentsMargins(0,10,0,10);main_vlay->setSpacing(0);iconLabel = new QLabel;iconLabel->setFixedSize(40,40);iconLabel->setObjectName("FriendsNewItem_IconLabel");iconLabel->setAlignment(Qt::AlignCenter);// 名称+内容QVBoxLayout*text_vlay = new QVBoxLayout;text_vlay->setContentsMargins(0,0,0,0);nameLabel = new QLabel;nameLabel->setObjectName("FriendsNewItem_NameLabel");contentLabel = new QLabel;contentLabel->setObjectName("FriendsNewsItem_ContentLabel");text_vlay->addWidget(nameLabel);text_vlay->addWidget(contentLabel);QHBoxLayout*top_hlay = new QHBoxLayout;top_hlay->setContentsMargins(0,0,0,0);top_hlay->addWidget(iconLabel);top_hlay->addLayout(text_vlay);QHBoxLayout*button_hlay = new QHBoxLayout;button_hlay->setContentsMargins(0,0,0,0);button_hlay->setSpacing(10);if (_isRely){acceptButton = new QPushButton;acceptButton->setObjectName("FriendsNewsItem_AcceptButton");acceptButton->setFixedSize(90,30);button_hlay->addWidget(acceptButton,Qt::AlignCenter);button_hlay->addStretch();}else{acceptButton = new QPushButton;acceptButton->setObjectName("FriendsNewsItem_AcceptButton");acceptButton->setFixedSize(90,30);rejectButton = new QPushButton;rejectButton->setObjectName("FriendsNewsItem_RejectButton");rejectButton->setFixedSize(90,30);button_hlay->addWidget(acceptButton);button_hlay->addWidget(rejectButton);}main_vlay->addLayout(top_hlay);main_vlay->addLayout(button_hlay);
}void SystemNewsItem::setConnections()
{if (_isRely){connect(acceptButton,&QPushButton::clicked,this,&SystemNewsItem::do_accept_clicked);}else{connect(acceptButton,&QPushButton::clicked,this,&SystemNewsItem::do_accept_clicked);connect(rejectButton,&QPushButton::clicked,this,&SystemNewsItem::do_reject_clcked);}
}void SystemNewsItem::do_accept_clicked()
{if (_isRely){emit on_confirm_clicked();}else{emit on_accepted_clicked();// tcp发送消息TODO:}
}void SystemNewsItem::do_reject_clcked()
{emit on_rejected_clicked();// tcp发送消息TODO:
}

在其中有很多的信号,来回传送,比较繁多,不再一一列举吗可以借助TcpManager头文件的信号的注释进行分析:

signals:void on_connect_success(bool success); // to LoginScreen::do_connect_successvoid on_send_data(RequestType requestType,QByteArray data); // to TcpManager::do_send_datavoid on_switch_interface(); // to MainWindow::[](){}void on_login_failed(int);  // to LoginScreen::do_login_failedvoid on_users_searched(QList<std::shared_ptr<UserInfo>>list);   // to AnimatedSearchBox::do_users_searchedvoid on_add_friend(const UserInfo&info); // to NotifycationPanel::do_add_friendvoid on_auth_friend(std::shared_ptr<UserInfo>info); // to NotificationPanel::do_auth_friend ; TopChatArea::do_show_red_dotvoid on_get_apply_list(const std::vector<std::shared_ptr<UserInfo>>&list); // to NotificationPanel::do_get_apply_list;void on_add_friend_to_list(std::shared_ptr<UserInfo>); // to Friendvoid on_message_to_list(const std::vector<std::shared_ptr<UserInfo>>&list);// to NotificationPanel::do_message_to_listvoid on_notify_friend(std::shared_ptr<UserInfo>info,bool accept);   // to NotificationPanel::do_notify_friendvoid on_notify_friend2(std::shared_ptr<UserInfo>info,bool accept);   // to_NotificationPanel::do_notify_friend2

下面是TcpManager的注册的回调函数,接受解析出一个信息或者一个列表,再将其通过信号传递给NotificationsPanel的槽函数进行展示:

/*** @brief 用户添加请求回包处理*/_handlers[RequestType::ID_ADD_FRIEND_RSP] = [this](RequestType requestType,int len,QByteArray data){QJsonDocument jsonDoc = QJsonDocument::fromJson(data);if (jsonDoc.isNull()){qDebug() << "Error occured about Json";return;}QJsonObject jsonObj = jsonDoc.object();if (!jsonObj.contains("error")){int err = static_cast<int>(ErrorCodes::ERROR_JSON);qDebug() << "AddFriend Failed,Error Is Json Parse Error " <<err;return;}int err = jsonObj["error"].toInt();if (err != static_cast<int>(ErrorCodes::SUCCESS)){qDebug() << "AddFriend Failed,Error Is " << err;return;}UserInfo info;info.id = jsonObj["fromUid"].toInt();// TODO:qDebug() << "申请添加好友成功";emit on_add_friend(info);};/*** @brief 用户请求添加好友通知处理*/_handlers[RequestType::ID_NOTIFY_ADD_FRIEND_REQ] = [this](RequestType requestType,int len,QByteArray data){QJsonDocument jsonDoc = QJsonDocument::fromJson(data);if (jsonDoc.isNull()){return;}QJsonObject jsonObj = jsonDoc.object();if (!jsonObj.contains("error")){int err = static_cast<int>(ErrorCodes::ERROR_JSON);return;}int err = jsonObj["error"].toInt();if (err != static_cast<int>(ErrorCodes::SUCCESS)){return;}int from_uid = jsonObj["from_uid"].toInt();int from_sex = jsonObj["sex"].toInt();QString from_name = jsonObj["from_name"].toString();QString from_icon = jsonObj["from_icon"].toString();QString from_desc = jsonObj["from_desc"].toString();auto user_info = std::make_shared<UserInfo>();user_info->id = from_uid;user_info->sex = from_sex;user_info->name = from_name;user_info->avatar = from_icon;user_info->desc = from_desc;emit on_auth_friend(user_info);};/*** @brief 用户请求添加好友通知回包*/_handlers[RequestType::ID_AUTH_FRIEND_RSP] = [this](RequestType requestType,int len,QByteArray data){QJsonDocument jsonDoc = QJsonDocument::fromJson(data);if (jsonDoc.isNull()){return;}QJsonObject jsonObj = jsonDoc.object();if (!jsonObj.contains("error")){int err = static_cast<int>(ErrorCodes::ERROR_JSON);return;}int err = jsonObj["error"].toInt();if (err != static_cast<int>(ErrorCodes::SUCCESS)){return;}// 接受信息如果成功,然后将对方信息加入好友列表if (jsonObj.contains("ok") && jsonObj["ok"].toBool()){auto info = std::make_shared<UserInfo>();info->id = jsonObj["to_uid"].toInt();info->status = jsonObj["to_status"].toInt();info->sex = jsonObj["to_sex"].toInt();info->name = jsonObj["to_name"].toString();info->avatar = jsonObj["to_icon"].toString();info->desc = jsonObj["to_desc"].toString();info->back = jsonObj["to_message"].toString();emit on_add_friend_to_list(info);emit on_notify_friend(info,jsonObj["accept"].toBool());}else{//TODO: 暂时忽略}};_handlers[RequestType::ID_NOTIFY_AUTH_FRIEND_REQ] = [this](RequestType requestType,int len,QByteArray data){QJsonDocument jsonDoc = QJsonDocument::fromJson(data);if (jsonDoc.isNull()){return;}QJsonObject jsonObj = jsonDoc.object();if (!jsonObj.contains("error")){int err = static_cast<int>(ErrorCodes::ERROR_JSON);return;}int err = jsonObj["error"].toInt();if (err != static_cast<int>(ErrorCodes::SUCCESS)){return;}auto info = std::make_shared<UserInfo>();info->id = jsonObj["from_uid"].toInt();info->name = jsonObj["from_name"].toString();info->sex = jsonObj["from_sex"].toInt();info->avatar = jsonObj["from_icon"].toString();info->status = jsonObj["from_status"].toInt();info->desc = jsonObj["message"].toString();     // 临时存放消息if (jsonObj["type"].toInt() == static_cast<int>(NotificationCodes::ID_NOTIFY_MAKE_FRIENDS)){emit on_add_friend_to_list(info);emit on_notify_friend2(info,true);}else{emit on_notify_friend2(info,false);}};

后端

我们添加了新的回调ID_AUTH_FRIEND_REQ,以及修改和补充了之前的ID_ADD_FRIEND_REQ/ID_CHAT_LOGIN

修改

ID_CHAT_LOGIN

在用户登陆的时候,我们返回给用户一些必要的信息,比如持久化的通知,好友申请,更新状态等等:

// 将登陆人的状态信息改变为1
std::string key = USER_STATUS_PREFIX + uid_str;
RedisManager::GetInstance()->Set(key, "1");// 登陆成功,通知所有在线好友
// TODO:jj["uid"] = uid;
jj["name"] = user_info->name;
jj["email"] = user_info->email;
jj["nick"] = user_info->nick;
jj["sex"] = user_info->sex;
jj["desc"] = user_info->desc;
jj["icon"] = user_info->icon;
jj["token"] = token;// 获取申请列表
std::vector<std::shared_ptr<UserInfo>> apply_list;
bool b_apply = MysqlManager::GetInstance()->GetFriendApplyList(uid_str, apply_list);
if (b_apply && apply_list.size() > 0) {// 我们这里规定哪怕数据库操作成功,但是没有数据也算失败,就直接跳过,避免多余判断。json apply_friends;for (auto& apply_user : apply_list) {json apply_friend;apply_friend["uid"] = apply_user->uid;apply_friend["name"] = apply_user->name;apply_friend["email"] = apply_user->email;apply_friend["icon"] = apply_user->icon;apply_friend["sex"] = apply_user->sex;apply_friend["desc"] = apply_user->desc;apply_friend["back"] = apply_user->back; // 时间apply_friends.push_back(apply_friend);}jj["apply_friends"] = apply_friends;
}
// 获取通知列表
std::vector<std::shared_ptr<UserInfo>> notification_list;
bool b_notify = MysqlManager::GetInstance()->GetNotificationList(uid_str, notification_list);
if (b_notify && notification_list.size() > 0) {json notifications;for (auto& notification : notification_list) {json item;item["uid"] = notification->uid;item["type"] = notification->status; // 用status代表type借用UserInfo的结构。item["message"] = notification->desc; // 用desc代表message借用UserInfo的结构。item["time"] = notification->back; // 备用字段表示时间。notifications.push_back(item);}jj["notifications"] = notifications;
}// 获取消息列表// 获取好友列表// 更新登陆数量
auto server_name = ConfigManager::GetInstance()["SelfServer"]["name"];
auto count_str = RedisManager::GetInstance()->HGet(LOGIN_COUNT_PREFIX, server_name);
int count = 0;
if (!count_str.empty()) {count = std::stoi(count_str);
}
count++;
count_str = std::to_string(count);
RedisManager::GetInstance()->HSet(LOGIN_COUNT_PREFIX, server_name, count_str);// session绑定uid
session->SetUid(uid);
// 绑定连接的服务器名称和用户uid
std::string ip_key = USERIP_PREFIX + std::to_string(uid);
RedisManager::GetInstance()->Set(ip_key, server_name);
// uid和session绑定管理,方便之后踢人
UserManager::GetInstance()->SetUserSession(uid, session);
// 设置用户状态在线
std::string status_key = USER_STATUS_PREFIX + uid_str;
RedisManager::GetInstance()->Set(status_key, "1");

ID_ADD_FRIEND_REQ

我们增加了如果双方互发还有申请,直接成为好友同时发送通知。其次是如果通知用户在线就直接发送,否则就数据库存储,下次发送

_function_callbacks[MsgId::ID_ADD_FRIEND_REQ] = [this](std::shared_ptr<Session> session, uint16_t msg_id, const std::string& msg) {json j = json::parse(msg);j["error"] = ErrorCodes::SUCCESS;Defer defer([this, &j, session]() {// 回复请求方的信息session->Send(j.dump(), static_cast<int>(MsgId::ID_ADD_FRIEND_RSP));});auto toUid = j["toUid"].get<int>();auto fromUid = j["fromUid"].get<int>();auto fromName = j["fromName"].get<std::string>();auto fromSex = j["fromSex"].get<int>();auto fromDesc = j["fromDesc"].get<std::string>();// auto fromIcon = j["fromIcon"].get<std::string>();auto fromIcon = j.value("fromIcon", "");std::string uid_str = std::to_string(toUid);// 先检查双方是否互相发送请求,如果是直接双方同意。bool apply_each = MysqlManager::GetInstance()->CheckApplied(std::to_string(toUid), std::to_string(fromUid));if (apply_each) {json jj;jj["error"] = ErrorCodes::SUCCESS;jj["from_uid"] = fromUid;jj["from_name"] = fromName;jj["from_sex"] = fromSex;jj["from_icon"] = fromIcon;std::string key;bool b_get = RedisManager::GetInstance()->Get(USER_STATUS_PREFIX + std::to_string(fromUid), key);if (b_get) {jj["from_status"] = std::stoi(key);} else {jj["from_status"] = 0;}jj["ok"] = true; // 标记成功MysqlManager::GetInstance()->AddNotification(uid_str, static_cast<int>(NotificationCodes::ID_NOTIFY_MAKE_FRIENDS), "成功和" + fromName + "成为好友");// 给对方发送请求信息auto& cfg = ConfigManager::GetInstance();auto self_name = cfg["SelfServer"]["name"];auto to_key = USERIP_PREFIX + uid_str;std::string to_ip_value;bool b_ip = RedisManager::GetInstance()->Get(to_key, to_ip_value);if (b_ip) {if (to_ip_value == self_name) {auto session2 = UserManager::GetInstance()->GetSession(toUid);if (session2) {SPDLOG_INFO("FROM UID:{},to:{}", fromUid, toUid);session2->Send(jj.dump(), static_cast<int>(MsgId::ID_NOTIFY_AUTH_FRIEND_REQ));}return;} else {NotifyMakeFriendsRequest req;req.set_fromuid(fromUid);req.set_touid(toUid);req.set_fromname(fromName);req.set_fromsex(fromSex);req.set_fromicon(fromIcon);req.set_type(static_cast<int>(NotificationCodes::ID_NOTIFY_MAKE_FRIENDS));req.set_message("成功和" + fromName + "成为好友");ChatGrpcClient::GetInstance()->NotifyMakeFriends(to_ip_value, req);}} else {// 这里没有查询到,不发送无妨。因为已经存入数据库,用户登录就可以直接获取。return;}}bool b_apply = MysqlManager::GetInstance()->AddFriendApply(std::to_string(fromUid), uid_str);if (!b_apply) {return;}auto to_key = USERIP_PREFIX + uid_str;std::string to_ip_value;bool b_ip = RedisManager::GetInstance()->Get(to_key, to_ip_value);if (!b_ip) {return;}// 给对方发送请求信息auto& cfg = ConfigManager::GetInstance();auto self_name = cfg["SelfServer"]["name"];if (to_ip_value == self_name) {auto session2 = UserManager::GetInstance()->GetSession(toUid);if (session2) {SPDLOG_INFO("FROM UID:{},to:{}", fromUid, toUid);SPDLOG_INFO("FROM SESSION:{},to:{}", session->GetSessionId(), session2->GetSessionId());json jj;jj["error"] = ErrorCodes::SUCCESS;jj["fromUid"] = fromUid;jj["fromName"] = fromName;session2->Send(jj.dump(), static_cast<int>(MsgId::ID_NOTIFY_ADD_FRIEND_REQ));}return;}AddFriendRequest req;req.set_fromuid(fromUid);req.set_touid(toUid);req.set_name(fromName);req.set_desc(fromDesc);req.set_sex(fromSex);req.set_icon(fromIcon);ChatGrpcClient::GetInstance()->NotifyAddFriend(to_ip_value, req);};

添加

ID_AUTH_FRIEND_REQ

被申请人点击同意或者拒绝之后,首先将好友信息或者申请信息存储在数据库中(比如是否成为好友,申请请求处理状态等,一般是0未处理,1已处理),同时检测申请人目前是否在线,在线直接发送,否则将通知存储在数据库。最后还要将处理结果发送给被申请人,消息处理ok了,被申请人只要点击确认即可。同理申请人上线接收到或直接接收到通知后,点击确认表示受到信息,这时候将Notifications的信息状态置为1.

_function_callbacks[MsgId::ID_AUTH_FRIEND_REQ] = [this](std::shared_ptr<Session> session, uint16_t msg_id, const std::string& msg) {json j = json::parse(msg);j["error"] = ErrorCodes::SUCCESS;j["ok"] = false; // 标记失败if (bool b = j.value("reply", false)) {if (b) {// 只是收到通知回复,我们把数据库状态更新一下// 如果失败说明当前双方都在线,消息就没有入库,所以这里不做处理。auto fromUid = j["from_uid"].get<int>();bool ok1 = MysqlManager::GetInstance()->ChangeMessageStatus(std::to_string(fromUid), 1);return;}}Defer defer([this, &j, session]() {// 这是给fromUid的回复信息// 目地是如果同意,那么就返回好友的信息session->Send(j.dump(), static_cast<int>(MsgId::ID_AUTH_FRIEND_RSP));});auto toUid = j["to_uid"].get<int>();auto fromUid = j["from_uid"].get<int>();auto fromName = j["from_name"].get<std::string>();auto fromSex = j["from_sex"].get<int>();auto fromIcon = j["from_icon"].get<std::string>();int fromStatus = 1;bool accept = j["accept"].get<bool>();// 不需要解析其他的信息,只需要按需发给对方即可// fromUid接受或者拒绝,服务器回复给toUidstd::string base_key = USER_BASE_INFO_PREFIX + std::to_string(toUid);auto apply_info = std::make_shared<UserInfo>();bool b_info = GetBaseInfo(base_key, toUid, apply_info);if (!b_info) {j["ok"] = true;// 发送请求的用户不在线,所以数据库持久存储if (!accept) {MysqlManager::GetInstance()->AddNotification(std::to_string(toUid), static_cast<int>(NotificationCodes::ID_NOTIFY_NOT_FRIENDS), "😭" + fromName + "拒绝了您的好友申请😭");} else {MysqlManager::GetInstance()->AddNotification(std::to_string(toUid), static_cast<int>(NotificationCodes::ID_NOTIFY_MAKE_FRIENDS), "😄" + fromName + "同意了您的好友申请😄");}return;} else {j["to_uid"] = apply_info->uid;j["to_sex"] = apply_info->sex;j["to_status"] = apply_info->status;j["to_name"] = apply_info->name;j["to_email"] = apply_info->email;j["to_icon"] = apply_info->icon;j["to_desc"] = apply_info->desc;j["to_meseage"] = apply_info->back; // 备用字段,用来展示最近消息j["ok"] = true;if (!accept) {j["type"] = static_cast<int>(NotificationCodes::ID_NOTIFY_NOT_FRIENDS);} else {j["type"] = static_cast<int>(NotificationCodes::ID_NOTIFY_MAKE_FRIENDS);}}if (accept) {bool ok1 = MysqlManager::GetInstance()->ChangeApplyStatus(std::to_string(toUid), std::to_string(fromUid), 1);bool ok2 = MysqlManager::GetInstance()->MakeFriends(std::to_string(toUid), std::to_string(fromUid));// 接下来就是获取好友信息,发送给被申请人} else {MysqlManager::GetInstance()->ChangeApplyStatus(std::to_string(toUid), std::to_string(fromUid), -1);}// TODO:接下来就是发送给申请人,也就是将from_uid的信息发送给to_uidstd::string to_key = USERIP_PREFIX + std::to_string(toUid);std::string to_ip_value;bool b_ip = RedisManager::GetInstance()->Get(to_key, to_ip_value);if (!b_ip) {// 不存在我们就需要加入mysqk持续等待下次用户登录处理bool ok = MysqlManager::GetInstance()->AddNotification(std::to_string(toUid), static_cast<int>(NotificationCodes::ID_NOTIFY_MAKE_FRIENDS), "😄" + fromName + "已经和您成为好友😄");return;}auto& cfg = ConfigManager::GetInstance();auto self_name = cfg["SelfServer"]["name"];if (to_ip_value == self_name) {auto session2 = UserManager::GetInstance()->GetSession(toUid);if (session2) {SPDLOG_INFO("FROM UID:{},to:{}", fromUid, toUid);SPDLOG_INFO("FROM SESSION:{},to:{}", session->GetSessionId(), session2->GetSessionId());session2->Send(j.dump(), static_cast<int>(MsgId::ID_NOTIFY_AUTH_FRIEND_REQ));}} else {NotifyMakeFriendsRequest req;req.set_fromuid(fromUid);req.set_touid(toUid);req.set_fromname(fromName);req.set_fromsex(fromSex);req.set_fromicon(fromIcon);req.set_fromstatus(fromStatus);if (!accept) {req.set_type(static_cast<int>(NotificationCodes::ID_NOTIFY_NOT_FRIENDS));req.set_message(fromName + "拒绝了你的好友申请");} else {req.set_type(static_cast<int>(NotificationCodes::ID_NOTIFY_MAKE_FRIENDS));req.set_message(fromName + "同意了您的好友申请");}ChatGrpcClient::GetInstance()->NotifyMakeFriends(to_ip_value, req);}
};

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询