【聊天室后端服务器开发】 好友管理子服务

实现思路

实现功能

  • 获取好友列表:登录后显示该用户的好友列表
  • 申请好友:搜索用户,发送请求,向申请的用户发送添加好友的请求
  • 获取待处理请求:用户登录后,获取离线状态下接收到的好友申请
  • 处理好友申请:同意或者拒绝好友申请
  • 删除好友:删除当前列表中的该好友
  • 用户搜索:进行用户的搜索然后申请好友
  • 获取聊天会话列表:获取单聊或者群聊会话的相关信息
  • 多个聊天会话的创建:多人聊天会话窗口需要通过接口进行手动创建
  • 获取聊天成员列表:点击群聊成员按钮,获取群成员的相关信息

实现逻辑

总体逻辑图

数据库模块

mysql数据管理

创建关系表

  • 好友关系表
  • 会话信息表
  • 会话成员表
  • 好友申请表

建立好友关系表

#pragma db object table("relation")
class Relation{
    public:
        Relation(){}
        Relation(const std::string &uid, const std::string &pid):
            _user_id(uid),  _peer_id(pid){}
        
        std::string user_id() const { return _user_id; }
        void user_id(std::string &uid)  { _user_id = uid; }
        
        std::string peer_id() const { return _peer_id; }
        void peer_id(std::string &uid)  { _peer_id = uid; }

    private:
        friend class odb::access;
        #pragma db id auto
        unsigned long _id;
        #pragma db type("varchar(64)") index
        std::string _user_id;
        #pragma db type("varchar(64)")
        std::string _peer_id;
};

聊天会话表(单聊群聊以及关联成员)

#pragma db object table("chat_session")
class ChatSession {
    public:
        // 默认构造函数
        ChatSession(){}
        
        // 带参数的构造函数,用于初始化聊天会话
        ChatSession(const std::string &ssid, 
                    const std::string &ssname, const ChatSessionType sstype):
            _chat_session_id(ssid),  
            _chat_session_name(ssname), 
            _chat_session_type(sstype){}

        // 获取聊天会话 ID
        std::string chat_session_id() const { return _chat_session_id; }
        
        // 设置聊天会话 ID
        void chat_session_id(std::string &ssid)  { _chat_session_id = ssid; }

        // 获取聊天会话名称
        std::string chat_session_name() const { return _chat_session_name; }
        
        // 设置聊天会话名称
        void chat_session_name(std::string &ssname)  { _chat_session_name = ssname; }
        
        // 获取聊天会话类型
        ChatSessionType chat_session_type() const { return _chat_session_type; }
        
        // 设置聊天会话类型
        void chat_session_type(ChatSessionType val) { _chat_session_type = val; }

    private:
        // ODB 访问友元,允许 ODB 访问私有成员
        friend class odb::access;
        
        // 数据库中的主键,自动生成
        #pragma db id auto
        unsigned long _id;
        
        // 聊天会话 ID,类型为 varchar(64),并且创建唯一索引
        #pragma db type("varchar(64)") index unique
        std::string _chat_session_id;
        
        // 聊天会话名称,类型为 varchar(64)
        #pragma db type("varchar(64)")
        std::string _chat_session_name;
        
        // 聊天会话类型,类型为 tinyint(1: 单聊, 2: 群聊)
        #pragma db type("tinyint")
        ChatSessionType _chat_session_type;
};

好友申请表

#pragma db object table("friend_apply")
class FriendApply{
    public:
        FriendApply() {}
        FriendApply(const std::string &eid, 
            const std::string &uid, const std::string &pid):
            _user_id(uid),  _peer_id(pid), _event_id(eid){}
            
        std::string event_id() const { return _event_id; }
        void event_id(std::string &eid)  { _event_id = eid; }

        std::string user_id() const { return _user_id; }
        void user_id(std::string &uid)  { _user_id = uid; }
        
        std::string peer_id() const { return _peer_id; }
        void peer_id(std::string &uid)  { _peer_id = uid; }
    private:
        friend class odb::access;
        #pragma db id auto
        unsigned long _id;
        #pragma db type("varchar(64)") index unique
        std::string _event_id;
        #pragma db type("varchar(64)") index 
        std::string _user_id;
        #pragma db type("varchar(64)") index 
        std::string _peer_id;
};

封装相应表操作

好友申请操作

  • 插入好友申请
  • 查询是否存在某个好友申请
  • 删除某个好友申请
  • 获取某个用户的申请ID
