当前位置: 首页 > news >正文

怎么创建手机网站襄阳市建设局网站

怎么创建手机网站,襄阳市建设局网站,品牌营销案例分析,国家企业信用信息公示系统网址用户管理子服务#xff08;user文件#xff09; 用户管理子服务也是这个项目中的一个业务最多的子服务#xff0c;接口多#xff0c;但是主要涉及的数据表只有user表#xff0c;Redis的键值对和ES的一个搜索引擎#xff0c;主要功能是对用户的个人信息进行修改管理#…用户管理子服务user文件 用户管理子服务也是这个项目中的一个业务最多的子服务接口多但是主要涉及的数据表只有user表Redis的键值对和ES的一个搜索引擎主要功能是对用户的个人信息进行修改管理对用户的登录进行管理用户注册进行管理 用户管理子服务的所有接口 service UserService { rpc UserRegister(UserRegisterReq) returns (UserRegisterRsp); rpc UserLogin(UserLoginReq) returns (UserLoginRsp); rpc GetPhoneVerifyCode(PhoneVerifyCodeReq) returns  (PhoneVerifyCodeRsp); rpc PhoneRegister(PhoneRegisterReq) returns  (PhoneRegisterRsp); rpc PhoneLogin(PhoneLoginReq) returns (PhoneLoginRsp); rpc GetUserInfo(GetUserInfoReq) returns (GetUserInfoRsp); rpc GetMultiUserInfo(GetMultiUserInfoReq) returns  (GetMultiUserInfoRsp); rpc SetUserAvatar(SetUserAvatarReq) returns  (SetUserAvatarRsp); rpc SetUserNickname(SetUserNicknameReq) returns  (SetUserNicknameRsp); rpc SetUserDescription(SetUserDescriptionReq) returns  (SetUserDescriptionRsp); rpc SetUserPhoneNumber(SetUserPhoneNumberReq) returns  (SetUserPhoneNumberRsp); } 用户昵称加密码的方式进行注册接口先从request里面接收用户的nickname和密码然后对nickname和密码进行检查看看是不是符合长度要求如果符合为其生成用户ID后添加到user数据库和ES的user搜索引擎里面设置response为成功如果不符合则不添加并且设置response为失败添加失败信息结束 virtual void UserRegister(::google::protobuf::RpcController* controller, const ::zhou::UserRegisterReq* request, ::zhou::UserRegisterRsp* response, ::google::protobuf::Closure* done) { LOG_DEBUG(收到用户注册请求); 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. 从请求中取出昵称和密码 std::string nickname  request-nickname(); std::string password  request-password(); //2. 检查昵称是否合法只能包含字母数字连字符-下划线_长度限制 3~15 之间 bool ret  nickname_check(nickname); if (ret  false) { LOG_ERROR({} - 用户名长度不合法, request-request_id()); return err_response(request-request_id(), 用户名长度不合法); } //3. 检查密码是否合法只能包含字母数字长度限制 6~15 之间 ret  password_check(password); if (ret  false) { LOG_ERROR({} - 密码格式不合法, request-request_id()); return err_response(request-request_id(), 密码格式不合法); } //4. 根据昵称在数据库进行判断是否昵称已存在 auto user  _mysql_user-select_by_nickname(nickname); if (user) { LOG_ERROR({} - 用户名被占用- {}, request-request_id(), nickname); return err_response(request-request_id(), 用户名被占用!); } //5. 向数据库新增数据 std::string uid  uuid(); user  std::make_sharedUser(uid, nickname, password); ret  _mysql_user-insert(user); if (ret  false) { LOG_ERROR({} - Mysql数据库新增数据失败, request-request_id()); return err_response(request-request_id(), Mysql数据库新增数据失败!); } //6. 向 ES 服务器中新增用户信息 ret  _es_user-appendData(uid, , nickname, , ); if (ret  false) { LOG_ERROR({} - ES搜索引擎新增数据失败, request-request_id()); return err_response(request-request_id(), ES搜索引擎新增数据失败); } //7. 组织响应进行成功与否的响应即可。 response-set_request_id(request-request_id()); response-set_success(true); } 用户昵称加密码登录接口设计先从request里面获取nickname和密码然后进行判断是否合法然后从Redis的状态键值对里面判断用户是否已经登录然后从数据库中获取nickname对应的密码如果不存在则设置response为失败检查密码是否和传入的密码相同如果是则登录成功为用户创建一个登录会话ID对Redis的状态键值对和会话键值对进行添加然后对response设置对应的回话ID然后返回 virtual void UserLogin(::google::protobuf::RpcController* controller, const ::zhou::UserLoginReq* request, ::zhou::UserLoginRsp* response, ::google::protobuf::Closure* done){ LOG_DEBUG(收到用户登录请求); 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. 从请求中取出昵称和密码 std::string nickname  request-nickname(); std::string password  request-password(); //2. 通过昵称获取用户信息进行密码是否一致的判断 auto user  _mysql_user-select_by_nickname(nickname); if (!user || password ! user-password()) { LOG_ERROR({} - 用户名或密码错误 - {}-{}, request-request_id(), nickname, password); return err_response(request-request_id(), 用户名或密码错误!); } //3. 根据 redis 中的登录标记信息是否存在判断用户是否已经登录。 bool ret  _redis_status-exists(user-user_id()); if (ret  true) { LOG_ERROR({} - 用户已在其他地方登录 - {}, request-request_id(), nickname); return err_response(request-request_id(), 用户已在其他地方登录!); } //4. 构造会话 ID生成会话键值对向 redis 中添加会话信息以及登录标记信息 std::string ssid  uuid(); _redis_session-append(ssid, user-user_id()); //5. 添加用户登录信息 _redis_status-append(user-user_id()); //5. 组织响应返回生成的会话 ID response-set_request_id(request-request_id()); response-set_login_session_id(ssid); response-set_success(true); } 手机号验证码发送接口从request里面获取电话号码判断电话号码是否合法然后生成四位随机验证码和事件ID将事件ID和验证码一起放入Redis的验证码键值对缓存里面然后调用验证码发送的SDK服务向对应的手机号发送验证码对response进行设置此次发送事件的ID结束 virtual void GetPhoneVerifyCode(::google::protobuf::RpcController* controller, const ::zhou::PhoneVerifyCodeReq* request, ::zhou::PhoneVerifyCodeRsp* response, ::google::protobuf::Closure* done){ LOG_DEBUG(收到短信验证码获取请求); 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. 从请求中取出手机号码 std::string phone  request-phone_number(); // 2. 验证手机号码格式是否正确必须以 1 开始第二位 3~9 之间后边 9 个数字字符 bool ret  phone_check(phone); if (ret  false) { LOG_ERROR({} - 手机号码格式错误 - {}, request-request_id(), phone); return err_response(request-request_id(), 手机号码格式错误!); } // 3. 生成 4 位随机验证码 std::string code_id  uuid(); std::string code  vcode(); // 4. 基于短信平台 SDK 发送验证码 ret  _dms_client-send(phone, code); if (ret  false) { LOG_ERROR({} - 短信验证码发送失败 - {}, request-request_id(), phone); return err_response(request-request_id(), 短信验证码发送失败!); } // 5. 构造验证码 ID添加到 redis 验证码映射键值索引中 _redis_codes-append(code_id, code); // 6. 组织响应返回生成的验证码 ID response-set_request_id(request-request_id()); response-set_success(true); response-set_verify_code_id(code_id); LOG_DEBUG(获取短信验证码处理完成); } 手机号注册接口实现先从request里面获取验证码事件ID和验证码手机号用户名然后从Redis里面的验证码键值对里面取出对应的验证码判断是否一致如果一致则进行注册就是向use表和ES所对应的搜索引擎里面添加对应数据用户名和手机号码然后设置response成功结束5并且手机号登录接口就是在nickname加密码登录的逻辑下加入了手机号判断和验证码判断其余都一样就不在赘述 virtual void PhoneRegister(::google::protobuf::RpcController* controller, const ::zhou::PhoneRegisterReq* request, ::zhou::PhoneRegisterRsp* response, ::google::protobuf::Closure* done){ LOG_DEBUG(收到手机号注册请求); 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 phone  request-phone_number(); std::string code_id  request-verify_code_id(); std::string code  request-verify_code(); // 2. 检查注册手机号码是否合法 bool ret  phone_check(phone); if (ret  false) { LOG_ERROR({} - 手机号码格式错误 - {}, request-request_id(), phone); return err_response(request-request_id(), 手机号码格式错误!); } // 3. 从 redis 数据库中进行验证码 ID-验证码一致性匹配 auto vcode  _redis_codes-code(code_id); if (vcode ! code) { LOG_ERROR({} - 验证码错误 - {}-{}, request-request_id(), code_id, code); return err_response(request-request_id(), 验证码错误!); } // 4. 通过数据库查询判断手机号是否已经注册过 auto user  _mysql_user-select_by_phone(phone); if (user) { LOG_ERROR({} - 该手机号已注册过用户 - {}, request-request_id(), phone); return err_response(request-request_id(), 该手机号已注册过用户!); } // 5. 向数据库新增用户信息 std::string uid  uuid(); user  std::make_sharedUser(uid, phone); ret  _mysql_user-insert(user); if (ret  false) { LOG_ERROR({} - 向数据库添加用户信息失败 - {}, request-request_id(), phone); return err_response(request-request_id(), 向数据库添加用户信息失败!); } // 6. 向 ES 服务器中新增用户信息 ret  _es_user-appendData(uid, phone, uid, , ); if (ret  false) { LOG_ERROR({} - ES搜索引擎新增数据失败, request-request_id()); return err_response(request-request_id(), ES搜索引擎新增数据失败); } //7. 组织响应进行成功与否的响应即可。 response-set_request_id(request-request_id()); response-set_success(true); } 获取用户个人信息接口先从request里面获取用户的uid然后从user表中查询获取用户的nicknamedescriptionphone头像ID然后根据头像ID向文件子服务发起请求获取头像文件数据然后将这些放入response中结束。批量获取用户信息的接口就是在这个接口的基础上取出的是用户ID列表然后再根据用户ID列表获取用户信息列表得到用户头像列表然后下载用户头像后面就不对这个接口进行赘述 virtual void GetUserInfo(::google::protobuf::RpcController* controller, const ::zhou::GetUserInfoReq* request, ::zhou::GetUserInfoRsp* response, ::google::protobuf::Closure* done){ LOG_DEBUG(收到获取单个用户信息请求); 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 uid  request-user_id(); // 2. 通过用户 ID从数据库中查询用户信息 auto user  _mysql_user-select_by_id(uid); if (!user) { LOG_ERROR({} - 未找到用户信息 - {}, request-request_id(), uid); return err_response(request-request_id(), 未找到用户信息!); } // 3. 根据用户信息中的头像 ID从文件服务器获取头像文件数据组织完整用户信息 UserInfo *user_info  response-mutable_user_info(); user_info-set_user_id(user-user_id()); user_info-set_nickname(user-nickname()); user_info-set_description(user-description()); user_info-set_phone(user-phone()); if (!user-avatar_id().empty()) { //从信道管理对象中获取到连接了文件管理子服务的channel auto channel  _mm_channels-choose(_file_service_name); if (!channel) { LOG_ERROR({} - 未找到文件管理子服务节点 - {} - {}, request-request_id(), _file_service_name, uid); return err_response(request-request_id(), 未找到文件管理子服务节点!); } //进行文件子服务的rpc请求进行头像文件下载 zhou::FileService_Stub stub(channel.get()); zhou::GetSingleFileReq req; zhou::GetSingleFileRsp rsp; req.set_request_id(request-request_id()); req.set_file_id(user-avatar_id()); brpc::Controller cntl; stub.GetSingleFile(cntl, req, rsp, nullptr); if (cntl.Failed()  true || rsp.success()  false) { LOG_ERROR({} - 文件子服务调用失败{}, request-request_id(), cntl.ErrorText()); return err_response(request-request_id(), 文件子服务调用失败!); } user_info-set_avatar(rsp.file_data().file_content()); } // 4. 组织响应返回用户信息 response-set_request_id(request-request_id()); response-set_success(true); } 修改用户头像接口从request里面取出用户ID和头像数据然后通过文件子服务将头像数据进行上传得到头像ID再对user表里面的用户ID对应的数据进行头像ID的修改然后再对ES表中搜索引擎的数据进行修改结束 virtual void SetUserAvatar(::google::protobuf::RpcController* controller, const ::zhou::SetUserAvatarReq* request, ::zhou::SetUserAvatarRsp* response, ::google::protobuf::Closure* done){ LOG_DEBUG(收到用户头像设置请求); 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 uid  request-user_id(); // 2. 从数据库通过用户 ID 进行用户信息查询判断用户是否存在 auto user  _mysql_user-select_by_id(uid); if (!user) { LOG_ERROR({} - 未找到用户信息 - {}, request-request_id(), uid); return err_response(request-request_id(), 未找到用户信息!); } // 3. 上传头像文件到文件子服务 auto channel  _mm_channels-choose(_file_service_name); if (!channel) { LOG_ERROR({} - 未找到文件管理子服务节点 - {}, request-request_id(), _file_service_name); return err_response(request-request_id(), 未找到文件管理子服务节点!); } zhou::FileService_Stub stub(channel.get()); zhou::PutSingleFileReq req; zhou::PutSingleFileRsp rsp; req.set_request_id(request-request_id()); req.mutable_file_data()-set_file_name(); req.mutable_file_data()-set_file_size(request-avatar().size()); req.mutable_file_data()-set_file_content(request-avatar()); brpc::Controller cntl; stub.PutSingleFile(cntl, req, rsp, nullptr); if (cntl.Failed()  true || rsp.success()  false) { LOG_ERROR({} - 文件子服务调用失败{}, request-request_id(), cntl.ErrorText()); return err_response(request-request_id(), 文件子服务调用失败!); } std::string avatar_id  rsp.file_info().file_id(); // 4. 将返回的头像文件 ID 更新到数据库中 user-avatar_id(avatar_id); bool ret  _mysql_user-update(user); if (ret  false) { LOG_ERROR({} - 更新数据库用户头像ID失败 {}, request-request_id(), avatar_id); return err_response(request-request_id(), 更新数据库用户头像ID失败!); } // 5. 更新 ES 服务器中用户信息 ret  _es_user-appendData(user-user_id(), user-phone(), user-nickname(), user-description(), user-avatar_id()); if (ret  false) { LOG_ERROR({} - 更新搜索引擎用户头像ID失败 {}, request-request_id(), avatar_id); return err_response(request-request_id(), 更新搜索引擎用户头像ID失败!); } // 6. 组织响应返回更新成功与否 response-set_request_id(request-request_id()); response-set_success(true); } 修改用户nickname接口从request里面获取用户ID和nickname然后在从数据库中对用户ID进行查询获取对应的用户数据将用户数据里面的nickname进行修改然后也对ES搜索引擎中对应用户ID的nickname进行修改结束同样的用户description修改的接口也是一样的而手机号的修改接口是在此基础上先判断手机号是否正确然后查询手机号是否已经被绑定然后再进行验证码判断如果都没问题就想数据库和ES里面更新数据所以后面就不再赘述 virtual void SetUserNickname(::google::protobuf::RpcController* controller, const ::zhou::SetUserNicknameReq* request, ::zhou::SetUserNicknameRsp* response, ::google::protobuf::Closure* done){ LOG_DEBUG(收到用户昵称设置请求); 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 uid  request-user_id(); std::string new_nickname  request-nickname(); // 2. 判断昵称格式是否正确 bool ret  nickname_check(new_nickname); if (ret  false) { LOG_ERROR({} - 用户名长度不合法, request-request_id()); return err_response(request-request_id(), 用户名长度不合法); } // 3. 从数据库通过用户 ID 进行用户信息查询判断用户是否存在 auto user  _mysql_user-select_by_id(uid); if (!user) { LOG_ERROR({} - 未找到用户信息 - {}, request-request_id(), uid); return err_response(request-request_id(), 未找到用户信息!); } // 4. 将新的昵称更新到数据库中 user-nickname(new_nickname); ret  _mysql_user-update(user); if (ret  false) { LOG_ERROR({} - 更新数据库用户昵称失败 {}, request-request_id(), new_nickname); return err_response(request-request_id(), 更新数据库用户昵称失败!); } // 5. 更新 ES 服务器中用户信息 ret  _es_user-appendData(user-user_id(), user-phone(), user-nickname(), user-description(), user-avatar_id()); if (ret  false) { LOG_ERROR({} - 更新搜索引擎用户昵称失败 {}, request-request_id(), new_nickname); return err_response(request-request_id(), 更新搜索引擎用户昵称失败!); } // 6. 组织响应返回更新成功与否 response-set_request_id(request-request_id()); response-set_success(true); } 网关子服务gateway文件 网关子服务的作用是对客户端发来的请求httplib库进行实现序列化为rpc请求进行转发发送到对应的微服务器然后将接收到的微服务器处理好的响应序列化为http报文返回客户端并且对于需要服务端主动传输到客户端的通知采用websocket来管理链接进行传输 先介绍对于长连接管理的类 主要含有的成员变量是这三个 std::mutex _mutex; std::unordered_mapstd::string, server_t::connection_ptr _uid_connections; std::unordered_mapserver_t::connection_ptr, Client _conn_clients; 其中的uid_connect是管理用户ID和对应的socket的链接 _conn_connect是通过链接来找到对应客户端的 下面是对websocket关键初始化的动作的代码解释 _ws_server.set_open_handler(std::bind(GatewayServer::onOpen, this, std::placeholders::_1)); _ws_server.set_close_handler(std::bind(GatewayServer::onClose, this, std::placeholders::_1)); auto wscb  std::bind(GatewayServer::onMessage, this, std::placeholders::_1, std::placeholders::_2); _ws_server.set_message_handler(wscb); Open函数只是简单的显示连接的产生这里不做赘述 Close函数是长连接断开后也就是客户端下线后将Redis里面的状态键值对和会话键值对进行删除然后移除管理的长连接 void onClose(websocketpp::connection_hdl hdl) { //长连接断开时做的清理工作 //0. 通过连接对象获取对应的用户ID与登录会话ID auto conn  _ws_server.get_con_from_hdl(hdl); std::string uid, ssid; bool ret  _connections-client(conn, uid, ssid); if (ret  false) { LOG_WARN(长连接断开未找到长连接对应的客户端信息); return ; } //1. 移除登录会话信息 _redis_session-remove(ssid); //2. 移除登录状态信息 _redis_status-remove(uid); //3. 移除长连接管理数据 _connections-remove(conn); LOG_DEBUG({} {} {} 长连接断开清理缓存数据!, ssid, uid, (size_t)conn.get()); } 而当websocket接收到消息以后就会实现对长连接的管理和保活就是将其添加到Redis上面的两个键值对中和connect类里面实现长连接和客户端的对应长连接保活主要是通过对客户端定期检查判断客户端的链接情况是否正常如果正常则进行ping维护长连接如果有问题则断开 auto conn  _ws_server.get_con_from_hdl(hdl); //2. 针对消息内容进行反序列化 -- ClientAuthenticationReq -- 提取登录会话ID ClientAuthenticationReq request; bool ret  request.ParseFromString(msg-get_payload()); if (ret  false) { LOG_ERROR(长连接身份识别失败正文反序列化失败); _ws_server.close(hdl, websocketpp::close::status::unsupported_data, 正文反序列化失败!); return; } //3. 在会话信息缓存中查找会话信息 std::string ssid  request.session_id(); auto uid  _redis_session-uid(ssid); //4. 会话信息不存在则关闭连接 if (!uid) { LOG_ERROR(长连接身份识别失败未找到会话信息 {}, ssid); _ws_server.close(hdl, websocketpp::close::status::unsupported_data, 未找到会话信息!); return; } //5. 会话信息存在则添加长连接管理 _connections-insert(conn, *uid, ssid); LOG_DEBUG(新增长连接管理{}-{}-{}, ssid, *uid, (size_t)conn.get()); keepAlive(conn); } void keepAlive(server_t::connection_ptr conn) { if (!conn || conn-get_state() ! websocketpp::session::state::value::open) { LOG_DEBUG(非正常连接状态结束连接保活); return; } conn-ping(); _ws_server.set_timer(60000, std::bind(GatewayServer::keepAlive, this, conn)); } 下面是httplib的请求路径和对应函数虽然看上去接口众多但是多数的接口逻辑都是一样的都是将http报文序列化为对应的微服务器的请求报文然后进行rpc调用获得对应微服务器的响应以后将响应序列化为对应的http响应报文不过部分会影响其他用户的操作比如添加好友的行为就会采用websocket的链接管理对被影响的用户发送对应的通知 _http_server.Post(GET_PHONE_VERIFY_CODE  , (httplib::Server::Handler)std::bind(GatewayServer::GetPhoneVerifyCode         , this, std::placeholders::_1, std::placeholders::_2)); _http_server.Post(USERNAME_REGISTER      , (httplib::Server::Handler)std::bind(GatewayServer::UserRegister               , this, std::placeholders::_1, std::placeholders::_2)); _http_server.Post(USERNAME_LOGIN         , (httplib::Server::Handler)std::bind(GatewayServer::UserLogin                  , this, std::placeholders::_1, std::placeholders::_2)); _http_server.Post(PHONE_REGISTER         , (httplib::Server::Handler)std::bind(GatewayServer::PhoneRegister              , this, std::placeholders::_1, std::placeholders::_2)); _http_server.Post(PHONE_LOGIN            , (httplib::Server::Handler)std::bind(GatewayServer::PhoneLogin                 , this, std::placeholders::_1, std::placeholders::_2)); _http_server.Post(GET_USERINFO           , (httplib::Server::Handler)std::bind(GatewayServer::GetUserInfo                , this, std::placeholders::_1, std::placeholders::_2)); _http_server.Post(SET_USER_AVATAR        , (httplib::Server::Handler)std::bind(GatewayServer::SetUserAvatar              , this, std::placeholders::_1, std::placeholders::_2)); _http_server.Post(SET_USER_NICKNAME      , (httplib::Server::Handler)std::bind(GatewayServer::SetUserNickname            , this, std::placeholders::_1, std::placeholders::_2)); _http_server.Post(SET_USER_DESC          , (httplib::Server::Handler)std::bind(GatewayServer::SetUserDescription         , this, std::placeholders::_1, std::placeholders::_2)); _http_server.Post(SET_USER_PHONE         , (httplib::Server::Handler)std::bind(GatewayServer::SetUserPhoneNumber         , this, std::placeholders::_1, std::placeholders::_2)); _http_server.Post(FRIEND_GET_LIST        , (httplib::Server::Handler)std::bind(GatewayServer::GetFriendList              , this, std::placeholders::_1, std::placeholders::_2)); _http_server.Post(FRIEND_APPLY           , (httplib::Server::Handler)std::bind(GatewayServer::FriendAdd                  , this, std::placeholders::_1, std::placeholders::_2)); _http_server.Post(FRIEND_APPLY_PROCESS   , (httplib::Server::Handler)std::bind(GatewayServer::FriendAddProcess           , this, std::placeholders::_1, std::placeholders::_2)); _http_server.Post(FRIEND_REMOVE          , (httplib::Server::Handler)std::bind(GatewayServer::FriendRemove               , this, std::placeholders::_1, std::placeholders::_2)); _http_server.Post(FRIEND_SEARCH          , (httplib::Server::Handler)std::bind(GatewayServer::FriendSearch               , this, std::placeholders::_1, std::placeholders::_2)); _http_server.Post(FRIEND_GET_PENDING_EV  , (httplib::Server::Handler)std::bind(GatewayServer::GetPendingFriendEventList  , this, std::placeholders::_1, std::placeholders::_2)); _http_server.Post(CSS_GET_LIST           , (httplib::Server::Handler)std::bind(GatewayServer::GetChatSessionList         , this, std::placeholders::_1, std::placeholders::_2)); _http_server.Post(CSS_CREATE             , (httplib::Server::Handler)std::bind(GatewayServer::ChatSessionCreate          , this, std::placeholders::_1, std::placeholders::_2)); _http_server.Post(CSS_GET_MEMBER         , (httplib::Server::Handler)std::bind(GatewayServer::GetChatSessionMember       , this, std::placeholders::_1, std::placeholders::_2)); _http_server.Post(MSG_GET_RANGE          , (httplib::Server::Handler)std::bind(GatewayServer::GetHistoryMsg              , this, std::placeholders::_1, std::placeholders::_2)); _http_server.Post(MSG_GET_RECENT         , (httplib::Server::Handler)std::bind(GatewayServer::GetRecentMsg               , this, std::placeholders::_1, std::placeholders::_2)); _http_server.Post(MSG_KEY_SEARCH         , (httplib::Server::Handler)std::bind(GatewayServer::MsgSearch                  , this, std::placeholders::_1, std::placeholders::_2)); _http_server.Post(NEW_MESSAGE            , (httplib::Server::Handler)std::bind(GatewayServer::NewMessage                 , this, std::placeholders::_1, std::placeholders::_2)); _http_server.Post(FILE_GET_SINGLE        , (httplib::Server::Handler)std::bind(GatewayServer::GetSingleFile              , this, std::placeholders::_1, std::placeholders::_2)); _http_server.Post(FILE_GET_MULTI         , (httplib::Server::Handler)std::bind(GatewayServer::GetMultiFile               , this, std::placeholders::_1, std::placeholders::_2)); _http_server.Post(FILE_PUT_SINGLE        , (httplib::Server::Handler)std::bind(GatewayServer::PutSingleFile              , this, std::placeholders::_1, std::placeholders::_2)); _http_server.Post(FILE_PUT_MULTI         , (httplib::Server::Handler)std::bind(GatewayServer::PutMultiFile               , this, std::placeholders::_1, std::placeholders::_2)); _http_server.Post(SPEECH_RECOGNITION     , (httplib::Server::Handler)std::bind(GatewayServer::SpeechRecognition          , this, std::placeholders::_1, std::placeholders::_2)); _http_thread  std::thread([this, http_port](){ _http_server.listen(0.0.0.0, http_port); }); 获取手机验证码接口得到httplib包request报头然后自己实例化一个phoneVerifyCodeReq报头这结构体在PROTOBUF文件里序列化的可以直接使用目的就是为了进行微服务器之间的网络传输然后通过request报头来初始化我们自己定义的那个通过rpc调用的请求然后再获取用户子服务的信道对信道进行将请求和响应放入调用对应的验证码发送接口实现验证码的发送得到对应的响应后再将这个用来微服务调用的响应初始化httplib的response报文返回给客户端便结束了从客户端接收响应后传到对应的微服务处理器然后接收微服务处理器响应在将响应转发给客户端的过程。后面的userRegister,UserLogin,PhoneRegister,PhoneLogind这四个接口都是这样的逻辑就不在赘述 void GetPhoneVerifyCode(const httplib::Request request, httplib::Response response) { //1. 取出http请求正文将正文进行反序列化 PhoneVerifyCodeReq req; PhoneVerifyCodeRsp rsp; auto err_response  [req, rsp, response](const std::string errmsg) - void { rsp.set_success(false); rsp.set_errmsg(errmsg); response.set_content(rsp.SerializeAsString(), application/x-protbuf); }; bool ret  req.ParseFromString(request.body); if (ret  false) { LOG_ERROR(获取短信验证码请求正文反序列化失败); return err_response(获取短信验证码请求正文反序列化失败); } //2. 将请求转发给用户子服务进行业务处理 auto channel  _mm_channels-choose(_user_service_name); if (!channel) { LOG_ERROR({} 未找到可提供业务处理的用户子服务节点, req.request_id()); return err_response(未找到可提供业务处理的用户子服务节点); } zhou::UserService_Stub stub(channel.get()); brpc::Controller cntl; stub.GetPhoneVerifyCode(cntl, req, rsp, nullptr); if (cntl.Failed()) { LOG_ERROR({} 用户子服务调用失败, req.request_id()); return err_response(用户子服务调用失败); } //3. 得到用户子服务的响应后将响应内容进行序列化作为http响应正文 response.set_content(rsp.SerializeAsString(), application/x-protbuf); } 获取用户身份信息接口在获取用户信息接口这里我们得到了httplib的报文以后就不能再直接原封不动的将报文转发到对应的用户子服务处理器了应该要判断是否用户是处于上线状态已经用户是否有权利查看这个用户的身份信息发现有这样的权力以后再将报文转发到对应的用户子服务的接口同理后面的SetUserAvatarSetUserNameSetUserDiscriptionSetUserNumberPhone,GetFriendList都是这个逻辑故后面不在赘述 void GetFriendList(const httplib::Request request, httplib::Response response) { //1. 取出http请求正文将正文进行反序列化 GetFriendListReq req; GetFriendListRsp rsp; auto err_response  [req, rsp, response](const std::string errmsg) - void { rsp.set_success(false); rsp.set_errmsg(errmsg); response.set_content(rsp.SerializeAsString(), application/x-protbuf); }; bool ret  req.ParseFromString(request.body); if (ret  false) { LOG_ERROR(获取好友列表请求正文反序列化失败); return err_response(获取好友列表请求正文反序列化失败); } //2. 客户端身份识别与鉴权 std::string ssid  req.session_id(); auto uid  _redis_session-uid(ssid); if (!uid) { LOG_ERROR({} 获取登录会话关联用户信息失败, ssid); return err_response(获取登录会话关联用户信息失败); } req.set_user_id(*uid); //2. 将请求转发给好友子服务进行业务处理 auto channel  _mm_channels-choose(_friend_service_name); if (!channel) { LOG_ERROR({} 未找到可提供业务处理的用户子服务节点, req.request_id()); return err_response(未找到可提供业务处理的用户子服务节点); } zhou::FriendService_Stub stub(channel.get()); brpc::Controller cntl; stub.GetFriendList(cntl, req, rsp, nullptr); if (cntl.Failed()) { LOG_ERROR({} 好友子服务调用失败, req.request_id()); return err_response(好友子服务调用失败); } //3. 得到用户子服务的响应后将响应内容进行序列化作为http响应正文 response.set_content(rsp.SerializeAsString(), application/x-protbuf); } 好友添加接口实现在上述的接口中都是简单的对httplib请求进行序列化解析后转发给对应的微服务器然后从微服务器中得到对应的响应再转发给客户端的过程并没有对websocket的连接使用也就是并没有在没有接收到请求的情况下将响应发出的行为但是加好友是一个双向的过程不仅需要发送者接收到对应的响应也需要被申请者接受到发送者的好友申请下面是对好友添加接口逻辑的描述和代码实现首先还是得将http报文转变为我们设计的PROTOBUF的结构然后再将报文通过好友子服务进行处理得到对应的PROTOBUF报文响应然后将响应转发给对应的客户端的对象但是不同的是如果发送成功后我门还需要从websocket管理里面取出被申请者ID对应的长连接然后主动实例化一个结构体通过websocket的长连接管理将包含着新朋友添加通知的消息告诉对应的被申请人这个通知的结构为User_info也就是申请者的个人信息 void FriendAdd(const httplib::Request request, httplib::Response response) { // 好友申请的业务处理中好友子服务其实只是在数据库创建了申请事件 // 网关需要做的事情当好友子服务将业务处理完毕后如果处理是成功的--需要通知被申请方 // 1. 正文的反序列化提取关键要素登录会话ID FriendAddReq req; FriendAddRsp rsp; auto err_response  [req, rsp, response](const std::string errmsg) - void { rsp.set_success(false); rsp.set_errmsg(errmsg); response.set_content(rsp.SerializeAsString(), application/x-protbuf); }; bool ret  req.ParseFromString(request.body); if (ret  false) { LOG_ERROR(申请好友请求正文反序列化失败); return err_response(申请好友请求正文反序列化失败); } // 2. 客户端身份识别与鉴权 std::string ssid  req.session_id(); auto uid  _redis_session-uid(ssid); if (!uid) { LOG_ERROR({} 获取登录会话关联用户信息失败, ssid); return err_response(获取登录会话关联用户信息失败); } req.set_user_id(*uid); // 3. 将请求转发给好友子服务进行业务处理 auto channel  _mm_channels-choose(_friend_service_name); if (!channel) { LOG_ERROR({} 未找到可提供业务处理的用户子服务节点, req.request_id()); return err_response(未找到可提供业务处理的用户子服务节点); } zhou::FriendService_Stub stub(channel.get()); brpc::Controller cntl; stub.FriendAdd(cntl, req, rsp, nullptr); if (cntl.Failed()) { LOG_ERROR({} 好友子服务调用失败, req.request_id()); return err_response(好友子服务调用失败); } // 4. 若业务处理成功 --- 且获取被申请方长连接成功则向被申请放进行好友申请事件通知 auto conn  _connections-connection(req.respondent_id()); if (rsp.success()  conn) { LOG_DEBUG(找到被申请人 {} 长连接对其进行好友申请通知, req.respondent_id()); auto user_rsp  _GetUserInfo(req.request_id(), *uid); if (!user_rsp) { LOG_ERROR({} 获取当前客户端用户信息失败, req.request_id()); return err_response(获取当前客户端用户信息失败); } NotifyMessage notify; notify.set_notify_type(NotifyType::FRIEND_ADD_APPLY_NOTIFY); notify.mutable_friend_add_apply()-mutable_user_info()-CopyFrom(user_rsp-user_info()); conn-send(notify.SerializeAsString(), websocketpp::frame::opcode::value::binary); } 好友添加处理事件结果接口上面我们将好友申请发送以后接下来就是事件的处理了我们添加好友的结果无非就两种第一种是对方同意然后你的好友列表多出一位新朋友同时也建立起新的会话列表如果对方没有同意则你就会收到对方拒绝你加好友的消息除此之外无事发生。我们这边也是这个逻辑通过request里面取出申请人和被申请人的ID以及结果是否同意添加联系人然后通过ID得到对应的身份信息以及对应的长连接先向申请人通过长连接通知申请结果同意或者拒绝如果是同意则需要为双方创建一个会话会话的头像就是双方的头像会话的名字就是双方的名字然后再将这个会话放入通知里面通过双方对应的链接传输这个通知使得客户端建立相应会话。在好友删除接口中除了对relation里面的二者关系进行删除还要对message表里面二者发送的所有信息进行删除然后再删除对应的回话表和会话成员表最后向被删除者发送一个通知让被删除者得知消息虽然有点残忍但是我觉得知道还是比不知道好后面的许多接口都是和前面的流程差不多都是将http报文转换成对应的PROTOBUF报文然后再进行鉴权后转发给对应的微服务器微服务器对于每一个请求报文的处理在前面讲述完成也不在此讲述接收到对应微服务器的响应报文以后再进行转换成http报文以后转发给客户端 void FriendAddProcess(const httplib::Request request, httplib::Response response) { //好友申请的处理----- FriendAddProcessReq req; FriendAddProcessRsp rsp; auto err_response  [req, rsp, response](const std::string errmsg) - void { rsp.set_success(false); rsp.set_errmsg(errmsg); response.set_content(rsp.SerializeAsString(), application/x-protbuf); }; bool ret  req.ParseFromString(request.body); if (ret  false) { LOG_ERROR(好友申请处理请求正文反序列化失败); return err_response(好友申请处理请求正文反序列化失败); } // 2. 客户端身份识别与鉴权 std::string ssid  req.session_id(); auto uid  _redis_session-uid(ssid); if (!uid) { LOG_ERROR({} 获取登录会话关联用户信息失败, ssid); return err_response(获取登录会话关联用户信息失败); } req.set_user_id(*uid); // 3. 将请求转发给好友子服务进行业务处理 auto channel  _mm_channels-choose(_friend_service_name); if (!channel) { LOG_ERROR({} 未找到可提供业务处理的用户子服务节点, req.request_id()); return err_response(未找到可提供业务处理的用户子服务节点); } zhou::FriendService_Stub stub(channel.get()); brpc::Controller cntl; stub.FriendAddProcess(cntl, req, rsp, nullptr); if (cntl.Failed()) { LOG_ERROR({} 好友子服务调用失败, req.request_id()); return err_response(好友子服务调用失败); } if (rsp.success()) { auto process_user_rsp  _GetUserInfo(req.request_id(), *uid); if (!process_user_rsp) { LOG_ERROR({} 获取用户信息失败, req.request_id()); return err_response(获取用户信息失败); } auto apply_user_rsp  _GetUserInfo(req.request_id(), req.apply_user_id()); if (!process_user_rsp) { LOG_ERROR({} 获取用户信息失败, req.request_id()); return err_response(获取用户信息失败); } auto process_conn  _connections-connection(*uid); if (process_conn) LOG_DEBUG(找到处理人的长连接); else LOG_DEBUG(未找到处理人的长连接); auto apply_conn  _connections-connection(req.apply_user_id()); if (apply_conn) LOG_DEBUG(找到申请人的长连接); else LOG_DEBUG(未找到申请人的长连接); //4. 将处理结果给申请人进行通知 if (apply_conn) { NotifyMessage notify; notify.set_notify_type(NotifyType::FRIEND_ADD_PROCESS_NOTIFY); auto process_result  notify.mutable_friend_process_result(); process_result-mutable_user_info()-CopyFrom(process_user_rsp-user_info()); process_result-set_agree(req.agree()); apply_conn-send(notify.SerializeAsString(), websocketpp::frame::opcode::value::binary); LOG_DEBUG(对申请人进行申请处理结果通知); } //5. 若处理结果是同意 --- 会伴随着单聊会话的创建 -- 因此需要对双方进行会话创建的通知 if (req.agree()  apply_conn) { //对申请人的通知---会话信息就是处理人信息 NotifyMessage notify; notify.set_notify_type(NotifyType::CHAT_SESSION_CREATE_NOTIFY); auto chat_session  notify.mutable_new_chat_session_info(); chat_session-mutable_chat_session_info()-set_single_chat_friend_id(*uid); chat_session-mutable_chat_session_info()-set_chat_session_id(rsp.new_session_id()); chat_session-mutable_chat_session_info()-set_chat_session_name(process_user_rsp-user_info().nickname()); chat_session-mutable_chat_session_info()-set_avatar(process_user_rsp-user_info().avatar()); apply_conn-send(notify.SerializeAsString(), websocketpp::frame::opcode::value::binary); LOG_DEBUG(对申请人进行会话创建通知); } if (req.agree()  process_conn) { //对处理人的通知 --- 会话信息就是申请人信息 NotifyMessage notify; notify.set_notify_type(NotifyType::CHAT_SESSION_CREATE_NOTIFY); auto chat_session  notify.mutable_new_chat_session_info(); chat_session-mutable_chat_session_info()-set_single_chat_friend_id(req.apply_user_id()); chat_session-mutable_chat_session_info()-set_chat_session_id(rsp.new_session_id()); chat_session-mutable_chat_session_info()-set_chat_session_name(apply_user_rsp-user_info().nickname()); chat_session-mutable_chat_session_info()-set_avatar(apply_user_rsp-user_info().avatar()); process_conn-send(notify.SerializeAsString(), websocketpp::frame::opcode::value::binary); LOG_DEBUG(对处理人进行会话创建通知); } } //6. 对客户端进行响应 response.set_content(rsp.SerializeAsString(), application/x-protbuf); } 最后一个接口新消息转发接口先将http报文序列化为对应的消息转发接口的报文然后调用消息转发子服务将报文进行处理得到消息转发子服务传来的响应以后说明消息已经被永久化到数据库和ES搜索引擎里面就对被发送消息的用户发送一个通知通知就是有一条新消息通知的发送也是通过用户ID找到对应的长连接进行发送通知。 void NewMessage(const httplib::Request request, httplib::Response response) { NewMessageReq req; NewMessageRsp rsp;//这是给客户端的响应 GetTransmitTargetRsp target_rsp;//这是请求子服务的响应 auto err_response  [req, rsp, response](const std::string errmsg) - void { rsp.set_success(false); rsp.set_errmsg(errmsg); response.set_content(rsp.SerializeAsString(), application/x-protbuf); }; bool ret  req.ParseFromString(request.body); if (ret  false) { LOG_ERROR(新消息请求正文反序列化失败); return err_response(新消息请求正文反序列化失败); } // 2. 客户端身份识别与鉴权 std::string ssid  req.session_id(); auto uid  _redis_session-uid(ssid); if (!uid) { LOG_ERROR({} 获取登录会话关联用户信息失败, ssid); return err_response(获取登录会话关联用户信息失败); } req.set_user_id(*uid); // 3. 将请求转发给好友子服务进行业务处理 auto channel  _mm_channels-choose(_transmite_service_name); if (!channel) { LOG_ERROR({} 未找到可提供业务处理的用户子服务节点, req.request_id()); return err_response(未找到可提供业务处理的用户子服务节点); } zhou::MsgTransmitService_Stub stub(channel.get()); brpc::Controller cntl; stub.GetTransmitTarget(cntl, req, target_rsp, nullptr); if (cntl.Failed()) { LOG_ERROR({} 消息转发子服务调用失败, req.request_id()); return err_response(消息转发子服务调用失败); } // 4. 若业务处理成功 --- 且获取被申请方长连接成功则向被申请放进行好友申请事件通知 if (target_rsp.success()){ for (int i  0; i  target_rsp.target_id_list_size(); i) { std::string notify_uid  target_rsp.target_id_list(i); if (notify_uid  *uid) continue; //不通知自己 auto conn  _connections-connection(notify_uid); if (!conn) { continue;} NotifyMessage notify; notify.set_notify_type(NotifyType::CHAT_MESSAGE_NOTIFY); auto msg_info  notify.mutable_new_message_info(); msg_info-mutable_message_info()-CopyFrom(target_rsp.message()); conn-send(notify.SerializeAsString(), websocketpp::frame::opcode::value::binary); } } // 5. 向客户端进行响应 rsp.set_request_id(req.request_id()); rsp.set_success(target_rsp.success()); rsp.set_errmsg(target_rsp.errmsg()); response.set_content(rsp.SerializeAsString(), application/x-protbuf); }
http://www.w-s-a.com/news/302781/

