您的位置:首页 > 教育 > 锐评 > discuz模板下载_石家庄营销推广网站_地推接单平台找推网_搜索网站关键词

discuz模板下载_石家庄营销推广网站_地推接单平台找推网_搜索网站关键词

2025/3/18 5:53:57 来源:https://blog.csdn.net/2302_80873119/article/details/145948466  浏览:    关键词:discuz模板下载_石家庄营销推广网站_地推接单平台找推网_搜索网站关键词
discuz模板下载_石家庄营销推广网站_地推接单平台找推网_搜索网站关键词

本章节,主要对项目中用户管理子服务模块进行分析、开发与测试。

功能设计

用户管理子服务,主要用于管理用户的数据,以及关于用户信息的各项操作,因此,在本模块中,用户管理子服务需要提供以下的功能性接口

用户注册用户输入 用户名(昵称) + 用户密码 进行注册。
用户登录用户通过 用户名(昵称) + 用户密码 进行登陆。
短信验证码获取当用户通过手机号注册/登陆时,需获取验证码。
手机号注册用户输入 手机号 + 验证码 进行注册。
手机号登陆用户输入 手机号 + 验证码 进行登陆。
用户信息获取当用户登陆之后,获取个人信息进行展示。(单个用户/多个用户)
设置头像设置用户头像。
设置昵称设置用户昵称。
设置签名设置用户个性签名。
设置手机号修改用户的绑定手机号。

模块划分

参数/配置文件解析模块基于gflags框架直接使用,进行参数/配置文件的解析。
日志模块基于spdlog封装的logger 直接进行日志输出。
服务注册模块基于etcd框架封装的注册模块 直接进行用户管理子服务模块的服务注册。
RPC服务模块基于brpc框架 搭建用户管理子服务的RPC服务器。
服务发现与调用模块

基于etcd框架封装的服务发现与brpc框架封装的服务调用模块。

1、连接文件管理子服务:获取用户信息的时候,用户头像是以文件的形式存储在文件管理子服务中的。

数据库数据操作模块

基于odb-mysql数据管理封装的模块,实现关系型数据库中数据的操作。

1、用户进行用户名/手机号注册的时候在数据库中新增信息。

2、用户修改个人信息的时候,修改数据库中的记录。

3、用户登陆的时候,在数据库中进行用户名密码的验证。

redis客户端模块

基于redis++封装的客户端进行内存数据库的数据操作。

1、当用户登陆的时候需要为用户创建登陆会话,会话信息保存在redis服务器中。

2、当用户手机号进行获取/验证验证码的时候,验证码与对应信息保存在redis服务器中。

ES客户端模块基于elasticsearch框架实现访问客户端,向ES服务器中存储用户简息,以便于用户的搜索。
短信平台客户端模块基于短信平台SDK封装使用,用于向用户手机号发送指定验证码。

业务接口/功能示意图

用户注册

用户登陆

短信验证码获取

手机号注册

手机号登陆

用户信息获取

用户头像修改

用户昵称/签名/手机号修改

服务实现流程

数据管理

MySQL(用户信息管理)

在用户管理子服务中,MySQL方面总体只进行了一个信息数据的存储与管理,只需要构建好用户信息表,提供好对应的操作即可。

用户数据表:

主键ID自动生成
用户ID用户唯一性标识
用户昵称用户的昵称,也可以用作登陆时的用户名
用户签名自我描述
登陆密码登陆时进行登陆验证
绑定手机号用户可以绑定手机号,绑定后可以通过手机号登陆
用户头像的文件ID头像文件存储的唯一性标识

提供的操作:

1、通过昵称获取用户信息。

2、通过手机号获取用户信息。

3、通过用户ID获取用户信息。

4、新增用户。

5、更新用户信息。

Redis(登陆会话信息、登陆状态、验证码)

在用户管理子服务中,Redis方面总体进行了一个登陆会话信息数据的存储与管理、登陆状态的管理(用于鉴权,后续是用于网关的)、验证码的存储与管理。

登陆会话信息管理

映射字段:登陆会话ID - 用户ID。

便于通过登录会话ID进行查找用户,只有查找到了用户,表明用户登陆成功,才能进行后续操作。

提供操作:

1、用户登陆时,新增登陆会话信息。

2、用户退出时,删除登陆会话信息。

3、通过登录会话ID,获取用户ID。

登陆状态管理

映射字段:用户ID - 空。

仅仅是用于标记用户是否登陆,避免重复登陆。

提供操作:

1、用户登陆时新增数据。

2、用户断开时,删除数据。

验证码管理

映射字段:验证码ID - 验证码。