// 插入新的好友申请事件
        bool insert(FriendApply &ev) {
            try {
                // 开始数据库事务
                odb::transaction trans(_db->begin());
                
                // 将 FriendApply 对象持久化到数据库
                _db->persist(ev);
                
                // 提交事务
                trans.commit();
            } catch (std::exception &e) {
                // 发生异常时记录错误信息
                LOG_ERROR("新增好友申请事件失败 {}-{}:{}!", ev.user_id(), ev.peer_id(), e.what());
                return false;
            }
            return true;
        }

        // 检查指定用户与目标用户之间是否已经有好友申请
        bool exists(const std::string &uid, const std::string &pid) {
            bool flag = false;
            try {
                // 定义查询条件
                typedef odb::query<FriendApply> query;
                typedef odb::result<FriendApply> result;

                // 开始数据库事务
                odb::transaction trans(_db->begin());

                // 查询是否存在符合条件的好友申请记录
                result r(_db->query<FriendApply>(query::user_id == uid && query::peer_id == pid));

                // 记录查询结果大小,判断是否存在此好友申请事件
                LOG_DEBUG("{} - {} 好友事件数量:{}", uid, pid, r.size());
                flag = !r.empty();

                // 提交事务
                trans.commit();
            } catch (std::exception &e) {
                // 发生异常时记录错误信息
                LOG_ERROR("获取好友申请事件失败:{}-{}-{}!", uid, pid, e.what());
            }
            return flag;
        }

        // 删除指定用户和目标用户之间的好友申请记录
        bool remove(const std::string &uid, const std::string &pid) {
            try {
                // 开始数据库事务
                odb::transaction trans(_db->begin());

                // 定义查询条件
                typedef odb::query<FriendApply> query;
                typedef odb::result<FriendApply> result;

                // 删除符合条件的好友申请记录
                _db->erase_query<FriendApply>(query::user_id == uid && query::peer_id == pid);

                // 提交事务
                trans.commit();
            } catch (std::exception &e) {
                // 发生异常时记录错误信息
                LOG_ERROR("删除好友申请事件失败 {}-{}:{}!", uid, pid, e.what());
                return false;
            }
            return true;
        }

        // 获取指定用户的所有好友申请者ID
        std::vector<std::string> applyUsers(const std::string &uid) {
            std::vector<std::string> res;
            try {
                // 开始数据库事务
                odb::transaction trans(_db->begin());

                // 定义查询条件
                typedef odb::query<FriendApply> query;
                typedef odb::result<FriendApply> result;

                // 查询所有目标用户(peer_id)为指定用户(uid)的好友申请记录
                result r(_db->query<FriendApply>(query::peer_id == uid));

                // 遍历查询结果并将申请者的 user_id 添加到结果数组
                for (result::iterator i(r.begin()); i != r.end(); ++i) {
                    res.push_back(i->user_id());
                }

                // 提交事务
                trans.commit();
            } catch (std::exception &e) {
                // 发生异常时记录错误信息
                LOG_ERROR("通过用户{}的好友申请者失败:{}!", uid, e.what());
            }
            return res;
        }

管理聊天会话相关操作

  • 插入会话:在数据库中插入新的会话记录
  • 删除会话:根据会话 ID 删除会话记录及其成员
  • 删除单聊会话:根据用户 ID 删除单聊会话(会话中包含两个成员)
  • 查询会话:根据会话 ID 获取会话的详细信息
  • 获取用户的单聊会话:获取指定用户的所有单聊会话
  • 获取用户的群聊会话:获取指定用户的所有群聊会话
