搜索好友与结果消息
enum FriendSearchType
{
FRIEND = 0;
GROUP = 1;
ALL = 2;
}
message FriendSearchRequest
{
uint64 request_id = 1;
string key_word = 2;
FriendSearchType search_type = 3;
uint32 current_page = 4;
uint32 page_size = 5;
}
message FriendSearchResponse
{
uint64 request_id = 1;
bool result = 2;
string reason = 3;
repeated UserInfo friends = 4;
repeated Group groups = 5;
uint32 current_page = 6;
uint64 total_page = 7;
uint64 total_num = 8;
}
用户信息消息
message UserInfo
{
int64 code = 1;
string name = 2;
string real_name = 3;
string nick_name = 4;
bytes header_image = 5;
string remark = 6;
string signature = 7;
GenderType gender = 8;
uint64 birthday = 9;
CityLocation continent = 10;
CityLocation country = 11;
CityLocation province = 12;
CityLocation city = 13;
string email = 14;
uint32 email_auth = 15;
string mobile = 16;
UserLevel user_level = 17;
string device_id = 18;
bool online = 19;
string group = 30;
bytes salt = 31;
bytes verifier = 32;
}
初始化添加好友和发起群聊按钮
void ContactNavWidget::initButtons()
{
QPixmap pix = IconHelper::getPixmap("#696969", 0xf234, 18, 20, 20);
ui->btnAddFriend->setText("");
ui->btnAddFriend->setIcon(QIcon(pix));
ui->btnAddFriend->setToolTip(tr("Add a new friend or group"));
pix = IconHelper::getPixmap("#696969", 0xf0c0, 18, 20, 20);
ui->btnStartGroupChat->setText("");
ui->btnStartGroupChat->setIcon(QIcon(pix));
ui->btnStartGroupChat->setToolTip(tr("Start group chat"));
}
搜索好友请求
void ContactNavWidget::on_btnAddFriend_clicked()
{
newFriendDlg_.reset(new NewFriendDialog());
newFriendDlg_->setWindowTitle(tr("Search new friends"));
newFriendDlg_->setFriendGroup(friendGroups_);
connect(newFriendDlg_.get(), &NewFriendDialog::searchTextChanged, this, &ContactNavWidget::searchFriendRequest);
connect(newFriendDlg_.get(), &NewFriendDialog::friendAddRequest, this, &ContactNavWidget::onFriendAddRequest);
if (newFriendDlg_->exec() == QDialog::Accepted) {
}
newFriendDlg_.reset();
}
void ContactNavWidget::searchFriendRequest(QString keyWord, proto::FriendSearchType searchType, uint32_t currentPage, uint32_t pageSize)
{
proto::internal::UiToService message;
proto::FriendSearchRequest* request = message.mutable_friend_search_request();
request->set_key_word(keyWord.toStdString());
request->set_search_type(searchType);
request->set_current_page(currentPage);
request->set_page_size(pageSize);
LOG(LS_INFO) << "Send search request, key words: " << keyWord.toStdString();
emit sendMessageToIpc(message);
}
搜索好友结果处理
void NewFriendDialog::onFriendSearchResponse(const proto::FriendSearchResponse& response)
{
std::vector<base::UserInfo> users;
int userCount = response.friends_size();
for (int i = 0; i < userCount; i++) {
base::UserInfo user;
const proto::UserInfo& userMsg = response.friends(i);
user.code = userMsg.code();
user.headerImage = userMsg.header_image();
user.name = base::utf16FromUtf8(userMsg.name());
user.nickName = base::utf16FromUtf8(userMsg.nick_name());
user.realName = base::utf16FromUtf8(userMsg.real_name());
user.birthday = userMsg.birthday();
user.country = base::CityLocation::parseFrom(userMsg.country());
user.province = base::CityLocation::parseFrom(userMsg.province());
user.city = base::CityLocation::parseFrom(userMsg.city());
user.remark = base::utf16FromUtf8(userMsg.remark());
user.signature = base::utf16FromUtf8(userMsg.signature());
user.gender = userMsg.gender();
users.emplace_back(std::move(user));
}
users_ = std::move(users);
currentPage_ = response.current_page();
totalPage_ = response.total_page();
totalNum_ = response.total_num();
LOG(LS_INFO) << "search result, user count: " << userCount << ", page: " << currentPage_ << ", total page: " << totalPage_ << ", total number: " << totalNum_;
updateUserList();
updatePageOption();
}
好友列表显示到QListWidget
void NewFriendDialog::updateUserList()
{
clearWidgets(contentLayout_);
userControlList_.clear();
size_t listCout = users_.size();
if (listCout <= 0) {
return;
}
for (int i = 0; i < listCout; i++) {
auto userControl = new NewUserControl(this, i);
userControl->setUser(users_[i]);
userControl->setStyleSheet("QWidget{background-color: white; height: 60px}"
);
userControl->setMinimumHeight(45);
userControl->update();
connect(userControl, &NewUserControl::friendAddRequest, this, &NewFriendDialog::onFriendAddRequest);
contentLayout_->addWidget(userControl);
contentLayout_->addSpacing(2);
userControlList_.push_back(userControl);
}
contentLayout_->addStretch();
contentLayout_->setContentsMargins(0, 0, 0, 0);
contentWidget_->setLayout(contentLayout_);
contentWidget_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
scrollArea_->setWidget(contentWidget_);
scrollArea_->setWidgetResizable(true);
mainLayout_->addWidget(scrollArea_);
mainLayout_->setContentsMargins(0, 0, 0, 0);
ui->listWidget->setLayout(mainLayout_);
ui->listWidget->setStyleSheet("border:none");
update();
}
std::vector<NewUserControl*> userControlList_;
std::vector<base::UserInfo> users_;
用户信息数据模型类 UserInfo
#ifndef BASE__PEER__USER_INFO__H
#define BASE__PEER__USER_INFO__H
#include "base/memory/byte_array.h"
#include "model/city_location.h"
#include "proto/common.pb.h"
namespace base
{
class UserInfo
{
public:
UserInfo() = default;
~UserInfo() = default;
UserInfo(const UserInfo& other) = default;
UserInfo& operator=(const UserInfo& other) = default;
UserInfo(UserInfo&& other) noexcept = default;
UserInfo& operator=(UserInfo&& other) noexcept = default;
static const size_t kMaxUserNameLength = 32;
static const size_t kMaxHeaderImageSize = 1024 * 64;
static const size_t kMinPasswordLength = 1;
static const size_t kMaxPasswordLength = 32;
static const size_t kSafePasswordLength = 8;
static bool isValidUserName(std::u16string_view username);
static bool isValidPassword(std::u16string_view password);
static bool isSafePassword(std::u16string_view password);
static UserInfo create(std::u16string_view name, std::u16string_view password, uint64_t userCode);
bool isValid() const;
bool isValidHeaderImage() const;
static UserInfo parseFrom(const proto::UserInfo& serialized_user);
proto::UserInfo serialize() const;
static const UserInfo kInvalidUser;
int64_t id = 0;
int64_t code = 0;
std::u16string name = u"";
std::u16string realName = u"";
std::u16string nickName = u"";
std::string headerImage;
std::string headerFormat;
std::u16string remark = u"";
std::u16string signature = u"";
proto::GenderType gender;
int64_t birthday = 0;
uint32_t level = 0;
int64_t continentId;
int64_t countryId;
int64_t provinceId;
int64_t cityId;
base::CityLocation continent = base::CityLocation::kInvalidLocation;
base::CityLocation country = base::CityLocation::kInvalidLocation;
base::CityLocation province = base::CityLocation::kInvalidLocation;
base::CityLocation city = base::CityLocation::kInvalidLocation;
std::string pwdGroup;
std::string pwdSalt;
std::string pwdVerifier;
std::string email;
int emailAuth = 0;
int online = 0;
std::string mobile;
int mobileAuth = 0;
std::string deviceId;
std::string revision;
std::string status;
std::string createBy;
int64_t createTime = 0;
std::string updateBy;
int64_t updateTime = 0;
};
}
#endif
protobuf消息与模型之间的序列化与反序列化
#include "model/user_info.h"
#include "base/base64.h"
#include "base/logging.h"
#include "base/crypto/random.h"
#include "base/crypto/srp_constants.h"
#include "base/crypto/srp_math.h"
#include "base/strings/unicode.h"
#include "base/utils.h"
#include <cwctype>
namespace base
{
namespace
{
const size_t kSaltSize = 64;
const char kDefaultGroup[] = "4096";
bool isValidUserNameChar(char16_t username_char)
{
if (std::iswalnum(username_char))
return true;
if (username_char == '.' ||
username_char == '_' ||
username_char == '-') {
return true;
}
return false;
}
}
const UserInfo UserInfo::kInvalidUser{};
bool UserInfo::isValidUserName(std::u16string_view username)
{
size_t length = username.length();
if (!length || length > kMaxUserNameLength)
return false;
for (size_t i = 0; i < length; ++i) {
if (!isValidUserNameChar(username[i]))
return false;
}
return true;
}
bool UserInfo::isValidPassword(std::u16string_view password)
{
size_t length = password.length();
if (length < kMinPasswordLength || length > kMaxPasswordLength)
return false;
return true;
}
bool UserInfo::isSafePassword(std::u16string_view password)
{
size_t length = password.length();
if (length < kSafePasswordLength)
return false;
bool has_upper = false;
bool has_lower = false;
bool has_digit = false;
for (size_t i = 0; i < length; ++i) {
char16_t character = password[i];
if (std::iswupper(character))
has_upper = true;
if (std::iswlower(character))
has_lower = true;
if (std::iswdigit(character))
has_digit = true;
}
return has_upper && has_lower && has_digit;
}
bool UserInfo::isValid() const
{
return !name.empty() && !pwdSalt.empty() && !pwdGroup.empty() && !pwdVerifier.empty();
}
bool UserInfo::isValidHeaderImage() const
{
if (0 == headerImage.size() || headerImage.size() >= kMaxHeaderImageSize)
return false;
return true;
}
UserInfo UserInfo::parseFrom(const proto::UserInfo& serialized)
{
UserInfo userInfo;
userInfo.code = serialized.code();
userInfo.name = utf16FromUtf8(serialized.name());
userInfo.realName = utf16FromUtf8(serialized.real_name());
userInfo.nickName = utf16FromUtf8(serialized.nick_name());
userInfo.headerImage = serialized.header_image();
userInfo.remark = utf16FromUtf8(serialized.remark());
userInfo.signature = utf16FromUtf8(serialized.signature());
userInfo.gender = serialized.gender();
userInfo.birthday = serialized.birthday();
userInfo.online = serialized.online();
userInfo.continent = base::CityLocation::parseFrom(serialized.continent());
userInfo.country = base::CityLocation::parseFrom(serialized.country());
userInfo.province = base::CityLocation::parseFrom(serialized.province());
userInfo.city = base::CityLocation::parseFrom(serialized.city());
userInfo.email = serialized.email();
userInfo.emailAuth = serialized.email_auth();
userInfo.mobile = serialized.mobile();
userInfo.pwdGroup = serialized.group();
userInfo.pwdSalt = serialized.salt();
userInfo.pwdVerifier = serialized.verifier();
return userInfo;
}
proto::UserInfo UserInfo::serialize() const
{
proto::UserInfo userInfo;
userInfo.set_code(code);
userInfo.set_name(utf8FromUtf16(name));
userInfo.set_real_name(utf8FromUtf16(realName));
userInfo.set_nick_name(utf8FromUtf16(nickName));
userInfo.set_header_image(headerImage);
userInfo.set_remark(utf8FromUtf16(remark));
userInfo.set_signature(utf8FromUtf16(signature));
userInfo.set_gender(proto::GenderType(gender));
userInfo.set_birthday(birthday);
userInfo.set_online(online);
userInfo.mutable_continent()->CopyFrom(continent.serialize());
userInfo.mutable_country()->CopyFrom(country.serialize());
userInfo.mutable_province()->CopyFrom(province.serialize());
userInfo.mutable_city()->CopyFrom(city.serialize());
userInfo.set_email(email);
userInfo.set_email_auth(emailAuth);
userInfo.set_mobile(mobile);
userInfo.set_group(pwdGroup);
userInfo.set_salt(pwdSalt);
userInfo.set_verifier(pwdVerifier);
return userInfo;
}
}