用于获取验证码、验证验证码是否存在,且有效(未过期)。

提供操作:

1、在用户获取短信验证码时,新增数据。

2、在验证码使用之后,删除验证码的管理。

3、通过验证码ID,获取验证码。(验证码ID是响应给用户的)

ES(用户简单信息存储管理)

用户信息的用户ID、手机号、昵称字段,在ES进行额外的存储,便于后续的用户搜索的功能实现。

用户搜索通常是一种字符串的模糊匹配,用传统的关系型数据库效率较低,因此采用ES对索引字段进行分词后构建倒排索引,根据关键词进行搜索,效率会大大提高。

提高接口:

1、创建用户索引。

2、新增/更新用户数据。

3、用户信息搜索。

总体流程

1、编写服务所需的proto文件,利用protoc工具生成RPC服务器所需的.pb.h 和 .pb.cc 项目文件。
2、服务端 创建子类,继承于proto文件中RPC调用类,并进行功能性接口函数重写。
3、服务端 完成用户管理子服务类。        
4、实例化 服务类对象,启动服务。

服务代码实现

数据管理

MySQL(用户信息管理)

User(odb文件)编写

想要实现MySQL对用户信息的管理,那么首先需要通过ODB编程,构造一个User表。

user.hxx:

#pragma once
#include <iostream>
#include <odb/nullable.hxx>
#include <odb/core.hxx>// odb -d mysql --std c++11 --generate-query --generate-schema --profile boost/date-time user.hxxnamespace yangz
{
#pragma db object table("user")class User{public:User() {}// 用户名注册新增用户信息 -- user_id, _nickname, _passwordUser(const std::string &user_id, const std::string &nickname, const std::string &password): _user_id(user_id), _nickname(nickname), _password(password){}// 手机号注册新增用户信息 -- user_id, _phone, _随机昵称User(const std::string &user_id, const std::string &phone): _user_id(user_id), _phone(phone), _nickname(user_id){}public:void set_user_id(const std::string &user_id) { _user_id = user_id; }std::string get_user_id() { return _user_id; }void set_nickname(const std::string &nickname) { _nickname = nickname; }std::string get_nickname(){if (_nickname)return *_nickname;return std::string();}void set_description(const std::string &description) { _description = description; }std::string get_description(){if (_description)return *_description;return std::string();}void set_password(const std::string &password) { _password = password; }std::string get_password(){if (_password)return *_password;return std::string();}void set_phone(const std::string &phone) { _phone = phone; }std::string get_phone(){if (_phone)return *_phone;return std::string();}void set_avatar_id(const std::string &avatar_id) { _avatar_id = avatar_id; }std::string get_avatar_id(){if (_avatar_id)return *_avatar_id;return std::string();}private:friend class odb::access;
#pragma db id autounsigned long _id; // 自增主键
#pragma db type("varchar(64)") index uniquestd::string _user_id; // 用户唯一性id, varchar(64), 被索引, 唯一性约束
#pragma db type("varchar(64)") index uniqueodb::nullable<std::string> _nickname;    // 用户昵称,varchar(64), 被索引, 唯一性约束, 允许为空odb::nullable<std::string> _description; // 用户签名,varchar(64), 被索引, 唯一性约束, 允许为空
#pragma db type("varchar(64)")odb::nullable<std::string> _password; // 用户密码,varchar(64), 允许为空
#pragma db type("varchar(64)") index uniqueodb::nullable<std::string> _phone; // 用户手机号,varchar(64), 被索引, 唯一性约束, 允许为空
#pragma db type("varchar(64)")odb::nullable<std::string> _avatar_id; // 用户头像文件ID, 允许为空};
}

编译生成sql文件指令:

odb -d mysql --std c++11 --generate-query --generate-schema --profile boost/date-time user.hxx

此时在 .sql文件里新增:

然后将该.sql 文件导入数据库中:

mysql -uroot -p 'MicroChat' < user.sql 
Enter password:

现在在数据库中就有对应的表了:

客户端操作编写(mysqlUserTable.hpp)

该模块主要提供五个接口:

1、通过昵称获取用户信息。

2、通过手机号获取用户信息。

3、通过用户ID获取用户信息。

4、新增用户。

5、更新用户信息。

6、通过批量用户ID获取用户信息。