// 插入会话信息到数据库
            bool insert(ChatSession &cs) {
                try {
                    odb::transaction trans(_db->begin());  // 开启数据库事务
                    _db->persist(cs);  // 保存会话数据
                    trans.commit();  // 提交事务
                } catch (std::exception &e) {
                    LOG_ERROR("新增会话失败 {}:{}!", cs.chat_session_name(), e.what());  // 出现异常时记录日志
                    return false;
                }
                return true;
            }

            // 根据会话ID删除会话及成员信息
            bool remove(const std::string &ssid) {
                try {
                    odb::transaction trans(_db->begin());  // 开启数据库事务
                    typedef odb::query<ChatSession> query;
                    typedef odb::result<ChatSession> result;
                    _db->erase_query<ChatSession>(query::chat_session_id == ssid);  // 删除会话

                    typedef odb::query<ChatSessionMember> mquery;
                    _db->erase_query<ChatSessionMember>(mquery::session_id == ssid);  // 删除会话成员
                    trans.commit();  // 提交事务
                } catch (std::exception &e) {
                    LOG_ERROR("删除会话失败 {}:{}!", ssid, e.what());  // 出现异常时记录日志
                    return false;
                }
                return true;
            }

            // 根据用户ID和对方ID删除单聊会话
            bool remove(const std::string &uid, const std::string &pid) {
                try {
                    odb::transaction trans(_db->begin());  // 开启数据库事务
                    typedef odb::query<SingleChatSession> query;
                    typedef odb::result<SingleChatSession> result;

                    // 查询单聊会话,匹配两个用户(uid 和 pid)
                    auto res = _db->query_one<SingleChatSession>(
                        query::csm1::user_id == uid && 
                        query::csm2::user_id == pid && 
                        query::css::chat_session_type == ChatSessionType::SINGLE);

                    std::string cssid = res->chat_session_id;  // 获取会话ID
                    typedef odb::query<ChatSession> cquery;
                    _db->erase_query<ChatSession>(cquery::chat_session_id == cssid);  // 删除会话

                    typedef odb::query<ChatSessionMember> mquery;
                    _db->erase_query<ChatSessionMember>(mquery::session_id == cssid);  // 删除会话成员
                    trans.commit();  // 提交事务
                } catch (std::exception &e) {
                    LOG_ERROR("删除会话失败 {}-{}:{}!", uid, pid, e.what());  // 出现异常时记录日志
                    return false;
                }
                return true;
            }

            // 根据会话ID查询会话信息
            std::shared_ptr<ChatSession> select(const std::string &ssid) {
                std::shared_ptr<ChatSession> res;
                try {
                    odb::transaction trans(_db->begin());  // 开启数据库事务
                    typedef odb::query<ChatSession> query;
                    typedef odb::result<ChatSession> result;
                    res.reset(_db->query_one<ChatSession>(query::chat_session_id == ssid));  // 查询会话
                    trans.commit();  // 提交事务
                } catch (std::exception &e) {
                    LOG_ERROR("通过会话ID获取会话信息失败 {}:{}!", ssid, e.what());  // 出现异常时记录日志
                }
                return res;
            }

            // 获取当前用户的所有单聊会话
            std::vector<SingleChatSession> singleChatSession(const std::string &uid) {
                std::vector<SingleChatSession> res;
                try {
                    odb::transaction trans(_db->begin());  // 开启数据库事务
                    typedef odb::query<SingleChatSession> query;
                    typedef odb::result<SingleChatSession> result;

                    // 查询用户的所有单聊会话(排除自己)
                    result r(_db->query<SingleChatSession>(
                        query::css::chat_session_type == ChatSessionType::SINGLE && 
                        query::csm1::user_id == uid && 
                        query::csm2::user_id != query::csm1::user_id));
                    for (result::iterator i(r.begin()); i != r.end(); ++i) {
                        res.push_back(*i);  // 将查询结果加入返回列表
                    }
                    trans.commit();  // 提交事务
                } catch (std::exception &e) {
                    LOG_ERROR("获取用户 {} 的单聊会话失败:{}!", uid, e.what());  // 出现异常时记录日志
                }
                return res;
            }

            // 获取当前用户的所有群聊会话
            std::vector<GroupChatSession> groupChatSession(const std::string &uid) {
                std::vector<GroupChatSession> res;
                try {
                    odb::transaction trans(_db->begin());  // 开启数据库事务
                    typedef odb::query<GroupChatSession> query;
                    typedef odb::result<GroupChatSession> result;

                    // 查询用户的所有群聊会话
                    result r(_db->query<GroupChatSession>(
                        query::css::chat_session_type == ChatSessionType::GROUP && 
                        query::csm::user_id == uid ));
                    for (result::iterator i(r.begin()); i != r.end(); ++i) {
                        res.push_back(*i);  // 将查询结果加入返回列表
                    }
                    trans.commit();  // 提交事务
                } catch (std::exception &e) {
                    LOG_ERROR("获取用户 {} 的群聊会话失败:{}!", uid, e.what());  // 出现异常时记录日志
                }
                return res;
            }

好友关系管理

  • 新增好友关系
  • 移除关系信息
  • 获取指定用户的好友ID
  • 判断好友关系是否存在
// 新增关系信息
            bool insert(const std::string &uid, const std::string &pid) {
                try {
                    // 创建两个 Relation 对象,分别为 (uid, pid) 和 (pid, uid),
                    // 表示双向的好友关系
                    Relation r1(uid, pid);
                    Relation r2(pid, uid);
                    
                    // 使用事务确保数据的一致性和原子性
                    odb::transaction trans(_db->begin());
                    
                    // 将两个关系对象持久化到数据库
                    _db->persist(r1);
                    _db->persist(r2);
                    
                    // 提交事务
                    trans.commit();
                } catch (std::exception &e) {
                    // 如果发生异常,记录错误日志并返回 false
                    LOG_ERROR("新增用户好友关系信息失败 {}-{}:{}!", uid, pid, e.what());
                    return false;
                }
                return true;  // 如果没有异常,返回 true
            }

            // 移除关系信息
            bool remove(const std::string &uid, const std::string &pid) {
                try {
                    // 使用事务确保删除操作的一致性
                    odb::transaction trans(_db->begin());
                    
                    // 查询并删除 (uid, pid) 和 (pid, uid) 这两个关系
                    typedef odb::query<Relation> query;
                    _db->erase_query<Relation>(query::user_id == uid && query::peer_id == pid);
                    _db->erase_query<Relation>(query::user_id == pid && query::peer_id == uid);
                    
                    // 提交事务
                    trans.commit();
                } catch (std::exception &e) {
                    // 如果发生异常,记录错误日志并返回 false
                    LOG_ERROR("删除好友关系信息失败 {}-{}:{}!", uid, pid, e.what());
                    return false;
                }
                return true;  // 如果没有异常,返回 true
            }

            // 判断关系是否存在
            bool exists(const std::string &uid, const std::string &pid) {
                typedef odb::query<Relation> query;
                typedef odb::result<Relation> result;
                result r;
                bool flag = false;
                try {
                    // 使用事务确保数据一致性
                    odb::transaction trans(_db->begin());
                    
                    // 查询是否存在 (uid, pid) 这个关系
                    r = _db->query<Relation>(query::user_id == uid && query::peer_id == pid);
                    
                    // 如果查询结果非空,说明关系存在
                    flag = !r.empty();
                    
                    // 提交事务
                    trans.commit();
                } catch (std::exception &e) {
                    // 如果发生异常,记录错误日志
                    LOG_ERROR("获取用户好友关系失败:{}-{}-{}!", uid, pid, e.what());
                }
                return flag;  // 返回查询结果
            }

            // 获取指定用户的好友ID
            std::vector<std::string> friends(const std::string &uid) {
                std::vector<std::string> res;
                try {
                    // 使用事务确保查询操作的一致性
                    odb::transaction trans(_db->begin());
                    
                    typedef odb::query<Relation> query;
                    typedef odb::result<Relation> result;
                    
                    // 查询该用户的所有好友关系 (user_id == uid)
                    result r(_db->query<Relation>(query::user_id == uid));
                    
                    // 遍历查询结果,将每个好友的 ID 添加到结果向量中
                    for (result::iterator i(r.begin()); i != r.end(); ++i) {
                        res.push_back(i->peer_id());
                    }
                    
                    // 提交事务
                    trans.commit();
                } catch (std::exception &e) {
                    // 如果发生异常,记录错误日志
                    LOG_ERROR("通过用户-{}的所有好友ID失败:{}!", uid, e.what());
                }
                return res;  // 返回好友列表
            }

