实现思路
实现功能
- 获取好友列表:登录后显示该用户的好友列表
- 申请好友:搜索用户,发送请求,向申请的用户发送添加好友的请求
- 获取待处理请求:用户登录后,获取离线状态下接收到的好友申请
- 处理好友申请:同意或者拒绝好友申请
- 删除好友:删除当前列表中的该好友
- 用户搜索:进行用户的搜索然后申请好友
- 获取聊天会话列表:获取单聊或者群聊会话的相关信息
- 多个聊天会话的创建:多人聊天会话窗口需要通过接口进行手动创建
- 获取聊天成员列表:点击群聊成员按钮,获取群成员的相关信息
实现逻辑
总体逻辑图
数据库模块
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);
}
}