相关文章:

  • 购物网站制作费用沧州新华区
  • 信宜网站设计公司在线购物商城系统
  • 网站维护是什么样如何制作网站教程视频讲解
  • 网站建设网络推广代理公司wordpress图片防盗链
  • 网站备案关站沈阳男科医院哪家好点
  • 王者荣耀网站建设的步骤网站页面用什么软件做
  • 典型网站开发的流程房屋装修效果图三室一厅
  • 制作微网站多少钱阿里巴巴做网站的电话号码
  • 风铃建站模板安卓手机软件开发外包
  • 深圳市住房和建设局门户网站域名转移影响网站访问吗
  • 做母婴网站赚钱汕头百姓网
  • 那个网站建设好动漫制作技术升本可以升什么专业
  • 网站建设企业响应式网站模板广西建设部投诉网站
  • app营销的特点wordpress优化方案
  • 静安网站建设公司如何编辑wordpress
  • 做网站的职位叫什么问题常州金坛网站建设
  • 保健品网站模板用jsp做的网站前后端交互
  • 网站带后台品牌网页设计图片
  • 保定清苑住房和城乡建设局网站分类信息网站程序
  • 可以做视频推广的网站选择大连网站建设
  • 在线网站开发网站在哪里
  • 建站的步骤上海快速优化排名
  • 招聘网站做一下要多少钱网站设计公司 国际
  • 巩义专业网站建设公司首选seo研究院
  • 大流量网站解决访问量友情链接如何添加
  • 教育网站建设网永康市住房和城乡建设局网站
  • 阿里巴巴官网网站django 做网站的代码
  • 网站建设 军报wordpress 订餐模板
  • 网站虚拟主机 会计处理石家庄站建设费用多少
  • 网站建设 服务内容 费用简述网站开发流程