数据表导入

RPC服务实现

辅助函数

获取最近会话和批量获取用户信息

private:
        ///
        ///获取最近的消息
        /
        bool GetRecentMsg(const std::string &rid, 
                  const std::string &cssid, 
                  MessageInfo &msg) {
                    // 选择消息服务信道
                    auto channel = _mm_channels->choose(_message_service_name);
                    if (!channel) {
                        // 获取信道失败,记录错误日志
                        LOG_ERROR("{} - 获取消息子服务信道失败!!", rid);
                        return false;
                    }

                    // 设置获取消息的请求
                    GetRecentMsgReq req;
                    GetRecentMsgRsp rsp;
                    req.set_request_id(rid);  // 设置请求ID
                    req.set_chat_session_id(cssid);  // 设置聊天会话ID
                    req.set_msg_count(1);  // 设置获取的消息数量为1
                    brpc::Controller cntl;  // 设置控制器
                    mag::MsgStorageService_Stub stub(channel.get());  // 创建消息存储服务的存根
                    stub.GetRecentMsg(&cntl, &req, &rsp, nullptr);  // 调用消息存储服务的 GetRecentMsg 方法

                    // 检查请求是否失败
                    if (cntl.Failed() == true) {
                        LOG_ERROR("{} - 消息存储子服务调用失败: {}", rid, cntl.ErrorText());
                        return false;
                    }

                    // 检查响应是否成功
                    if (rsp.success() == false) {
                        LOG_ERROR("{} - 获取会话 {} 最近消息失败: {}", rid, cssid, rsp.errmsg());
                        return false;
                    }

                    // 如果返回的消息列表不为空,将第一个消息复制到 msg 中
                    if (rsp.msg_list_size() > 0) {
                        msg.CopyFrom(rsp.msg_list(0));
                        return true;
                    }

                    // 如果消息列表为空,返回 false
                    return false;
                }

        ///
        ///批量获取用户信息
        /
        bool GetUserInfo(const std::string &rid, 
                 const std::unordered_set<std::string> &uid_list,
                 std::unordered_map<std::string, UserInfo> &user_list) {
                // 选择用户服务信道
                auto channel = _mm_channels->choose(_user_service_name);
                if (!channel) {
                    // 获取信道失败,记录错误日志
                    LOG_ERROR("{} - 获取用户子服务信道失败!!", rid);
                    return false;
                }

                // 设置批量获取用户信息的请求
                GetMultiUserInfoReq req;
                GetMultiUserInfoRsp rsp;
                req.set_request_id(rid);  // 设置请求ID
                for (auto &id : uid_list) {
                    req.add_users_id(id);  // 将每个用户ID添加到请求中
                }

                brpc::Controller cntl;  // 设置控制器
                mag::UserService_Stub stub(channel.get());  // 创建用户服务的存根
                stub.GetMultiUserInfo(&cntl, &req, &rsp, nullptr);  // 调用用户服务的 GetMultiUserInfo 方法

                // 检查请求是否失败
                if (cntl.Failed() == true) {
                    LOG_ERROR("{} - 用户子服务调用失败: {}", rid, cntl.ErrorText());
                    return false;
                }

                // 检查响应是否成功
                if (rsp.success() == false) {
                    LOG_ERROR("{} - 批量获取用户信息失败: {}", rid, rsp.errmsg());
                    return false;
                }

                // 将返回的用户信息添加到 user_list 中
                for (const auto &user_it : rsp.users_info()) {
                    user_list.insert(std::make_pair(user_it.first, user_it.second));  // 插入用户ID和对应的信息
                }

                return true;
            }

功能实现

好友获取

Protobuf

message GetFriendListReq {
    string request_id = 1;          // 请求标识ID
    optional string user_id = 2;    // 当前请求的发起者用户ID
    optional string session_id = 3; //登录会话ID--用于网关进行身份识别--其他子服务用不到
}
message GetFriendListRsp {
    string request_id = 1;
    bool success = 2;
    string errmsg = 3; 
    repeated UserInfo friend_list = 4; //要返回的用户信息
}