#pragma once
#include "odbMysqlHandleFactory.hpp"
#include "user.hxx"
#include "user-odb.hxx"
#include "logger.hpp"namespace yangz
{class UserTableClient{public:using ptr = std::shared_ptr<UserTableClient>;UserTableClient(const std::shared_ptr<odb::mysql::database> &mysql_client) : _mysql_client(mysql_client) {}public:// 新增用户信息数据bool insert(const std::shared_ptr<User> &user){try{odb::transaction trans(_mysql_client->begin());_mysql_client->persist(*user);trans.commit();}catch (const std::exception &e){LOG_ERROR("新增用户信息失败, 用户名: {}, 失败原因: {}", user->get_nickname(), e.what());return false;}return true;}// 更新用户信息bool update(const std::shared_ptr<User> &user){try{odb::transaction trans(_mysql_client->begin());_mysql_client->update(*user);trans.commit();}catch (const std::exception &e){LOG_ERROR("更新用户信息失败, 用户名: {}, 失败原因: {}", user->get_nickname(), e.what());return false;}return true;}// 通过nickname获取用户信息std::shared_ptr<User> select_by_nickname(const std::string &nickname){std::shared_ptr<User> user;try{odb::transaction trans(_mysql_client->begin());typedef odb::query<User> query;typedef odb::result<User> result;user.reset(_mysql_client->query_one<User>(query::nickname == nickname));trans.commit();}catch (const std::exception &e){LOG_ERROR("通过nickname查询用户信息失败, 用户名: {}, 失败原因: {}", nickname, e.what());}return user;}// 通过phone获取用户信息std::shared_ptr<User> select_by_phone(const std::string &phone){std::shared_ptr<User> user;try{odb::transaction trans(_mysql_client->begin());typedef odb::query<User> query;typedef odb::result<User> result;user.reset(_mysql_client->query_one<User>(query::phone == phone));trans.commit();}catch (const std::exception &e){LOG_ERROR("通过phone查询用户信息失败, 手机号: {}, 失败原因: {}", phone, e.what());}return user;}// 通过user_id获取用户信息std::shared_ptr<User> select_by_user_id(const std::string &user_id){std::shared_ptr<User> user;try{odb::transaction trans(_mysql_client->begin());typedef odb::query<User> query;typedef odb::result<User> result;user.reset(_mysql_client->query_one<User>(query::user_id == user_id));trans.commit();}catch (const std::exception &e){LOG_ERROR("通过user_id查询用户信息失败, user_id: {}, 失败原因: {}", user_id, e.what());}return user;}// 通过批量user_id获取多个用户信息std::vector<User> select_by_multi_user_id(const std::vector<std::string> &user_id_list){// select * from user where user_id in ("user_id1", "user_id2", ...)if (user_id_list.empty())return std::vector<User>();std::vector<User> users;try{odb::transaction trans(_mysql_client->begin());typedef odb::query<User> query;typedef odb::result<User> result;std::stringstream ss;ss << "user_id in (";for (const auto &user_id : user_id_list){ss << "'" << user_id << "',";}std::string condition = ss.str();condition.pop_back();condition += ")";result r(_mysql_client->query<User>(condition));for (result::iterator i(r.begin()); i != r.end(); ++i){users.push_back(*i);}trans.commit();}catch (const std::exception &e){LOG_ERROR("通过批量user_id查询用户信息失败, 失败原因: {}", e.what());}return users;}private:std::shared_ptr<odb::mysql::database> _mysql_client;};
}

Redis(登陆会话信息、登陆状态、验证码)

redisDataManage.hpp:

登录会话信息管理

映射字段:登陆会话ID - 用户ID。

便于通过登录会话ID进行查找用户,只有查找到了用户,表明用户登陆成功,才能进行后续操作。

提供操作:

1、用户登陆时,新增登陆会话信息。

2、用户退出时,删除登陆会话信息。

3、通过登录会话ID,获取用户ID。

    class LoginSessionManage{public:using ptr = std::shared_ptr<LoginSessionManage>;LoginSessionManage(const std::shared_ptr<sw::redis::Redis> &redis_client) : _redis_client(redis_client) {}~LoginSessionManage() {}public:// 新增登陆会话信息void append(const std::string &lssid, const std::string &uid){_redis_client->set(lssid, uid);}// 移除登陆会话信息void remove(const std::string &lssid){_redis_client->del(lssid);}// 通过lssid获取对应uidsw::redis::OptionalString get_uid(const std::string &lssid){return _redis_client->get(lssid);}private:std::shared_ptr<sw::redis::Redis> _redis_client;};
登陆状态管理

映射字段:用户ID - 空。

仅仅是用于标记用户是否登陆,避免重复登陆。

提供操作:

1、用户登陆时新增数据。

2、用户断开时,删除数据。

3、判断某个用户是否登陆。

    class LoginStatusManage{public:using ptr = std::shared_ptr<LoginStatusManage>;LoginStatusManage(const std::shared_ptr<sw::redis::Redis> &redis_client) : _redis_client(redis_client) {}~LoginStatusManage() {}public:// 新增登陆状态信息void append(const std::string &uid){_redis_client->set(uid, "");}// 移除登陆状态信息void remove(const std::string &uid){_redis_client->del(uid);}// 判断某用户是否登陆bool exists(const std::string &uid){auto res = _redis_client->get(uid);if (res)return true;return false;}private:std::shared_ptr<sw::redis::Redis> _redis_client;};
验证码管理

映射字段:验证码ID - 验证码。

用于获取验证码、验证验证码是否存在,且有效(未过期)。

提供操作:

1、在用户获取短信验证码时,新增数据。

2、在验证码使用之后,删除验证码的管理。

3、通过验证码ID,获取验证码。(验证码ID是响应给用户的)

    class VerificationCodeManage{public:using ptr = std::shared_ptr<VerificationCodeManage>;VerificationCodeManage(const std::shared_ptr<sw::redis::Redis> &redis_client) : _redis_client(redis_client) {}~VerificationCodeManage() {}public:// 新增验证码信息, 并设置60s过期时间void append(const std::string &code_id, const std::string &code, const std::chrono::milliseconds &ttl = std::chrono::milliseconds(60000)){_redis_client->set(code_id, code, ttl);}// 移除验证码信息void remove(const std::string &code_id){_redis_client->del(code_id);}// 通过code_id获取codesw::redis::OptionalString get_code(const std::string &code_id){return _redis_client->get(code_id);}private:std::shared_ptr<sw::redis::Redis> _redis_client;};

ES(用户简单信息存储管理)

用户信息的用户ID、手机号、昵称字段,在ES进行额外的存储,便于后续的用户搜索的功能实现。

用户搜索通常是一种字符串的模糊匹配,用传统的关系型数据库效率较低,因此采用ES对索引字段进行分词后构建倒排索引,根据关键词进行搜索,效率会大大提高。

提高接口:

1、创建用户索引。

2、新增/更新用户数据。

3、用户信息搜索。

#pragma once
#include "elasticSearch.hpp"
#include "user.hxx"namespace yangz
{class ESUserInfoManage{public:using ptr = std::shared_ptr<ESUserInfoManage>;ESUserInfoManage(const std::shared_ptr<elasticlient::Client> &es_client) : _es_client(es_client) {}~ESUserInfoManage() {}public:// 创建用户信息索引bool createIndex(){bool res = ESIndexCreate(_es_client, "user").append("user_id", "keyword", "standard", true).append("nickname").append("phone", "keyword", "standard", true).append("description", "text", "standard", true).append("avatar_id", "keyword", "standard", true).create();if (res == false){LOG_INFO("用户信息索引创建失败");return false;}return true;}// 新增/更新用户数据bool appendData(const std::string &user_id,const std::string &nickname,const std::string &phone,const std::string &description,const std::string &avatar_id){bool res = ESDataInsert(_es_client, "user").append("user_id", user_id).append("nickname", nickname).append("phone", phone).append("description", description).append("avatar_id", avatar_id).insert(user_id);if (res == false){LOG_ERROR("用户数据插入/更新失败");return false;}return true;}// 用户信息搜索std::vector<User> search(const std::string &key, const std::vector<std::string> &user_id_list){std::vector<User> users;Json::Value json_user = ESDataSearch(_es_client, "user").append_should_match("user_id.keyword", key).append_should_match("phone.keyword", key).append_should_match("nickname", key).append_must_not_term("user_id.keyword", user_id_list).search();if (json_user.isArray() == false){LOG_INFO("用户搜索结果为空, 或者结果不是数组类型");return users;}int size = json_user.size();for (int i = 0; i < size; ++i){User user;user.set_user_id(json_user[i]["_source"]["user_id"].asString());user.set_nickname(json_user[i]["_source"]["nickname"].asString());user.set_phone(json_user[i]["_source"]["phone"].asString());user.set_description(json_user[i]["_source"]["description"].asString());user.set_avatar_id(json_user[i]["_source"]["avatar_id"].asString());users.push_back(user);}}private:std::shared_ptr<elasticlient::Client> _es_client;};
}
查看User文档表 
GET /user/_doc/_search?pretty 
{ "query": { "match_all": {} } 
} 

 

编写proto文件

用户元信息

base.proto:

对于用户来说,首先我们应当编写一个关于用户元信息的message。

其中包含:user_id、nickname、phone、description、avatar。

用户元信息(UserInfo)成员:

1、user_id :

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com