实现

virtual void GetFriendList(::google::protobuf::RpcController* controller,
            const ::mag::GetFriendListReq* request,
            ::mag::GetFriendListRsp* response,
            ::google::protobuf::Closure* done){
                brpc::ClosureGuard rpc_guard(done);
                //1. 定义错误回调
                auto err_response = [this, response](const std::string &rid, 
                    const std::string &errmsg) -> void {
                    response->set_request_id(rid);
                    response->set_success(false);
                    response->set_errmsg(errmsg);
                    return;
                };
                
                //1. 提取请求中的关键要素:用户ID
                std::string rid = request->request_id();
                std::string uid = request->user_id();
                //2. 从数据库中查询获取用户的好友ID
                auto friend_id_lists = _mysql_relation->friends(uid);
                std::unordered_set<std::string> user_id_lists;
                for (auto &id : friend_id_lists) {
                    user_id_lists.insert(id);
                }
                //3. 从用户子服务批量获取用户信息
                std::unordered_map<std::string, UserInfo> user_list;
                bool ret = GetUserInfo(rid, user_id_lists, user_list);
                if (ret == false) {
                    LOG_ERROR("{} - 批量获取用户信息失败!", rid);
                    return err_response(rid, "批量获取用户信息失败!");
                }
                //4. 组织响应
                response->set_request_id(rid);
                response->set_success(true);
                for (const auto & user_it : user_list) {
                    auto user_info = response->add_friend_list();
                    user_info->CopyFrom(user_it.second);
                }
            }

删除好友

Protobuf

//好友删除
message FriendRemoveReq {
    string request_id = 1;
    optional string user_id = 2; //当前用户ID
    optional string session_id = 3;
    string peer_id = 4;          //要删除的好友ID
}
message FriendRemoveRsp {
    string request_id = 1;
    bool success = 2;
    string errmsg = 3; 
}

实现

virtual void FriendRemove(::google::protobuf::RpcController* controller,
            const ::mag::FriendRemoveReq* request,
            ::mag::FriendRemoveRsp* response,
            ::google::protobuf::Closure* done){
                brpc::ClosureGuard rpc_guard(done);
                //1. 定义错误回调
                auto err_response = [this, response](const std::string &rid, 
                    const std::string &errmsg) -> void {
                    response->set_request_id(rid);
                    response->set_success(false);
                    response->set_errmsg(errmsg);
                    return;
                };
                //1. 提取关键要素:当前用户ID,要删除的好友ID
                std::string rid = request->request_id();
                std::string uid = request->user_id();
                std::string pid = request->peer_id();
                //2. 从好友关系表中删除好友关系信息
                bool ret = _mysql_relation->remove(uid, pid);
                if (ret == false) {
                    LOG_ERROR("{} - 从数据库删除好友信息失败!", rid);
                    return err_response(rid, "从数据库删除好友信息失败!");
                }
                //3. 从会话信息表中,删除对应的聊天会话 -- 同时删除会话成员表中的成员信息
                ret = _mysql_chat_session->remove(uid, pid);
                if (ret == false) {
                    LOG_ERROR("{}- 从数据库删除好友会话信息失败!", rid);
                    return err_response(rid, "从数据库删除好友会话信息失败!");
                }
                //4. 组织响应
                response->set_request_id(rid);
                response->set_success(true);
            }

发送好友申请

Protobuf

//添加好友--发送好友申请
message FriendAddReq {
    string request_id = 1;
    optional string session_id = 2;
    optional string user_id = 3;//申请人id
    string respondent_id = 4;//被申请人id
}
message FriendAddRsp {
    string request_id = 1;
    bool success = 2;
    string errmsg = 3; 
    string notify_event_id = 4;//通知事件id
}

实现

        ///
        ///添加好友
        //
        virtual void FriendAdd(::google::protobuf::RpcController* controller,
            const ::mag::FriendAddReq* request,
            ::mag::FriendAddRsp* response,
            ::google::protobuf::Closure* done){
                brpc::ClosureGuard rpc_guard(done);
                //1. 定义错误回调
                auto err_response = [this, response](const std::string &rid, 
                    const std::string &errmsg) -> void {
                    response->set_request_id(rid);
                    response->set_success(false);
                    response->set_errmsg(errmsg);
                    return;
                };
                //1. 提取请求中的关键要素:申请人用户ID; 被申请人用户ID
                std::string rid = request->request_id();
                std::string uid = request->user_id();
                std::string pid = request->respondent_id();//被申请人用户ID
                //2. 判断两人是否已经是好友
                bool ret = _mysql_relation->exists(uid, pid);
                if (ret == true) {
                    LOG_ERROR("{}- 申请好友失败-两者{}-{}已经是好友关系", rid, uid, pid);
                    return err_response(rid, "两者已经是好友关系!");
                }
                //3. 当前是否已经申请过好友
                ret = _mysql_apply->exists(uid, pid);
                if (ret == true) {
                    LOG_ERROR("{}- 申请好友失败-已经申请过对方好友!", rid, uid, pid);
                    return err_response(rid, "已经申请过对方好友!");
                }
                //4. 向好友申请表中,新增申请信息
                std::string eid = uuid();
                FriendApply ev(eid, uid, pid);
                ret = _mysql_apply->insert(ev);
                if (ret == false) {
                    LOG_ERROR("{} - 向数据库新增好友申请事件失败!", rid);
                    return err_response(rid, "向数据库新增好友申请事件失败!");
                }
                //3. 组织响应
                response->set_request_id(rid);
                response->set_success(true);
                response->set_notify_event_id(eid); 
            }

处理好友申请

Protobuf

//好友申请的处理
message FriendAddProcessReq {
    string request_id = 1;
    string notify_event_id = 2;//通知事件id
    bool agree = 3;//是否同意好友申请
    string apply_user_id = 4; //申请人的用户id
    optional string session_id = 5;
    optional string user_id = 6; // 被申请人
}

实现

        ///
        ///处理好友申请
        //
        virtual void FriendAddProcess(::google::protobuf::RpcController* controller,
            const ::mag::FriendAddProcessReq* request,
            ::mag::FriendAddProcessRsp* response,
            ::google::protobuf::Closure* done){
                brpc::ClosureGuard rpc_guard(done);
                //1. 定义错误回调
                auto err_response = [this, response](const std::string &rid, 
                    const std::string &errmsg) -> void {
                    response->set_request_id(rid);
                    response->set_success(false);
                    response->set_errmsg(errmsg);
                    return;
                };
                //1. 提取请求中的关键要素:申请人用户ID;被申请人用户ID;处理结果;事件ID
                std::string rid = request->request_id();
                std::string eid = request->notify_event_id();
                std::string uid = request->user_id(); //被申请人
                std::string pid = request->apply_user_id();//申请人
                bool agree = request->agree();
                //2. 判断有没有该申请事件
                bool ret = _mysql_apply->exists(pid, uid);
                if (ret == false) {
                    LOG_ERROR("{}- 没有找到{}-{}对应的好友申请事件!", rid, pid, uid);
                    return err_response(rid, "没有找到对应的好友申请事件!");
                }
                //3. 如果有: 可以处理; --- 删除申请事件--事件已经处理完毕
                ret = _mysql_apply->remove(pid, uid);
                if (ret == false) {
                    LOG_ERROR("{}- 从数据库删除申请事件 {}-{} 失败!", rid, pid, uid);
                    return err_response(rid, "从数据库删除申请事件失败!");
                }
                //4. 如果处理结果是同意:向数据库新增好友关系信息;新增单聊会话信息及会话成员
                std::string cssid;
                if (agree == true) {
                    ret = _mysql_relation->insert(uid, pid);
                    if (ret == false) {
                        LOG_ERROR("{}- 新增好友关系信息-{}-{}!", rid, uid, pid);
                        return err_response(rid, "新增好友关系信息!");
                    }
                    cssid = uuid();
                    ChatSession cs(cssid, "", ChatSessionType::SINGLE);
                    ret = _mysql_chat_session->insert(cs);
                    if (ret == false) {
                        LOG_ERROR("{}- 新增单聊会话信息-{}!", rid, cssid);
                        return err_response(rid, "新增单聊会话信息失败!");
                    }
                    ChatSessionMember csm1(cssid, uid);
                    ChatSessionMember csm2(cssid, pid);
                    std::vector<ChatSessionMember> mlist = {csm1, csm2};
                    ret = _mysql_chat_session_member->append(mlist);
                    if (ret == false) {
                        LOG_ERROR("{}- 没有找到{}-{}对应的好友申请事件!", rid, pid, uid);
                        return err_response(rid, "没有找到对应的好友申请事件!");
                    }
                }
                //5. 组织响应
                response->set_request_id(rid);
                response->set_success(true);
                response->set_new_session_id(cssid);
            }

根据关键字搜索朋友

Protobuf

//好友搜索
message FriendSearchReq {
    string request_id = 1;
    string search_key = 2;//就是名称模糊匹配关键字
    optional string session_id = 3;
    optional string user_id = 4;
}
message FriendSearchRsp {
    string request_id = 1;
    bool success = 2;
    string errmsg = 3; 
    repeated UserInfo user_info = 4;
}

实现

///
        ///根据关键字搜索朋友
        //
        virtual void FriendSearch(::google::protobuf::RpcController* controller,
            const ::mag::FriendSearchReq* request,
            ::mag::FriendSearchRsp* response,
            ::google::protobuf::Closure* done){
                brpc::ClosureGuard rpc_guard(done);
                //1. 定义错误回调
                auto err_response = [this, response](const std::string &rid, 
                    const std::string &errmsg) -> void {
                    response->set_request_id(rid);
                    response->set_success(false);
                    response->set_errmsg(errmsg);
                    return;
                };

                //2. 提取请求关键信息
                std::string rid = request->request_id();
                std::string uid = request->user_id();
                std::string skey = request->search_key();
                LOG_DEBUG("{} 好友搜索 : {}", uid, skey);

                //3. 根据用户ID,获取好友ID列表
                auto friend_id_lists = _mysql_relation->friends(uid);

                //4. 从ES中进行用户信息搜索,过滤掉当前的好友
                std::unordered_set<std::string> user_id_lists;
                friend_id_lists.push_back(uid);// 把自己也过滤掉
                auto search_res = _es_user->search(skey, friend_id_lists);
                for (auto &it : search_res) {
                    user_id_lists.insert(it.user_id());
                }

                //5. 获取获取的用户ID,从用户子服务器中获取用户信息
                std::unordered_map<std::string, UserInfo> user_list;
                bool ret = GetUserInfo(rid, user_id_lists, user_list);
                if (ret == false) {
                    LOG_ERROR("{} - 批量获取用户信息失败!", rid);
                    return err_response(rid, "批量获取用户信息失败!");
                }

                //6. 组织响应
                response->set_request_id(rid);
                response->set_success(true);
                for (const auto & user_it : user_list) {
                    auto user_info = response->add_user_info();
                    user_info->CopyFrom(user_it.second);
                }
            }

获取当前用户聊天会话聊天列表

Protobuf

message GetPendingFriendEventListReq {
    string request_id = 1;
    optional string session_id = 2;
    optional string user_id = 3;
}

message FriendEvent {
    optional string event_id = 1;
    UserInfo sender = 3;
}
message GetPendingFriendEventListRsp {
    string request_id = 1;
    bool success = 2;
    string errmsg = 3; 
    repeated FriendEvent event = 4;
}

实现

        ///
        ///获取当前用户的待处理好友申请事件列表
        //
        virtual void GetChatSessionList(::google::protobuf::RpcController* controller,
            const ::bite_im::GetChatSessionListReq* request,
            ::bite_im::GetChatSessionListRsp* response,
            ::google::protobuf::Closure* done){
                brpc::ClosureGuard rpc_guard(done);
                //1. 定义错误回调
                auto err_response = [this, response](const std::string &rid, 
                    const std::string &errmsg) -> void {
                    response->set_request_id(rid);
                    response->set_success(false);
                    response->set_errmsg(errmsg);
                    return;
                };

                //2. 提取请求关键字段
                std::string rid = request->request_id();  // 请求的唯一 ID
                std::string uid = request->user_id();     // 当前请求用户的 ID

                //3. 查询用户单聊会话列表
                auto sf_list = _mysql_chat_session->singleChatSession(uid);

                //4. 从单聊会话列表中获取所有的好友ID,并获取好友的用户信息
                sstd::unordered_set<std::string> users_id_list;
                for (const auto &f : sf_list) {
                    users_id_list.insert(f.friend_id);
                }
                std::unordered_map<std::string, UserInfo> user_list;
                //4.1 批量获取好友的用户信息
                bool ret = GetUserInfo(rid, users_id_list, user_list);
                if (ret == false) {
                    LOG_ERROR("{} - 批量获取用户信息失败!", rid);
                    return err_response(rid, "批量获取用户信息失败!");
                }

                //5. 设置单聊会话的响应信息
                for (const auto &f : sf_list) {
                auto chat_session_info = response->add_chat_session_info_list();
                chat_session_info->set_single_chat_friend_id(f.friend_id);
                chat_session_info->set_chat_session_id(f.chat_session_id);
                chat_session_info->set_chat_session_name(user_list[f.friend_id].nickname());
                chat_session_info->set_avatar(user_list[f.friend_id].avatar());
                MessageInfo msg;
                ret = GetRecentMsg(rid, f.chat_session_id, msg);
                if (ret == false) { continue; }
                chat_session_info->mutable_prev_message()->CopyFrom(msg);
                }

                //6. 查询用户的群聊会话列表
                auto gc_list = _mysql_chat_session->groupChatSession(uid);

                //7. 设置群聊会话的响应信息
                for (const auto &f : gc_list) {
                auto chat_session_info = response->add_chat_session_info_list();
                chat_session_info->set_chat_session_id(f.chat_session_id);
                chat_session_info->set_chat_session_name(f.chat_session_name);
                MessageInfo msg;
                ret = GetRecentMsg(rid, f.chat_session_id, msg);
                if (ret == false) { continue; }
                chat_session_info->mutable_prev_message()->CopyFrom(msg);
                }

                //8. 最终响应
                response->set_request_id(rid);
                response->set_success(true);
            }

获取当前用户待处理的申请列表

Protobuf

message GetChatSessionListReq {
    string request_id = 1;
    optional string session_id = 2;
    optional string user_id = 3;
}
message GetChatSessionListRsp {
    string request_id = 1;
    bool success = 2;
    string errmsg = 3; 
    repeated ChatSessionInfo chat_session_info_list = 4;
}

实现

        ///
        ///获取当前用户的待处理好友申请事件列表
        //
        virtual void GetPendingFriendEventList(::google::protobuf::RpcController* controller,
            const ::mag::GetPendingFriendEventListReq* request,
            ::mag::GetPendingFriendEventListRsp* response,
            ::google::protobuf::Closure* done){
                brpc::ClosureGuard rpc_guard(done);
                //1. 定义错误回调
                auto err_response = [this, response](const std::string &rid, 
                    const std::string &errmsg) -> void {
                    response->set_request_id(rid);
                    response->set_success(false);
                    response->set_errmsg(errmsg);
                    return;
                };

                //2. 提取关键要素:当前用户ID
                std::string rid = request->request_id();
                std::string uid = request->user_id();

                //3. 从数据库获取待处理的申请事件信息 --- 申请人用户ID列表
                auto res = _mysql_apply->applyUsers(uid);
                std::unordered_set<std::string> user_id_lists;
                for (auto &id : res) {
                    user_id_lists.insert(id);
                }

                //4. 批量获取申请人信息
                std::unordered_map<std::string, UserInfo> user_list;
                bool ret = GetUserInfo(rid, user_id_lists, user_list);
                if (ret == false) {
                    LOG_ERROR("{} - 批量获取用户信息失败!", rid);
                    return err_response(rid, "批量获取用户信息失败!");
                }

                //5. 组织响应
                response->set_request_id(rid);
                response->set_success(true);
                for (const auto & user_it : user_list) {
                    auto ev = response->add_event();
                    ev->mutable_sender()->CopyFrom(user_it.second);
                }
            }

创建群聊会话

Protobuf

message ChatSessionCreateReq {
    string request_id = 1;
    optional string session_id = 2;
    optional string user_id = 3;
    string chat_session_name = 4;
    //需要注意的是,这个列表中也必须包含创建者自己的用户ID
    repeated string member_id_list = 5;
}
message ChatSessionCreateRsp {
    string request_id = 1;
    bool success = 2;
    string errmsg = 3; 
    //这个字段属于后台之间的数据,给前端回复的时候不需要这个字段,会话信息通过通知进行发送
    optional ChatSessionInfo chat_session_info = 4; 
}

实现

///
        ///创建一个群聊会话
        //
        virtual void ChatSessionCreate(::google::protobuf::RpcController* controller,
            const ::mag::ChatSessionCreateReq* request,
            ::mag::ChatSessionCreateRsp* response,
            ::google::protobuf::Closure* done){
                brpc::ClosureGuard rpc_guard(done);
                //1. 定义错误回调
                auto err_response = [this, response](const std::string &rid, 
                    const std::string &errmsg) -> void {
                    response->set_request_id(rid);
                    response->set_success(false);
                    response->set_errmsg(errmsg);
                    return;
                };
                
                //2. 提取请求中的信息
                std::string rid = request->request_id();
                std::string uid = request->user_id();
                std::string cssname = request->chat_session_name();

                //3. 生成会话ID,向数据库中添加会话信息以及会话成员信息
                std::string cssid = uuid();
                ChatSession cs(cssid, cssname, ChatSessionType::GROUP);
                bool ret = _mysql_chat_session->insert(cs);
                if (ret == false) {
                    LOG_ERROR("{} - 向数据库添加会话信息失败: {}", rid, cssname);
                    return err_response(rid, "向数据库添加会话信息失败!");
                }

                std::vector<ChatSessionMember> member_list;
                for (int i = 0; i < request->member_id_list_size(); i++) {
                    ChatSessionMember csm(cssid, request->member_id_list(i));
                    member_list.push_back(csm);
                }
                ret = _mysql_chat_session_member->append(member_list);
                if (ret == false) {
                    LOG_ERROR("{} - 向数据库添加会话成员信息失败: {}", rid, cssname);
                    return err_response(rid, "向数据库添加会话成员信息失败!");
                }

                //4. 组织响应
                response->set_request_id(rid);
                response->set_success(true);
                response->mutable_chat_session_info()->set_chat_session_id(cssid);
                response->mutable_chat_session_info()->set_chat_session_name(cssname);

            }

获取群聊会话的成员信息

 ///
        ///获取群聊会话的成员信息
        //
        virtual void GetChatSessionMember(::google::protobuf::RpcController* controller,
            const ::mag::GetChatSessionMemberReq* request,
            ::mag::GetChatSessionMemberRsp* response,
            ::google::protobuf::Closure* done){

                brpc::ClosureGuard rpc_guard(done);
                auto err_response = [this, response](const std::string &rid, 
                    const std::string &errmsg) -> void {
                    response->set_request_id(rid);
                    response->set_success(false);
                    response->set_errmsg(errmsg);
                    return;
                };

                //1. 提取关键要素:聊天会话ID
                std::string rid = request->request_id();
                std::string uid = request->user_id();
                std::string cssid = request->chat_session_id();

                //2. 获取回话成员List列表
                auto member_id_lists = _mysql_chat_session_member->members(cssid);
                std::unordered_set<std::string> uid_list;
                for (const auto &id : member_id_lists) {
                    uid_list.insert(id);
                }

                //3. 从用户子服务中批量获取用户信息
                std::unordered_map<std::string, UserInfo> user_list;
                bool ret = GetUserInfo(rid, uid_list, user_list);
                if (ret == false) {
                    LOG_ERROR("{} - 从用户子服务获取用户信息失败!", rid);
                    return err_response(rid, "从用户子服务获取用户信息失败!");
                }

                //4. 组织响应
                response->set_request_id(rid);
                response->set_success(true);
                for (const auto &uit : user_list) {
                    auto user_info = response->add_member_info_list();
                    user_info->CopyFrom(uit.second);
                }
            }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值