mirror of
https://gitee.com/Zhaoxin59/my-chat_-server.git
synced 2026-03-31 06:01:48 +08:00
update
This commit is contained in:
173
.gitignore
vendored
Normal file
173
.gitignore
vendored
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
# ================================
|
||||||
|
# 构建系统和编译产物
|
||||||
|
# ================================
|
||||||
|
# CMake 构建目录
|
||||||
|
*/build/
|
||||||
|
CMakeCache.txt
|
||||||
|
CMakeFiles/
|
||||||
|
cmake_install.cmake
|
||||||
|
Makefile
|
||||||
|
*.cmake
|
||||||
|
CMakeOutput.log
|
||||||
|
CMakeRuleHashes.txt
|
||||||
|
cmake.check_cache
|
||||||
|
CMakeDirectoryInformation.cmake
|
||||||
|
CMakeScratch/
|
||||||
|
CMakeScripts/
|
||||||
|
*.cmake.*
|
||||||
|
pkgRedirects/
|
||||||
|
progress.marks
|
||||||
|
TargetDirectories.txt
|
||||||
|
|
||||||
|
# 编译产物
|
||||||
|
*.o
|
||||||
|
*.o.d
|
||||||
|
*.obj
|
||||||
|
*.exe
|
||||||
|
*.out
|
||||||
|
*.app
|
||||||
|
|
||||||
|
# 协议缓冲区生成的文件
|
||||||
|
proto/generated/
|
||||||
|
|
||||||
|
# ODB 生成的数据库文件
|
||||||
|
*-odb.cxx
|
||||||
|
*-odb.hxx
|
||||||
|
*-odb.ixx
|
||||||
|
*.sql
|
||||||
|
|
||||||
|
# ================================
|
||||||
|
# 可执行文件和二进制文件
|
||||||
|
# ================================
|
||||||
|
# 服务端可执行文件
|
||||||
|
*/file_server
|
||||||
|
*/file_client
|
||||||
|
*/friend_server
|
||||||
|
*/friend_client
|
||||||
|
*/gateway_server
|
||||||
|
*/message_server
|
||||||
|
*/speech_server
|
||||||
|
*/speech_client
|
||||||
|
*/transmite_server
|
||||||
|
*/transmite_client
|
||||||
|
*/trans_user_client
|
||||||
|
*/user_server
|
||||||
|
*/user_client
|
||||||
|
|
||||||
|
# 测试可执行文件
|
||||||
|
*/test/*/main
|
||||||
|
*/test/mysql_test/main
|
||||||
|
*/test/es_test/main
|
||||||
|
*/test/redis_test/main
|
||||||
|
|
||||||
|
# 其他二进制文件
|
||||||
|
*/nc
|
||||||
|
*/make_file_download
|
||||||
|
*/base_download_file1
|
||||||
|
*/file_download_file2
|
||||||
|
|
||||||
|
# ================================
|
||||||
|
# 运行时数据和日志
|
||||||
|
# ================================
|
||||||
|
# 中间件数据
|
||||||
|
middle/data/
|
||||||
|
middle/elasticsearch/
|
||||||
|
middle/etcd/
|
||||||
|
middle/logs/
|
||||||
|
middle/mysql/
|
||||||
|
middle/rabbitmq/
|
||||||
|
middle/redis/
|
||||||
|
|
||||||
|
# 文件服务数据
|
||||||
|
file/build/data/
|
||||||
|
|
||||||
|
# 语音服务数据
|
||||||
|
speech/build/16k.pcm
|
||||||
|
speech/test/16k.pcm
|
||||||
|
|
||||||
|
# 日志文件
|
||||||
|
*.log
|
||||||
|
*.log.*
|
||||||
|
log/
|
||||||
|
logs/
|
||||||
|
|
||||||
|
# ================================
|
||||||
|
# 依赖库文件
|
||||||
|
# ================================
|
||||||
|
# 本地依赖库
|
||||||
|
*/depends/
|
||||||
|
*.so
|
||||||
|
*.so.*
|
||||||
|
*.dylib
|
||||||
|
*.dll
|
||||||
|
|
||||||
|
# ================================
|
||||||
|
# 临时文件和缓存
|
||||||
|
# ================================
|
||||||
|
*.tmp
|
||||||
|
*.temp
|
||||||
|
.tmp/
|
||||||
|
.cache/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# CMake 临时文件
|
||||||
|
*/CMakeFiles/*/CompilerIdC/a.out
|
||||||
|
*/CMakeFiles/*/CompilerIdCXX/a.out
|
||||||
|
*/CMakeFiles/*/CompilerIdC/tmp/
|
||||||
|
*/CMakeFiles/*/CompilerIdCXX/tmp/
|
||||||
|
|
||||||
|
# ================================
|
||||||
|
# 系统文件
|
||||||
|
# ================================
|
||||||
|
.DS_Store
|
||||||
|
.DS_Store?
|
||||||
|
._*
|
||||||
|
.Spotlight-V100
|
||||||
|
.Trashes
|
||||||
|
ehthumbs.db
|
||||||
|
Thumbs.db
|
||||||
|
Desktop.ini
|
||||||
|
|
||||||
|
# ================================
|
||||||
|
# IDE 和编辑器文件
|
||||||
|
# ================================
|
||||||
|
# VS Code
|
||||||
|
.vscode/
|
||||||
|
*.code-workspace
|
||||||
|
|
||||||
|
# Vim
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# ================================
|
||||||
|
# 协议缓冲区源文件(保留 .proto,忽略生成的 .pb.*)
|
||||||
|
# ================================
|
||||||
|
# 注意:保留 .proto 文件,但忽略生成的文件
|
||||||
|
*.pb.cc
|
||||||
|
*.pb.h
|
||||||
|
|
||||||
|
# ================================
|
||||||
|
# 项目特定文件
|
||||||
|
# ================================
|
||||||
|
# 树状结构输出文件
|
||||||
|
tree.txt
|
||||||
|
|
||||||
|
# 下载的文件
|
||||||
|
base_download_file1
|
||||||
|
file_download_file2
|
||||||
|
|
||||||
|
# 测试目录中的构建文件
|
||||||
|
*/test/*/Makefile
|
||||||
|
*/test/*/*.o
|
||||||
|
*/test/*/*.o.d
|
||||||
|
|
||||||
|
# ================================
|
||||||
|
# Docker 相关(可选)
|
||||||
|
# ================================
|
||||||
|
# 如果您使用 Docker,可以取消注释以下行
|
||||||
|
# .dockerignore
|
||||||
|
# !.dockerignore
|
||||||
|
# docker-compose.override.yml
|
||||||
10
CMakeLists.txt
Executable file
10
CMakeLists.txt
Executable file
@ -0,0 +1,10 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.1.3)
|
||||||
|
project(all-test)
|
||||||
|
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/message)
|
||||||
|
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/user)
|
||||||
|
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/file)
|
||||||
|
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/speech)
|
||||||
|
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/transmite)
|
||||||
|
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/friend)
|
||||||
|
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/gateway)
|
||||||
|
set(CMAKE_INSTALL_PREFIX ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
25
common/asr.hpp
Normal file
25
common/asr.hpp
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "aip-cpp-sdk/speech.h"
|
||||||
|
#include "logger.hpp"
|
||||||
|
|
||||||
|
namespace bite_im{
|
||||||
|
class ASRClient {
|
||||||
|
public:
|
||||||
|
using ptr = std::shared_ptr<ASRClient>;
|
||||||
|
ASRClient(const std::string &app_id,
|
||||||
|
const std::string &api_key,
|
||||||
|
const std::string &secret_key):
|
||||||
|
_client(app_id, api_key, secret_key) {}
|
||||||
|
std::string recognize(const std::string &speech_data, std::string &err){
|
||||||
|
Json::Value result = _client.recognize(speech_data, "pcm", 16000, aip::null);
|
||||||
|
if (result["err_no"].asInt() != 0) {
|
||||||
|
LOG_ERROR("语音识别失败:{}", result["err_msg"].asString());
|
||||||
|
err = result["err_msg"].asString();
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
return result["result"][0].asString();
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
aip::Speech _client;
|
||||||
|
};
|
||||||
|
}
|
||||||
151
common/channel.hpp
Normal file
151
common/channel.hpp
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <brpc/channel.h>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <mutex>
|
||||||
|
#include "logger.hpp"
|
||||||
|
|
||||||
|
namespace bite_im{
|
||||||
|
//1. 封装单个服务的信道管理类:
|
||||||
|
class ServiceChannel {
|
||||||
|
public:
|
||||||
|
using ptr = std::shared_ptr<ServiceChannel>;
|
||||||
|
using ChannelPtr = std::shared_ptr<brpc::Channel>;
|
||||||
|
ServiceChannel(const std::string &name):
|
||||||
|
_service_name(name), _index(0){}
|
||||||
|
//服务上线了一个节点,则调用append新增信道
|
||||||
|
void append(const std::string &host) {
|
||||||
|
auto channel = std::make_shared<brpc::Channel>();
|
||||||
|
brpc::ChannelOptions options;
|
||||||
|
options.connect_timeout_ms = -1;
|
||||||
|
options.timeout_ms = -1;
|
||||||
|
options.max_retry = 3;
|
||||||
|
options.protocol = "baidu_std";
|
||||||
|
int ret = channel->Init(host.c_str(), &options);
|
||||||
|
if (ret == -1) {
|
||||||
|
LOG_ERROR("初始化{}-{}信道失败!", _service_name, host);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::unique_lock<std::mutex> lock(_mutex);
|
||||||
|
_hosts.insert(std::make_pair(host, channel));
|
||||||
|
_channels.push_back(channel);
|
||||||
|
}
|
||||||
|
//服务下线了一个节点,则调用remove释放信道
|
||||||
|
void remove(const std::string &host) {
|
||||||
|
std::unique_lock<std::mutex> lock(_mutex);
|
||||||
|
auto it = _hosts.find(host);
|
||||||
|
if (it == _hosts.end()) {
|
||||||
|
LOG_WARN("{}-{}节点删除信道时,没有找到信道信息!", _service_name, host);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (auto vit = _channels.begin(); vit != _channels.end(); ++vit) {
|
||||||
|
if (*vit == it->second) {
|
||||||
|
_channels.erase(vit);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_hosts.erase(it);
|
||||||
|
}
|
||||||
|
//通过RR轮转策略,获取一个Channel用于发起对应服务的Rpc调用
|
||||||
|
ChannelPtr choose() {
|
||||||
|
std::unique_lock<std::mutex> lock(_mutex);
|
||||||
|
if (_channels.size() == 0) {
|
||||||
|
LOG_ERROR("当前没有能够提供 {} 服务的节点!", _service_name);
|
||||||
|
return ChannelPtr();
|
||||||
|
}
|
||||||
|
int32_t idx = _index++ % _channels.size();
|
||||||
|
return _channels[idx];
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::mutex _mutex;
|
||||||
|
int32_t _index; //当前轮转下标计数器
|
||||||
|
std::string _service_name;//服务名称
|
||||||
|
std::vector<ChannelPtr> _channels; //当前服务对应的信道集合
|
||||||
|
std::unordered_map<std::string, ChannelPtr> _hosts; //主机地址与信道映射关系
|
||||||
|
};
|
||||||
|
|
||||||
|
//总体的服务信道管理类
|
||||||
|
class ServiceManager {
|
||||||
|
public:
|
||||||
|
using ptr = std::shared_ptr<ServiceManager>;
|
||||||
|
ServiceManager() {}
|
||||||
|
//获取指定服务的节点信道
|
||||||
|
ServiceChannel::ChannelPtr choose(const std::string &service_name) {
|
||||||
|
std::unique_lock<std::mutex> lock(_mutex);
|
||||||
|
auto sit = _services.find(service_name);
|
||||||
|
if (sit == _services.end()) {
|
||||||
|
LOG_ERROR("当前没有能够提供 {} 服务的节点!", service_name);
|
||||||
|
return ServiceChannel::ChannelPtr();
|
||||||
|
}
|
||||||
|
return sit->second->choose();
|
||||||
|
}
|
||||||
|
|
||||||
|
//先声明,我关注哪些服务的上下线,不关心的就不需要管理了
|
||||||
|
void declared(const std::string &service_name) {
|
||||||
|
std::unique_lock<std::mutex> lock(_mutex);
|
||||||
|
_follow_services.insert(service_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
//服务上线时调用的回调接口,将服务节点管理起来
|
||||||
|
void onServiceOnline(const std::string &service_instance, const std::string &host) {
|
||||||
|
std::string service_name = getServiceName(service_instance);
|
||||||
|
ServiceChannel::ptr service;
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(_mutex);
|
||||||
|
auto fit = _follow_services.find(service_name);
|
||||||
|
if (fit == _follow_services.end()) {
|
||||||
|
LOG_DEBUG("{}-{} 服务上线了,但是当前并不关心!", service_name, host);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//先获取管理对象,没有则创建,有则添加节点
|
||||||
|
auto sit = _services.find(service_name);
|
||||||
|
if (sit == _services.end()) {
|
||||||
|
service = std::make_shared<ServiceChannel>(service_name);
|
||||||
|
_services.insert(std::make_pair(service_name, service));
|
||||||
|
}else {
|
||||||
|
service = sit->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!service) {
|
||||||
|
LOG_ERROR("新增 {} 服务管理节点失败!", service_name);
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
service->append(host);
|
||||||
|
LOG_DEBUG("{}-{} 服务上线新节点,进行添加管理!", service_name, host);
|
||||||
|
}
|
||||||
|
|
||||||
|
//服务下线时调用的回调接口,从服务信道管理中,删除指定节点信道
|
||||||
|
void onServiceOffline(const std::string &service_instance, const std::string &host) {
|
||||||
|
std::string service_name = getServiceName(service_instance);
|
||||||
|
ServiceChannel::ptr service;
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(_mutex);
|
||||||
|
auto fit = _follow_services.find(service_name);
|
||||||
|
if (fit == _follow_services.end()) {
|
||||||
|
LOG_DEBUG("{}-{} 服务下线了,但是当前并不关心!", service_name, host);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//先获取管理对象,没有则创建,有则添加节点
|
||||||
|
auto sit = _services.find(service_name);
|
||||||
|
if (sit == _services.end()) {
|
||||||
|
LOG_WARN("删除{}服务节点时,没有找到管理对象", service_name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
service = sit->second;
|
||||||
|
}
|
||||||
|
service->remove(host);
|
||||||
|
LOG_DEBUG("{}-{} 服务下线节点,进行删除管理!", service_name, host);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::string getServiceName(const std::string &service_instance) {
|
||||||
|
auto pos = service_instance.find_last_of('/');
|
||||||
|
if (pos == std::string::npos) return service_instance;
|
||||||
|
return service_instance.substr(0, pos);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::mutex _mutex;
|
||||||
|
std::unordered_set<std::string> _follow_services;
|
||||||
|
std::unordered_map<std::string, ServiceChannel::ptr> _services;
|
||||||
|
};
|
||||||
|
}
|
||||||
164
common/data_es.hpp
Normal file
164
common/data_es.hpp
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
#include "icsearch.hpp"
|
||||||
|
#include "user.hxx"
|
||||||
|
#include "message.hxx"
|
||||||
|
|
||||||
|
namespace bite_im {
|
||||||
|
class ESClientFactory {
|
||||||
|
public:
|
||||||
|
static std::shared_ptr<elasticlient::Client> create(const std::vector<std::string> host_list) {
|
||||||
|
return std::make_shared<elasticlient::Client>(host_list);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
class ESUser {
|
||||||
|
public:
|
||||||
|
using ptr = std::shared_ptr<ESUser>;
|
||||||
|
ESUser(const std::shared_ptr<elasticlient::Client> &client):
|
||||||
|
_es_client(client){}
|
||||||
|
|
||||||
|
bool createIndex() {
|
||||||
|
bool ret = ESIndex(_es_client, "user")
|
||||||
|
.append("user_id", "keyword", "standard", true)
|
||||||
|
.append("nickname")
|
||||||
|
.append("phone", "keyword", "standard", true)
|
||||||
|
.append("description", "text", "standard", false)
|
||||||
|
.append("avatar_id", "keyword", "standard", false)
|
||||||
|
.create();
|
||||||
|
if (ret == false) {
|
||||||
|
LOG_INFO("用户信息索引创建失败!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LOG_INFO("用户信息索引创建成功!");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool appendData(const std::string &uid,
|
||||||
|
const std::string &phone,
|
||||||
|
const std::string &nickname,
|
||||||
|
const std::string &description,
|
||||||
|
const std::string &avatar_id) {
|
||||||
|
bool ret = ESInsert(_es_client, "user")
|
||||||
|
.append("user_id", uid)
|
||||||
|
.append("nickname", nickname)
|
||||||
|
.append("phone", phone)
|
||||||
|
.append("description", description)
|
||||||
|
.append("avatar_id", avatar_id)
|
||||||
|
.insert(uid);
|
||||||
|
if (ret == false) {
|
||||||
|
LOG_ERROR("用户数据插入/更新失败!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LOG_INFO("用户数据新增/更新成功!");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<User> search(const std::string &key, const std::vector<std::string> &uid_list) {
|
||||||
|
std::vector<User> res;
|
||||||
|
Json::Value json_user = ESSearch(_es_client, "user")
|
||||||
|
.append_should_match("phone.keyword", key)
|
||||||
|
.append_should_match("user_id.keyword", key)
|
||||||
|
.append_should_match("nickname", key)
|
||||||
|
.append_must_not_terms("user_id.keyword", uid_list)
|
||||||
|
.search();
|
||||||
|
if (json_user.isArray() == false) {
|
||||||
|
LOG_ERROR("用户搜索结果为空,或者结果不是数组类型");
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
int sz = json_user.size();
|
||||||
|
LOG_DEBUG("检索结果条目数量:{}", sz);
|
||||||
|
for (int i = 0; i < sz; i++) {
|
||||||
|
User user;
|
||||||
|
user.user_id(json_user[i]["_source"]["user_id"].asString());
|
||||||
|
user.nickname(json_user[i]["_source"]["nickname"].asString());
|
||||||
|
user.description(json_user[i]["_source"]["description"].asString());
|
||||||
|
user.phone(json_user[i]["_source"]["phone"].asString());
|
||||||
|
user.avatar_id(json_user[i]["_source"]["avatar_id"].asString());
|
||||||
|
res.push_back(user);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
// const std::string _uid_key = "user_id";
|
||||||
|
// const std::string _desc_key = "user_id";
|
||||||
|
// const std::string _phone_key = "user_id";
|
||||||
|
// const std::string _name_key = "user_id";
|
||||||
|
// const std::string _avatar_key = "user_id";
|
||||||
|
std::shared_ptr<elasticlient::Client> _es_client;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ESMessage {
|
||||||
|
public:
|
||||||
|
using ptr = std::shared_ptr<ESMessage>;
|
||||||
|
ESMessage(const std::shared_ptr<elasticlient::Client> &es_client):
|
||||||
|
_es_client(es_client){}
|
||||||
|
bool createIndex() {
|
||||||
|
bool ret = ESIndex(_es_client, "message")
|
||||||
|
.append("user_id", "keyword", "standard", false)
|
||||||
|
.append("message_id", "keyword", "standard", false)
|
||||||
|
.append("create_time", "long", "standard", false)
|
||||||
|
.append("chat_session_id", "keyword", "standard", true)
|
||||||
|
.append("content")
|
||||||
|
.create();
|
||||||
|
if (ret == false) {
|
||||||
|
LOG_INFO("消息信息索引创建失败!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LOG_INFO("消息信息索引创建成功!");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool appendData(const std::string &user_id,
|
||||||
|
const std::string &message_id,
|
||||||
|
const long create_time,
|
||||||
|
const std::string &chat_session_id,
|
||||||
|
const std::string &content) {
|
||||||
|
bool ret = ESInsert(_es_client, "message")
|
||||||
|
.append("message_id", message_id)
|
||||||
|
.append("create_time", create_time)
|
||||||
|
.append("user_id", user_id)
|
||||||
|
.append("chat_session_id", chat_session_id)
|
||||||
|
.append("content", content)
|
||||||
|
.insert(message_id);
|
||||||
|
if (ret == false) {
|
||||||
|
LOG_ERROR("消息数据插入/更新失败!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LOG_INFO("消息数据新增/更新成功!");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool remove(const std::string &mid) {
|
||||||
|
bool ret = ESRemove(_es_client, "message").remove(mid);
|
||||||
|
if (ret == false) {
|
||||||
|
LOG_ERROR("消息数据删除失败!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LOG_INFO("消息数据删除成功!");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
std::vector<bite_im::Message> search(const std::string &key, const std::string &ssid) {
|
||||||
|
std::vector<bite_im::Message> res;
|
||||||
|
Json::Value json_user = ESSearch(_es_client, "message")
|
||||||
|
.append_must_term("chat_session_id.keyword", ssid)
|
||||||
|
.append_must_match("content", key)
|
||||||
|
.search();
|
||||||
|
if (json_user.isArray() == false) {
|
||||||
|
LOG_ERROR("用户搜索结果为空,或者结果不是数组类型");
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
int sz = json_user.size();
|
||||||
|
LOG_DEBUG("检索结果条目数量:{}", sz);
|
||||||
|
for (int i = 0; i < sz; i++) {
|
||||||
|
bite_im::Message message;
|
||||||
|
message.user_id(json_user[i]["_source"]["user_id"].asString());
|
||||||
|
message.message_id(json_user[i]["_source"]["message_id"].asString());
|
||||||
|
boost::posix_time::ptime ctime(boost::posix_time::from_time_t(
|
||||||
|
json_user[i]["_source"]["create_time"].asInt64()));
|
||||||
|
message.create_time(ctime);
|
||||||
|
message.session_id(json_user[i]["_source"]["chat_session_id"].asString());
|
||||||
|
message.content(json_user[i]["_source"]["content"].asString());
|
||||||
|
res.push_back(message);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::shared_ptr<elasticlient::Client> _es_client;
|
||||||
|
};
|
||||||
|
}
|
||||||
75
common/data_redis.hpp
Normal file
75
common/data_redis.hpp
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
#include <sw/redis++/redis.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace bite_im {
|
||||||
|
class RedisClientFactory {
|
||||||
|
public:
|
||||||
|
static std::shared_ptr<sw::redis::Redis> create(
|
||||||
|
const std::string &host,
|
||||||
|
int port,
|
||||||
|
int db,
|
||||||
|
bool keep_alive) {
|
||||||
|
sw::redis::ConnectionOptions opts;
|
||||||
|
opts.host = host;
|
||||||
|
opts.port = port;
|
||||||
|
opts.db = db;
|
||||||
|
opts.keep_alive = keep_alive;
|
||||||
|
auto res = std::make_shared<sw::redis::Redis>(opts);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
class Session {
|
||||||
|
public:
|
||||||
|
using ptr = std::shared_ptr<Session>;
|
||||||
|
Session(const std::shared_ptr<sw::redis::Redis> &redis_client):
|
||||||
|
_redis_client(redis_client){}
|
||||||
|
void append(const std::string &ssid, const std::string &uid) {
|
||||||
|
_redis_client->set(ssid, uid);
|
||||||
|
}
|
||||||
|
void remove(const std::string &ssid) {
|
||||||
|
_redis_client->del(ssid);
|
||||||
|
}
|
||||||
|
sw::redis::OptionalString uid(const std::string &ssid) {
|
||||||
|
return _redis_client->get(ssid);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::shared_ptr<sw::redis::Redis> _redis_client;
|
||||||
|
};
|
||||||
|
class Status {
|
||||||
|
public:
|
||||||
|
using ptr = std::shared_ptr<Status>;
|
||||||
|
Status(const std::shared_ptr<sw::redis::Redis> &redis_client):
|
||||||
|
_redis_client(redis_client){}
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
class Codes {
|
||||||
|
public:
|
||||||
|
using ptr = std::shared_ptr<Codes>;
|
||||||
|
Codes(const std::shared_ptr<sw::redis::Redis> &redis_client):
|
||||||
|
_redis_client(redis_client){}
|
||||||
|
void append(const std::string &cid, const std::string &code,
|
||||||
|
const std::chrono::milliseconds &t = std::chrono::milliseconds(300000)) {
|
||||||
|
_redis_client->set(cid, code, t);
|
||||||
|
}
|
||||||
|
void remove(const std::string &cid) {
|
||||||
|
_redis_client->del(cid);
|
||||||
|
}
|
||||||
|
sw::redis::OptionalString code(const std::string &cid) {
|
||||||
|
return _redis_client->get(cid);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::shared_ptr<sw::redis::Redis> _redis_client;
|
||||||
|
};
|
||||||
|
}
|
||||||
46
common/dms.hpp
Normal file
46
common/dms.hpp
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <alibabacloud/core/AlibabaCloud.h>
|
||||||
|
#include <alibabacloud/core/CommonRequest.h>
|
||||||
|
#include <alibabacloud/core/CommonClient.h>
|
||||||
|
#include <alibabacloud/core/CommonResponse.h>
|
||||||
|
#include "logger.hpp"
|
||||||
|
|
||||||
|
namespace bite_im{
|
||||||
|
class DMSClient {
|
||||||
|
public:
|
||||||
|
using ptr = std::shared_ptr<DMSClient>;
|
||||||
|
DMSClient(const std::string &access_key_id,
|
||||||
|
const std::string &access_key_secret) {
|
||||||
|
AlibabaCloud::InitializeSdk();
|
||||||
|
AlibabaCloud::ClientConfiguration configuration( "cn-chengdu" );
|
||||||
|
configuration.setConnectTimeout(1500);
|
||||||
|
configuration.setReadTimeout(4000);
|
||||||
|
AlibabaCloud::Credentials credential(access_key_id, access_key_secret);
|
||||||
|
_client = std::make_unique<AlibabaCloud::CommonClient>(credential, configuration);
|
||||||
|
}
|
||||||
|
~DMSClient() { AlibabaCloud::ShutdownSdk(); }
|
||||||
|
bool send(const std::string &phone, const std::string &code) {
|
||||||
|
AlibabaCloud::CommonRequest request(AlibabaCloud::CommonRequest::RequestPattern::RpcPattern);
|
||||||
|
request.setHttpMethod(AlibabaCloud::HttpRequest::Method::Post);
|
||||||
|
request.setDomain("dysmsapi.aliyuncs.com");
|
||||||
|
request.setVersion("2017-05-25");
|
||||||
|
request.setQueryParameter("Action", "SendSms");
|
||||||
|
request.setQueryParameter("SignName", "bitejiuyeke");
|
||||||
|
request.setQueryParameter("TemplateCode", "SMS_465324787");
|
||||||
|
request.setQueryParameter("PhoneNumbers", phone);
|
||||||
|
std::string param_code = "{\"code\":\"" + code + "\"}";
|
||||||
|
request.setQueryParameter("TemplateParam", param_code);
|
||||||
|
auto response = _client->commonResponse(request);
|
||||||
|
if (!response.isSuccess()) {
|
||||||
|
LOG_ERROR("短信验证码请求失败:{}", response.error().errorMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::unique_ptr<AlibabaCloud::CommonClient> _client;
|
||||||
|
};
|
||||||
|
}
|
||||||
83
common/etcd.hpp
Normal file
83
common/etcd.hpp
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <etcd/Client.hpp>
|
||||||
|
#include <etcd/KeepAlive.hpp>
|
||||||
|
#include <etcd/Response.hpp>
|
||||||
|
#include <etcd/Watcher.hpp>
|
||||||
|
#include <etcd/Value.hpp>
|
||||||
|
#include <functional>
|
||||||
|
#include "logger.hpp"
|
||||||
|
|
||||||
|
namespace bite_im{
|
||||||
|
//服务注册客户端类
|
||||||
|
class Registry {
|
||||||
|
public:
|
||||||
|
using ptr = std::shared_ptr<Registry>;
|
||||||
|
Registry(const std::string &host):
|
||||||
|
_client(std::make_shared<etcd::Client>(host)) ,
|
||||||
|
_keep_alive(_client->leasekeepalive(3).get()),
|
||||||
|
_lease_id(_keep_alive->Lease()){}
|
||||||
|
~Registry() { _keep_alive->Cancel(); }
|
||||||
|
bool registry(const std::string &key, const std::string &val) {
|
||||||
|
auto resp = _client->put(key, val, _lease_id).get();
|
||||||
|
if (resp.is_ok() == false) {
|
||||||
|
LOG_ERROR("注册数据失败:{}", resp.error_message());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::shared_ptr<etcd::Client> _client;
|
||||||
|
std::shared_ptr<etcd::KeepAlive> _keep_alive;
|
||||||
|
uint64_t _lease_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
//服务发现客户端类
|
||||||
|
class Discovery {
|
||||||
|
public:
|
||||||
|
using ptr = std::shared_ptr<Discovery>;
|
||||||
|
using NotifyCallback = std::function<void(std::string, std::string)>;
|
||||||
|
Discovery(const std::string &host,
|
||||||
|
const std::string &basedir,
|
||||||
|
const NotifyCallback &put_cb,
|
||||||
|
const NotifyCallback &del_cb):
|
||||||
|
_client(std::make_shared<etcd::Client>(host)) ,
|
||||||
|
_put_cb(put_cb), _del_cb(del_cb){
|
||||||
|
//先进行服务发现,先获取到当前已有的数据
|
||||||
|
auto resp = _client->ls(basedir).get();
|
||||||
|
if (resp.is_ok() == false) {
|
||||||
|
LOG_ERROR("获取服务信息数据失败:{}", resp.error_message());
|
||||||
|
}
|
||||||
|
int sz = resp.keys().size();
|
||||||
|
for (int i = 0; i < sz; ++i) {
|
||||||
|
if (_put_cb) _put_cb(resp.key(i), resp.value(i).as_string());
|
||||||
|
}
|
||||||
|
//然后进行事件监控,监控数据发生的改变并调用回调进行处理
|
||||||
|
_watcher = std::make_shared<etcd::Watcher>(*_client.get(), basedir,
|
||||||
|
std::bind(&Discovery::callback, this, std::placeholders::_1), true);
|
||||||
|
}
|
||||||
|
~Discovery() {
|
||||||
|
_watcher->Cancel();
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
void callback(const etcd::Response &resp) {
|
||||||
|
if (resp.is_ok() == false) {
|
||||||
|
LOG_ERROR("收到一个错误的事件通知: {}", resp.error_message());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (auto const& ev : resp.events()) {
|
||||||
|
if (ev.event_type() == etcd::Event::EventType::PUT) {
|
||||||
|
if (_put_cb) _put_cb(ev.kv().key(), ev.kv().as_string());
|
||||||
|
LOG_DEBUG("新增服务:{}-{}", ev.kv().key(), ev.kv().as_string());
|
||||||
|
}else if (ev.event_type() == etcd::Event::EventType::DELETE_) {
|
||||||
|
if (_del_cb) _del_cb(ev.prev_kv().key(), ev.prev_kv().as_string());
|
||||||
|
LOG_DEBUG("下线服务:{}-{}", ev.prev_kv().key(), ev.prev_kv().as_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
NotifyCallback _put_cb;
|
||||||
|
NotifyCallback _del_cb;
|
||||||
|
std::shared_ptr<etcd::Client> _client;
|
||||||
|
std::shared_ptr<etcd::Watcher> _watcher;
|
||||||
|
};
|
||||||
|
}
|
||||||
10508
common/httplib.h
Normal file
10508
common/httplib.h
Normal file
File diff suppressed because it is too large
Load Diff
255
common/icsearch.hpp
Normal file
255
common/icsearch.hpp
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <elasticlient/client.h>
|
||||||
|
#include <cpr/cpr.h>
|
||||||
|
#include <json/json.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include "logger.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
namespace bite_im{
|
||||||
|
bool Serialize(const Json::Value &val, std::string &dst)
|
||||||
|
{
|
||||||
|
//先定义Json::StreamWriter 工厂类 Json::StreamWriterBuilder
|
||||||
|
Json::StreamWriterBuilder swb;
|
||||||
|
swb.settings_["emitUTF8"] = true;
|
||||||
|
std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());
|
||||||
|
//通过Json::StreamWriter中的write接口进行序列化
|
||||||
|
std::stringstream ss;
|
||||||
|
int ret = sw->write(val, &ss);
|
||||||
|
if (ret != 0) {
|
||||||
|
std::cout << "Json反序列化失败!\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
dst = ss.str();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool UnSerialize(const std::string &src, Json::Value &val)
|
||||||
|
{
|
||||||
|
Json::CharReaderBuilder crb;
|
||||||
|
std::unique_ptr<Json::CharReader> cr(crb.newCharReader());
|
||||||
|
std::string err;
|
||||||
|
bool ret = cr->parse(src.c_str(), src.c_str() + src.size(), &val, &err);
|
||||||
|
if (ret == false) {
|
||||||
|
std::cout << "json反序列化失败: " << err << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ESIndex {
|
||||||
|
public:
|
||||||
|
ESIndex(std::shared_ptr<elasticlient::Client> &client,
|
||||||
|
const std::string &name,
|
||||||
|
const std::string &type = "_doc"):
|
||||||
|
_name(name), _type(type), _client(client) {
|
||||||
|
Json::Value analysis;
|
||||||
|
Json::Value analyzer;
|
||||||
|
Json::Value ik;
|
||||||
|
Json::Value tokenizer;
|
||||||
|
tokenizer["tokenizer"] = "ik_max_word";
|
||||||
|
ik["ik"] = tokenizer;
|
||||||
|
analyzer["analyzer"] = ik;
|
||||||
|
analysis["analysis"] = analyzer;
|
||||||
|
_index["settings"] = analysis;
|
||||||
|
}
|
||||||
|
ESIndex& append(const std::string &key,
|
||||||
|
const std::string &type = "text",
|
||||||
|
const std::string &analyzer = "ik_max_word",
|
||||||
|
bool enabled = true) {
|
||||||
|
Json::Value fields;
|
||||||
|
fields["type"] = type;
|
||||||
|
fields["analyzer"] = analyzer;
|
||||||
|
if (enabled == false ) fields["enabled"] = enabled;
|
||||||
|
_properties[key] = fields;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
bool create(const std::string &index_id = "default_index_id") {
|
||||||
|
Json::Value mappings;
|
||||||
|
mappings["dynamic"] = true;
|
||||||
|
mappings["properties"] = _properties;
|
||||||
|
_index["mappings"] = mappings;
|
||||||
|
|
||||||
|
std::string body;
|
||||||
|
bool ret = Serialize(_index, body);
|
||||||
|
if (ret == false) {
|
||||||
|
LOG_ERROR("索引序列化失败!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// LOG_DEBUG("{}", body);
|
||||||
|
//2. 发起搜索请求
|
||||||
|
try {
|
||||||
|
auto rsp = _client->index(_name, _type, index_id, body);
|
||||||
|
if (rsp.status_code < 200 || rsp.status_code >= 300) {
|
||||||
|
LOG_ERROR("创建ES索引 {} 失败,响应状态码异常: {}", _name, rsp.status_code);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch(std::exception &e) {
|
||||||
|
LOG_ERROR("创建ES索引 {} 失败: {}", _name, e.what());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::string _name;
|
||||||
|
std::string _type;
|
||||||
|
Json::Value _properties;
|
||||||
|
Json::Value _index;
|
||||||
|
std::shared_ptr<elasticlient::Client> _client;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ESInsert {
|
||||||
|
public:
|
||||||
|
ESInsert(std::shared_ptr<elasticlient::Client> &client,
|
||||||
|
const std::string &name,
|
||||||
|
const std::string &type = "_doc"):
|
||||||
|
_name(name), _type(type), _client(client)
|
||||||
|
{}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
ESInsert &append(const std::string &key, const T &val){
|
||||||
|
_item[key] = val;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool insert(const std::string id = "") {
|
||||||
|
std::string body;
|
||||||
|
bool ret = Serialize(_item, body);
|
||||||
|
if (ret == false) {
|
||||||
|
LOG_ERROR("索引序列化失败!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LOG_DEBUG("{}", body);
|
||||||
|
//2. 发起搜索请求
|
||||||
|
try {
|
||||||
|
auto rsp = _client->index(_name, _type, id, body);
|
||||||
|
if (rsp.status_code < 200 || rsp.status_code >= 300) {
|
||||||
|
LOG_ERROR("新增数据 {} 失败,响应状态码异常: {}", body, rsp.status_code);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch(std::exception &e) {
|
||||||
|
LOG_ERROR("新增数据 {} 失败: {}", body, e.what());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::string _name;
|
||||||
|
std::string _type;
|
||||||
|
Json::Value _item;
|
||||||
|
std::shared_ptr<elasticlient::Client> _client;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ESRemove {
|
||||||
|
public:
|
||||||
|
ESRemove(std::shared_ptr<elasticlient::Client> &client,
|
||||||
|
const std::string &name,
|
||||||
|
const std::string &type = "_doc"):
|
||||||
|
_name(name), _type(type), _client(client){}
|
||||||
|
bool remove(const std::string &id) {
|
||||||
|
try {
|
||||||
|
auto rsp = _client->remove(_name, _type, id);
|
||||||
|
if (rsp.status_code < 200 || rsp.status_code >= 300) {
|
||||||
|
LOG_ERROR("删除数据 {} 失败,响应状态码异常: {}", id, rsp.status_code);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch(std::exception &e) {
|
||||||
|
LOG_ERROR("删除数据 {} 失败: {}", id, e.what());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::string _name;
|
||||||
|
std::string _type;
|
||||||
|
std::shared_ptr<elasticlient::Client> _client;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ESSearch {
|
||||||
|
public:
|
||||||
|
ESSearch(std::shared_ptr<elasticlient::Client> &client,
|
||||||
|
const std::string &name,
|
||||||
|
const std::string &type = "_doc"):
|
||||||
|
_name(name), _type(type), _client(client){}
|
||||||
|
ESSearch& append_must_not_terms(const std::string &key, const std::vector<std::string> &vals) {
|
||||||
|
Json::Value fields;
|
||||||
|
for (const auto& val : vals){
|
||||||
|
fields[key].append(val);
|
||||||
|
}
|
||||||
|
Json::Value terms;
|
||||||
|
terms["terms"] = fields;
|
||||||
|
_must_not.append(terms);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
ESSearch& append_should_match(const std::string &key, const std::string &val) {
|
||||||
|
Json::Value field;
|
||||||
|
field[key] = val;
|
||||||
|
Json::Value match;
|
||||||
|
match["match"] = field;
|
||||||
|
_should.append(match);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
ESSearch& append_must_term(const std::string &key, const std::string &val) {
|
||||||
|
Json::Value field;
|
||||||
|
field[key] = val;
|
||||||
|
Json::Value term;
|
||||||
|
term["term"] = field;
|
||||||
|
_must.append(term);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
ESSearch& append_must_match(const std::string &key, const std::string &val){
|
||||||
|
Json::Value field;
|
||||||
|
field[key] = val;
|
||||||
|
Json::Value match;
|
||||||
|
match["match"] = field;
|
||||||
|
_must.append(match);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
Json::Value search(){
|
||||||
|
Json::Value cond;
|
||||||
|
if (_must_not.empty() == false) cond["must_not"] = _must_not;
|
||||||
|
if (_should.empty() == false) cond["should"] = _should;
|
||||||
|
if (_must.empty() == false) cond["must"] = _must;
|
||||||
|
Json::Value query;
|
||||||
|
query["bool"] = cond;
|
||||||
|
Json::Value root;
|
||||||
|
root["query"] = query;
|
||||||
|
|
||||||
|
std::string body;
|
||||||
|
bool ret = Serialize(root, body);
|
||||||
|
if (ret == false) {
|
||||||
|
LOG_ERROR("索引序列化失败!");
|
||||||
|
return Json::Value();
|
||||||
|
}
|
||||||
|
LOG_DEBUG("{}", body);
|
||||||
|
//2. 发起搜索请求
|
||||||
|
cpr::Response rsp;
|
||||||
|
try {
|
||||||
|
rsp = _client->search(_name, _type, body);
|
||||||
|
if (rsp.status_code < 200 || rsp.status_code >= 300) {
|
||||||
|
LOG_ERROR("检索数据 {} 失败,响应状态码异常: {}", body, rsp.status_code);
|
||||||
|
return Json::Value();
|
||||||
|
}
|
||||||
|
} catch(std::exception &e) {
|
||||||
|
LOG_ERROR("检索数据 {} 失败: {}", body, e.what());
|
||||||
|
return Json::Value();
|
||||||
|
}
|
||||||
|
//3. 需要对响应正文进行反序列化
|
||||||
|
LOG_DEBUG("检索响应正文: [{}]", rsp.text);
|
||||||
|
Json::Value json_res;
|
||||||
|
ret = UnSerialize(rsp.text, json_res);
|
||||||
|
if (ret == false) {
|
||||||
|
LOG_ERROR("检索数据 {} 结果反序列化失败", rsp.text);
|
||||||
|
return Json::Value();
|
||||||
|
}
|
||||||
|
return json_res["hits"]["hits"];
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::string _name;
|
||||||
|
std::string _type;
|
||||||
|
Json::Value _must_not;
|
||||||
|
Json::Value _should;
|
||||||
|
Json::Value _must;
|
||||||
|
std::shared_ptr<elasticlient::Client> _client;
|
||||||
|
};
|
||||||
|
}
|
||||||
34
common/logger.hpp
Normal file
34
common/logger.hpp
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
#include <spdlog/sinks/stdout_color_sinks.h>
|
||||||
|
#include <spdlog/sinks/basic_file_sink.h>
|
||||||
|
#include <spdlog/async.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
// mode - 运行模式: true-发布模式; false调试模式
|
||||||
|
|
||||||
|
namespace bite_im{
|
||||||
|
std::shared_ptr<spdlog::logger> g_default_logger;
|
||||||
|
void init_logger(bool mode, const std::string &file, int32_t level)
|
||||||
|
{
|
||||||
|
if (mode == false) {
|
||||||
|
//如果是调试模式,则创建标准输出日志器,输出等级为最低
|
||||||
|
g_default_logger = spdlog::stdout_color_mt("default-logger");
|
||||||
|
g_default_logger->set_level(spdlog::level::level_enum::trace);
|
||||||
|
g_default_logger->flush_on(spdlog::level::level_enum::trace);
|
||||||
|
}else {
|
||||||
|
//否则是发布模式,则创建文件输出日志器,输出等级根据参数而定
|
||||||
|
g_default_logger = spdlog::basic_logger_mt("default-logger", file);
|
||||||
|
g_default_logger->set_level((spdlog::level::level_enum)level);
|
||||||
|
g_default_logger->flush_on((spdlog::level::level_enum)level);
|
||||||
|
}
|
||||||
|
g_default_logger->set_pattern("[%n][%H:%M:%S][%t][%-8l]%v");
|
||||||
|
}
|
||||||
|
|
||||||
|
#define LOG_TRACE(format, ...) bite_im::g_default_logger->trace(std::string("[{}:{}] ") + format, __FILE__, __LINE__, ##__VA_ARGS__)
|
||||||
|
#define LOG_DEBUG(format, ...) bite_im::g_default_logger->debug(std::string("[{}:{}] ") + format, __FILE__, __LINE__, ##__VA_ARGS__)
|
||||||
|
#define LOG_INFO(format, ...) bite_im::g_default_logger->info(std::string("[{}:{}] ") + format, __FILE__, __LINE__, ##__VA_ARGS__)
|
||||||
|
#define LOG_WARN(format, ...) bite_im::g_default_logger->warn(std::string("[{}:{}] ") + format, __FILE__, __LINE__, ##__VA_ARGS__)
|
||||||
|
#define LOG_ERROR(format, ...) bite_im::g_default_logger->error(std::string("[{}:{}] ") + format, __FILE__, __LINE__, ##__VA_ARGS__)
|
||||||
|
#define LOG_FATAL(format, ...) bite_im::g_default_logger->critical(std::string("[{}:{}] ") + format, __FILE__, __LINE__, ##__VA_ARGS__)
|
||||||
|
}
|
||||||
30
common/mysql.hpp
Normal file
30
common/mysql.hpp
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <memory> // std::auto_ptr
|
||||||
|
#include <cstdlib> // std::exit
|
||||||
|
#include <iostream>
|
||||||
|
#include <odb/database.hxx>
|
||||||
|
#include <odb/mysql/database.hxx>
|
||||||
|
#include "logger.hpp"
|
||||||
|
|
||||||
|
// 用户注册, 用户登录, 验证码获取, 手机号注册,手机号登录, 获取用户信息, 用户信息修改
|
||||||
|
// 用信息新增, 通过昵称获取用户信息,通过手机号获取用户信息, 通过用户ID获取用户信息, 通过多个用户ID获取多个用户信息,信息修改
|
||||||
|
namespace bite_im {
|
||||||
|
class ODBFactory {
|
||||||
|
public:
|
||||||
|
static std::shared_ptr<odb::core::database> create(
|
||||||
|
const std::string &user,
|
||||||
|
const std::string &pswd,
|
||||||
|
const std::string &host,
|
||||||
|
const std::string &db,
|
||||||
|
const std::string &cset,
|
||||||
|
int port,
|
||||||
|
int conn_pool_count) {
|
||||||
|
std::unique_ptr<odb::mysql::connection_pool_factory> cpf(
|
||||||
|
new odb::mysql::connection_pool_factory(conn_pool_count, 0));
|
||||||
|
auto res = std::make_shared<odb::mysql::database>(user, pswd,
|
||||||
|
db, host, port, "", cset, 0, std::move(cpf));
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
71
common/mysql_apply.hpp
Normal file
71
common/mysql_apply.hpp
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "mysql.hpp"
|
||||||
|
#include "friend_apply.hxx"
|
||||||
|
#include "friend_apply-odb.hxx"
|
||||||
|
|
||||||
|
namespace bite_im {
|
||||||
|
class FriendApplyTable {
|
||||||
|
public:
|
||||||
|
using ptr = std::shared_ptr<FriendApplyTable>;
|
||||||
|
FriendApplyTable(const std::shared_ptr<odb::core::database> &db) : _db(db){}
|
||||||
|
bool insert(FriendApply &ev) {
|
||||||
|
try {
|
||||||
|
odb::transaction trans(_db->begin());
|
||||||
|
_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;
|
||||||
|
//当前的uid是被申请者的用户ID
|
||||||
|
result r(_db->query<FriendApply>(query::peer_id == uid));
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::shared_ptr<odb::core::database> _db;
|
||||||
|
};
|
||||||
|
}
|
||||||
118
common/mysql_chat_session.hpp
Normal file
118
common/mysql_chat_session.hpp
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "mysql.hpp"
|
||||||
|
#include "chat_session.hxx"
|
||||||
|
#include "chat_session-odb.hxx"
|
||||||
|
#include "mysql_chat_session_member.hpp"
|
||||||
|
|
||||||
|
namespace bite_im {
|
||||||
|
class ChatSessionTable {
|
||||||
|
public:
|
||||||
|
using ptr = std::shared_ptr<ChatSessionTable>;
|
||||||
|
ChatSessionTable(const std::shared_ptr<odb::core::database> &db):_db(db){}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
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;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
//当前的uid是被申请者的用户ID
|
||||||
|
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;
|
||||||
|
//当前的uid是被申请者的用户ID
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::shared_ptr<odb::core::database> _db;
|
||||||
|
};
|
||||||
|
}
|
||||||
87
common/mysql_chat_session_member.hpp
Normal file
87
common/mysql_chat_session_member.hpp
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "mysql.hpp"
|
||||||
|
#include "chat_session_member.hxx"
|
||||||
|
#include "chat_session_member-odb.hxx"
|
||||||
|
|
||||||
|
namespace bite_im {
|
||||||
|
class ChatSessionMemeberTable {
|
||||||
|
public:
|
||||||
|
using ptr = std::shared_ptr<ChatSessionMemeberTable>;
|
||||||
|
ChatSessionMemeberTable(const std::shared_ptr<odb::core::database> &db):_db(db){}
|
||||||
|
//单个会话成员的新增 --- ssid & uid
|
||||||
|
bool append(ChatSessionMember &csm) {
|
||||||
|
try {
|
||||||
|
odb::transaction trans(_db->begin());
|
||||||
|
_db->persist(csm);
|
||||||
|
trans.commit();
|
||||||
|
}catch (std::exception &e) {
|
||||||
|
LOG_ERROR("新增单会话成员失败 {}-{}:{}!",
|
||||||
|
csm.session_id(), csm.user_id(), e.what());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool append(std::vector<ChatSessionMember> &csm_lists) {
|
||||||
|
try {
|
||||||
|
odb::transaction trans(_db->begin());
|
||||||
|
for (auto &csm : csm_lists) {
|
||||||
|
_db->persist(csm);
|
||||||
|
}
|
||||||
|
trans.commit();
|
||||||
|
}catch (std::exception &e) {
|
||||||
|
LOG_ERROR("新增多会话成员失败 {}-{}:{}!",
|
||||||
|
csm_lists[0].session_id(), csm_lists.size(), e.what());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//删除指定会话中的指定成员 -- ssid & uid
|
||||||
|
bool remove(ChatSessionMember &csm) {
|
||||||
|
try {
|
||||||
|
odb::transaction trans(_db->begin());
|
||||||
|
typedef odb::query<ChatSessionMember> query;
|
||||||
|
typedef odb::result<ChatSessionMember> result;
|
||||||
|
_db->erase_query<ChatSessionMember>(query::session_id == csm.session_id() &&
|
||||||
|
query::user_id == csm.user_id());
|
||||||
|
trans.commit();
|
||||||
|
}catch (std::exception &e) {
|
||||||
|
LOG_ERROR("删除单会话成员失败 {}-{}:{}!",
|
||||||
|
csm.session_id(), csm.user_id(), e.what());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//删除会话的所有成员信息
|
||||||
|
bool remove(const std::string &ssid) {
|
||||||
|
try {
|
||||||
|
odb::transaction trans(_db->begin());
|
||||||
|
typedef odb::query<ChatSessionMember> query;
|
||||||
|
typedef odb::result<ChatSessionMember> result;
|
||||||
|
_db->erase_query<ChatSessionMember>(query::session_id == ssid);
|
||||||
|
trans.commit();
|
||||||
|
}catch (std::exception &e) {
|
||||||
|
LOG_ERROR("删除会话所有成员失败 {}:{}!", ssid, e.what());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
std::vector<std::string> members(const std::string &ssid) {
|
||||||
|
std::vector<std::string> res;
|
||||||
|
try {
|
||||||
|
odb::transaction trans(_db->begin());
|
||||||
|
typedef odb::query<ChatSessionMember> query;
|
||||||
|
typedef odb::result<ChatSessionMember> result;
|
||||||
|
result r(_db->query<ChatSessionMember>(query::session_id == ssid));
|
||||||
|
for (result::iterator i(r.begin()); i != r.end(); ++i) {
|
||||||
|
res.push_back(i->user_id());
|
||||||
|
}
|
||||||
|
trans.commit();
|
||||||
|
}catch (std::exception &e) {
|
||||||
|
LOG_ERROR("获取会话成员失败:{}-{}!", ssid, e.what());
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::shared_ptr<odb::core::database> _db;
|
||||||
|
};
|
||||||
|
}
|
||||||
86
common/mysql_message.hpp
Normal file
86
common/mysql_message.hpp
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
#include "mysql.hpp"
|
||||||
|
#include "message.hxx"
|
||||||
|
#include "message-odb.hxx"
|
||||||
|
|
||||||
|
namespace bite_im {
|
||||||
|
class MessageTable {
|
||||||
|
public:
|
||||||
|
using ptr = std::shared_ptr<MessageTable>;
|
||||||
|
MessageTable(const std::shared_ptr<odb::core::database> &db): _db(db){}
|
||||||
|
~MessageTable(){}
|
||||||
|
bool insert(Message &msg) {
|
||||||
|
try {
|
||||||
|
odb::transaction trans(_db->begin());
|
||||||
|
_db->persist(msg);
|
||||||
|
trans.commit();
|
||||||
|
}catch (std::exception &e) {
|
||||||
|
LOG_ERROR("新增消息失败 {}:{}!", msg.message_id(),e.what());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool remove(const std::string &ssid) {
|
||||||
|
try {
|
||||||
|
odb::transaction trans(_db->begin());
|
||||||
|
typedef odb::query<Message> query;
|
||||||
|
typedef odb::result<Message> result;
|
||||||
|
_db->erase_query<Message>(query::session_id == ssid);
|
||||||
|
trans.commit();
|
||||||
|
}catch (std::exception &e) {
|
||||||
|
LOG_ERROR("删除会话所有消息失败 {}:{}!", ssid, e.what());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Message> recent(const std::string &ssid, int count) {
|
||||||
|
std::vector<Message> res;
|
||||||
|
try {
|
||||||
|
odb::transaction trans(_db->begin());
|
||||||
|
typedef odb::query<Message> query;
|
||||||
|
typedef odb::result<Message> result;
|
||||||
|
//本次查询是以ssid作为过滤条件,然后进行以时间字段进行逆序,通过limit
|
||||||
|
// session_id='xx' order by create_time desc limit count;
|
||||||
|
std::stringstream cond;
|
||||||
|
cond << "session_id='" << ssid << "' ";
|
||||||
|
cond << "order by create_time desc limit " << count;
|
||||||
|
result r(_db->query<Message>(cond.str()));
|
||||||
|
for (result::iterator i(r.begin()); i != r.end(); ++i) {
|
||||||
|
res.push_back(*i);
|
||||||
|
}
|
||||||
|
std::reverse(res.begin(), res.end());
|
||||||
|
trans.commit();
|
||||||
|
}catch (std::exception &e) {
|
||||||
|
LOG_ERROR("获取最近消息失败:{}-{}-{}!", ssid, count, e.what());
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Message> range(const std::string &ssid,
|
||||||
|
boost::posix_time::ptime &stime,
|
||||||
|
boost::posix_time::ptime &etime) {
|
||||||
|
std::vector<Message> res;
|
||||||
|
try {
|
||||||
|
odb::transaction trans(_db->begin());
|
||||||
|
typedef odb::query<Message> query;
|
||||||
|
typedef odb::result<Message> result;
|
||||||
|
//获取指定会话指定时间段的信息
|
||||||
|
result r(_db->query<Message>(query::session_id == ssid &&
|
||||||
|
query::create_time >= stime &&
|
||||||
|
query::create_time <= etime));
|
||||||
|
for (result::iterator i(r.begin()); i != r.end(); ++i) {
|
||||||
|
res.push_back(*i);
|
||||||
|
}
|
||||||
|
trans.commit();
|
||||||
|
}catch (std::exception &e) {
|
||||||
|
LOG_ERROR("获取区间消息失败:{}-{}:{}-{}!", ssid,
|
||||||
|
boost::posix_time::to_simple_string(stime),
|
||||||
|
boost::posix_time::to_simple_string(etime), e.what());
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::shared_ptr<odb::core::database> _db;
|
||||||
|
};
|
||||||
|
}
|
||||||
78
common/mysql_relation.hpp
Normal file
78
common/mysql_relation.hpp
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "mysql.hpp"
|
||||||
|
#include "relation.hxx"
|
||||||
|
#include "relation-odb.hxx"
|
||||||
|
|
||||||
|
namespace bite_im {
|
||||||
|
class RelationTable {
|
||||||
|
public:
|
||||||
|
using ptr = std::shared_ptr<RelationTable>;
|
||||||
|
RelationTable(const std::shared_ptr<odb::core::database> &db) : _db(db){}
|
||||||
|
//新增关系信息
|
||||||
|
bool insert(const std::string &uid, const std::string &pid) {
|
||||||
|
//{1,2} {2,1}
|
||||||
|
try {
|
||||||
|
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) {
|
||||||
|
LOG_ERROR("新增用户好友关系信息失败 {}-{}:{}!", uid, pid, e.what());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//移除关系信息
|
||||||
|
bool remove(const std::string &uid, const std::string &pid) {
|
||||||
|
try {
|
||||||
|
odb::transaction trans(_db->begin());
|
||||||
|
typedef odb::query<Relation> query;
|
||||||
|
typedef odb::result<Relation> result;
|
||||||
|
_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) {
|
||||||
|
LOG_ERROR("删除好友关系信息失败 {}-{}:{}!", uid, pid, e.what());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return 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());
|
||||||
|
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;
|
||||||
|
result r(_db->query<Relation>(query::user_id == uid));
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::shared_ptr<odb::core::database> _db;
|
||||||
|
};
|
||||||
|
}
|
||||||
102
common/mysql_user.hpp
Normal file
102
common/mysql_user.hpp
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
#include "mysql.hpp"
|
||||||
|
#include "user.hxx"
|
||||||
|
#include "user-odb.hxx"
|
||||||
|
|
||||||
|
namespace bite_im {
|
||||||
|
class UserTable {
|
||||||
|
public:
|
||||||
|
using ptr = std::shared_ptr<UserTable>;
|
||||||
|
UserTable(const std::shared_ptr<odb::core::database> &db):_db(db){}
|
||||||
|
bool insert(const std::shared_ptr<User> &user) {
|
||||||
|
try {
|
||||||
|
odb::transaction trans(_db->begin());
|
||||||
|
_db->persist(*user);
|
||||||
|
trans.commit();
|
||||||
|
}catch (std::exception &e) {
|
||||||
|
LOG_ERROR("新增用户失败 {}:{}!", user->nickname(),e.what());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool update(const std::shared_ptr<User> &user) {
|
||||||
|
try {
|
||||||
|
odb::transaction trans(_db->begin());
|
||||||
|
_db->update(*user);
|
||||||
|
trans.commit();
|
||||||
|
}catch (std::exception &e) {
|
||||||
|
LOG_ERROR("更新用户失败 {}:{}!", user->nickname(), e.what());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
std::shared_ptr<User> select_by_nickname(const std::string &nickname) {
|
||||||
|
std::shared_ptr<User> res;
|
||||||
|
try {
|
||||||
|
odb::transaction trans(_db->begin());
|
||||||
|
typedef odb::query<User> query;
|
||||||
|
typedef odb::result<User> result;
|
||||||
|
res.reset(_db->query_one<User>(query::nickname == nickname));
|
||||||
|
trans.commit();
|
||||||
|
}catch (std::exception &e) {
|
||||||
|
LOG_ERROR("通过昵称查询用户失败 {}:{}!", nickname, e.what());
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
std::shared_ptr<User> select_by_phone(const std::string &phone) {
|
||||||
|
std::shared_ptr<User> res;
|
||||||
|
try {
|
||||||
|
odb::transaction trans(_db->begin());
|
||||||
|
typedef odb::query<User> query;
|
||||||
|
typedef odb::result<User> result;
|
||||||
|
res.reset(_db->query_one<User>(query::phone == phone));
|
||||||
|
trans.commit();
|
||||||
|
}catch (std::exception &e) {
|
||||||
|
LOG_ERROR("通过手机号查询用户失败 {}:{}!", phone, e.what());
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
std::shared_ptr<User> select_by_id(const std::string &user_id) {
|
||||||
|
std::shared_ptr<User> res;
|
||||||
|
try {
|
||||||
|
odb::transaction trans(_db->begin());
|
||||||
|
typedef odb::query<User> query;
|
||||||
|
typedef odb::result<User> result;
|
||||||
|
res.reset(_db->query_one<User>(query::user_id == user_id));
|
||||||
|
trans.commit();
|
||||||
|
}catch (std::exception &e) {
|
||||||
|
LOG_ERROR("通过用户ID查询用户失败 {}:{}!", user_id, e.what());
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
std::vector<User> select_multi_users(const std::vector<std::string> &id_list) {
|
||||||
|
// select * from user where id in ('id1', 'id2', ...)
|
||||||
|
if (id_list.empty()) {
|
||||||
|
return std::vector<User>();
|
||||||
|
}
|
||||||
|
std::vector<User> res;
|
||||||
|
try {
|
||||||
|
odb::transaction trans(_db->begin());
|
||||||
|
typedef odb::query<User> query;
|
||||||
|
typedef odb::result<User> result;
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "user_id in (";
|
||||||
|
for (const auto &id : id_list) {
|
||||||
|
ss << "'" << id << "',";
|
||||||
|
}
|
||||||
|
std::string condition = ss.str();
|
||||||
|
condition.pop_back();
|
||||||
|
condition += ")";
|
||||||
|
result r(_db->query<User>(condition));
|
||||||
|
for (result::iterator i(r.begin()); i != r.end(); ++i) {
|
||||||
|
res.push_back(*i);
|
||||||
|
}
|
||||||
|
trans.commit();
|
||||||
|
}catch (std::exception &e) {
|
||||||
|
LOG_ERROR("通过用户ID批量查询用户失败:{}!", e.what());
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::shared_ptr<odb::core::database> _db;
|
||||||
|
};
|
||||||
|
}
|
||||||
106
common/rabbitmq.hpp
Normal file
106
common/rabbitmq.hpp
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <ev.h>
|
||||||
|
#include <amqpcpp.h>
|
||||||
|
#include <amqpcpp/libev.h>
|
||||||
|
#include <openssl/ssl.h>
|
||||||
|
#include <openssl/opensslv.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <functional>
|
||||||
|
#include "logger.hpp"
|
||||||
|
|
||||||
|
namespace bite_im{
|
||||||
|
class MQClient {
|
||||||
|
public:
|
||||||
|
using MessageCallback = std::function<void(const char*, size_t)>;
|
||||||
|
using ptr = std::shared_ptr<MQClient>;
|
||||||
|
MQClient(const std::string &user,
|
||||||
|
const std::string passwd,
|
||||||
|
const std::string host) {
|
||||||
|
_loop = EV_DEFAULT;
|
||||||
|
_handler = std::make_unique<AMQP::LibEvHandler>(_loop);
|
||||||
|
//amqp://root:123456@127.0.0.1:5672/
|
||||||
|
std::string url = "amqp://" + user + ":" + passwd + "@" + host + "/";
|
||||||
|
AMQP::Address address(url);
|
||||||
|
_connection = std::make_unique<AMQP::TcpConnection>(_handler.get(), address);
|
||||||
|
_channel = std::make_unique<AMQP::TcpChannel>(_connection.get());
|
||||||
|
|
||||||
|
_loop_thread = std::thread([this]() {
|
||||||
|
ev_run(_loop, 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
~MQClient() {
|
||||||
|
ev_async_init(&_async_watcher, watcher_callback);
|
||||||
|
ev_async_start(_loop, &_async_watcher);
|
||||||
|
ev_async_send(_loop, &_async_watcher);
|
||||||
|
_loop_thread.join();
|
||||||
|
_loop = nullptr;
|
||||||
|
}
|
||||||
|
void declareComponents(const std::string &exchange,
|
||||||
|
const std::string &queue,
|
||||||
|
const std::string &routing_key = "routing_key",
|
||||||
|
AMQP::ExchangeType echange_type = AMQP::ExchangeType::direct) {
|
||||||
|
_channel->declareExchange(exchange, echange_type)
|
||||||
|
.onError([](const char *message) {
|
||||||
|
LOG_ERROR("声明交换机失败:{}", message);
|
||||||
|
exit(0);
|
||||||
|
})
|
||||||
|
.onSuccess([exchange](){
|
||||||
|
LOG_ERROR("{} 交换机创建成功!", exchange);
|
||||||
|
});
|
||||||
|
_channel->declareQueue(queue)
|
||||||
|
.onError([](const char *message) {
|
||||||
|
LOG_ERROR("声明队列失败:{}", message);
|
||||||
|
exit(0);
|
||||||
|
})
|
||||||
|
.onSuccess([queue](){
|
||||||
|
LOG_ERROR("{} 队列创建成功!", queue);
|
||||||
|
});
|
||||||
|
//6. 针对交换机和队列进行绑定
|
||||||
|
_channel->bindQueue(exchange, queue, routing_key)
|
||||||
|
.onError([exchange, queue](const char *message) {
|
||||||
|
LOG_ERROR("{} - {} 绑定失败:", exchange, queue);
|
||||||
|
exit(0);
|
||||||
|
})
|
||||||
|
.onSuccess([exchange, queue, routing_key](){
|
||||||
|
LOG_ERROR("{} - {} - {} 绑定成功!", exchange, queue, routing_key);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool publish(const std::string &exchange,
|
||||||
|
const std::string &msg,
|
||||||
|
const std::string &routing_key = "routing_key") {
|
||||||
|
LOG_DEBUG("向交换机 {}-{} 发布消息!", exchange, routing_key);
|
||||||
|
bool ret = _channel->publish(exchange, routing_key, msg);
|
||||||
|
if (ret == false) {
|
||||||
|
LOG_ERROR("{} 发布消息失败:", exchange);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
void consume(const std::string &queue, const MessageCallback &cb) {
|
||||||
|
LOG_DEBUG("开始订阅 {} 队列消息!", queue);
|
||||||
|
_channel->consume(queue, "consume-tag") //返回值 DeferredConsumer
|
||||||
|
.onReceived([this, cb](const AMQP::Message &message,
|
||||||
|
uint64_t deliveryTag,
|
||||||
|
bool redelivered) {
|
||||||
|
cb(message.body(), message.bodySize());
|
||||||
|
_channel->ack(deliveryTag);
|
||||||
|
})
|
||||||
|
.onError([queue](const char *message){
|
||||||
|
LOG_ERROR("订阅 {} 队列消息失败: {}", queue, message);
|
||||||
|
exit(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
static void watcher_callback(struct ev_loop *loop, ev_async *watcher, int32_t revents) {
|
||||||
|
ev_break(loop, EVBREAK_ALL);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
struct ev_async _async_watcher;
|
||||||
|
struct ev_loop *_loop;
|
||||||
|
std::unique_ptr<AMQP::LibEvHandler> _handler;
|
||||||
|
std::unique_ptr<AMQP::TcpConnection> _connection;
|
||||||
|
std::unique_ptr<AMQP::TcpChannel> _channel;
|
||||||
|
std::thread _loop_thread;
|
||||||
|
};
|
||||||
|
}
|
||||||
262
common/sendemail.hpp
Normal file
262
common/sendemail.hpp
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <chrono>
|
||||||
|
#include <random>
|
||||||
|
#include <ctime>
|
||||||
|
#include <cstring> // for memset
|
||||||
|
#include <openssl/ssl.h>
|
||||||
|
#include <openssl/err.h>
|
||||||
|
#include <openssl/buffer.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
class SendEmail
|
||||||
|
{
|
||||||
|
// 请确保在链接器设置中加入 libssl.lib 与 libcrypto.lib(或通过 vcpkg 自动集成)
|
||||||
|
public:
|
||||||
|
using ptr = std::shared_ptr<SendEmail>;
|
||||||
|
|
||||||
|
// 辅助函数:通过 SSL 发送数据,并检查返回值
|
||||||
|
bool sendSSL(SSL* ssl, const std::string& msg, const char* label)
|
||||||
|
{
|
||||||
|
int bytesSent = SSL_write(ssl, msg.c_str(), static_cast<int>(msg.length()));
|
||||||
|
if (bytesSent <= 0) {
|
||||||
|
std::cerr << "Failed to send " << label << " message via SSL." << std::endl;
|
||||||
|
ERR_print_errors_fp(stderr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string base64_encode(const std::string& input)
|
||||||
|
{
|
||||||
|
BIO* bmem = BIO_new(BIO_s_mem());
|
||||||
|
BIO* b64 = BIO_new(BIO_f_base64());
|
||||||
|
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
|
||||||
|
b64 = BIO_push(b64, bmem);
|
||||||
|
BIO_write(b64, input.c_str(), static_cast<int>(input.length()));
|
||||||
|
BIO_flush(b64);
|
||||||
|
|
||||||
|
BUF_MEM* bptr;
|
||||||
|
BIO_get_mem_ptr(b64, &bptr);
|
||||||
|
|
||||||
|
std::string result(bptr->data, bptr->length);
|
||||||
|
BIO_free_all(b64);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 辅助函数:通过 SSL 接收数据,并打印输出
|
||||||
|
bool recvSSL(SSL* ssl, char* buff, int buffSize, const char* label)
|
||||||
|
{
|
||||||
|
int bytesReceived = SSL_read(ssl, buff, buffSize);
|
||||||
|
if (bytesReceived <= 0) {
|
||||||
|
std::cerr << "Failed to receive " << label << " message via SSL." << std::endl;
|
||||||
|
ERR_print_errors_fp(stderr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
buff[bytesReceived] = '\0';
|
||||||
|
std::cout << label << " response: " << buff << std::endl;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发送邮件到指定邮箱
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="收件人邮箱"></param>
|
||||||
|
/// <returns>成功 = true,失败 = false</returns>
|
||||||
|
bool SEND_email(const std::string& email)
|
||||||
|
{
|
||||||
|
// 1. DNS解析 smtp.qq.com(使用端口 465)
|
||||||
|
struct addrinfo hints = {};
|
||||||
|
hints.ai_family = AF_INET; // IPv4
|
||||||
|
hints.ai_socktype = SOCK_STREAM; // TCP
|
||||||
|
|
||||||
|
struct addrinfo* addrResult = nullptr;
|
||||||
|
int ret = getaddrinfo("smtp.qq.com", "465", &hints, &addrResult);
|
||||||
|
if (ret != 0 || addrResult == nullptr) {
|
||||||
|
std::cerr << "DNS resolution failed: " << gai_strerror(ret) << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 建立 TCP 连接
|
||||||
|
int sock = socket(addrResult->ai_family, addrResult->ai_socktype, addrResult->ai_protocol);
|
||||||
|
if (sock == -1) {
|
||||||
|
std::cerr << "Socket creation failed!" << std::endl;
|
||||||
|
freeaddrinfo(addrResult);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connect(sock, addrResult->ai_addr, addrResult->ai_addrlen) != 0) {
|
||||||
|
std::cerr << "Connection failed!" << std::endl;
|
||||||
|
close(sock);
|
||||||
|
freeaddrinfo(addrResult);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
freeaddrinfo(addrResult);
|
||||||
|
|
||||||
|
// 3. 初始化 OpenSSL
|
||||||
|
SSL_library_init();
|
||||||
|
SSL_load_error_strings();
|
||||||
|
OpenSSL_add_all_algorithms();
|
||||||
|
|
||||||
|
const SSL_METHOD* method = TLS_client_method();
|
||||||
|
SSL_CTX* sslCtx = SSL_CTX_new(method);
|
||||||
|
if (!sslCtx) {
|
||||||
|
std::cerr << "Unable to create SSL context." << std::endl;
|
||||||
|
close(sock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SSL* ssl = SSL_new(sslCtx);
|
||||||
|
if (!ssl) {
|
||||||
|
std::cerr << "Unable to create SSL structure." << std::endl;
|
||||||
|
SSL_CTX_free(sslCtx);
|
||||||
|
close(sock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绑定 socket 到 SSL
|
||||||
|
SSL_set_fd(ssl, sock);
|
||||||
|
if (SSL_connect(ssl) <= 0) {
|
||||||
|
std::cerr << "SSL_connect failed." << std::endl;
|
||||||
|
ERR_print_errors_fp(stderr);
|
||||||
|
SSL_free(ssl);
|
||||||
|
SSL_CTX_free(sslCtx);
|
||||||
|
close(sock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 读取服务器欢迎信息
|
||||||
|
char buff[2048] = { 0 };
|
||||||
|
if (!recvSSL(ssl, buff, sizeof(buff) - 1, "Server")) {
|
||||||
|
SSL_free(ssl);
|
||||||
|
SSL_CTX_free(sslCtx);
|
||||||
|
close(sock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 发送 EHLO 命令
|
||||||
|
std::string sendMsg = "EHLO localhost\r\n";
|
||||||
|
if (!sendSSL(ssl, sendMsg, "EHLO") || !recvSSL(ssl, buff, sizeof(buff) - 1, "EHLO")) {
|
||||||
|
SSL_free(ssl);
|
||||||
|
SSL_CTX_free(sslCtx);
|
||||||
|
close(sock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. 认证流程:AUTH LOGIN
|
||||||
|
sendMsg = "AUTH LOGIN\r\n";
|
||||||
|
if (!sendSSL(ssl, sendMsg, "AUTH") || !recvSSL(ssl, buff, sizeof(buff) - 1, "AUTH")) {
|
||||||
|
SSL_free(ssl);
|
||||||
|
SSL_CTX_free(sslCtx);
|
||||||
|
close(sock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. 发送用户名(Base64编码后的)
|
||||||
|
std::string username = base64_encode("zxiao_xin@qq.com") + "\r\n";
|
||||||
|
if (!sendSSL(ssl, username, "Username") || !recvSSL(ssl, buff, sizeof(buff) - 1, "Username")) {
|
||||||
|
SSL_free(ssl);
|
||||||
|
SSL_CTX_free(sslCtx);
|
||||||
|
close(sock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8. 发送密码(Base64编码后的)
|
||||||
|
std::string password = base64_encode("ydfslvabdryvejai") + "\r\n";
|
||||||
|
if (!sendSSL(ssl, password, "Password") || !recvSSL(ssl, buff, sizeof(buff) - 1, "Password")) {
|
||||||
|
SSL_free(ssl);
|
||||||
|
SSL_CTX_free(sslCtx);
|
||||||
|
close(sock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 9. 设置发件人
|
||||||
|
sendMsg = "MAIL FROM:<zxiao_xin@qq.com>\r\n";
|
||||||
|
if (!sendSSL(ssl, sendMsg, "MAIL FROM") || !recvSSL(ssl, buff, sizeof(buff) - 1, "MAIL FROM")) {
|
||||||
|
SSL_free(ssl);
|
||||||
|
SSL_CTX_free(sslCtx);
|
||||||
|
close(sock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 10. 设置收件人
|
||||||
|
sendMsg = "RCPT TO:<" + email + ">\r\n";
|
||||||
|
if (!sendSSL(ssl, sendMsg, "RCPT TO") || !recvSSL(ssl, buff, sizeof(buff) - 1, "RCPT TO")) {
|
||||||
|
SSL_free(ssl);
|
||||||
|
SSL_CTX_free(sslCtx);
|
||||||
|
close(sock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查回复是否为成功代码
|
||||||
|
std::string s = buff;
|
||||||
|
s = s.substr(0, 3);
|
||||||
|
if (s != "250") {
|
||||||
|
SSL_free(ssl);
|
||||||
|
SSL_CTX_free(sslCtx);
|
||||||
|
close(sock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 11. 命令 DATA
|
||||||
|
sendMsg = "DATA\r\n";
|
||||||
|
if (!sendSSL(ssl, sendMsg, "DATA") || !recvSSL(ssl, buff, sizeof(buff) - 1, "DATA")) {
|
||||||
|
SSL_free(ssl);
|
||||||
|
SSL_CTX_free(sslCtx);
|
||||||
|
close(sock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 12. 生成验证码
|
||||||
|
std::default_random_engine engine(static_cast<unsigned int>(std::time(nullptr)));
|
||||||
|
std::uniform_int_distribution<int> distribution(10000, 99999);
|
||||||
|
std::string verificationCode = std::to_string(distribution(engine));
|
||||||
|
|
||||||
|
_verifyCode = verificationCode;
|
||||||
|
|
||||||
|
// 13. 构造邮件头和正文
|
||||||
|
sendMsg =
|
||||||
|
"From: \"Mysterious系统\" <zxiao_xin@qq.com>\r\n"
|
||||||
|
"Reply-To: \"请勿回复\" <no-reply@qq.com>\r\n"
|
||||||
|
"To: <" + email + ">\r\n"
|
||||||
|
"Subject: Mysterious验证码\r\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\r\n"
|
||||||
|
"\r\n"
|
||||||
|
"这是您的验证码: " + verificationCode + "\r\n"
|
||||||
|
"用于身份验证,请勿泄露。如非本人操作,请忽略此邮件。\r\n"
|
||||||
|
"\r\n.\r\n";
|
||||||
|
|
||||||
|
if (!sendSSL(ssl, sendMsg, "Email DATA") || !recvSSL(ssl, buff, sizeof(buff) - 1, "DATA send")) {
|
||||||
|
SSL_free(ssl);
|
||||||
|
SSL_CTX_free(sslCtx);
|
||||||
|
close(sock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 14. 命令 QUIT
|
||||||
|
sendMsg = "QUIT\r\n";
|
||||||
|
sendSSL(ssl, sendMsg, "QUIT");
|
||||||
|
recvSSL(ssl, buff, sizeof(buff) - 1, "QUIT");
|
||||||
|
|
||||||
|
// 15. 清理资源
|
||||||
|
SSL_shutdown(ssl);
|
||||||
|
SSL_free(ssl);
|
||||||
|
SSL_CTX_free(sslCtx);
|
||||||
|
close(sock);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string getVerifyCode() {
|
||||||
|
return _verifyCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// std::string _email;
|
||||||
|
std::string _verifyCode;
|
||||||
|
};
|
||||||
|
|
||||||
86
common/utils.hpp
Normal file
86
common/utils.hpp
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
//实现项目中一些公共的工具类接口
|
||||||
|
//1. 生成一个唯一ID的接口
|
||||||
|
//2. 文件的读写操作接口
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <atomic>
|
||||||
|
#include <random>
|
||||||
|
#include <iomanip>
|
||||||
|
#include "logger.hpp"
|
||||||
|
|
||||||
|
namespace bite_im {
|
||||||
|
|
||||||
|
std::string uuid() {
|
||||||
|
//生成一个由16位随机字符组成的字符串作为唯一ID
|
||||||
|
// 1. 生成6个0~255之间的随机数字(1字节-转换为16进制字符)--生成12位16进制字符
|
||||||
|
std::random_device rd;//实例化设备随机数对象-用于生成设备随机数
|
||||||
|
std::mt19937 generator(rd());//以设备随机数为种子,实例化伪随机数对象
|
||||||
|
std::uniform_int_distribution<int> distribution(0,255); //限定数据范围
|
||||||
|
|
||||||
|
std::stringstream ss;
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
if (i == 2) ss << "-";
|
||||||
|
ss << std::setw(2) << std::setfill('0') << std::hex << distribution(generator);
|
||||||
|
}
|
||||||
|
ss << "-";
|
||||||
|
// 2. 通过一个静态变量生成一个2字节的编号数字--生成4位16进制数字字符
|
||||||
|
static std::atomic<short> idx(0);
|
||||||
|
short tmp = idx.fetch_add(1);
|
||||||
|
ss << std::setw(4) << std::setfill('0') << std::hex << tmp;
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string vcode() {
|
||||||
|
std::random_device rd;//实例化设备随机数对象-用于生成设备随机数
|
||||||
|
std::mt19937 generator(rd());//以设备随机数为种子,实例化伪随机数对象
|
||||||
|
std::uniform_int_distribution<int> distribution(0,9); //限定数据范围
|
||||||
|
|
||||||
|
std::stringstream ss;
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
ss << distribution(generator);
|
||||||
|
}
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool readFile(const std::string &filename, std::string &body){
|
||||||
|
//实现读取一个文件的所有数据,放入body中
|
||||||
|
std::ifstream ifs(filename, std::ios::binary | std::ios::in);
|
||||||
|
if (ifs.is_open() == false) {
|
||||||
|
LOG_ERROR("打开文件 {} 失败!", filename);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ifs.seekg(0, std::ios::end);//跳转到文件末尾
|
||||||
|
size_t flen = ifs.tellg(); //获取当前偏移量-- 文件大小
|
||||||
|
ifs.seekg(0, std::ios::beg);//跳转到文件起始
|
||||||
|
body.resize(flen);
|
||||||
|
ifs.read(&body[0], flen);
|
||||||
|
if (ifs.good() == false) {
|
||||||
|
LOG_ERROR("读取文件 {} 数据失败!", filename);
|
||||||
|
ifs.close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ifs.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool writeFile(const std::string &filename, const std::string &body){
|
||||||
|
//实现将body中的数据,写入filename对应的文件中
|
||||||
|
std::ofstream ofs(filename, std::ios::out | std::ios::binary | std::ios::trunc);
|
||||||
|
if (ofs.is_open() == false) {
|
||||||
|
LOG_ERROR("打开文件 {} 失败!", filename);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ofs.write(body.c_str(), body.size());
|
||||||
|
if (ofs.good() == false) {
|
||||||
|
LOG_ERROR("读取文件 {} 数据失败!", filename);
|
||||||
|
ofs.close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ofs.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
11
conf/file_server.conf
Normal file
11
conf/file_server.conf
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
-run_mode=true
|
||||||
|
-log_file=/im/logs/file.log
|
||||||
|
-log_level=0
|
||||||
|
-registry_host=http://10.0.0.235:2379
|
||||||
|
-base_service=/service
|
||||||
|
-instance_name=/file_service/instance
|
||||||
|
-access_host=10.0.0.235:10002
|
||||||
|
-storage_path=/im/data/
|
||||||
|
-listen_port=10002
|
||||||
|
-rpc_timeout=-1
|
||||||
|
-rpc_threads=1
|
||||||
20
conf/friend_server.conf
Normal file
20
conf/friend_server.conf
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
-run_mode=true
|
||||||
|
-log_file=/im/logs/friend.log
|
||||||
|
-log_level=0
|
||||||
|
-registry_host=http://10.0.0.235:2379
|
||||||
|
-instance_name=/friend_service/instance
|
||||||
|
-access_host=10.0.0.235:10006
|
||||||
|
-listen_port=10006
|
||||||
|
-rpc_timeout=-1
|
||||||
|
-rpc_threads=1
|
||||||
|
-base_service=/service
|
||||||
|
-user_service=/service/user_service
|
||||||
|
-message_service=/service/message_service
|
||||||
|
-es_host=http://10.0.0.235:9200/
|
||||||
|
-mysql_host=10.0.0.235
|
||||||
|
-mysql_user=root
|
||||||
|
-mysql_pswd=123456
|
||||||
|
-mysql_db=bite_im
|
||||||
|
-mysql_cset=utf8
|
||||||
|
-mysql_port=0
|
||||||
|
-mysql_pool_count=4
|
||||||
17
conf/gateway_server.conf
Normal file
17
conf/gateway_server.conf
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
-run_mode=true
|
||||||
|
-log_file=/im/logs/gateway.log
|
||||||
|
-log_level=0
|
||||||
|
-http_listen_port=9000
|
||||||
|
-websocket_listen_port=9001
|
||||||
|
-registry_host=http://10.0.0.235:2379
|
||||||
|
-base_service=/service
|
||||||
|
-file_service=/service/file_service
|
||||||
|
-friend_service=/service/friend_service
|
||||||
|
-message_service=/service/message_service
|
||||||
|
-user_service=/service/user_service
|
||||||
|
-speech_service=/service/speech_service
|
||||||
|
-transmite_service=/service/transmite_service
|
||||||
|
-redis_host=10.0.0.235
|
||||||
|
-redis_port=6379
|
||||||
|
-redis_db=0
|
||||||
|
-redis_keep_alive=true
|
||||||
26
conf/message_server.conf
Normal file
26
conf/message_server.conf
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
-run_mode=true
|
||||||
|
-log_file=/im/logs/message.log
|
||||||
|
-log_level=0
|
||||||
|
-registry_host=http://10.0.0.235:2379
|
||||||
|
-instance_name=/message_service/instance
|
||||||
|
-access_host=10.0.0.235:10005
|
||||||
|
-listen_port=10005
|
||||||
|
-rpc_timeout=-1
|
||||||
|
-rpc_threads=1
|
||||||
|
-base_service=/service
|
||||||
|
-user_service=/service/user_service
|
||||||
|
-file_service=/service/file_service
|
||||||
|
-es_host=http://10.0.0.235:9200/
|
||||||
|
-mysql_host=10.0.0.235
|
||||||
|
-mysql_user=root
|
||||||
|
-mysql_pswd=123456
|
||||||
|
-mysql_db=bite_im
|
||||||
|
-mysql_cset=utf8
|
||||||
|
-mysql_port=0
|
||||||
|
-mysql_pool_count=4
|
||||||
|
-mq_user=root
|
||||||
|
-mq_pswd=123456
|
||||||
|
-mq_host=10.0.0.235:5672
|
||||||
|
-mq_msg_exchange=msg_exchange
|
||||||
|
-mq_msg_queue=msg_queue
|
||||||
|
-mq_msg_binding_key=msg_queue
|
||||||
13
conf/speech_server.conf
Normal file
13
conf/speech_server.conf
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
-run_mode=true
|
||||||
|
-log_file=/im/logs/speech.log
|
||||||
|
-log_level=0
|
||||||
|
-registry_host=http://10.0.0.235:2379
|
||||||
|
-instance_name=/speech_service/instance
|
||||||
|
-access_host=10.0.0.235:10001
|
||||||
|
-base_service=/service
|
||||||
|
-listen_port=10001
|
||||||
|
-rpc_timeout=-1
|
||||||
|
-rpc_threads=1
|
||||||
|
-app_id=60694095
|
||||||
|
-api_key=PWn6zlsxym8VwpBW8Or4PPGe
|
||||||
|
-secret_key=Bl0mn74iyAkr3FzCo5TZV7lBq7NYoms9
|
||||||
24
conf/transmite_server.conf
Normal file
24
conf/transmite_server.conf
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
-run_mode=true
|
||||||
|
-log_file=/im/logs/transmite.log
|
||||||
|
-log_level=0
|
||||||
|
-registry_host=http://10.0.0.235:2379
|
||||||
|
-instance_name=/transmite_service/instance
|
||||||
|
-access_host=10.0.0.235:10004
|
||||||
|
-listen_port=10004
|
||||||
|
-rpc_timeout=-1
|
||||||
|
-rpc_threads=1
|
||||||
|
-base_service=/service
|
||||||
|
-user_service=/service/user_service
|
||||||
|
-mysql_host=10.0.0.235
|
||||||
|
-mysql_user=root
|
||||||
|
-mysql_pswd=123456
|
||||||
|
-mysql_db=bite_im
|
||||||
|
-mysql_cset=utf8
|
||||||
|
-mysql_port=0
|
||||||
|
-mysql_pool_count=4
|
||||||
|
-mq_user=root
|
||||||
|
-mq_pswd=123456
|
||||||
|
-mq_host=10.0.0.235:5672
|
||||||
|
-mq_msg_exchange=msg_exchange
|
||||||
|
-mq_msg_queue=msg_queue
|
||||||
|
-mq_msg_binding_key=msg_queue
|
||||||
25
conf/user_server.conf
Normal file
25
conf/user_server.conf
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
-run_mode=true
|
||||||
|
-log_file=/im/logs/user.log
|
||||||
|
-log_level=0
|
||||||
|
-registry_host=http://10.0.0.235:2379
|
||||||
|
-instance_name=/user_service/instance
|
||||||
|
-access_host=10.0.0.235:10003
|
||||||
|
-listen_port=10003
|
||||||
|
-rpc_timeout=-1
|
||||||
|
-rpc_threads=1
|
||||||
|
-base_service=/service
|
||||||
|
-file_service=/service/file_service
|
||||||
|
-es_host=http://10.0.0.235:9200/
|
||||||
|
-mysql_host=10.0.0.235
|
||||||
|
-mysql_user=root
|
||||||
|
-mysql_pswd=123456
|
||||||
|
-mysql_db=bite_im
|
||||||
|
-mysql_cset=utf8
|
||||||
|
-mysql_port=0
|
||||||
|
-mysql_pool_count=4
|
||||||
|
-redis_host=10.0.0.235
|
||||||
|
-redis_port=6379
|
||||||
|
-redis_db=0
|
||||||
|
-redis_keep_alive=true
|
||||||
|
-dms_key_id=LTAI5t6NF7vt499UeqYX6LB9
|
||||||
|
-dms_key_secret=5hx1qvpXHDKfQDk73aJs6j53Q8KcF2
|
||||||
34
depends.sh
Executable file
34
depends.sh
Executable file
@ -0,0 +1,34 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
#传递两个参数:
|
||||||
|
# 1. 可执行程序的路径名
|
||||||
|
# 2. 目录名称 --- 将这个程序的依赖库拷贝到指定目录下
|
||||||
|
declare depends
|
||||||
|
get_depends() {
|
||||||
|
depends=$(ldd $1 | awk '{if (match($3,"/")){print $3}}')
|
||||||
|
#mkdir $2
|
||||||
|
cp -Lr $depends $2
|
||||||
|
}
|
||||||
|
|
||||||
|
get_depends ./gateway/build/gateway_server ./gateway/depends
|
||||||
|
get_depends ./file/build/file_server ./file/depends
|
||||||
|
get_depends ./friend/build/friend_server ./friend/depends
|
||||||
|
get_depends ./message/build/message_server ./message/depends
|
||||||
|
get_depends ./speech/build/speech_server ./speech/depends
|
||||||
|
get_depends ./transmite/build/transmite_server ./transmite/depends
|
||||||
|
get_depends ./user/build/user_server ./user/depends
|
||||||
|
|
||||||
|
cp /bin/nc ./gateway/
|
||||||
|
cp /bin/nc ./file/
|
||||||
|
cp /bin/nc ./friend/
|
||||||
|
cp /bin/nc ./message/
|
||||||
|
cp /bin/nc ./speech/
|
||||||
|
cp /bin/nc ./transmite/
|
||||||
|
cp /bin/nc ./user/
|
||||||
|
get_depends /bin/nc ./gateway/depends
|
||||||
|
get_depends /bin/nc ./file/depends
|
||||||
|
get_depends /bin/nc ./friend/depends
|
||||||
|
get_depends /bin/nc ./message/depends
|
||||||
|
get_depends /bin/nc ./speech/depends
|
||||||
|
get_depends /bin/nc ./user/depends
|
||||||
|
get_depends /bin/nc ./transmite/depends
|
||||||
221
docker-compose.yml
Normal file
221
docker-compose.yml
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
etcd:
|
||||||
|
image: quay.io/coreos/etcd:v3.5.0
|
||||||
|
container_name: etcd-service
|
||||||
|
environment:
|
||||||
|
- ETCD_NAME=etcd-s1
|
||||||
|
- ETCD_DATA_DIR=/var/lib/etcd
|
||||||
|
- ETCD_LISTEN_CLIENT_URLS=http://0.0.0.0:2379
|
||||||
|
- ETCD_ADVERTISE_CLIENT_URLS=http://0.0.0.0:2379
|
||||||
|
volumes:
|
||||||
|
# 1. 希望容器内的程序能够访问宿主机上的文件
|
||||||
|
# 2. 希望容器内程序运行所产生的数据文件能落在宿主机上
|
||||||
|
- ./middle/data/etcd:/var/lib/etcd:rw
|
||||||
|
ports:
|
||||||
|
- 2379:2379
|
||||||
|
restart: always
|
||||||
|
mysql:
|
||||||
|
image: mysql:8.0.42
|
||||||
|
container_name: mysql-service
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: 123456
|
||||||
|
volumes:
|
||||||
|
# 1. 希望容器内的程序能够访问宿主机上的文件
|
||||||
|
# 2. 希望容器内程序运行所产生的数据文件能落在宿主机上
|
||||||
|
- ./sql:/docker-entrypoint-initdb.d/:rw
|
||||||
|
- ./middle/data/mysql:/var/lib/mysql:rw
|
||||||
|
ports:
|
||||||
|
- 3306:3306
|
||||||
|
restart: always
|
||||||
|
redis:
|
||||||
|
image: redis:7.0.15
|
||||||
|
container_name: redis-service
|
||||||
|
volumes:
|
||||||
|
# 1. 希望容器内的程序能够访问宿主机上的文件
|
||||||
|
# 2. 希望容器内程序运行所产生的数据文件能落在宿主机上
|
||||||
|
- ./middle/data/redis:/var/lib/redis:rw
|
||||||
|
ports:
|
||||||
|
- 6379:6379
|
||||||
|
restart: always
|
||||||
|
elasticsearch:
|
||||||
|
image: elasticsearch:7.17.21
|
||||||
|
container_name: elasticsearch-service
|
||||||
|
environment:
|
||||||
|
- "discovery.type=single-node"
|
||||||
|
volumes:
|
||||||
|
# 1. 希望容器内的程序能够访问宿主机上的文件
|
||||||
|
# 2. 希望容器内程序运行所产生的数据文件能落在宿主机上
|
||||||
|
- ./middle/data/elasticsearch:/data:rw
|
||||||
|
ports:
|
||||||
|
- 9200:9200
|
||||||
|
- 9300:9300
|
||||||
|
restart: always
|
||||||
|
rabbitmq:
|
||||||
|
image: rabbitmq:3.10.8
|
||||||
|
container_name: rabbitmq-service
|
||||||
|
environment:
|
||||||
|
RABBITMQ_DEFAULT_USER: root
|
||||||
|
RABBITMQ_DEFAULT_PASS: 123456
|
||||||
|
volumes:
|
||||||
|
# 1. 希望容器内的程序能够访问宿主机上的文件
|
||||||
|
# 2. 希望容器内程序运行所产生的数据文件能落在宿主机上
|
||||||
|
- ./middle/data/rabbitmq:/var/lib/rabbitmq:rw
|
||||||
|
ports:
|
||||||
|
- 5672:5672
|
||||||
|
restart: always
|
||||||
|
|
||||||
|
file_server:
|
||||||
|
build: ./file
|
||||||
|
#image: server-user_server
|
||||||
|
container_name: file_server-service
|
||||||
|
volumes:
|
||||||
|
# 1. 希望容器内的程序能够访问宿主机上的文件
|
||||||
|
# 2. 希望容器内程序运行所产生的数据文件能落在宿主机上
|
||||||
|
# 挂载的信息: entrypoint.sh文件 数据目录(im/logs, im/data), 配置文件
|
||||||
|
- ./conf/file_server.conf:/im/conf/file_server.conf
|
||||||
|
- ./middle/data/logs:/im/logs:rw
|
||||||
|
- ./middle/data/data:/im/data:rw
|
||||||
|
- ./entrypoint.sh:/im/bin/entrypoint.sh
|
||||||
|
ports:
|
||||||
|
- 10002:10002
|
||||||
|
restart: always
|
||||||
|
entrypoint:
|
||||||
|
# 跟dockerfile中的cmd比较类似,都是容器启动后的默认操作--替代dockerfile中的cmd
|
||||||
|
/im/bin/entrypoint.sh -h 10.0.0.235 -p 2379 -c "/im/bin/file_server -flagfile=/im/conf/file_server.conf"
|
||||||
|
depends_on:
|
||||||
|
- etcd
|
||||||
|
friend_server:
|
||||||
|
build: ./friend
|
||||||
|
#image: file-server:v1
|
||||||
|
container_name: friend_server-service
|
||||||
|
volumes:
|
||||||
|
# 1. 希望容器内的程序能够访问宿主机上的文件
|
||||||
|
# 2. 希望容器内程序运行所产生的数据文件能落在宿主机上
|
||||||
|
# 挂载的信息: entrypoint.sh文件 数据目录(im/logs, im/data), 配置文件
|
||||||
|
- ./conf/friend_server.conf:/im/conf/friend_server.conf
|
||||||
|
- ./middle/data/logs:/im/logs:rw
|
||||||
|
- ./middle/data/data:/im/data:rw
|
||||||
|
- ./entrypoint.sh:/im/bin/entrypoint.sh
|
||||||
|
ports:
|
||||||
|
- 10006:10006
|
||||||
|
restart: always
|
||||||
|
depends_on:
|
||||||
|
- etcd
|
||||||
|
- mysql
|
||||||
|
- elasticsearch
|
||||||
|
entrypoint:
|
||||||
|
# 跟dockerfile中的cmd比较类似,都是容器启动后的默认操作--替代dockerfile中的cmd
|
||||||
|
/im/bin/entrypoint.sh -h 10.0.0.235 -p 2379,3306,9200 -c "/im/bin/friend_server -flagfile=/im/conf/friend_server.conf"
|
||||||
|
gateway_server:
|
||||||
|
build: ./gateway
|
||||||
|
#image: file-server:v1
|
||||||
|
container_name: gateway_server-service
|
||||||
|
volumes:
|
||||||
|
# 1. 希望容器内的程序能够访问宿主机上的文件
|
||||||
|
# 2. 希望容器内程序运行所产生的数据文件能落在宿主机上
|
||||||
|
# 挂载的信息: entrypoint.sh文件 数据目录(im/logs, im/data), 配置文件
|
||||||
|
- ./conf/gateway_server.conf:/im/conf/gateway_server.conf
|
||||||
|
- ./middle/data/logs:/im/logs:rw
|
||||||
|
- ./middle/data/data:/im/data:rw
|
||||||
|
- ./entrypoint.sh:/im/bin/entrypoint.sh
|
||||||
|
ports:
|
||||||
|
- 9000:9000
|
||||||
|
- 9001:9001
|
||||||
|
restart: always
|
||||||
|
depends_on:
|
||||||
|
- etcd
|
||||||
|
- redis
|
||||||
|
entrypoint:
|
||||||
|
# 跟dockerfile中的cmd比较类似,都是容器启动后的默认操作--替代dockerfile中的cmd
|
||||||
|
/im/bin/entrypoint.sh -h 10.0.0.235 -p 2379,6379 -c "/im/bin/gateway_server -flagfile=/im/conf/gateway_server.conf"
|
||||||
|
message_server:
|
||||||
|
build: ./message
|
||||||
|
#image: file-server:v1
|
||||||
|
container_name: message_server-service
|
||||||
|
volumes:
|
||||||
|
# 1. 希望容器内的程序能够访问宿主机上的文件
|
||||||
|
# 2. 希望容器内程序运行所产生的数据文件能落在宿主机上
|
||||||
|
# 挂载的信息: entrypoint.sh文件 数据目录(im/logs, im/data), 配置文件
|
||||||
|
- ./conf/message_server.conf:/im/conf/message_server.conf
|
||||||
|
- ./middle/data/logs:/im/logs:rw
|
||||||
|
- ./middle/data/data:/im/data:rw
|
||||||
|
- ./entrypoint.sh:/im/bin/entrypoint.sh
|
||||||
|
ports:
|
||||||
|
- 10005:10005
|
||||||
|
restart: always
|
||||||
|
depends_on:
|
||||||
|
- etcd
|
||||||
|
- mysql
|
||||||
|
- elasticsearch
|
||||||
|
- rabbitmq
|
||||||
|
entrypoint:
|
||||||
|
# 跟dockerfile中的cmd比较类似,都是容器启动后的默认操作--替代dockerfile中的cmd
|
||||||
|
/im/bin/entrypoint.sh -h 10.0.0.235 -p 2379,3306,9200,5672 -c "/im/bin/message_server -flagfile=/im/conf/message_server.conf"
|
||||||
|
speech_server:
|
||||||
|
build: ./speech
|
||||||
|
#image: file-server:v1
|
||||||
|
container_name: speech_server-service
|
||||||
|
volumes:
|
||||||
|
# 1. 希望容器内的程序能够访问宿主机上的文件
|
||||||
|
# 2. 希望容器内程序运行所产生的数据文件能落在宿主机上
|
||||||
|
# 挂载的信息: entrypoint.sh文件 数据目录(im/logs, im/data), 配置文件
|
||||||
|
- ./conf/speech_server.conf:/im/conf/speech_server.conf
|
||||||
|
- ./middle/data/logs:/im/logs:rw
|
||||||
|
- ./middle/data/data:/im/data:rw
|
||||||
|
- ./entrypoint.sh:/im/bin/entrypoint.sh
|
||||||
|
ports:
|
||||||
|
- 10001:10001
|
||||||
|
restart: always
|
||||||
|
depends_on:
|
||||||
|
- etcd
|
||||||
|
entrypoint:
|
||||||
|
# 跟dockerfile中的cmd比较类似,都是容器启动后的默认操作--替代dockerfile中的cmd
|
||||||
|
/im/bin/entrypoint.sh -h 10.0.0.235 -p 2379 -c "/im/bin/speech_server -flagfile=/im/conf/speech_server.conf"
|
||||||
|
transmite_server:
|
||||||
|
build: ./transmite
|
||||||
|
#image: file-server:v1
|
||||||
|
container_name: transmite_server-service
|
||||||
|
volumes:
|
||||||
|
# 1. 希望容器内的程序能够访问宿主机上的文件
|
||||||
|
# 2. 希望容器内程序运行所产生的数据文件能落在宿主机上
|
||||||
|
# 挂载的信息: entrypoint.sh文件 数据目录(im/logs, im/data), 配置文件
|
||||||
|
- ./conf/transmite_server.conf:/im/conf/transmite_server.conf
|
||||||
|
- ./middle/data/logs:/im/logs:rw
|
||||||
|
- ./middle/data/data:/im/data:rw
|
||||||
|
- ./entrypoint.sh:/im/bin/entrypoint.sh
|
||||||
|
ports:
|
||||||
|
- 10004:10004
|
||||||
|
restart: always
|
||||||
|
depends_on:
|
||||||
|
- etcd
|
||||||
|
- mysql
|
||||||
|
- rabbitmq
|
||||||
|
entrypoint:
|
||||||
|
# 跟dockerfile中的cmd比较类似,都是容器启动后的默认操作--替代dockerfile中的cmd
|
||||||
|
/im/bin/entrypoint.sh -h 10.0.0.235 -p 2379,3306,5672 -c "/im/bin/transmite_server -flagfile=/im/conf/transmite_server.conf"
|
||||||
|
user_server:
|
||||||
|
build: ./user
|
||||||
|
#image: file-server:v1
|
||||||
|
container_name: user_server-service
|
||||||
|
volumes:
|
||||||
|
# 1. 希望容器内的程序能够访问宿主机上的文件
|
||||||
|
# 2. 希望容器内程序运行所产生的数据文件能落在宿主机上
|
||||||
|
# 挂载的信息: entrypoint.sh文件 数据目录(im/logs, im/data), 配置文件
|
||||||
|
- ./conf/user_server.conf:/im/conf/user_server.conf
|
||||||
|
- ./middle/data/logs:/im/logs:rw
|
||||||
|
- ./middle/data/data:/im/data:rw
|
||||||
|
- ./entrypoint.sh:/im/bin/entrypoint.sh
|
||||||
|
ports:
|
||||||
|
- 10003:10003
|
||||||
|
restart: always
|
||||||
|
depends_on:
|
||||||
|
- etcd
|
||||||
|
- mysql
|
||||||
|
- redis
|
||||||
|
- elasticsearch
|
||||||
|
entrypoint:
|
||||||
|
# 跟dockerfile中的cmd比较类似,都是容器启动后的默认操作--替代dockerfile中的cmd
|
||||||
|
/im/bin/entrypoint.sh -h 10.0.0.235 -p 2379,3306,5672,9200 -c "/im/bin/user_server -flagfile=/im/conf/user_server.conf"
|
||||||
|
|
||||||
38
entrypoint.sh
Executable file
38
entrypoint.sh
Executable file
@ -0,0 +1,38 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
#./entrypoint.sh -h 127.0.0.1 -p 3306,2379,6379 -c '/im/bin/file_server -flagfile=./xx.conf'
|
||||||
|
|
||||||
|
# 1. 编写一个端口探测函数,端口连接不上则循环等待
|
||||||
|
# wait_for 127.0.0.1 3306
|
||||||
|
wait_for() {
|
||||||
|
while ! nc -z $1 $2
|
||||||
|
do
|
||||||
|
echo "$2 端口连接失败,休眠等待!";
|
||||||
|
sleep 1;
|
||||||
|
done
|
||||||
|
echo "$1:$2 检测成功!";
|
||||||
|
}
|
||||||
|
# 2. 对脚本运行参数进行解析,获取到ip,port,command
|
||||||
|
declare ip
|
||||||
|
declare ports
|
||||||
|
declare command
|
||||||
|
while getopts "h:p:c:" arg
|
||||||
|
do
|
||||||
|
case $arg in
|
||||||
|
h)
|
||||||
|
ip=$OPTARG;;
|
||||||
|
p)
|
||||||
|
ports=$OPTARG;;
|
||||||
|
c)
|
||||||
|
command=$OPTARG;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
# 3. 通过执行脚本进行端口检测
|
||||||
|
# ${port //,/ } 针对port中的内容,以空格替换字符串中的, shell中数组--一种以空格间隔的字符串
|
||||||
|
for port in ${ports//,/ }
|
||||||
|
do
|
||||||
|
wait_for $ip $port
|
||||||
|
done
|
||||||
|
# 4. 执行command
|
||||||
|
# eval 对一个字符串进行二次检测,将其当作命令进行执行
|
||||||
|
eval $command
|
||||||
55
file/CMakeLists.txt
Normal file
55
file/CMakeLists.txt
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
# 1. 添加cmake版本说明
|
||||||
|
cmake_minimum_required(VERSION 3.1.3)
|
||||||
|
# 2. 声明工程名称
|
||||||
|
project(file_server)
|
||||||
|
|
||||||
|
set(target "file_server")
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
|
||||||
|
|
||||||
|
# 3. 检测并生成ODB框架代码
|
||||||
|
# 1. 添加所需的proto映射代码文件名称
|
||||||
|
set(proto_path ${CMAKE_CURRENT_SOURCE_DIR}/../proto)
|
||||||
|
set(proto_files file.proto base.proto)
|
||||||
|
# 2. 检测框架代码文件是否已经生成
|
||||||
|
set(proto_hxx "")
|
||||||
|
set(proto_cxx "")
|
||||||
|
set(proto_srcs "")
|
||||||
|
foreach(proto_file ${proto_files})
|
||||||
|
# 3. 如果没有生成,则预定义生成指令 -- 用于在构建项目之间先生成框架代码
|
||||||
|
string(REPLACE ".proto" ".pb.cc" proto_cc ${proto_file})
|
||||||
|
string(REPLACE ".proto" ".pb.h" proto_hh ${proto_file})
|
||||||
|
if (NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}${proto_cc})
|
||||||
|
add_custom_command(
|
||||||
|
PRE_BUILD
|
||||||
|
COMMAND protoc
|
||||||
|
ARGS --cpp_out=${CMAKE_CURRENT_BINARY_DIR} -I ${proto_path} --experimental_allow_proto3_optional ${proto_path}/${proto_file}
|
||||||
|
DEPENDS ${proto_path}/${proto_file}
|
||||||
|
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${proto_cc}
|
||||||
|
COMMENT "生成Protobuf框架代码文件:" ${CMAKE_CURRENT_BINARY_DIR}/${proto_cc}
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
list(APPEND proto_srcs ${CMAKE_CURRENT_BINARY_DIR}/${proto_cc})
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# 4. 获取源码目录下的所有源码文件
|
||||||
|
set(src_files "")
|
||||||
|
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/source src_files)
|
||||||
|
# 5. 声明目标及依赖
|
||||||
|
add_executable(${target} ${src_files} ${proto_srcs})
|
||||||
|
# 7. 设置需要连接的库
|
||||||
|
target_link_libraries(${target} -lgflags -lspdlog -lfmt -lbrpc -lssl -lcrypto -lprotobuf -lleveldb -letcd-cpp-api -lcpprest -lcurl /usr/lib/x86_64-linux-gnu/libjsoncpp.so.19)
|
||||||
|
|
||||||
|
|
||||||
|
set(test_client "file_client")
|
||||||
|
set(test_files "")
|
||||||
|
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/test test_files)
|
||||||
|
add_executable(${test_client} ${test_files} ${proto_srcs})
|
||||||
|
target_link_libraries(${test_client} -lgtest -lgflags -lspdlog -lfmt -lbrpc -lssl -lcrypto -lprotobuf -lleveldb -letcd-cpp-api -lcpprest -lcurl /usr/lib/x86_64-linux-gnu/libjsoncpp.so.19)
|
||||||
|
|
||||||
|
# 6. 设置头文件默认搜索路径
|
||||||
|
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../common)
|
||||||
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../third/include)
|
||||||
|
|
||||||
|
#8. 设置安装路径
|
||||||
|
INSTALL(TARGETS ${target} ${test_client} RUNTIME DESTINATION bin)
|
||||||
16
file/dockerfile
Normal file
16
file/dockerfile
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# 声明基础经镜像来源
|
||||||
|
FROM debian:12
|
||||||
|
|
||||||
|
# 声明工作目录
|
||||||
|
WORKDIR /im
|
||||||
|
RUN mkdir -p /im/logs &&\
|
||||||
|
mkdir -p /im/data &&\
|
||||||
|
mkdir -p /im/conf &&\
|
||||||
|
mkdir -p /im/bin
|
||||||
|
|
||||||
|
# 将可执行程序依赖,拷贝进镜像
|
||||||
|
COPY ./build/file_server /im/bin/
|
||||||
|
# 将可执行程序文件,拷贝进镜像
|
||||||
|
COPY ./depends /lib/x86_64-linux-gnu/
|
||||||
|
# 设置容器启动的默认操作 ---运行程序
|
||||||
|
CMD /im/bin/file_server -flagfile=/im/conf/file_server.conf
|
||||||
11
file/file_server.conf
Normal file
11
file/file_server.conf
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
-run_mode=true
|
||||||
|
-log_file=/im/logs/file.log
|
||||||
|
-log_level=0
|
||||||
|
-registry_host=http://10.0.0.235:2379
|
||||||
|
-base_service=/service
|
||||||
|
-instance_name=/file_service/instance
|
||||||
|
-access_host=10.0.0.235:10002
|
||||||
|
-storage_path=/im/data/
|
||||||
|
-listen_port=10002
|
||||||
|
-rpc_timeout=-1
|
||||||
|
-rpc_threads=1
|
||||||
33
file/source/file_server.cc
Normal file
33
file/source/file_server.cc
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
//按照流程完成服务器的搭建
|
||||||
|
//1. 参数解析
|
||||||
|
//2. 日志初始化
|
||||||
|
//3. 构造服务器对象,启动服务器
|
||||||
|
#include "file_server.hpp"
|
||||||
|
|
||||||
|
DEFINE_bool(run_mode, false, "程序的运行模式,false-调试; true-发布;");
|
||||||
|
DEFINE_string(log_file, "", "发布模式下,用于指定日志的输出文件");
|
||||||
|
DEFINE_int32(log_level, 0, "发布模式下,用于指定日志输出等级");
|
||||||
|
|
||||||
|
DEFINE_string(registry_host, "http://127.0.0.1:2379", "服务注册中心地址");
|
||||||
|
DEFINE_string(base_service, "/service", "服务监控根目录");
|
||||||
|
DEFINE_string(instance_name, "/file_service/instance", "当前实例名称");
|
||||||
|
DEFINE_string(access_host, "127.0.0.1:10002", "当前实例的外部访问地址");
|
||||||
|
|
||||||
|
DEFINE_string(storage_path, "./data/", "当前实例的外部访问地址");
|
||||||
|
|
||||||
|
DEFINE_int32(listen_port, 10002, "Rpc服务器监听端口");
|
||||||
|
DEFINE_int32(rpc_timeout, -1, "Rpc调用超时时间");
|
||||||
|
DEFINE_int32(rpc_threads, 1, "Rpc的IO线程数量");
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
google::ParseCommandLineFlags(&argc, &argv, true);
|
||||||
|
bite_im::init_logger(FLAGS_run_mode, FLAGS_log_file, FLAGS_log_level);
|
||||||
|
|
||||||
|
bite_im::FileServerBuilder fsb;
|
||||||
|
fsb.make_rpc_server(FLAGS_listen_port, FLAGS_rpc_timeout, FLAGS_rpc_threads, FLAGS_storage_path);
|
||||||
|
fsb.make_reg_object(FLAGS_registry_host, FLAGS_base_service + FLAGS_instance_name, FLAGS_access_host);
|
||||||
|
auto server = fsb.build();
|
||||||
|
server->start();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
190
file/source/file_server.hpp
Normal file
190
file/source/file_server.hpp
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
//实现文件存储子服务
|
||||||
|
//1. 实现文件rpc服务类 --- 实现rpc调用的业务处理接口
|
||||||
|
//2. 实现文件存储子服务的服务器类
|
||||||
|
//3. 实现文件存储子服务类的构造者
|
||||||
|
#include <brpc/server.h>
|
||||||
|
#include <butil/logging.h>
|
||||||
|
|
||||||
|
#include "asr.hpp"
|
||||||
|
#include "etcd.hpp" // 服务注册模块封装
|
||||||
|
#include "logger.hpp" // 日志模块封装
|
||||||
|
#include "utils.hpp" // uuid生成、文件读写等工具函数
|
||||||
|
#include "base.pb.h"
|
||||||
|
#include "file.pb.h"
|
||||||
|
|
||||||
|
namespace bite_im{
|
||||||
|
class FileServiceImpl : public bite_im::FileService {
|
||||||
|
public:
|
||||||
|
FileServiceImpl(const std::string &storage_path):
|
||||||
|
_storage_path(storage_path){
|
||||||
|
umask(0);
|
||||||
|
mkdir(storage_path.c_str(), 0775);
|
||||||
|
if (_storage_path.back() != '/') _storage_path.push_back('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
~FileServiceImpl(){}
|
||||||
|
|
||||||
|
void GetSingleFile(google::protobuf::RpcController* controller,
|
||||||
|
const ::bite_im::GetSingleFileReq* request,
|
||||||
|
::bite_im::GetSingleFileRsp* response,
|
||||||
|
::google::protobuf::Closure* done) {
|
||||||
|
brpc::ClosureGuard rpc_guard(done);
|
||||||
|
response->set_request_id(request->request_id());
|
||||||
|
//1. 取出请求中的文件ID(起始就是文件名)
|
||||||
|
std::string fid = request->file_id();
|
||||||
|
std::string filename = _storage_path + fid;
|
||||||
|
//2. 将文件ID作为文件名,读取文件数据
|
||||||
|
std::string body;
|
||||||
|
bool ret = readFile(filename, body);
|
||||||
|
if (ret == false) {
|
||||||
|
response->set_success(false);
|
||||||
|
response->set_errmsg("读取文件数据失败!");
|
||||||
|
LOG_ERROR("{} 读取文件数据失败!", request->request_id());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//3. 组织响应
|
||||||
|
response->set_success(true);
|
||||||
|
response->mutable_file_data()->set_file_id(fid);
|
||||||
|
response->mutable_file_data()->set_file_content(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetMultiFile(google::protobuf::RpcController* controller,
|
||||||
|
const ::bite_im::GetMultiFileReq* request,
|
||||||
|
::bite_im::GetMultiFileRsp* response,
|
||||||
|
::google::protobuf::Closure* done) {
|
||||||
|
brpc::ClosureGuard rpc_guard(done);
|
||||||
|
response->set_request_id(request->request_id());
|
||||||
|
// 循环取出请求中的文件ID,读取文件数据进行填充
|
||||||
|
for (int i = 0; i < request->file_id_list_size(); i++) {
|
||||||
|
std::string fid = request->file_id_list(i);
|
||||||
|
std::string filename = _storage_path + fid;
|
||||||
|
std::string body;
|
||||||
|
bool ret = readFile(filename, body);
|
||||||
|
if (ret == false) {
|
||||||
|
response->set_success(false);
|
||||||
|
response->set_errmsg("读取文件数据失败!");
|
||||||
|
LOG_ERROR("{} 读取文件数据失败!", request->request_id());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FileDownloadData data;
|
||||||
|
data.set_file_id(fid);
|
||||||
|
data.set_file_content(body);
|
||||||
|
response->mutable_file_data()->insert({fid, data});
|
||||||
|
}
|
||||||
|
response->set_success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PutSingleFile(google::protobuf::RpcController* controller,
|
||||||
|
const ::bite_im::PutSingleFileReq* request,
|
||||||
|
::bite_im::PutSingleFileRsp* response,
|
||||||
|
::google::protobuf::Closure* done) {
|
||||||
|
brpc::ClosureGuard rpc_guard(done);
|
||||||
|
response->set_request_id(request->request_id());
|
||||||
|
//1. 为文件生成一个唯一uudi作为文件名 以及 文件ID
|
||||||
|
std::string fid = uuid();
|
||||||
|
std::string filename = _storage_path + fid;
|
||||||
|
//2. 取出请求中的文件数据,进行文件数据写入
|
||||||
|
bool ret = writeFile(filename, request->file_data().file_content());
|
||||||
|
if (ret == false) {
|
||||||
|
response->set_success(false);
|
||||||
|
response->set_errmsg("读取文件数据失败!");
|
||||||
|
LOG_ERROR("{} 写入文件数据失败!", request->request_id());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//3. 组织响应
|
||||||
|
response->set_success(true);
|
||||||
|
response->mutable_file_info()->set_file_id(fid);
|
||||||
|
response->mutable_file_info()->set_file_size(request->file_data().file_size());
|
||||||
|
response->mutable_file_info()->set_file_name(request->file_data().file_name());
|
||||||
|
}
|
||||||
|
|
||||||
|
void PutMultiFile(google::protobuf::RpcController* controller,
|
||||||
|
const ::bite_im::PutMultiFileReq* request,
|
||||||
|
::bite_im::PutMultiFileRsp* response,
|
||||||
|
::google::protobuf::Closure* done) {
|
||||||
|
brpc::ClosureGuard rpc_guard(done);
|
||||||
|
response->set_request_id(request->request_id());
|
||||||
|
for (int i = 0; i < request->file_data_size(); i++) {
|
||||||
|
std::string fid = uuid();
|
||||||
|
std::string filename = _storage_path + fid;
|
||||||
|
bool ret = writeFile(filename, request->file_data(i).file_content());
|
||||||
|
if (ret == false) {
|
||||||
|
response->set_success(false);
|
||||||
|
response->set_errmsg("读取文件数据失败!");
|
||||||
|
LOG_ERROR("{} 写入文件数据失败!", request->request_id());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bite_im::FileMessageInfo *info = response->add_file_info();
|
||||||
|
info->set_file_id(fid);
|
||||||
|
info->set_file_size(request->file_data(i).file_size());
|
||||||
|
info->set_file_name(request->file_data(i).file_name());
|
||||||
|
}
|
||||||
|
response->set_success(true);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::string _storage_path;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FileServer {
|
||||||
|
public:
|
||||||
|
using ptr = std::shared_ptr<FileServer>;
|
||||||
|
FileServer(const Registry::ptr ®_client,
|
||||||
|
const std::shared_ptr<brpc::Server> &server):
|
||||||
|
_reg_client(reg_client),
|
||||||
|
_rpc_server(server){}
|
||||||
|
~FileServer(){}
|
||||||
|
//搭建RPC服务器,并启动服务器
|
||||||
|
void start() {
|
||||||
|
_rpc_server->RunUntilAskedToQuit();
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
Registry::ptr _reg_client;
|
||||||
|
std::shared_ptr<brpc::Server> _rpc_server;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FileServerBuilder {
|
||||||
|
public:
|
||||||
|
//用于构造服务注册客户端对象
|
||||||
|
void make_reg_object(const std::string ®_host,
|
||||||
|
const std::string &service_name,
|
||||||
|
const std::string &access_host) {
|
||||||
|
_reg_client = std::make_shared<Registry>(reg_host);
|
||||||
|
_reg_client->registry(service_name, access_host);
|
||||||
|
}
|
||||||
|
//构造RPC服务器对象
|
||||||
|
void make_rpc_server(uint16_t port, int32_t timeout,
|
||||||
|
uint8_t num_threads, const std::string &path = "./data/") {
|
||||||
|
_rpc_server = std::make_shared<brpc::Server>();
|
||||||
|
FileServiceImpl *file_service = new FileServiceImpl(path);
|
||||||
|
int ret = _rpc_server->AddService(file_service,
|
||||||
|
brpc::ServiceOwnership::SERVER_OWNS_SERVICE);
|
||||||
|
if (ret == -1) {
|
||||||
|
LOG_ERROR("添加Rpc服务失败!");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
brpc::ServerOptions options;
|
||||||
|
options.idle_timeout_sec = timeout;
|
||||||
|
options.num_threads = num_threads;
|
||||||
|
ret = _rpc_server->Start(port, &options);
|
||||||
|
if (ret == -1) {
|
||||||
|
LOG_ERROR("服务启动失败!");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FileServer::ptr build() {
|
||||||
|
if (!_reg_client) {
|
||||||
|
LOG_ERROR("还未初始化服务注册模块!");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
if (!_rpc_server) {
|
||||||
|
LOG_ERROR("还未初始化RPC服务器模块!");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
FileServer::ptr server = std::make_shared<FileServer>(_reg_client, _rpc_server);
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
Registry::ptr _reg_client;
|
||||||
|
std::shared_ptr<brpc::Server> _rpc_server;
|
||||||
|
};
|
||||||
|
}
|
||||||
152
file/test/file_client.cc
Normal file
152
file/test/file_client.cc
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
//编写一个file客户端程序,对文件存储子服务进行单元测试
|
||||||
|
// 1. 封装四个接口进行rpc调用,实现对于四个业务接口的测试
|
||||||
|
#include <gflags/gflags.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <thread>
|
||||||
|
#include "etcd.hpp"
|
||||||
|
#include "channel.hpp"
|
||||||
|
#include "logger.hpp"
|
||||||
|
#include "file.pb.h"
|
||||||
|
#include "base.pb.h"
|
||||||
|
#include "utils.hpp"
|
||||||
|
|
||||||
|
DEFINE_bool(run_mode, false, "程序的运行模式,false-调试; true-发布;");
|
||||||
|
DEFINE_string(log_file, "", "发布模式下,用于指定日志的输出文件");
|
||||||
|
DEFINE_int32(log_level, 0, "发布模式下,用于指定日志输出等级");
|
||||||
|
|
||||||
|
DEFINE_string(etcd_host, "http://127.0.0.1:2379", "服务注册中心地址");
|
||||||
|
DEFINE_string(base_service, "/service", "服务监控根目录");
|
||||||
|
DEFINE_string(file_service, "/service/file_service", "服务监控根目录");
|
||||||
|
|
||||||
|
|
||||||
|
bite_im::ServiceChannel::ChannelPtr channel;
|
||||||
|
std::string single_file_id;
|
||||||
|
|
||||||
|
|
||||||
|
TEST(put_test, single_file) {
|
||||||
|
//1. 读取当前目录下的指定文件数据
|
||||||
|
std::string body;
|
||||||
|
ASSERT_TRUE(bite_im::readFile("./Makefile", body));
|
||||||
|
//2. 实例化rpc调用客户端对象,发起rpc调用
|
||||||
|
bite_im::FileService_Stub stub(channel.get());
|
||||||
|
|
||||||
|
bite_im::PutSingleFileReq req;
|
||||||
|
req.set_request_id("1111");
|
||||||
|
req.mutable_file_data()->set_file_name("Makefile");
|
||||||
|
req.mutable_file_data()->set_file_size(body.size());
|
||||||
|
req.mutable_file_data()->set_file_content(body);
|
||||||
|
|
||||||
|
brpc::Controller *cntl = new brpc::Controller();
|
||||||
|
bite_im::PutSingleFileRsp *rsp = new bite_im::PutSingleFileRsp();
|
||||||
|
stub.PutSingleFile(cntl, &req, rsp, nullptr);
|
||||||
|
ASSERT_FALSE(cntl->Failed());
|
||||||
|
//3. 检测返回值中上传是否成功
|
||||||
|
ASSERT_TRUE(rsp->success());
|
||||||
|
ASSERT_EQ(rsp->file_info().file_size(), body.size());
|
||||||
|
ASSERT_EQ(rsp->file_info().file_name(), "Makefile");
|
||||||
|
single_file_id = rsp->file_info().file_id();
|
||||||
|
LOG_DEBUG("文件ID:{}", rsp->file_info().file_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(get_test, single_file) {
|
||||||
|
//先发起Rpc调用,进行文件下载
|
||||||
|
bite_im::FileService_Stub stub(channel.get());
|
||||||
|
bite_im::GetSingleFileReq req;
|
||||||
|
bite_im::GetSingleFileRsp *rsp;
|
||||||
|
req.set_request_id("2222");
|
||||||
|
req.set_file_id(single_file_id);
|
||||||
|
|
||||||
|
brpc::Controller *cntl = new brpc::Controller();
|
||||||
|
rsp = new bite_im::GetSingleFileRsp();
|
||||||
|
stub.GetSingleFile(cntl, &req, rsp, nullptr);
|
||||||
|
ASSERT_FALSE(cntl->Failed());
|
||||||
|
ASSERT_TRUE(rsp->success());
|
||||||
|
//将文件数据,存储到文件中
|
||||||
|
ASSERT_EQ(single_file_id, rsp->file_data().file_id());
|
||||||
|
bite_im::writeFile("make_file_download", rsp->file_data().file_content());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> multi_file_id;
|
||||||
|
|
||||||
|
TEST(put_test, multi_file) {
|
||||||
|
//1. 读取当前目录下的指定文件数据
|
||||||
|
std::string body1;
|
||||||
|
ASSERT_TRUE(bite_im::readFile("./base.pb.h", body1));
|
||||||
|
std::string body2;
|
||||||
|
ASSERT_TRUE(bite_im::readFile("./file.pb.h", body2));
|
||||||
|
//2. 实例化rpc调用客户端对象,发起rpc调用
|
||||||
|
bite_im::FileService_Stub stub(channel.get());
|
||||||
|
|
||||||
|
bite_im::PutMultiFileReq req;
|
||||||
|
req.set_request_id("3333");
|
||||||
|
|
||||||
|
auto file_data = req.add_file_data();
|
||||||
|
file_data->set_file_name("base.pb.h");
|
||||||
|
file_data->set_file_size(body1.size());
|
||||||
|
file_data->set_file_content(body1);
|
||||||
|
|
||||||
|
file_data = req.add_file_data();
|
||||||
|
file_data->set_file_name("file.pb.h");
|
||||||
|
file_data->set_file_size(body2.size());
|
||||||
|
file_data->set_file_content(body2);
|
||||||
|
|
||||||
|
brpc::Controller *cntl = new brpc::Controller();
|
||||||
|
bite_im::PutMultiFileRsp *rsp = new bite_im::PutMultiFileRsp();
|
||||||
|
stub.PutMultiFile(cntl, &req, rsp, nullptr);
|
||||||
|
ASSERT_FALSE(cntl->Failed());
|
||||||
|
//3. 检测返回值中上传是否成功
|
||||||
|
ASSERT_TRUE(rsp->success());
|
||||||
|
for (int i = 0; i < rsp->file_info_size(); i++){
|
||||||
|
multi_file_id.push_back(rsp->file_info(i).file_id());
|
||||||
|
LOG_DEBUG("文件ID:{}", multi_file_id[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(get_test, multi_file) {
|
||||||
|
//先发起Rpc调用,进行文件下载
|
||||||
|
bite_im::FileService_Stub stub(channel.get());
|
||||||
|
bite_im::GetMultiFileReq req;
|
||||||
|
bite_im::GetMultiFileRsp *rsp;
|
||||||
|
req.set_request_id("4444");
|
||||||
|
req.add_file_id_list(multi_file_id[0]);
|
||||||
|
req.add_file_id_list(multi_file_id[1]);
|
||||||
|
|
||||||
|
brpc::Controller *cntl = new brpc::Controller();
|
||||||
|
rsp = new bite_im::GetMultiFileRsp();
|
||||||
|
stub.GetMultiFile(cntl, &req, rsp, nullptr);
|
||||||
|
ASSERT_FALSE(cntl->Failed());
|
||||||
|
ASSERT_TRUE(rsp->success());
|
||||||
|
//将文件数据,存储到文件中
|
||||||
|
ASSERT_TRUE(rsp->file_data().find(multi_file_id[0]) != rsp->file_data().end());
|
||||||
|
ASSERT_TRUE(rsp->file_data().find(multi_file_id[1]) != rsp->file_data().end());
|
||||||
|
auto map = rsp->file_data();
|
||||||
|
auto file_data1 = map[multi_file_id[0]];
|
||||||
|
bite_im::writeFile("base_download_file1",file_data1.file_content());
|
||||||
|
auto file_data2 = map[multi_file_id[1]];
|
||||||
|
bite_im::writeFile("file_download_file2", file_data2.file_content());
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
testing::InitGoogleTest(&argc, argv);
|
||||||
|
google::ParseCommandLineFlags(&argc, &argv, true);
|
||||||
|
|
||||||
|
bite_im::init_logger(FLAGS_run_mode, FLAGS_log_file, FLAGS_log_level);
|
||||||
|
|
||||||
|
//1. 先构造Rpc信道管理对象
|
||||||
|
auto sm = std::make_shared<bite_im::ServiceManager>();
|
||||||
|
sm->declared(FLAGS_file_service);
|
||||||
|
auto put_cb = std::bind(&bite_im::ServiceManager::onServiceOnline, sm.get(), std::placeholders::_1, std::placeholders::_2);
|
||||||
|
auto del_cb = std::bind(&bite_im::ServiceManager::onServiceOffline, sm.get(), std::placeholders::_1, std::placeholders::_2);
|
||||||
|
//2. 构造服务发现对象
|
||||||
|
bite_im::Discovery::ptr dclient = std::make_shared<bite_im::Discovery>(FLAGS_etcd_host, FLAGS_base_service, put_cb, del_cb);
|
||||||
|
|
||||||
|
//3. 通过Rpc信道管理对象,获取提供Echo服务的信道
|
||||||
|
channel = sm->choose(FLAGS_file_service);
|
||||||
|
if (!channel) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RUN_ALL_TESTS();
|
||||||
|
}
|
||||||
88
friend/CMakeLists.txt
Normal file
88
friend/CMakeLists.txt
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
# 1. 添加cmake版本说明
|
||||||
|
cmake_minimum_required(VERSION 3.1.3)
|
||||||
|
# 2. 声明工程名称
|
||||||
|
project(friend_server)
|
||||||
|
|
||||||
|
set(target "friend_server")
|
||||||
|
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
|
||||||
|
|
||||||
|
# 3. 检测并生成ODB框架代码
|
||||||
|
# 1. 添加所需的proto映射代码文件名称
|
||||||
|
set(proto_path ${CMAKE_CURRENT_SOURCE_DIR}/../proto)
|
||||||
|
set(proto_files base.proto user.proto message.proto friend.proto)
|
||||||
|
# 2. 检测框架代码文件是否已经生成
|
||||||
|
set(proto_hxx "")
|
||||||
|
set(proto_cxx "")
|
||||||
|
set(proto_srcs "")
|
||||||
|
foreach(proto_file ${proto_files})
|
||||||
|
# 3. 如果没有生成,则预定义生成指令 -- 用于在构建项目之间先生成框架代码
|
||||||
|
string(REPLACE ".proto" ".pb.cc" proto_cc ${proto_file})
|
||||||
|
string(REPLACE ".proto" ".pb.h" proto_hh ${proto_file})
|
||||||
|
if (NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}${proto_cc})
|
||||||
|
add_custom_command(
|
||||||
|
PRE_BUILD
|
||||||
|
COMMAND protoc
|
||||||
|
ARGS --cpp_out=${CMAKE_CURRENT_BINARY_DIR} -I ${proto_path} --experimental_allow_proto3_optional ${proto_path}/${proto_file}
|
||||||
|
DEPENDS ${proto_path}/${proto_file}
|
||||||
|
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${proto_cc}
|
||||||
|
COMMENT "生成Protobuf框架代码文件:" ${CMAKE_CURRENT_BINARY_DIR}/${proto_cc}
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
list(APPEND proto_srcs ${CMAKE_CURRENT_BINARY_DIR}/${proto_cc})
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# 3. 检测并生成ODB框架代码
|
||||||
|
# 1. 添加所需的odb映射代码文件名称
|
||||||
|
set(odb_path ${CMAKE_CURRENT_SOURCE_DIR}/../odb)
|
||||||
|
set(odb_files chat_session_member.hxx chat_session.hxx friend_apply.hxx relation.hxx)
|
||||||
|
# 2. 检测框架代码文件是否已经生成
|
||||||
|
set(odb_hxx "")
|
||||||
|
set(odb_cxx "")
|
||||||
|
set(odb_srcs "")
|
||||||
|
foreach(odb_file ${odb_files})
|
||||||
|
# 3. 如果没有生成,则预定义生成指令 -- 用于在构建项目之间先生成框架代码
|
||||||
|
string(REPLACE ".hxx" "-odb.hxx" odb_hxx ${odb_file})
|
||||||
|
string(REPLACE ".hxx" "-odb.cxx" odb_cxx ${odb_file})
|
||||||
|
if (NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}${odb_cxx})
|
||||||
|
add_custom_command(
|
||||||
|
PRE_BUILD
|
||||||
|
COMMAND odb
|
||||||
|
ARGS -d mysql --std c++11 --generate-query --generate-schema --profile boost/date-time ${odb_path}/${odb_file}
|
||||||
|
DEPENDS ${odb_path}/${odb_file}
|
||||||
|
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${odb_cxx}
|
||||||
|
COMMENT "生成ODB框架代码文件:" ${CMAKE_CURRENT_BINARY_DIR}/${odb_cxx}
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
# 4. 将所有生成的框架源码文件名称保存起来 student-odb.cxx classes-odb.cxx
|
||||||
|
list(APPEND odb_srcs ${CMAKE_CURRENT_BINARY_DIR}/${odb_cxx})
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# 4. 获取源码目录下的所有源码文件
|
||||||
|
set(src_files "")
|
||||||
|
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/source src_files)
|
||||||
|
# 5. 声明目标及依赖
|
||||||
|
add_executable(${target} ${src_files} ${proto_srcs} ${odb_srcs})
|
||||||
|
# 7. 设置需要连接的库
|
||||||
|
target_link_libraries(${target} -lgflags
|
||||||
|
-lspdlog -lfmt -lbrpc -lssl -lcrypto
|
||||||
|
-lprotobuf -lleveldb -letcd-cpp-api
|
||||||
|
-lcpprest -lcurl -lodb-mysql -lodb -lodb-boost
|
||||||
|
/usr/lib/x86_64-linux-gnu/libjsoncpp.so.19
|
||||||
|
-lcpr -lelasticlient)
|
||||||
|
|
||||||
|
|
||||||
|
set(test_client "friend_client")
|
||||||
|
set(test_files "")
|
||||||
|
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/test test_files)
|
||||||
|
add_executable(${test_client} ${test_files} ${proto_srcs})
|
||||||
|
target_link_libraries(${test_client} -pthread -lgtest -lgflags -lspdlog -lfmt -lbrpc -lssl -lcrypto -lprotobuf -lleveldb -letcd-cpp-api -lcpprest -lcurl /usr/lib/x86_64-linux-gnu/libjsoncpp.so.19)
|
||||||
|
|
||||||
|
# 6. 设置头文件默认搜索路径
|
||||||
|
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../common)
|
||||||
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../odb)
|
||||||
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../third/include)
|
||||||
|
|
||||||
|
#8. 设置安装路径
|
||||||
|
INSTALL(TARGETS ${target} ${test_client} RUNTIME DESTINATION bin)
|
||||||
16
friend/dockerfile
Normal file
16
friend/dockerfile
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# 声明基础经镜像来源
|
||||||
|
FROM debian:12
|
||||||
|
|
||||||
|
# 声明工作目录
|
||||||
|
WORKDIR /im
|
||||||
|
RUN mkdir -p /im/logs &&\
|
||||||
|
mkdir -p /im/data &&\
|
||||||
|
mkdir -p /im/conf &&\
|
||||||
|
mkdir -p /im/bin
|
||||||
|
|
||||||
|
# 将可执行程序依赖,拷贝进镜像
|
||||||
|
COPY ./build/friend_server /im/bin/
|
||||||
|
# 将可执行程序文件,拷贝进镜像
|
||||||
|
COPY ./depends /lib/x86_64-linux-gnu/
|
||||||
|
# 设置容器启动的默认操作 ---运行程序
|
||||||
|
CMD /im/bin/friend_server -flagfile=/im/conf/friend_server.conf
|
||||||
20
friend/friend_server.conf
Normal file
20
friend/friend_server.conf
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
-run_mode=true
|
||||||
|
-log_file=/im/logs/friend.log
|
||||||
|
-log_level=0
|
||||||
|
-registry_host=http://10.0.0.235:2379
|
||||||
|
-instance_name=/friend_service/instance
|
||||||
|
-access_host=10.0.0.235:10006
|
||||||
|
-listen_port=10006
|
||||||
|
-rpc_timeout=-1
|
||||||
|
-rpc_threads=1
|
||||||
|
-base_service=/service
|
||||||
|
-user_service=/service/user_service
|
||||||
|
-message_service=/service/message_service
|
||||||
|
-es_host=http://10.0.0.235:9200/
|
||||||
|
-mysql_host=10.0.0.235
|
||||||
|
-mysql_user=root
|
||||||
|
-mysql_pswd=123456
|
||||||
|
-mysql_db=bite_im
|
||||||
|
-mysql_cset=utf8
|
||||||
|
-mysql_port=0
|
||||||
|
-mysql_pool_count=4
|
||||||
49
friend/source/friend_server.cc
Normal file
49
friend/source/friend_server.cc
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
//主要实现语音识别子服务的服务器的搭建
|
||||||
|
#include "friend_server.hpp"
|
||||||
|
|
||||||
|
DEFINE_bool(run_mode, false, "程序的运行模式,false-调试; true-发布;");
|
||||||
|
DEFINE_string(log_file, "", "发布模式下,用于指定日志的输出文件");
|
||||||
|
DEFINE_int32(log_level, 0, "发布模式下,用于指定日志输出等级");
|
||||||
|
|
||||||
|
DEFINE_string(registry_host, "http://127.0.0.1:2379", "服务注册中心地址");
|
||||||
|
DEFINE_string(instance_name, "/friend_service/instance", "当前实例名称");
|
||||||
|
DEFINE_string(access_host, "127.0.0.1:10006", "当前实例的外部访问地址");
|
||||||
|
|
||||||
|
DEFINE_int32(listen_port, 10006, "Rpc服务器监听端口");
|
||||||
|
DEFINE_int32(rpc_timeout, -1, "Rpc调用超时时间");
|
||||||
|
DEFINE_int32(rpc_threads, 1, "Rpc的IO线程数量");
|
||||||
|
|
||||||
|
|
||||||
|
DEFINE_string(base_service, "/service", "服务监控根目录");
|
||||||
|
DEFINE_string(user_service, "/service/user_service", "用户管理子服务名称");
|
||||||
|
DEFINE_string(message_service, "/service/message_service", "消息存储子服务名称");
|
||||||
|
|
||||||
|
DEFINE_string(es_host, "http://127.0.0.1:9200/", "ES搜索引擎服务器URL");
|
||||||
|
|
||||||
|
DEFINE_string(mysql_host, "127.0.0.1", "Mysql服务器访问地址");
|
||||||
|
DEFINE_string(mysql_user, "root", "Mysql服务器访问用户名");
|
||||||
|
DEFINE_string(mysql_pswd, "123456", "Mysql服务器访问密码");
|
||||||
|
DEFINE_string(mysql_db, "bite_im", "Mysql默认库名称");
|
||||||
|
DEFINE_string(mysql_cset, "utf8", "Mysql客户端字符集");
|
||||||
|
DEFINE_int32(mysql_port, 0, "Mysql服务器访问端口");
|
||||||
|
DEFINE_int32(mysql_pool_count, 4, "Mysql连接池最大连接数量");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
google::ParseCommandLineFlags(&argc, &argv, true);
|
||||||
|
bite_im::init_logger(FLAGS_run_mode, FLAGS_log_file, FLAGS_log_level);
|
||||||
|
|
||||||
|
bite_im::FriendServerBuilder fsb;
|
||||||
|
fsb.make_es_object({FLAGS_es_host});
|
||||||
|
fsb.make_mysql_object(FLAGS_mysql_user, FLAGS_mysql_pswd, FLAGS_mysql_host,
|
||||||
|
FLAGS_mysql_db, FLAGS_mysql_cset, FLAGS_mysql_port, FLAGS_mysql_pool_count);
|
||||||
|
fsb.make_discovery_object(FLAGS_registry_host, FLAGS_base_service, FLAGS_user_service, FLAGS_message_service);
|
||||||
|
fsb.make_rpc_server(FLAGS_listen_port, FLAGS_rpc_timeout, FLAGS_rpc_threads);
|
||||||
|
fsb.make_registry_object(FLAGS_registry_host, FLAGS_base_service + FLAGS_instance_name, FLAGS_access_host);
|
||||||
|
auto server = fsb.build();
|
||||||
|
server->start();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
637
friend/source/friend_server.hpp
Normal file
637
friend/source/friend_server.hpp
Normal file
@ -0,0 +1,637 @@
|
|||||||
|
//实现语音识别子服务
|
||||||
|
#include <brpc/server.h>
|
||||||
|
#include <butil/logging.h>
|
||||||
|
|
||||||
|
#include "data_es.hpp" // es数据管理客户端封装
|
||||||
|
#include "mysql_chat_session_member.hpp" // mysql数据管理客户端封装
|
||||||
|
#include "mysql_chat_session.hpp" // mysql数据管理客户端封装
|
||||||
|
#include "mysql_relation.hpp" // mysql数据管理客户端封装
|
||||||
|
#include "mysql_apply.hpp" // mysql数据管理客户端封装
|
||||||
|
#include "etcd.hpp" // 服务注册模块封装
|
||||||
|
#include "logger.hpp" // 日志模块封装
|
||||||
|
#include "utils.hpp" // 基础工具接口
|
||||||
|
#include "channel.hpp" // 信道管理模块封装
|
||||||
|
|
||||||
|
|
||||||
|
#include "friend.pb.h" // protobuf框架代码
|
||||||
|
#include "base.pb.h" // protobuf框架代码
|
||||||
|
#include "user.pb.h" // protobuf框架代码
|
||||||
|
#include "message.pb.h" // protobuf框架代码
|
||||||
|
|
||||||
|
namespace bite_im{
|
||||||
|
class FriendServiceImpl : public bite_im::FriendService {
|
||||||
|
public:
|
||||||
|
FriendServiceImpl(
|
||||||
|
const std::shared_ptr<elasticlient::Client> &es_client,
|
||||||
|
const std::shared_ptr<odb::core::database> &mysql_client,
|
||||||
|
const ServiceManager::ptr &channel_manager,
|
||||||
|
const std::string &user_service_name,
|
||||||
|
const std::string &message_service_name) :
|
||||||
|
_es_user(std::make_shared<ESUser>(es_client)),
|
||||||
|
_mysql_apply(std::make_shared<FriendApplyTable>(mysql_client)),
|
||||||
|
_mysql_chat_session(std::make_shared<ChatSessionTable>(mysql_client)),
|
||||||
|
_mysql_chat_session_member(std::make_shared<ChatSessionMemeberTable>(mysql_client)),
|
||||||
|
_mysql_relation(std::make_shared<RelationTable>(mysql_client)),
|
||||||
|
_user_service_name(user_service_name),
|
||||||
|
_message_service_name(message_service_name),
|
||||||
|
_mm_channels(channel_manager){}
|
||||||
|
~FriendServiceImpl(){}
|
||||||
|
virtual void GetFriendList(::google::protobuf::RpcController* controller,
|
||||||
|
const ::bite_im::GetFriendListReq* request,
|
||||||
|
::bite_im::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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
virtual void FriendRemove(::google::protobuf::RpcController* controller,
|
||||||
|
const ::bite_im::FriendRemoveReq* request,
|
||||||
|
::bite_im::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);
|
||||||
|
}
|
||||||
|
virtual void FriendAdd(::google::protobuf::RpcController* controller,
|
||||||
|
const ::bite_im::FriendAddReq* request,
|
||||||
|
::bite_im::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();
|
||||||
|
//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);
|
||||||
|
}
|
||||||
|
virtual void FriendAddProcess(::google::protobuf::RpcController* controller,
|
||||||
|
const ::bite_im::FriendAddProcessReq* request,
|
||||||
|
::bite_im::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);
|
||||||
|
}
|
||||||
|
virtual void FriendSearch(::google::protobuf::RpcController* controller,
|
||||||
|
const ::bite_im::FriendSearchReq* request,
|
||||||
|
::bite_im::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;
|
||||||
|
};
|
||||||
|
//1. 提取请求中的关键要素:搜索关键字(可能是用户ID,可能是手机号,可能是昵称的一部分)
|
||||||
|
std::string rid = request->request_id();
|
||||||
|
std::string uid = request->user_id();
|
||||||
|
std::string skey = request->search_key();
|
||||||
|
LOG_DEBUG("{} 好友搜索 : {}", uid, skey);
|
||||||
|
//2. 根据用户ID,获取用户的好友ID列表
|
||||||
|
auto friend_id_lists = _mysql_relation->friends(uid);
|
||||||
|
//3. 从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());
|
||||||
|
}
|
||||||
|
//4. 根据获取到的用户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, "批量获取用户信息失败!");
|
||||||
|
}
|
||||||
|
//5. 组织响应
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
virtual void GetPendingFriendEventList(::google::protobuf::RpcController* controller,
|
||||||
|
const ::bite_im::GetPendingFriendEventListReq* request,
|
||||||
|
::bite_im::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;
|
||||||
|
};
|
||||||
|
//1. 提取关键要素:当前用户ID
|
||||||
|
std::string rid = request->request_id();
|
||||||
|
std::string uid = request->user_id();
|
||||||
|
//2. 从数据库获取待处理的申请事件信息 --- 申请人用户ID列表
|
||||||
|
auto res = _mysql_apply->applyUsers(uid);
|
||||||
|
std::unordered_set<std::string> user_id_lists;
|
||||||
|
for (auto &id : res) {
|
||||||
|
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 ev = response->add_event();
|
||||||
|
ev->mutable_sender()->CopyFrom(user_it.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
//获取聊天会话的作用:一个用户登录成功后,能够展示自己的历史聊天信息
|
||||||
|
//1. 提取请求中的关键要素:当前请求用户ID
|
||||||
|
std::string rid = request->request_id();
|
||||||
|
std::string uid = request->user_id();
|
||||||
|
//2. 从数据库中查询出用户的单聊会话列表
|
||||||
|
auto sf_list = _mysql_chat_session->singleChatSession(uid);
|
||||||
|
// 1. 从单聊会话列表中,取出所有的好友ID,从用户子服务获取用户信息
|
||||||
|
std::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;
|
||||||
|
bool ret = GetUserInfo(rid, users_id_list, user_list);
|
||||||
|
if (ret == false) {
|
||||||
|
LOG_ERROR("{} - 批量获取用户信息失败!", rid);
|
||||||
|
return err_response(rid, "批量获取用户信息失败!");
|
||||||
|
}
|
||||||
|
// 2. 设置响应会话信息:会话名称就是好友名称;会话头像就是好友头像
|
||||||
|
//3. 从数据库中查询出用户的群聊会话列表
|
||||||
|
auto gc_list = _mysql_chat_session->groupChatSession(uid);
|
||||||
|
|
||||||
|
//4. 根据所有的会话ID,从消息存储子服务获取会话最后一条消息
|
||||||
|
//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);
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
response->set_request_id(rid);
|
||||||
|
response->set_success(true);
|
||||||
|
}
|
||||||
|
virtual void ChatSessionCreate(::google::protobuf::RpcController* controller,
|
||||||
|
const ::bite_im::ChatSessionCreateReq* request,
|
||||||
|
::bite_im::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;
|
||||||
|
};
|
||||||
|
//创建会话,其实针对的是用户要创建一个群聊会话
|
||||||
|
//1. 提取请求关键要素:会话名称,会话成员
|
||||||
|
std::string rid = request->request_id();
|
||||||
|
std::string uid = request->user_id();
|
||||||
|
std::string cssname = request->chat_session_name();
|
||||||
|
|
||||||
|
//2. 生成会话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, "向数据库添加会话成员信息失败!");
|
||||||
|
}
|
||||||
|
//3. 组织响应---组织会话信息
|
||||||
|
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 ::bite_im::GetChatSessionMemberReq* request,
|
||||||
|
::bite_im::GetChatSessionMemberRsp* 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();
|
||||||
|
std::string cssid = request->chat_session_id();
|
||||||
|
//2. 从数据库获取会话成员ID列表
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
req.set_chat_session_id(cssid);
|
||||||
|
req.set_msg_count(1);
|
||||||
|
brpc::Controller cntl;
|
||||||
|
bite_im::MsgStorageService_Stub stub(channel.get());
|
||||||
|
stub.GetRecentMsg(&cntl, &req, &rsp, nullptr);
|
||||||
|
if (cntl.Failed() == true) {
|
||||||
|
LOG_ERROR("{} - 消息存储子服务调用失败: {}", rid, cntl.ErrorText());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ( rsp.success() == false) {
|
||||||
|
LOG_ERROR("{} - 获取会话 {} 最近消息失败: {}", rid, cssid, rsp.errmsg());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (rsp.msg_list_size() > 0) {
|
||||||
|
msg.CopyFrom(rsp.msg_list(0));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
for (auto &id : uid_list) {
|
||||||
|
req.add_users_id(id);
|
||||||
|
}
|
||||||
|
brpc::Controller cntl;
|
||||||
|
bite_im::UserService_Stub stub(channel.get());
|
||||||
|
stub.GetMultiUserInfo(&cntl, &req, &rsp, nullptr);
|
||||||
|
if (cntl.Failed() == true) {
|
||||||
|
LOG_ERROR("{} - 用户子服务调用失败: {}", rid, cntl.ErrorText());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ( rsp.success() == false) {
|
||||||
|
LOG_ERROR("{} - 批量获取用户信息失败: {}", rid, rsp.errmsg());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (const auto & user_it : rsp.users_info()) {
|
||||||
|
user_list.insert(std::make_pair(user_it.first, user_it.second));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ESUser::ptr _es_user;
|
||||||
|
|
||||||
|
FriendApplyTable::ptr _mysql_apply;
|
||||||
|
ChatSessionTable::ptr _mysql_chat_session;
|
||||||
|
ChatSessionMemeberTable::ptr _mysql_chat_session_member;
|
||||||
|
RelationTable::ptr _mysql_relation;
|
||||||
|
|
||||||
|
//这边是rpc调用客户端相关对象
|
||||||
|
std::string _user_service_name;
|
||||||
|
std::string _message_service_name;
|
||||||
|
ServiceManager::ptr _mm_channels;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FriendServer {
|
||||||
|
public:
|
||||||
|
using ptr = std::shared_ptr<FriendServer>;
|
||||||
|
FriendServer(const Discovery::ptr service_discoverer,
|
||||||
|
const Registry::ptr ®_client,
|
||||||
|
const std::shared_ptr<elasticlient::Client> &es_client,
|
||||||
|
const std::shared_ptr<odb::core::database> &mysql_client,
|
||||||
|
const std::shared_ptr<brpc::Server> &server):
|
||||||
|
_service_discoverer(service_discoverer),
|
||||||
|
_registry_client(reg_client),
|
||||||
|
_es_client(es_client),
|
||||||
|
_mysql_client(mysql_client),
|
||||||
|
_rpc_server(server){}
|
||||||
|
~FriendServer(){}
|
||||||
|
//搭建RPC服务器,并启动服务器
|
||||||
|
void start() {
|
||||||
|
_rpc_server->RunUntilAskedToQuit();
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
Discovery::ptr _service_discoverer;
|
||||||
|
Registry::ptr _registry_client;
|
||||||
|
std::shared_ptr<elasticlient::Client> _es_client;
|
||||||
|
std::shared_ptr<odb::core::database> _mysql_client;
|
||||||
|
std::shared_ptr<brpc::Server> _rpc_server;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FriendServerBuilder {
|
||||||
|
public:
|
||||||
|
//构造es客户端对象
|
||||||
|
void make_es_object(const std::vector<std::string> host_list) {
|
||||||
|
_es_client = ESClientFactory::create(host_list);
|
||||||
|
}
|
||||||
|
//构造mysql客户端对象
|
||||||
|
void make_mysql_object(
|
||||||
|
const std::string &user,
|
||||||
|
const std::string &pswd,
|
||||||
|
const std::string &host,
|
||||||
|
const std::string &db,
|
||||||
|
const std::string &cset,
|
||||||
|
int port,
|
||||||
|
int conn_pool_count) {
|
||||||
|
_mysql_client = ODBFactory::create(user, pswd, host, db, cset, port, conn_pool_count);
|
||||||
|
}
|
||||||
|
//用于构造服务发现客户端&信道管理对象
|
||||||
|
void make_discovery_object(const std::string ®_host,
|
||||||
|
const std::string &base_service_name,
|
||||||
|
const std::string &user_service_name,
|
||||||
|
const std::string &message_service_name) {
|
||||||
|
_user_service_name = user_service_name;
|
||||||
|
_message_service_name = message_service_name;
|
||||||
|
_mm_channels = std::make_shared<ServiceManager>();
|
||||||
|
_mm_channels->declared(user_service_name);
|
||||||
|
_mm_channels->declared(message_service_name);
|
||||||
|
LOG_DEBUG("设置用户子服务为需添加管理的子服务:{}", user_service_name);
|
||||||
|
LOG_DEBUG("设置消息子服务为需添加管理的子服务:{}", message_service_name);
|
||||||
|
auto put_cb = std::bind(&ServiceManager::onServiceOnline, _mm_channels.get(), std::placeholders::_1, std::placeholders::_2);
|
||||||
|
auto del_cb = std::bind(&ServiceManager::onServiceOffline, _mm_channels.get(), std::placeholders::_1, std::placeholders::_2);
|
||||||
|
_service_discoverer = std::make_shared<Discovery>(reg_host, base_service_name, put_cb, del_cb);
|
||||||
|
}
|
||||||
|
//用于构造服务注册客户端对象
|
||||||
|
void make_registry_object(const std::string ®_host,
|
||||||
|
const std::string &service_name,
|
||||||
|
const std::string &access_host) {
|
||||||
|
_registry_client = std::make_shared<Registry>(reg_host);
|
||||||
|
_registry_client->registry(service_name, access_host);
|
||||||
|
}
|
||||||
|
void make_rpc_server(uint16_t port, int32_t timeout, uint8_t num_threads) {
|
||||||
|
if (!_es_client) {
|
||||||
|
LOG_ERROR("还未初始化ES搜索引擎模块!");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
if (!_mysql_client) {
|
||||||
|
LOG_ERROR("还未初始化Mysql数据库模块!");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
if (!_mm_channels) {
|
||||||
|
LOG_ERROR("还未初始化信道管理模块!");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
_rpc_server = std::make_shared<brpc::Server>();
|
||||||
|
|
||||||
|
FriendServiceImpl *friend_service = new FriendServiceImpl(_es_client,
|
||||||
|
_mysql_client, _mm_channels, _user_service_name, _message_service_name);
|
||||||
|
int ret = _rpc_server->AddService(friend_service,
|
||||||
|
brpc::ServiceOwnership::SERVER_OWNS_SERVICE);
|
||||||
|
if (ret == -1) {
|
||||||
|
LOG_ERROR("添加Rpc服务失败!");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
brpc::ServerOptions options;
|
||||||
|
options.idle_timeout_sec = timeout;
|
||||||
|
options.num_threads = num_threads;
|
||||||
|
ret = _rpc_server->Start(port, &options);
|
||||||
|
if (ret == -1) {
|
||||||
|
LOG_ERROR("服务启动失败!");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//构造RPC服务器对象
|
||||||
|
FriendServer::ptr build() {
|
||||||
|
if (!_service_discoverer) {
|
||||||
|
LOG_ERROR("还未初始化服务发现模块!");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
if (!_registry_client) {
|
||||||
|
LOG_ERROR("还未初始化服务注册模块!");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
if (!_rpc_server) {
|
||||||
|
LOG_ERROR("还未初始化RPC服务器模块!");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
FriendServer::ptr server = std::make_shared<FriendServer>(
|
||||||
|
_service_discoverer, _registry_client,
|
||||||
|
_es_client, _mysql_client, _rpc_server);
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
Registry::ptr _registry_client;
|
||||||
|
|
||||||
|
std::shared_ptr<elasticlient::Client> _es_client;
|
||||||
|
std::shared_ptr<odb::core::database> _mysql_client;
|
||||||
|
|
||||||
|
std::string _user_service_name;
|
||||||
|
std::string _message_service_name;
|
||||||
|
ServiceManager::ptr _mm_channels;
|
||||||
|
Discovery::ptr _service_discoverer;
|
||||||
|
|
||||||
|
std::shared_ptr<brpc::Server> _rpc_server;
|
||||||
|
};
|
||||||
|
}
|
||||||
286
friend/test/friend_client.cc
Normal file
286
friend/test/friend_client.cc
Normal file
@ -0,0 +1,286 @@
|
|||||||
|
#include "etcd.hpp"
|
||||||
|
#include "channel.hpp"
|
||||||
|
#include "utils.hpp"
|
||||||
|
#include <gflags/gflags.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <thread>
|
||||||
|
#include "friend.pb.h"
|
||||||
|
|
||||||
|
|
||||||
|
DEFINE_bool(run_mode, false, "程序的运行模式,false-调试; true-发布;");
|
||||||
|
DEFINE_string(log_file, "", "发布模式下,用于指定日志的输出文件");
|
||||||
|
DEFINE_int32(log_level, 0, "发布模式下,用于指定日志输出等级");
|
||||||
|
|
||||||
|
DEFINE_string(etcd_host, "http://127.0.0.1:2379", "服务注册中心地址");
|
||||||
|
DEFINE_string(base_service, "/service", "服务监控根目录");
|
||||||
|
DEFINE_string(friend_service, "/service/friend_service", "服务监控根目录");
|
||||||
|
|
||||||
|
bite_im::ServiceManager::ptr sm;
|
||||||
|
|
||||||
|
void apply_test(const std::string &uid1, const std::string &uid2) {
|
||||||
|
auto channel = sm->choose(FLAGS_friend_service);
|
||||||
|
if (!channel) {
|
||||||
|
std::cout << "获取通信信道失败!" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bite_im::FriendService_Stub stub(channel.get());
|
||||||
|
bite_im::FriendAddReq req;
|
||||||
|
bite_im::FriendAddRsp rsp;
|
||||||
|
req.set_request_id(bite_im::uuid());
|
||||||
|
req.set_user_id(uid1);
|
||||||
|
req.set_respondent_id(uid2);
|
||||||
|
brpc::Controller cntl;
|
||||||
|
stub.FriendAdd(&cntl, &req, &rsp, nullptr);
|
||||||
|
ASSERT_FALSE(cntl.Failed());
|
||||||
|
ASSERT_TRUE(rsp.success());
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_apply_list(const std::string &uid1) {
|
||||||
|
auto channel = sm->choose(FLAGS_friend_service);
|
||||||
|
if (!channel) {
|
||||||
|
std::cout << "获取通信信道失败!" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bite_im::FriendService_Stub stub(channel.get());
|
||||||
|
bite_im::GetPendingFriendEventListReq req;
|
||||||
|
bite_im::GetPendingFriendEventListRsp rsp;
|
||||||
|
req.set_request_id(bite_im::uuid());
|
||||||
|
req.set_user_id(uid1);
|
||||||
|
brpc::Controller cntl;
|
||||||
|
stub.GetPendingFriendEventList(&cntl, &req, &rsp, nullptr);
|
||||||
|
ASSERT_FALSE(cntl.Failed());
|
||||||
|
ASSERT_TRUE(rsp.success());
|
||||||
|
for (int i = 0; i < rsp.event_size(); i++) {
|
||||||
|
std::cout << "---------------\n";
|
||||||
|
std::cout << rsp.event(i).sender().user_id() << std::endl;
|
||||||
|
std::cout << rsp.event(i).sender().nickname() << std::endl;
|
||||||
|
std::cout << rsp.event(i).sender().avatar() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void process_apply_test(const std::string &uid1, bool agree, const std::string &apply_user_id) {
|
||||||
|
auto channel = sm->choose(FLAGS_friend_service);
|
||||||
|
if (!channel) {
|
||||||
|
std::cout << "获取通信信道失败!" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bite_im::FriendService_Stub stub(channel.get());
|
||||||
|
bite_im::FriendAddProcessReq req;
|
||||||
|
bite_im::FriendAddProcessRsp rsp;
|
||||||
|
req.set_request_id(bite_im::uuid());
|
||||||
|
req.set_user_id(uid1);
|
||||||
|
req.set_agree(agree);
|
||||||
|
req.set_apply_user_id(apply_user_id);
|
||||||
|
brpc::Controller cntl;
|
||||||
|
stub.FriendAddProcess(&cntl, &req, &rsp, nullptr);
|
||||||
|
ASSERT_FALSE(cntl.Failed());
|
||||||
|
ASSERT_TRUE(rsp.success());
|
||||||
|
if (agree) {
|
||||||
|
std::cout << rsp.new_session_id() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void search_test(const std::string &uid1, const std::string &key) {
|
||||||
|
auto channel = sm->choose(FLAGS_friend_service);
|
||||||
|
if (!channel) {
|
||||||
|
std::cout << "获取通信信道失败!" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bite_im::FriendService_Stub stub(channel.get());
|
||||||
|
bite_im::FriendSearchReq req;
|
||||||
|
bite_im::FriendSearchRsp rsp;
|
||||||
|
req.set_request_id(bite_im::uuid());
|
||||||
|
req.set_user_id(uid1);
|
||||||
|
req.set_search_key(key);
|
||||||
|
brpc::Controller cntl;
|
||||||
|
stub.FriendSearch(&cntl, &req, &rsp, nullptr);
|
||||||
|
ASSERT_FALSE(cntl.Failed());
|
||||||
|
ASSERT_TRUE(rsp.success());
|
||||||
|
for (int i = 0; i < rsp.user_info_size(); i++) {
|
||||||
|
std::cout << "-------------------\n";
|
||||||
|
std::cout << rsp.user_info(i).user_id() << std::endl;
|
||||||
|
std::cout << rsp.user_info(i).nickname() << std::endl;
|
||||||
|
std::cout << rsp.user_info(i).avatar() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void friend_list_test(const std::string &uid1) {
|
||||||
|
auto channel = sm->choose(FLAGS_friend_service);
|
||||||
|
if (!channel) {
|
||||||
|
std::cout << "获取通信信道失败!" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bite_im::FriendService_Stub stub(channel.get());
|
||||||
|
bite_im::GetFriendListReq req;
|
||||||
|
bite_im::GetFriendListRsp rsp;
|
||||||
|
req.set_request_id(bite_im::uuid());
|
||||||
|
req.set_user_id(uid1);
|
||||||
|
brpc::Controller cntl;
|
||||||
|
stub.GetFriendList(&cntl, &req, &rsp, nullptr);
|
||||||
|
ASSERT_FALSE(cntl.Failed());
|
||||||
|
ASSERT_TRUE(rsp.success());
|
||||||
|
for (int i = 0; i < rsp.friend_list_size(); i++) {
|
||||||
|
std::cout << "-------------------\n";
|
||||||
|
std::cout << rsp.friend_list(i).user_id() << std::endl;
|
||||||
|
std::cout << rsp.friend_list(i).nickname() << std::endl;
|
||||||
|
std::cout << rsp.friend_list(i).avatar() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void remove_test(const std::string &uid1, const std::string &uid2) {
|
||||||
|
auto channel = sm->choose(FLAGS_friend_service);
|
||||||
|
if (!channel) {
|
||||||
|
std::cout << "获取通信信道失败!" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bite_im::FriendService_Stub stub(channel.get());
|
||||||
|
bite_im::FriendRemoveReq req;
|
||||||
|
bite_im::FriendRemoveRsp rsp;
|
||||||
|
req.set_request_id(bite_im::uuid());
|
||||||
|
req.set_user_id(uid1);
|
||||||
|
req.set_peer_id(uid2);
|
||||||
|
brpc::Controller cntl;
|
||||||
|
stub.FriendRemove(&cntl, &req, &rsp, nullptr);
|
||||||
|
ASSERT_FALSE(cntl.Failed());
|
||||||
|
ASSERT_TRUE(rsp.success());
|
||||||
|
}
|
||||||
|
void create_css_test(const std::string &uid1, const std::vector<std::string> &uidlist) {
|
||||||
|
auto channel = sm->choose(FLAGS_friend_service);
|
||||||
|
if (!channel) {
|
||||||
|
std::cout << "获取通信信道失败!" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bite_im::FriendService_Stub stub(channel.get());
|
||||||
|
bite_im::ChatSessionCreateReq req;
|
||||||
|
bite_im::ChatSessionCreateRsp rsp;
|
||||||
|
req.set_request_id(bite_im::uuid());
|
||||||
|
req.set_user_id(uid1);
|
||||||
|
req.set_chat_session_name("快乐一家人");
|
||||||
|
for (auto &id : uidlist) {
|
||||||
|
req.add_member_id_list(id);
|
||||||
|
}
|
||||||
|
brpc::Controller cntl;
|
||||||
|
stub.ChatSessionCreate(&cntl, &req, &rsp, nullptr);
|
||||||
|
ASSERT_FALSE(cntl.Failed());
|
||||||
|
ASSERT_TRUE(rsp.success());
|
||||||
|
std::cout << rsp.chat_session_info().chat_session_id() << std::endl;
|
||||||
|
std::cout << rsp.chat_session_info().chat_session_name() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void cssmember_test(const std::string &uid1, const std::string &cssid) {
|
||||||
|
auto channel = sm->choose(FLAGS_friend_service);
|
||||||
|
if (!channel) {
|
||||||
|
std::cout << "获取通信信道失败!" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bite_im::FriendService_Stub stub(channel.get());
|
||||||
|
bite_im::GetChatSessionMemberReq req;
|
||||||
|
bite_im::GetChatSessionMemberRsp rsp;
|
||||||
|
req.set_request_id(bite_im::uuid());
|
||||||
|
req.set_user_id(uid1);
|
||||||
|
req.set_chat_session_id(cssid);
|
||||||
|
brpc::Controller cntl;
|
||||||
|
stub.GetChatSessionMember(&cntl, &req, &rsp, nullptr);
|
||||||
|
ASSERT_FALSE(cntl.Failed());
|
||||||
|
ASSERT_TRUE(rsp.success());
|
||||||
|
for (int i = 0; i < rsp.member_info_list_size(); i++) {
|
||||||
|
std::cout << "-------------------\n";
|
||||||
|
std::cout << rsp.member_info_list(i).user_id() << std::endl;
|
||||||
|
std::cout << rsp.member_info_list(i).nickname() << std::endl;
|
||||||
|
std::cout << rsp.member_info_list(i).avatar() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void csslist_test(const std::string &uid1) {
|
||||||
|
auto channel = sm->choose(FLAGS_friend_service);
|
||||||
|
if (!channel) {
|
||||||
|
std::cout << "获取通信信道失败!" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bite_im::FriendService_Stub stub(channel.get());
|
||||||
|
bite_im::GetChatSessionListReq req;
|
||||||
|
bite_im::GetChatSessionListRsp rsp;
|
||||||
|
req.set_request_id(bite_im::uuid());
|
||||||
|
req.set_user_id(uid1);
|
||||||
|
brpc::Controller cntl;
|
||||||
|
std::cout << "发送获取聊天会话列表请求!!\n";
|
||||||
|
stub.GetChatSessionList(&cntl, &req, &rsp, nullptr);
|
||||||
|
std::cout << "请求发送完毕1!!\n";
|
||||||
|
ASSERT_FALSE(cntl.Failed());
|
||||||
|
std::cout << "请求发送完毕2!!\n";
|
||||||
|
ASSERT_TRUE(rsp.success());
|
||||||
|
std::cout << "请求发送完毕,且成功!!\n";
|
||||||
|
for (int i = 0; i < rsp.chat_session_info_list_size(); i++) {
|
||||||
|
std::cout << "-------------------\n";
|
||||||
|
std::cout << rsp.chat_session_info_list(i).single_chat_friend_id() << std::endl;
|
||||||
|
std::cout << rsp.chat_session_info_list(i).chat_session_id() << std::endl;
|
||||||
|
std::cout << rsp.chat_session_info_list(i).chat_session_name() << std::endl;
|
||||||
|
std::cout << rsp.chat_session_info_list(i).avatar() << std::endl;
|
||||||
|
std::cout << "消息内容:\n";
|
||||||
|
std::cout << rsp.chat_session_info_list(i).prev_message().message_id() << std::endl;
|
||||||
|
std::cout << rsp.chat_session_info_list(i).prev_message().chat_session_id() << std::endl;
|
||||||
|
std::cout << rsp.chat_session_info_list(i).prev_message().timestamp() << std::endl;
|
||||||
|
std::cout << rsp.chat_session_info_list(i).prev_message().sender().user_id() << std::endl;
|
||||||
|
std::cout << rsp.chat_session_info_list(i).prev_message().sender().nickname() << std::endl;
|
||||||
|
std::cout << rsp.chat_session_info_list(i).prev_message().sender().avatar() << std::endl;
|
||||||
|
std::cout << rsp.chat_session_info_list(i).prev_message().message().file_message().file_name() << std::endl;
|
||||||
|
std::cout << rsp.chat_session_info_list(i).prev_message().message().file_message().file_contents() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
google::ParseCommandLineFlags(&argc, &argv, true);
|
||||||
|
bite_im::init_logger(FLAGS_run_mode, FLAGS_log_file, FLAGS_log_level);
|
||||||
|
|
||||||
|
|
||||||
|
//1. 先构造Rpc信道管理对象
|
||||||
|
sm = std::make_shared<bite_im::ServiceManager>();
|
||||||
|
sm->declared(FLAGS_friend_service);
|
||||||
|
auto put_cb = std::bind(&bite_im::ServiceManager::onServiceOnline, sm.get(), std::placeholders::_1, std::placeholders::_2);
|
||||||
|
auto del_cb = std::bind(&bite_im::ServiceManager::onServiceOffline, sm.get(), std::placeholders::_1, std::placeholders::_2);
|
||||||
|
//2. 构造服务发现对象
|
||||||
|
bite_im::Discovery::ptr dclient = std::make_shared<bite_im::Discovery>(FLAGS_etcd_host, FLAGS_base_service, put_cb, del_cb);
|
||||||
|
|
||||||
|
|
||||||
|
// apply_test("ee55-9043bfd7-0001", "672f-c755e83e-0000");
|
||||||
|
// apply_test("67b1-35ca1b76-0000", "672f-c755e83e-0000");
|
||||||
|
// apply_test("d9ff-65692d4a-0001", "672f-c755e83e-0000");
|
||||||
|
|
||||||
|
// get_apply_list("672f-c755e83e-0000");
|
||||||
|
|
||||||
|
// process_apply_test("672f-c755e83e-0000", true, "ee55-9043bfd7-0001");
|
||||||
|
// process_apply_test("672f-c755e83e-0000", false, "67b1-35ca1b76-0000");
|
||||||
|
// process_apply_test("672f-c755e83e-0000", true, "d9ff-65692d4a-0001");
|
||||||
|
|
||||||
|
// std::cout << "**********************\n";
|
||||||
|
// search_test("672f-c755e83e-0000", "猪");
|
||||||
|
// std::cout << "++++++++++++++++++++++\n";
|
||||||
|
// search_test("ee55-9043bfd7-0001", "猪");
|
||||||
|
// std::cout << "======================\n";
|
||||||
|
// search_test("67b1-35ca1b76-0000", "乔治");
|
||||||
|
|
||||||
|
// friend_list_test("c4dc-68239a9a-0001");
|
||||||
|
// std::cout << "++++++++++++++++++++++\n";
|
||||||
|
// friend_list_test("731f-50086884-0000");
|
||||||
|
// std::cout << "++++++++++++++++++++++\n";
|
||||||
|
// friend_list_test("31ab-86a1209d-0000");
|
||||||
|
|
||||||
|
// remove_test("c4dc-68239a9a-0001", "053f-04e5e4c5-0001");
|
||||||
|
|
||||||
|
// std::vector<std::string> uidlist = {
|
||||||
|
// "731f-50086884-0000",
|
||||||
|
// "c4dc-68239a9a-0001",
|
||||||
|
// "31ab-86a1209d-0000",
|
||||||
|
// "053f-04e5e4c5-0001"};
|
||||||
|
// create_css_test("731f-50086884-0000", uidlist);
|
||||||
|
// cssmember_test("731f-50086884-0000", "36b5-edaf4987-0000");
|
||||||
|
// std::cout << "++++++++++++++++++++++\n";
|
||||||
|
// cssmember_test("c4dc-68239a9a-0001", "36b5-edaf4987-0000");
|
||||||
|
|
||||||
|
// csslist_test("c4dc-68239a9a-0001");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
121
friend/test/mysql_test/main.cc
Normal file
121
friend/test/mysql_test/main.cc
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
#include "mysql_chat_session.hpp"
|
||||||
|
#include "mysql_apply.hpp"
|
||||||
|
#include "mysql_relation.hpp"
|
||||||
|
#include <gflags/gflags.h>
|
||||||
|
|
||||||
|
DEFINE_bool(run_mode, false, "程序的运行模式,false-调试; true-发布;");
|
||||||
|
DEFINE_string(log_file, "", "发布模式下,用于指定日志的输出文件");
|
||||||
|
DEFINE_int32(log_level, 0, "发布模式下,用于指定日志输出等级");
|
||||||
|
|
||||||
|
void r_insert_test(bite_im::RelationTable &tb) {
|
||||||
|
tb.insert("用户ID1", "用户ID2");
|
||||||
|
tb.insert("用户ID1", "用户ID3");
|
||||||
|
}
|
||||||
|
void r_select_test(bite_im::RelationTable &tb) {
|
||||||
|
auto res = tb.friends("用户ID1");
|
||||||
|
for (auto &uid:res) {
|
||||||
|
std::cout << uid << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void r_remove_test(bite_im::RelationTable &tb) {
|
||||||
|
tb.remove("用户ID2", "用户ID1");
|
||||||
|
}
|
||||||
|
|
||||||
|
void r_exists_test(bite_im::RelationTable &tb) {
|
||||||
|
std::cout << tb.exists("用户ID2", "用户ID1") << std::endl;
|
||||||
|
std::cout << tb.exists("用户ID3", "用户ID1") << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void a_insert_test(bite_im::FriendApplyTable &tb) {
|
||||||
|
bite_im::FriendApply fa1("uuid1", "用户ID1", "用户ID2");
|
||||||
|
tb.insert(fa1);
|
||||||
|
|
||||||
|
bite_im::FriendApply fa2("uuid2", "用户ID1", "用户ID3");
|
||||||
|
tb.insert(fa2);
|
||||||
|
|
||||||
|
bite_im::FriendApply fa3("uuid3", "用户ID2", "用户ID3");
|
||||||
|
tb.insert(fa3);
|
||||||
|
}
|
||||||
|
void a_remove_test(bite_im::FriendApplyTable &tb) {
|
||||||
|
tb.remove("用户ID2", "用户ID3");
|
||||||
|
}
|
||||||
|
|
||||||
|
void a_select_test(bite_im::FriendApplyTable &tb) {
|
||||||
|
// bite_im::FriendApply fa3("uuid3", "用户ID2", "用户ID3");
|
||||||
|
// tb.insert(fa3);
|
||||||
|
|
||||||
|
auto res = tb.applyUsers("用户ID2");
|
||||||
|
for (auto &uid:res) {
|
||||||
|
std::cout << uid << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void a_exists_test(bite_im::FriendApplyTable &tb) {
|
||||||
|
std::cout << tb.exists("用户ID1", "用户ID2") << std::endl;
|
||||||
|
std::cout << tb.exists("31ab-86a1209d-0000", "c4dc-68239a9a-0001") << std::endl;
|
||||||
|
std::cout << tb.exists("053f-04e5e4c5-0001", "c4dc-68239a9a-0001") << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void c_insert_test(bite_im::ChatSessionTable &tb) {
|
||||||
|
bite_im::ChatSession cs1("会话ID1", "会话名称1", bite_im::ChatSessionType::SINGLE);
|
||||||
|
tb.insert(cs1);
|
||||||
|
bite_im::ChatSession cs2("会话ID2", "会话名称2", bite_im::ChatSessionType::GROUP);
|
||||||
|
tb.insert(cs2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void c_select_test(bite_im::ChatSessionTable &tb) {
|
||||||
|
auto res = tb.select("会话ID1");
|
||||||
|
std::cout << res->chat_session_id() << std::endl;
|
||||||
|
std::cout << res->chat_session_name() << std::endl;
|
||||||
|
std::cout << (int)res->chat_session_type() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void c_single_test(bite_im::ChatSessionTable &tb) {
|
||||||
|
auto res = tb.singleChatSession("731f-50086884-0000");
|
||||||
|
for (auto &info : res) {
|
||||||
|
std::cout << info.chat_session_id << std::endl;
|
||||||
|
std::cout << info.friend_id << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void c_group_test(bite_im::ChatSessionTable &tb) {
|
||||||
|
auto res = tb.groupChatSession("用户ID1");
|
||||||
|
for (auto &info : res) {
|
||||||
|
std::cout << info.chat_session_id << std::endl;
|
||||||
|
std::cout << info.chat_session_name << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void c_remove_test(bite_im::ChatSessionTable &tb) {
|
||||||
|
tb.remove("会话ID3");
|
||||||
|
}
|
||||||
|
void c_remove_test2(bite_im::ChatSessionTable &tb) {
|
||||||
|
tb.remove("731f-50086884-0000", "c4dc-68239a9a-0001");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
google::ParseCommandLineFlags(&argc, &argv, true);
|
||||||
|
bite_im::init_logger(FLAGS_run_mode, FLAGS_log_file, FLAGS_log_level);
|
||||||
|
|
||||||
|
auto db = bite_im::ODBFactory::create("root", "123456", "127.0.0.1", "bite_im", "utf8", 0, 1);
|
||||||
|
bite_im::RelationTable rtb(db);
|
||||||
|
bite_im::FriendApplyTable fatb(db);
|
||||||
|
bite_im::ChatSessionTable cstb(db);
|
||||||
|
|
||||||
|
// r_insert_test(rtb);
|
||||||
|
// r_select_test(rtb);
|
||||||
|
// r_remove_test(rtb);
|
||||||
|
// r_exists_test(rtb);
|
||||||
|
// a_insert_test(fatb);
|
||||||
|
// a_remove_test(fatb);
|
||||||
|
// a_select_test(fatb);
|
||||||
|
// a_exists_test(fatb);
|
||||||
|
// c_insert_test(cstb);
|
||||||
|
// c_select_test(cstb);
|
||||||
|
// c_single_test(cstb);
|
||||||
|
// std::cout << "--------------\n";
|
||||||
|
// c_group_test(cstb);
|
||||||
|
// c_remove_test(cstb);
|
||||||
|
// c_remove_test2(cstb);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
56
gateway/CMakeLists.txt
Normal file
56
gateway/CMakeLists.txt
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
# 1. 添加cmake版本说明
|
||||||
|
cmake_minimum_required(VERSION 3.1.3)
|
||||||
|
# 2. 声明工程名称
|
||||||
|
project(gateway_server)
|
||||||
|
|
||||||
|
set(target "gateway_server")
|
||||||
|
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
|
||||||
|
|
||||||
|
# 3. 检测并生成ODB框架代码
|
||||||
|
# 1. 添加所需的proto映射代码文件名称
|
||||||
|
set(proto_path ${CMAKE_CURRENT_SOURCE_DIR}/../proto)
|
||||||
|
set(proto_files base.proto user.proto file.proto friend.proto gateway.proto message.proto notify.proto speech.proto transmite.proto )
|
||||||
|
# 2. 检测框架代码文件是否已经生成
|
||||||
|
set(proto_hxx "")
|
||||||
|
set(proto_cxx "")
|
||||||
|
set(proto_srcs "")
|
||||||
|
foreach(proto_file ${proto_files})
|
||||||
|
# 3. 如果没有生成,则预定义生成指令 -- 用于在构建项目之间先生成框架代码
|
||||||
|
string(REPLACE ".proto" ".pb.cc" proto_cc ${proto_file})
|
||||||
|
string(REPLACE ".proto" ".pb.h" proto_hh ${proto_file})
|
||||||
|
if (NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}${proto_cc})
|
||||||
|
add_custom_command(
|
||||||
|
PRE_BUILD
|
||||||
|
COMMAND protoc
|
||||||
|
ARGS --cpp_out=${CMAKE_CURRENT_BINARY_DIR} -I ${proto_path} --experimental_allow_proto3_optional ${proto_path}/${proto_file}
|
||||||
|
DEPENDS ${proto_path}/${proto_file}
|
||||||
|
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${proto_cc}
|
||||||
|
COMMENT "生成Protobuf框架代码文件:" ${CMAKE_CURRENT_BINARY_DIR}/${proto_cc}
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
list(APPEND proto_srcs ${CMAKE_CURRENT_BINARY_DIR}/${proto_cc})
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# 4. 获取源码目录下的所有源码文件
|
||||||
|
set(src_files "")
|
||||||
|
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/source src_files)
|
||||||
|
# 5. 声明目标及依赖
|
||||||
|
add_executable(${target} ${src_files} ${proto_srcs} ${odb_srcs})
|
||||||
|
# 7. 设置需要连接的库
|
||||||
|
target_link_libraries(${target} -lgflags
|
||||||
|
-lspdlog -lfmt -lbrpc -lssl -lcrypto
|
||||||
|
-lprotobuf -lleveldb -letcd-cpp-api
|
||||||
|
-lodb-mysql -lodb -lodb-boost
|
||||||
|
-lhiredis -lredis++
|
||||||
|
-lcpprest -lcurl
|
||||||
|
-lpthread -lboost_system)
|
||||||
|
|
||||||
|
|
||||||
|
# 6. 设置头文件默认搜索路径
|
||||||
|
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../common)
|
||||||
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../third/include)
|
||||||
|
|
||||||
|
#8. 设置安装路径
|
||||||
|
INSTALL(TARGETS ${target} RUNTIME DESTINATION bin)
|
||||||
16
gateway/dockerfile
Normal file
16
gateway/dockerfile
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# 声明基础经镜像来源
|
||||||
|
FROM debian:12
|
||||||
|
|
||||||
|
# 声明工作目录
|
||||||
|
WORKDIR /im
|
||||||
|
RUN mkdir -p /im/logs &&\
|
||||||
|
mkdir -p /im/data &&\
|
||||||
|
mkdir -p /im/conf &&\
|
||||||
|
mkdir -p /im/bin
|
||||||
|
|
||||||
|
# 将可执行程序依赖,拷贝进镜像
|
||||||
|
COPY ./build/gateway_server /im/bin/
|
||||||
|
# 将可执行程序文件,拷贝进镜像
|
||||||
|
COPY ./depends /lib/x86_64-linux-gnu/
|
||||||
|
# 设置容器启动的默认操作 ---运行程序
|
||||||
|
CMD /im/bin/gateway_server -flagfile=/im/conf/gateway_server.conf
|
||||||
17
gateway/gateway_server.conf
Normal file
17
gateway/gateway_server.conf
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
-run_mode=true
|
||||||
|
-log_file=/im/logs/gateway.log
|
||||||
|
-log_level=0
|
||||||
|
-http_listen_port=9000
|
||||||
|
-websocket_listen_port=9001
|
||||||
|
-registry_host=http://10.0.0.235:2379
|
||||||
|
-base_service=/service
|
||||||
|
-file_service=/service/file_service
|
||||||
|
-friend_service=/service/friend_service
|
||||||
|
-message_service=/service/message_service
|
||||||
|
-user_service=/service/user_service
|
||||||
|
-speech_service=/service/speech_service
|
||||||
|
-transmite_service=/service/transmite_service
|
||||||
|
-redis_host=10.0.0.235
|
||||||
|
-redis_port=6379
|
||||||
|
-redis_db=0
|
||||||
|
-redis_keep_alive=true
|
||||||
64
gateway/source/connection.hpp
Normal file
64
gateway/source/connection.hpp
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#include <websocketpp/config/asio_no_tls.hpp>
|
||||||
|
#include <websocketpp/server.hpp>
|
||||||
|
#include "logger.hpp"
|
||||||
|
|
||||||
|
namespace bite_im {
|
||||||
|
typedef websocketpp::server<websocketpp::config::asio> server_t;
|
||||||
|
// 连接的类型: server_t::connection_ptr
|
||||||
|
|
||||||
|
class Connection {
|
||||||
|
public:
|
||||||
|
struct Client {
|
||||||
|
Client(const std::string &u, const std::string &s):uid(u), ssid(s){}
|
||||||
|
std::string uid;
|
||||||
|
std::string ssid;
|
||||||
|
};
|
||||||
|
using ptr = std::shared_ptr<Connection>;
|
||||||
|
Connection(){}
|
||||||
|
~Connection() {}
|
||||||
|
void insert(const server_t::connection_ptr &conn,
|
||||||
|
const std::string &uid, const std::string &ssid) {
|
||||||
|
std::unique_lock<std::mutex> lock(_mutex);
|
||||||
|
_uid_connections.insert(std::make_pair(uid, conn));
|
||||||
|
_conn_clients.insert(std::make_pair(conn, Client(uid, ssid)));
|
||||||
|
LOG_DEBUG("新增长连接用户信息:{}-{}-{}", (size_t)conn.get(), uid, ssid);
|
||||||
|
}
|
||||||
|
server_t::connection_ptr connection(const std::string &uid) {
|
||||||
|
std::unique_lock<std::mutex> lock(_mutex);
|
||||||
|
auto it = _uid_connections.find(uid);
|
||||||
|
if (it == _uid_connections.end()) {
|
||||||
|
LOG_ERROR("未找到 {} 客户端的长连接!", uid);
|
||||||
|
return server_t::connection_ptr();
|
||||||
|
}
|
||||||
|
LOG_DEBUG("找到 {} 客户端的长连接!", uid);
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
bool client(const server_t::connection_ptr &conn, std::string &uid, std::string &ssid) {
|
||||||
|
std::unique_lock<std::mutex> lock(_mutex);
|
||||||
|
auto it = _conn_clients.find(conn);
|
||||||
|
if (it == _conn_clients.end()) {
|
||||||
|
LOG_ERROR("获取-未找到长连接 {} 对应的客户端信息!", (size_t)conn.get());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uid = it->second.uid;
|
||||||
|
ssid = it->second.ssid;
|
||||||
|
LOG_DEBUG("获取长连接客户端信息成功!");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
void remove(const server_t::connection_ptr &conn) {
|
||||||
|
std::unique_lock<std::mutex> lock(_mutex);
|
||||||
|
auto it = _conn_clients.find(conn);
|
||||||
|
if (it == _conn_clients.end()) {
|
||||||
|
LOG_ERROR("删除-未找到长连接 {} 对应的客户端信息!", (size_t)conn.get());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_uid_connections.erase(it->second.uid);
|
||||||
|
_conn_clients.erase(it);
|
||||||
|
LOG_DEBUG("删除长连接信息完毕!");
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::mutex _mutex;
|
||||||
|
std::unordered_map<std::string, server_t::connection_ptr> _uid_connections;
|
||||||
|
std::unordered_map<server_t::connection_ptr, Client> _conn_clients;
|
||||||
|
};
|
||||||
|
}
|
||||||
39
gateway/source/gateway_server.cc
Normal file
39
gateway/source/gateway_server.cc
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
//主要实现语音识别子服务的服务器的搭建
|
||||||
|
#include "gateway_server.hpp"
|
||||||
|
|
||||||
|
DEFINE_bool(run_mode, false, "程序的运行模式,false-调试; true-发布;");
|
||||||
|
DEFINE_string(log_file, "", "发布模式下,用于指定日志的输出文件");
|
||||||
|
DEFINE_int32(log_level, 0, "发布模式下,用于指定日志输出等级");
|
||||||
|
|
||||||
|
DEFINE_int32(http_listen_port, 9000, "HTTP服务器监听端口");
|
||||||
|
DEFINE_int32(websocket_listen_port, 9001, "Websocket服务器监听端口");
|
||||||
|
|
||||||
|
DEFINE_string(registry_host, "http://127.0.0.1:2379", "服务注册中心地址");
|
||||||
|
DEFINE_string(base_service, "/service", "服务监控根目录");
|
||||||
|
DEFINE_string(file_service, "/service/file_service", "文件存储子服务名称");
|
||||||
|
DEFINE_string(friend_service, "/service/friend_service", "好友管理子服务名称");
|
||||||
|
DEFINE_string(message_service, "/service/message_service", "消息存储子服务名称");
|
||||||
|
DEFINE_string(user_service, "/service/user_service", "用户管理子服务名称");
|
||||||
|
DEFINE_string(speech_service, "/service/speech_service", "语音识别子服务名称");
|
||||||
|
DEFINE_string(transmite_service, "/service/transmite_service", "转发管理子服务名称");
|
||||||
|
|
||||||
|
DEFINE_string(redis_host, "127.0.0.1", "Redis服务器访问地址");
|
||||||
|
DEFINE_int32(redis_port, 6379, "Redis服务器访问端口");
|
||||||
|
DEFINE_int32(redis_db, 0, "Redis默认库号");
|
||||||
|
DEFINE_bool(redis_keep_alive, true, "Redis长连接保活选项");
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
google::ParseCommandLineFlags(&argc, &argv, true);
|
||||||
|
bite_im::init_logger(FLAGS_run_mode, FLAGS_log_file, FLAGS_log_level);
|
||||||
|
|
||||||
|
bite_im::GatewayServerBuilder gsb;
|
||||||
|
gsb.make_redis_object(FLAGS_redis_host, FLAGS_redis_port, FLAGS_redis_db, FLAGS_redis_keep_alive);
|
||||||
|
gsb.make_discovery_object(FLAGS_registry_host, FLAGS_base_service, FLAGS_file_service,
|
||||||
|
FLAGS_speech_service, FLAGS_message_service, FLAGS_friend_service,
|
||||||
|
FLAGS_user_service, FLAGS_transmite_service);
|
||||||
|
gsb.make_server_object(FLAGS_websocket_listen_port, FLAGS_http_listen_port);
|
||||||
|
auto server = gsb.build();
|
||||||
|
server->start();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
1421
gateway/source/gateway_server.hpp
Normal file
1421
gateway/source/gateway_server.hpp
Normal file
File diff suppressed because it is too large
Load Diff
89
message/CMakeLists.txt
Normal file
89
message/CMakeLists.txt
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
# 1. 添加cmake版本说明
|
||||||
|
cmake_minimum_required(VERSION 3.1.3)
|
||||||
|
# 2. 声明工程名称
|
||||||
|
project(message_server)
|
||||||
|
|
||||||
|
set(target "message_server")
|
||||||
|
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
|
||||||
|
|
||||||
|
# 3. 检测并生成ODB框架代码
|
||||||
|
# 1. 添加所需的proto映射代码文件名称
|
||||||
|
set(proto_path ${CMAKE_CURRENT_SOURCE_DIR}/../proto)
|
||||||
|
set(proto_files base.proto user.proto file.proto message.proto)
|
||||||
|
# 2. 检测框架代码文件是否已经生成
|
||||||
|
set(proto_hxx "")
|
||||||
|
set(proto_cxx "")
|
||||||
|
set(proto_srcs "")
|
||||||
|
foreach(proto_file ${proto_files})
|
||||||
|
# 3. 如果没有生成,则预定义生成指令 -- 用于在构建项目之间先生成框架代码
|
||||||
|
string(REPLACE ".proto" ".pb.cc" proto_cc ${proto_file})
|
||||||
|
string(REPLACE ".proto" ".pb.h" proto_hh ${proto_file})
|
||||||
|
if (NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}${proto_cc})
|
||||||
|
add_custom_command(
|
||||||
|
PRE_BUILD
|
||||||
|
COMMAND protoc
|
||||||
|
ARGS --cpp_out=${CMAKE_CURRENT_BINARY_DIR} -I ${proto_path} --experimental_allow_proto3_optional ${proto_path}/${proto_file}
|
||||||
|
DEPENDS ${proto_path}/${proto_file}
|
||||||
|
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${proto_cc}
|
||||||
|
COMMENT "生成Protobuf框架代码文件:" ${CMAKE_CURRENT_BINARY_DIR}/${proto_cc}
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
list(APPEND proto_srcs ${CMAKE_CURRENT_BINARY_DIR}/${proto_cc})
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# 3. 检测并生成ODB框架代码
|
||||||
|
# 1. 添加所需的odb映射代码文件名称
|
||||||
|
set(odb_path ${CMAKE_CURRENT_SOURCE_DIR}/../odb)
|
||||||
|
set(odb_files message.hxx)
|
||||||
|
# 2. 检测框架代码文件是否已经生成
|
||||||
|
set(odb_hxx "")
|
||||||
|
set(odb_cxx "")
|
||||||
|
set(odb_srcs "")
|
||||||
|
foreach(odb_file ${odb_files})
|
||||||
|
# 3. 如果没有生成,则预定义生成指令 -- 用于在构建项目之间先生成框架代码
|
||||||
|
string(REPLACE ".hxx" "-odb.hxx" odb_hxx ${odb_file})
|
||||||
|
string(REPLACE ".hxx" "-odb.cxx" odb_cxx ${odb_file})
|
||||||
|
if (NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}${odb_cxx})
|
||||||
|
add_custom_command(
|
||||||
|
PRE_BUILD
|
||||||
|
COMMAND odb
|
||||||
|
ARGS -d mysql --std c++11 --generate-query --generate-schema --profile boost/date-time ${odb_path}/${odb_file}
|
||||||
|
DEPENDS ${odb_path}/${odb_file}
|
||||||
|
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${odb_cxx}
|
||||||
|
COMMENT "生成ODB框架代码文件:" ${CMAKE_CURRENT_BINARY_DIR}/${odb_cxx}
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
# 4. 将所有生成的框架源码文件名称保存起来 student-odb.cxx classes-odb.cxx
|
||||||
|
list(APPEND odb_srcs ${CMAKE_CURRENT_BINARY_DIR}/${odb_cxx})
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# 4. 获取源码目录下的所有源码文件
|
||||||
|
set(src_files "")
|
||||||
|
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/source src_files)
|
||||||
|
# 5. 声明目标及依赖
|
||||||
|
add_executable(${target} ${src_files} ${proto_srcs} ${odb_srcs})
|
||||||
|
# 7. 设置需要连接的库
|
||||||
|
target_link_libraries(${target} -lgflags
|
||||||
|
-lspdlog -lfmt -lbrpc -lssl -lcrypto
|
||||||
|
-lprotobuf -lleveldb -letcd-cpp-api
|
||||||
|
-lcpprest -lcurl -lodb-mysql -lodb -lodb-boost
|
||||||
|
/usr/lib/x86_64-linux-gnu/libjsoncpp.so.19
|
||||||
|
-lcpr -lelasticlient
|
||||||
|
-lamqpcpp -lev)
|
||||||
|
|
||||||
|
|
||||||
|
set(test_client "message_client")
|
||||||
|
set(test_files "")
|
||||||
|
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/test test_files)
|
||||||
|
add_executable(${test_client} ${test_files} ${proto_srcs})
|
||||||
|
target_link_libraries(${test_client} -pthread -lgtest -lgflags -lspdlog -lfmt -lbrpc -lssl -lcrypto -lprotobuf -lleveldb -letcd-cpp-api -lcpprest -lcurl /usr/lib/x86_64-linux-gnu/libjsoncpp.so.19)
|
||||||
|
|
||||||
|
# 6. 设置头文件默认搜索路径
|
||||||
|
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../common)
|
||||||
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../odb)
|
||||||
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../third/include)
|
||||||
|
|
||||||
|
#8. 设置安装路径
|
||||||
|
INSTALL(TARGETS ${target} ${test_client} RUNTIME DESTINATION bin)
|
||||||
16
message/dockerfile
Normal file
16
message/dockerfile
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# 声明基础经镜像来源
|
||||||
|
FROM debian:12
|
||||||
|
|
||||||
|
# 声明工作目录
|
||||||
|
WORKDIR /im
|
||||||
|
RUN mkdir -p /im/logs &&\
|
||||||
|
mkdir -p /im/data &&\
|
||||||
|
mkdir -p /im/conf &&\
|
||||||
|
mkdir -p /im/bin
|
||||||
|
|
||||||
|
# 将可执行程序依赖,拷贝进镜像
|
||||||
|
COPY ./build/message_server /im/bin/
|
||||||
|
# 将可执行程序文件,拷贝进镜像
|
||||||
|
COPY ./depends /lib/x86_64-linux-gnu/
|
||||||
|
# 设置容器启动的默认操作 ---运行程序
|
||||||
|
CMD /im/bin/message_server -flagfile=/im/conf/message_server.conf
|
||||||
26
message/message_server.conf
Normal file
26
message/message_server.conf
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
-run_mode=true
|
||||||
|
-log_file=/im/logs/message.log
|
||||||
|
-log_level=0
|
||||||
|
-registry_host=http://10.0.0.235:2379
|
||||||
|
-instance_name=/message_service/instance
|
||||||
|
-access_host=10.0.0.235:10005
|
||||||
|
-listen_port=10005
|
||||||
|
-rpc_timeout=-1
|
||||||
|
-rpc_threads=1
|
||||||
|
-base_service=/service
|
||||||
|
-user_service=/service/user_service
|
||||||
|
-file_service=/service/file_service
|
||||||
|
-es_host=http://10.0.0.235:9200/
|
||||||
|
-mysql_host=10.0.0.235
|
||||||
|
-mysql_user=root
|
||||||
|
-mysql_pswd=123456
|
||||||
|
-mysql_db=bite_im
|
||||||
|
-mysql_cset=utf8
|
||||||
|
-mysql_port=0
|
||||||
|
-mysql_pool_count=4
|
||||||
|
-mq_user=root
|
||||||
|
-mq_pswd=123456
|
||||||
|
-mq_host=10.0.0.235:5672
|
||||||
|
-mq_msg_exchange=msg_exchange
|
||||||
|
-mq_msg_queue=msg_queue
|
||||||
|
-mq_msg_binding_key=msg_queue
|
||||||
56
message/source/message_server.cc
Normal file
56
message/source/message_server.cc
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
//主要实现语音识别子服务的服务器的搭建
|
||||||
|
#include "message_server.hpp"
|
||||||
|
|
||||||
|
DEFINE_bool(run_mode, false, "程序的运行模式,false-调试; true-发布;");
|
||||||
|
DEFINE_string(log_file, "", "发布模式下,用于指定日志的输出文件");
|
||||||
|
DEFINE_int32(log_level, 0, "发布模式下,用于指定日志输出等级");
|
||||||
|
|
||||||
|
DEFINE_string(registry_host, "http://127.0.0.1:2379", "服务注册中心地址");
|
||||||
|
DEFINE_string(instance_name, "/message_service/instance", "当前实例名称");
|
||||||
|
DEFINE_string(access_host, "127.0.0.1:10005", "当前实例的外部访问地址");
|
||||||
|
|
||||||
|
DEFINE_int32(listen_port, 10005, "Rpc服务器监听端口");
|
||||||
|
DEFINE_int32(rpc_timeout, -1, "Rpc调用超时时间");
|
||||||
|
DEFINE_int32(rpc_threads, 1, "Rpc的IO线程数量");
|
||||||
|
|
||||||
|
|
||||||
|
DEFINE_string(base_service, "/service", "服务监控根目录");
|
||||||
|
DEFINE_string(file_service, "/service/file_service", "文件管理子服务名称");
|
||||||
|
DEFINE_string(user_service, "/service/user_service", "用户管理子服务名称");
|
||||||
|
|
||||||
|
DEFINE_string(es_host, "http://127.0.0.1:9200/", "ES搜索引擎服务器URL");
|
||||||
|
|
||||||
|
DEFINE_string(mysql_host, "127.0.0.1", "Mysql服务器访问地址");
|
||||||
|
DEFINE_string(mysql_user, "root", "Mysql服务器访问用户名");
|
||||||
|
DEFINE_string(mysql_pswd, "123456", "Mysql服务器访问密码");
|
||||||
|
DEFINE_string(mysql_db, "bite_im", "Mysql默认库名称");
|
||||||
|
DEFINE_string(mysql_cset, "utf8", "Mysql客户端字符集");
|
||||||
|
DEFINE_int32(mysql_port, 0, "Mysql服务器访问端口");
|
||||||
|
DEFINE_int32(mysql_pool_count, 4, "Mysql连接池最大连接数量");
|
||||||
|
|
||||||
|
DEFINE_string(mq_user, "root", "消息队列服务器访问用户名");
|
||||||
|
DEFINE_string(mq_pswd, "123456", "消息队列服务器访问密码");
|
||||||
|
DEFINE_string(mq_host, "127.0.0.1:5672", "消息队列服务器访问地址");
|
||||||
|
DEFINE_string(mq_msg_exchange, "msg_exchange", "持久化消息的发布交换机名称");
|
||||||
|
DEFINE_string(mq_msg_queue, "msg_queue", "持久化消息的发布队列名称");
|
||||||
|
DEFINE_string(mq_msg_binding_key, "msg_queue", "持久化消息的发布队列名称");
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
google::ParseCommandLineFlags(&argc, &argv, true);
|
||||||
|
bite_im::init_logger(FLAGS_run_mode, FLAGS_log_file, FLAGS_log_level);
|
||||||
|
|
||||||
|
bite_im::MessageServerBuilder msb;
|
||||||
|
msb.make_mq_object(FLAGS_mq_user, FLAGS_mq_pswd, FLAGS_mq_host,
|
||||||
|
FLAGS_mq_msg_exchange, FLAGS_mq_msg_queue, FLAGS_mq_msg_binding_key);
|
||||||
|
msb.make_es_object({FLAGS_es_host});
|
||||||
|
msb.make_mysql_object(FLAGS_mysql_user, FLAGS_mysql_pswd, FLAGS_mysql_host,
|
||||||
|
FLAGS_mysql_db, FLAGS_mysql_cset, FLAGS_mysql_port, FLAGS_mysql_pool_count);
|
||||||
|
msb.make_discovery_object(FLAGS_registry_host, FLAGS_base_service, FLAGS_file_service, FLAGS_user_service);
|
||||||
|
msb.make_rpc_server(FLAGS_listen_port, FLAGS_rpc_timeout, FLAGS_rpc_threads);
|
||||||
|
msb.make_registry_object(FLAGS_registry_host, FLAGS_base_service + FLAGS_instance_name, FLAGS_access_host);
|
||||||
|
auto server = msb.build();
|
||||||
|
server->start();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
579
message/source/message_server.hpp
Normal file
579
message/source/message_server.hpp
Normal file
@ -0,0 +1,579 @@
|
|||||||
|
//实现语音识别子服务
|
||||||
|
#include <brpc/server.h>
|
||||||
|
#include <butil/logging.h>
|
||||||
|
|
||||||
|
#include "data_es.hpp" // es数据管理客户端封装
|
||||||
|
#include "mysql_message.hpp" // mysql数据管理客户端封装
|
||||||
|
#include "etcd.hpp" // 服务注册模块封装
|
||||||
|
#include "logger.hpp" // 日志模块封装
|
||||||
|
#include "utils.hpp" // 基础工具接口
|
||||||
|
#include "channel.hpp" // 信道管理模块封装
|
||||||
|
#include "rabbitmq.hpp"
|
||||||
|
|
||||||
|
#include "message.pb.h" // protobuf框架代码
|
||||||
|
#include "base.pb.h" // protobuf框架代码
|
||||||
|
#include "file.pb.h" // protobuf框架代码
|
||||||
|
#include "user.pb.h" // protobuf框架代码
|
||||||
|
|
||||||
|
namespace bite_im{
|
||||||
|
class MessageServiceImpl : public bite_im::MsgStorageService {
|
||||||
|
public:
|
||||||
|
MessageServiceImpl(
|
||||||
|
const std::shared_ptr<elasticlient::Client> &es_client,
|
||||||
|
const std::shared_ptr<odb::core::database> &mysql_client,
|
||||||
|
const ServiceManager::ptr &channel_manager,
|
||||||
|
const std::string &file_service_name,
|
||||||
|
const std::string &user_service_name) :
|
||||||
|
_es_message(std::make_shared<ESMessage>(es_client)),
|
||||||
|
_mysql_message(std::make_shared<MessageTable>(mysql_client)),
|
||||||
|
_file_service_name(file_service_name),
|
||||||
|
_user_service_name(user_service_name),
|
||||||
|
_mm_channels(channel_manager){
|
||||||
|
_es_message->createIndex();
|
||||||
|
}
|
||||||
|
~MessageServiceImpl(){}
|
||||||
|
virtual void GetHistoryMsg(::google::protobuf::RpcController* controller,
|
||||||
|
const ::bite_im::GetHistoryMsgReq* request,
|
||||||
|
::bite_im::GetHistoryMsgRsp* 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 chat_ssid = request->chat_session_id();
|
||||||
|
boost::posix_time::ptime stime = boost::posix_time::from_time_t(request->start_time());
|
||||||
|
boost::posix_time::ptime etime = boost::posix_time::from_time_t(request->over_time());
|
||||||
|
//2. 从数据库中进行消息查询
|
||||||
|
auto msg_lists = _mysql_message->range(chat_ssid, stime, etime);
|
||||||
|
if (msg_lists.empty()) {
|
||||||
|
response->set_request_id(rid);
|
||||||
|
response->set_success(true);
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
//3. 统计所有文件类型消息的文件ID,并从文件子服务进行批量文件下载
|
||||||
|
std::unordered_set<std::string> file_id_lists;
|
||||||
|
for (const auto &msg : msg_lists) {
|
||||||
|
if (msg.file_id().empty()) continue;
|
||||||
|
LOG_DEBUG("需要下载的文件ID: {}", msg.file_id());
|
||||||
|
file_id_lists.insert(msg.file_id());
|
||||||
|
}
|
||||||
|
std::unordered_map<std::string, std::string> file_data_lists;
|
||||||
|
bool ret = _GetFile(rid, file_id_lists, file_data_lists);
|
||||||
|
if (ret == false) {
|
||||||
|
LOG_ERROR("{} 批量文件数据下载失败!", rid);
|
||||||
|
return err_response(rid, "批量文件数据下载失败!");
|
||||||
|
}
|
||||||
|
//4. 统计所有消息的发送者用户ID,从用户子服务进行批量用户信息获取
|
||||||
|
std::unordered_set<std::string> user_id_lists; // {猪爸爸吧, 祝妈妈,猪爸爸吧,祝爸爸}
|
||||||
|
for (const auto &msg : msg_lists) {
|
||||||
|
user_id_lists.insert(msg.user_id());
|
||||||
|
}
|
||||||
|
std::unordered_map<std::string, UserInfo> user_lists;
|
||||||
|
ret = _GetUser(rid, user_id_lists, user_lists);
|
||||||
|
if (ret == false) {
|
||||||
|
LOG_ERROR("{} 批量用户数据获取失败!", rid);
|
||||||
|
return err_response(rid, "批量用户数据获取失败!");
|
||||||
|
}
|
||||||
|
//5. 组织响应
|
||||||
|
response->set_request_id(rid);
|
||||||
|
response->set_success(true);
|
||||||
|
for (const auto &msg : msg_lists) {
|
||||||
|
auto message_info = response->add_msg_list();
|
||||||
|
message_info->set_message_id(msg.message_id());
|
||||||
|
message_info->set_chat_session_id(msg.session_id());
|
||||||
|
message_info->set_timestamp(boost::posix_time::to_time_t(msg.create_time()));
|
||||||
|
message_info->mutable_sender()->CopyFrom(user_lists[msg.user_id()]);
|
||||||
|
switch(msg.message_type()) {
|
||||||
|
case MessageType::STRING:
|
||||||
|
message_info->mutable_message()->set_message_type(MessageType::STRING);
|
||||||
|
message_info->mutable_message()->mutable_string_message()->set_content(msg.content());
|
||||||
|
break;
|
||||||
|
case MessageType::IMAGE:
|
||||||
|
message_info->mutable_message()->set_message_type(MessageType::IMAGE);
|
||||||
|
message_info->mutable_message()->mutable_image_message()->set_file_id(msg.file_id());
|
||||||
|
message_info->mutable_message()->mutable_image_message()->set_image_content(file_data_lists[msg.file_id()]);
|
||||||
|
break;
|
||||||
|
case MessageType::FILE:
|
||||||
|
message_info->mutable_message()->set_message_type(MessageType::FILE);
|
||||||
|
message_info->mutable_message()->mutable_file_message()->set_file_id(msg.file_id());
|
||||||
|
message_info->mutable_message()->mutable_file_message()->set_file_size(msg.file_size());
|
||||||
|
message_info->mutable_message()->mutable_file_message()->set_file_name(msg.file_name());
|
||||||
|
message_info->mutable_message()->mutable_file_message()->set_file_contents(file_data_lists[msg.file_id()]);
|
||||||
|
break;
|
||||||
|
case MessageType::SPEECH:
|
||||||
|
message_info->mutable_message()->set_message_type(MessageType::SPEECH);
|
||||||
|
message_info->mutable_message()->mutable_speech_message()->set_file_id(msg.file_id());
|
||||||
|
message_info->mutable_message()->mutable_speech_message()->set_file_contents(file_data_lists[msg.file_id()]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG_ERROR("消息类型错误!!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
virtual void GetRecentMsg(::google::protobuf::RpcController* controller,
|
||||||
|
const ::bite_im::GetRecentMsgReq* request,
|
||||||
|
::bite_im::GetRecentMsgRsp* 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,会话ID,要获取的消息数量
|
||||||
|
std::string rid = request->request_id();
|
||||||
|
std::string chat_ssid = request->chat_session_id();
|
||||||
|
int msg_count = request->msg_count();
|
||||||
|
//2. 从数据库,获取最近的消息元信息
|
||||||
|
auto msg_lists = _mysql_message->recent(chat_ssid, msg_count);
|
||||||
|
if (msg_lists.empty()) {
|
||||||
|
response->set_request_id(rid);
|
||||||
|
response->set_success(true);
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
//3. 统计所有消息中文件类型消息的文件ID列表,从文件子服务下载文件
|
||||||
|
std::unordered_set<std::string> file_id_lists;
|
||||||
|
for (const auto &msg : msg_lists) {
|
||||||
|
if (msg.file_id().empty()) continue;
|
||||||
|
LOG_DEBUG("需要下载的文件ID: {}", msg.file_id());
|
||||||
|
file_id_lists.insert(msg.file_id());
|
||||||
|
}
|
||||||
|
std::unordered_map<std::string, std::string> file_data_lists;
|
||||||
|
bool ret = _GetFile(rid, file_id_lists, file_data_lists);
|
||||||
|
if (ret == false) {
|
||||||
|
LOG_ERROR("{} 批量文件数据下载失败!", rid);
|
||||||
|
return err_response(rid, "批量文件数据下载失败!");
|
||||||
|
}
|
||||||
|
//4. 统计所有消息的发送者用户ID,从用户子服务进行批量用户信息获取
|
||||||
|
std::unordered_set<std::string> user_id_lists;
|
||||||
|
for (const auto &msg : msg_lists) {
|
||||||
|
user_id_lists.insert(msg.user_id());
|
||||||
|
}
|
||||||
|
std::unordered_map<std::string, UserInfo> user_lists;
|
||||||
|
ret = _GetUser(rid, user_id_lists, user_lists);
|
||||||
|
if (ret == false) {
|
||||||
|
LOG_ERROR("{} 批量用户数据获取失败!", rid);
|
||||||
|
return err_response(rid, "批量用户数据获取失败!");
|
||||||
|
}
|
||||||
|
//5. 组织响应
|
||||||
|
response->set_request_id(rid);
|
||||||
|
response->set_success(true);
|
||||||
|
for (const auto &msg : msg_lists) {
|
||||||
|
auto message_info = response->add_msg_list();
|
||||||
|
message_info->set_message_id(msg.message_id());
|
||||||
|
message_info->set_chat_session_id(msg.session_id());
|
||||||
|
message_info->set_timestamp(boost::posix_time::to_time_t(msg.create_time()));
|
||||||
|
message_info->mutable_sender()->CopyFrom(user_lists[msg.user_id()]);
|
||||||
|
switch(msg.message_type()) {
|
||||||
|
case MessageType::STRING:
|
||||||
|
message_info->mutable_message()->set_message_type(MessageType::STRING);
|
||||||
|
message_info->mutable_message()->mutable_string_message()->set_content(msg.content());
|
||||||
|
break;
|
||||||
|
case MessageType::IMAGE:
|
||||||
|
message_info->mutable_message()->set_message_type(MessageType::IMAGE);
|
||||||
|
message_info->mutable_message()->mutable_image_message()->set_file_id(msg.file_id());
|
||||||
|
message_info->mutable_message()->mutable_image_message()->set_image_content(file_data_lists[msg.file_id()]);
|
||||||
|
break;
|
||||||
|
case MessageType::FILE:
|
||||||
|
message_info->mutable_message()->set_message_type(MessageType::FILE);
|
||||||
|
message_info->mutable_message()->mutable_file_message()->set_file_id(msg.file_id());
|
||||||
|
message_info->mutable_message()->mutable_file_message()->set_file_size(msg.file_size());
|
||||||
|
message_info->mutable_message()->mutable_file_message()->set_file_name(msg.file_name());
|
||||||
|
message_info->mutable_message()->mutable_file_message()->set_file_contents(file_data_lists[msg.file_id()]);
|
||||||
|
break;
|
||||||
|
case MessageType::SPEECH:
|
||||||
|
message_info->mutable_message()->set_message_type(MessageType::SPEECH);
|
||||||
|
message_info->mutable_message()->mutable_speech_message()->set_file_id(msg.file_id());
|
||||||
|
message_info->mutable_message()->mutable_speech_message()->set_file_contents(file_data_lists[msg.file_id()]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG_ERROR("消息类型错误!!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
virtual void MsgSearch(::google::protobuf::RpcController* controller,
|
||||||
|
const ::bite_im::MsgSearchReq* request,
|
||||||
|
::bite_im::MsgSearchRsp* 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,会话ID, 关键字
|
||||||
|
std::string rid = request->request_id();
|
||||||
|
std::string chat_ssid = request->chat_session_id();
|
||||||
|
std::string skey = request->search_key();
|
||||||
|
//2. 从ES搜索引擎中进行关键字消息搜索,得到消息列表
|
||||||
|
auto msg_lists = _es_message->search(skey, chat_ssid);
|
||||||
|
if (msg_lists.empty()) {
|
||||||
|
response->set_request_id(rid);
|
||||||
|
response->set_success(true);
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
//3. 组织所有消息的用户ID,从用户子服务获取用户信息
|
||||||
|
std::unordered_set<std::string> user_id_lists;
|
||||||
|
for (const auto &msg : msg_lists) {
|
||||||
|
user_id_lists.insert(msg.user_id());
|
||||||
|
}
|
||||||
|
std::unordered_map<std::string, UserInfo> user_lists;
|
||||||
|
bool ret = _GetUser(rid, user_id_lists, user_lists);
|
||||||
|
if (ret == false) {
|
||||||
|
LOG_ERROR("{} 批量用户数据获取失败!", rid);
|
||||||
|
return err_response(rid, "批量用户数据获取失败!");
|
||||||
|
}
|
||||||
|
//4. 组织响应
|
||||||
|
response->set_request_id(rid);
|
||||||
|
response->set_success(true);
|
||||||
|
for (const auto &msg : msg_lists) {
|
||||||
|
auto message_info = response->add_msg_list();
|
||||||
|
message_info->set_message_id(msg.message_id());
|
||||||
|
message_info->set_chat_session_id(msg.session_id());
|
||||||
|
message_info->set_timestamp(boost::posix_time::to_time_t(msg.create_time()));
|
||||||
|
message_info->mutable_sender()->CopyFrom(user_lists[msg.user_id()]);
|
||||||
|
message_info->mutable_message()->set_message_type(MessageType::STRING);
|
||||||
|
message_info->mutable_message()->mutable_string_message()->set_content(msg.content());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//rabbitmq获得消息
|
||||||
|
void onMessage(const char *body, size_t sz) {
|
||||||
|
LOG_DEBUG("收到新消息,进行存储处理!");
|
||||||
|
//1. 取出序列化的消息内容,进行反序列化
|
||||||
|
bite_im::MessageInfo message;
|
||||||
|
bool ret = message.ParseFromArray(body, sz);
|
||||||
|
if (ret == false) {
|
||||||
|
LOG_ERROR("对消费到的消息进行反序列化失败!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//2. 根据不同的消息类型进行不同的处理
|
||||||
|
std::string file_id, file_name, content;
|
||||||
|
int64_t file_size;
|
||||||
|
switch(message.message().message_type()) {
|
||||||
|
// 1. 如果是一个文本类型消息,取元信息存储到ES中
|
||||||
|
case MessageType::STRING:
|
||||||
|
content = message.message().string_message().content();
|
||||||
|
ret = _es_message->appendData(
|
||||||
|
message.sender().user_id(),
|
||||||
|
message.message_id(),
|
||||||
|
message.timestamp(),
|
||||||
|
message.chat_session_id(),
|
||||||
|
content);
|
||||||
|
if (ret == false) {
|
||||||
|
LOG_ERROR("文本消息向存储引擎进行存储失败!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
// 2. 如果是一个图片/语音/文件消息,则取出数据存储到文件子服务中,并获取文件ID
|
||||||
|
case MessageType::IMAGE:
|
||||||
|
{
|
||||||
|
const auto &msg = message.message().image_message();
|
||||||
|
ret = _PutFile("", msg.image_content(), msg.image_content().size(), file_id);
|
||||||
|
if (ret == false) {
|
||||||
|
LOG_ERROR("上传图片到文件子服务失败!");
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MessageType::FILE:
|
||||||
|
{
|
||||||
|
const auto &msg = message.message().file_message();
|
||||||
|
file_name = msg.file_name();
|
||||||
|
file_size = msg.file_size();
|
||||||
|
ret = _PutFile(file_name, msg.file_contents(), file_size, file_id);
|
||||||
|
if (ret == false) {
|
||||||
|
LOG_ERROR("上传文件到文件子服务失败!");
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MessageType::SPEECH:
|
||||||
|
{
|
||||||
|
const auto &msg = message.message().speech_message();
|
||||||
|
ret = _PutFile("", msg.file_contents(), msg.file_contents().size(), file_id);
|
||||||
|
if (ret == false) {
|
||||||
|
LOG_ERROR("上传语音到文件子服务失败!");
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG_ERROR("消息类型错误!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//3. 提取消息的元信息,存储到mysql数据库中
|
||||||
|
bite_im::Message msg(message.message_id(),
|
||||||
|
message.chat_session_id(),
|
||||||
|
message.sender().user_id(),
|
||||||
|
message.message().message_type(),
|
||||||
|
boost::posix_time::from_time_t(message.timestamp()));
|
||||||
|
msg.content(content);
|
||||||
|
msg.file_id(file_id);
|
||||||
|
msg.file_name(file_name);
|
||||||
|
msg.file_size(file_size);
|
||||||
|
ret = _mysql_message->insert(msg);
|
||||||
|
if (ret == false) {
|
||||||
|
LOG_ERROR("向数据库插入新消息失败!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
bool _GetUser(const std::string &rid,
|
||||||
|
const std::unordered_set<std::string> &user_id_lists,
|
||||||
|
std::unordered_map<std::string, UserInfo> &user_lists) {
|
||||||
|
auto channel = _mm_channels->choose(_user_service_name);
|
||||||
|
if (!channel) {
|
||||||
|
LOG_ERROR("{} 没有可供访问的用户子服务节点!", _user_service_name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
UserService_Stub stub(channel.get());
|
||||||
|
GetMultiUserInfoReq req;
|
||||||
|
GetMultiUserInfoRsp rsp;
|
||||||
|
req.set_request_id(rid);
|
||||||
|
for (const auto &id : user_id_lists) {
|
||||||
|
req.add_users_id(id);
|
||||||
|
}
|
||||||
|
brpc::Controller cntl;
|
||||||
|
stub.GetMultiUserInfo(&cntl, &req, &rsp, nullptr);
|
||||||
|
if (cntl.Failed() == true || rsp.success() == false) {
|
||||||
|
LOG_ERROR("用户子服务调用失败:{}!", cntl.ErrorText());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto &umap = rsp.users_info();
|
||||||
|
for (auto it = umap.begin(); it != umap.end(); ++it) {
|
||||||
|
user_lists.insert(std::make_pair(it->first, it->second));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool _GetFile(const std::string &rid,
|
||||||
|
const std::unordered_set<std::string> &file_id_lists,
|
||||||
|
std::unordered_map<std::string, std::string> &file_data_lists) {
|
||||||
|
auto channel = _mm_channels->choose(_file_service_name);
|
||||||
|
if (!channel) {
|
||||||
|
LOG_ERROR("{} 没有可供访问的文件子服务节点!", _file_service_name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
FileService_Stub stub(channel.get());
|
||||||
|
GetMultiFileReq req;
|
||||||
|
GetMultiFileRsp rsp;
|
||||||
|
req.set_request_id(rid);
|
||||||
|
for (const auto &id : file_id_lists) {
|
||||||
|
req.add_file_id_list(id);
|
||||||
|
}
|
||||||
|
brpc::Controller cntl;
|
||||||
|
stub.GetMultiFile(&cntl, &req, &rsp, nullptr);
|
||||||
|
if (cntl.Failed() == true || rsp.success() == false) {
|
||||||
|
LOG_ERROR("文件子服务调用失败:{}!", cntl.ErrorText());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto &fmap = rsp.file_data();
|
||||||
|
for (auto it = fmap.begin(); it != fmap.end(); ++it) {
|
||||||
|
file_data_lists.insert(std::make_pair(it->first, it->second.file_content()));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool _PutFile(const std::string &filename,
|
||||||
|
const std::string &body,
|
||||||
|
const int64_t fsize,
|
||||||
|
std::string &file_id) {
|
||||||
|
//实现文件数据的上传
|
||||||
|
auto channel = _mm_channels->choose(_file_service_name);
|
||||||
|
if (!channel) {
|
||||||
|
LOG_ERROR("{} 没有可供访问的文件子服务节点!", _file_service_name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
FileService_Stub stub(channel.get());
|
||||||
|
PutSingleFileReq req;
|
||||||
|
PutSingleFileRsp rsp;
|
||||||
|
req.mutable_file_data()->set_file_name(filename);
|
||||||
|
req.mutable_file_data()->set_file_size(fsize);
|
||||||
|
req.mutable_file_data()->set_file_content(body);
|
||||||
|
brpc::Controller cntl;
|
||||||
|
stub.PutSingleFile(&cntl, &req, &rsp, nullptr);
|
||||||
|
if (cntl.Failed() == true || rsp.success() == false) {
|
||||||
|
LOG_ERROR("文件子服务调用失败:{}!", cntl.ErrorText());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
file_id = rsp.file_info().file_id();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
ESMessage::ptr _es_message;
|
||||||
|
MessageTable::ptr _mysql_message;
|
||||||
|
//这边是rpc调用客户端相关对象
|
||||||
|
std::string _user_service_name;
|
||||||
|
std::string _file_service_name;
|
||||||
|
ServiceManager::ptr _mm_channels;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MessageServer {
|
||||||
|
public:
|
||||||
|
using ptr = std::shared_ptr<MessageServer>;
|
||||||
|
MessageServer(const MQClient::ptr &mq_client,
|
||||||
|
const Discovery::ptr service_discoverer,
|
||||||
|
const Registry::ptr ®_client,
|
||||||
|
const std::shared_ptr<elasticlient::Client> &es_client,
|
||||||
|
const std::shared_ptr<odb::core::database> &mysql_client,
|
||||||
|
const std::shared_ptr<brpc::Server> &server):
|
||||||
|
_mq_client(mq_client),
|
||||||
|
_service_discoverer(service_discoverer),
|
||||||
|
_registry_client(reg_client),
|
||||||
|
_es_client(es_client),
|
||||||
|
_mysql_client(mysql_client),
|
||||||
|
_rpc_server(server){}
|
||||||
|
~MessageServer(){}
|
||||||
|
//搭建RPC服务器,并启动服务器
|
||||||
|
void start() {
|
||||||
|
_rpc_server->RunUntilAskedToQuit();
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
Discovery::ptr _service_discoverer;
|
||||||
|
Registry::ptr _registry_client;
|
||||||
|
MQClient::ptr _mq_client;
|
||||||
|
std::shared_ptr<elasticlient::Client> _es_client;
|
||||||
|
std::shared_ptr<odb::core::database> _mysql_client;
|
||||||
|
std::shared_ptr<brpc::Server> _rpc_server;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MessageServerBuilder {
|
||||||
|
public:
|
||||||
|
//构造es客户端对象
|
||||||
|
void make_es_object(const std::vector<std::string> host_list) {
|
||||||
|
_es_client = ESClientFactory::create(host_list);
|
||||||
|
}
|
||||||
|
//构造mysql客户端对象
|
||||||
|
void make_mysql_object(
|
||||||
|
const std::string &user,
|
||||||
|
const std::string &pswd,
|
||||||
|
const std::string &host,
|
||||||
|
const std::string &db,
|
||||||
|
const std::string &cset,
|
||||||
|
int port,
|
||||||
|
int conn_pool_count) {
|
||||||
|
_mysql_client = ODBFactory::create(user, pswd, host, db, cset, port, conn_pool_count);
|
||||||
|
}
|
||||||
|
//用于构造服务发现客户端&信道管理对象
|
||||||
|
void make_discovery_object(const std::string ®_host,
|
||||||
|
const std::string &base_service_name,
|
||||||
|
const std::string &file_service_name,
|
||||||
|
const std::string &user_service_name) {
|
||||||
|
_user_service_name = user_service_name;
|
||||||
|
_file_service_name = file_service_name;
|
||||||
|
_mm_channels = std::make_shared<ServiceManager>();
|
||||||
|
_mm_channels->declared(file_service_name);
|
||||||
|
_mm_channels->declared(user_service_name);
|
||||||
|
LOG_DEBUG("设置文件子服务为需添加管理的子服务:{}", file_service_name);
|
||||||
|
auto put_cb = std::bind(&ServiceManager::onServiceOnline, _mm_channels.get(), std::placeholders::_1, std::placeholders::_2);
|
||||||
|
auto del_cb = std::bind(&ServiceManager::onServiceOffline, _mm_channels.get(), std::placeholders::_1, std::placeholders::_2);
|
||||||
|
_service_discoverer = std::make_shared<Discovery>(reg_host, base_service_name, put_cb, del_cb);
|
||||||
|
}
|
||||||
|
//用于构造服务注册客户端对象
|
||||||
|
void make_registry_object(const std::string ®_host,
|
||||||
|
const std::string &service_name,
|
||||||
|
const std::string &access_host) {
|
||||||
|
_registry_client = std::make_shared<Registry>(reg_host);
|
||||||
|
_registry_client->registry(service_name, access_host);
|
||||||
|
}
|
||||||
|
//用于构造消息队列客户端对象
|
||||||
|
void make_mq_object(const std::string &user,
|
||||||
|
const std::string &passwd,
|
||||||
|
const std::string &host,
|
||||||
|
const std::string &exchange_name,
|
||||||
|
const std::string &queue_name,
|
||||||
|
const std::string &binding_key) {
|
||||||
|
_exchange_name = exchange_name;
|
||||||
|
_queue_name = queue_name;
|
||||||
|
_mq_client = std::make_shared<MQClient>(user, passwd, host);
|
||||||
|
_mq_client->declareComponents(exchange_name, queue_name, binding_key);
|
||||||
|
}
|
||||||
|
void make_rpc_server(uint16_t port, int32_t timeout, uint8_t num_threads) {
|
||||||
|
if (!_es_client) {
|
||||||
|
LOG_ERROR("还未初始化ES搜索引擎模块!");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
if (!_mysql_client) {
|
||||||
|
LOG_ERROR("还未初始化Mysql数据库模块!");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
if (!_mm_channels) {
|
||||||
|
LOG_ERROR("还未初始化信道管理模块!");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
_rpc_server = std::make_shared<brpc::Server>();
|
||||||
|
|
||||||
|
MessageServiceImpl *msg_service = new MessageServiceImpl(_es_client,
|
||||||
|
_mysql_client, _mm_channels, _file_service_name, _user_service_name);
|
||||||
|
int ret = _rpc_server->AddService(msg_service,
|
||||||
|
brpc::ServiceOwnership::SERVER_OWNS_SERVICE);
|
||||||
|
if (ret == -1) {
|
||||||
|
LOG_ERROR("添加Rpc服务失败!");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
brpc::ServerOptions options;
|
||||||
|
options.idle_timeout_sec = timeout;
|
||||||
|
options.num_threads = num_threads;
|
||||||
|
ret = _rpc_server->Start(port, &options);
|
||||||
|
if (ret == -1) {
|
||||||
|
LOG_ERROR("服务启动失败!");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto callback = std::bind(&MessageServiceImpl::onMessage, msg_service,
|
||||||
|
std::placeholders::_1, std::placeholders::_2);
|
||||||
|
_mq_client->consume(_queue_name, callback);
|
||||||
|
}
|
||||||
|
//构造RPC服务器对象
|
||||||
|
MessageServer::ptr build() {
|
||||||
|
if (!_service_discoverer) {
|
||||||
|
LOG_ERROR("还未初始化服务发现模块!");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
if (!_registry_client) {
|
||||||
|
LOG_ERROR("还未初始化服务注册模块!");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
if (!_rpc_server) {
|
||||||
|
LOG_ERROR("还未初始化RPC服务器模块!");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageServer::ptr server = std::make_shared<MessageServer>(
|
||||||
|
_mq_client, _service_discoverer, _registry_client,
|
||||||
|
_es_client, _mysql_client, _rpc_server);
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
Registry::ptr _registry_client;
|
||||||
|
|
||||||
|
std::shared_ptr<elasticlient::Client> _es_client;
|
||||||
|
std::shared_ptr<odb::core::database> _mysql_client;
|
||||||
|
|
||||||
|
std::string _user_service_name;
|
||||||
|
std::string _file_service_name;
|
||||||
|
ServiceManager::ptr _mm_channels;
|
||||||
|
Discovery::ptr _service_discoverer;
|
||||||
|
|
||||||
|
std::string _exchange_name;
|
||||||
|
std::string _queue_name;
|
||||||
|
MQClient::ptr _mq_client;
|
||||||
|
|
||||||
|
std::shared_ptr<brpc::Server> _rpc_server;
|
||||||
|
};
|
||||||
|
}
|
||||||
34
message/test/es_test/main.cc
Normal file
34
message/test/es_test/main.cc
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#include "../../../common/data_es.hpp"
|
||||||
|
#include <gflags/gflags.h>
|
||||||
|
|
||||||
|
DEFINE_bool(run_mode, false, "程序的运行模式,false-调试; true-发布;");
|
||||||
|
DEFINE_string(log_file, "", "发布模式下,用于指定日志的输出文件");
|
||||||
|
DEFINE_int32(log_level, 0, "发布模式下,用于指定日志输出等级");
|
||||||
|
|
||||||
|
|
||||||
|
DEFINE_string(es_host, "http://127.0.0.1:9200/", "es服务器URL");
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
google::ParseCommandLineFlags(&argc, &argv, true);
|
||||||
|
bite_im::init_logger(FLAGS_run_mode, FLAGS_log_file, FLAGS_log_level);
|
||||||
|
|
||||||
|
auto es_client = bite_im::ESClientFactory::create({FLAGS_es_host});
|
||||||
|
|
||||||
|
auto es_msg = std::make_shared<bite_im::ESMessage>(es_client);
|
||||||
|
// es_msg->createIndex();
|
||||||
|
// es_msg->appendData("用户ID1", "消息ID1", 1723025035, "会话ID1", "吃饭了吗?");
|
||||||
|
// es_msg->appendData("用户ID2", "消息ID2", 1723025035 - 100, "会话ID1", "吃的盖浇饭!");
|
||||||
|
// es_msg->appendData("用户ID3", "消息ID3", 1723025035, "会话ID2", "吃饭了吗?");
|
||||||
|
// es_msg->appendData("用户ID4", "消息ID4", 1723025035 - 100, "会话ID2", "吃的盖浇饭!");
|
||||||
|
auto res = es_msg->search("盖浇", "会话ID1");
|
||||||
|
for (auto &u : res) {
|
||||||
|
std::cout << "-----------------" << std::endl;
|
||||||
|
std::cout << u.user_id() << std::endl;
|
||||||
|
std::cout << u.message_id() << std::endl;
|
||||||
|
std::cout << u.session_id() << std::endl;
|
||||||
|
std::cout << boost::posix_time::to_simple_string(u.create_time()) << std::endl;
|
||||||
|
std::cout << u.content() << std::endl;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
72
message/test/es_test/请求格式规范.txt
Normal file
72
message/test/es_test/请求格式规范.txt
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
POST /message/_doc
|
||||||
|
{
|
||||||
|
"settings" : {
|
||||||
|
"analysis" : {
|
||||||
|
"analyzer" : {
|
||||||
|
"ik" : {
|
||||||
|
"tokenizer" : "ik_max_word"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mappings" : {
|
||||||
|
"dynamic" : true,
|
||||||
|
"properties" : {
|
||||||
|
"chat_session_id" : {
|
||||||
|
"type" : "keyword",
|
||||||
|
"analyzer" : "standard"
|
||||||
|
},
|
||||||
|
"message_id" : {
|
||||||
|
"type" : "keyword",
|
||||||
|
"analyzer" : "standard"
|
||||||
|
},
|
||||||
|
"content" : {
|
||||||
|
"type" : "text",
|
||||||
|
"analyzer" : "ik_max_word"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
GET /message/_doc/_search?pretty
|
||||||
|
{
|
||||||
|
"query": {
|
||||||
|
"match_all": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
POST /message/_doc/_bulk
|
||||||
|
{"index":{"_id":"1"}}
|
||||||
|
{"chat_session_id" : "会话ID1","message_id" : "消息ID1","content" : "吃饭了么?"}
|
||||||
|
{"index":{"_id":"2"}}
|
||||||
|
{"chat_session_id" : "会话ID1","message_id" : "消息ID2","content" : "吃的盖浇饭。"}
|
||||||
|
{"index":{"_id":"3"}}
|
||||||
|
{"chat_session_id" : "会话ID2","message_id" : "消息ID3","content" : "昨天吃饭了么?"}
|
||||||
|
{"index":{"_id":"4"}}
|
||||||
|
{"chat_session_id" : "会话ID2","message_id" : "消息ID4","content" : "昨天吃的盖浇饭。"}
|
||||||
|
|
||||||
|
|
||||||
|
GET /message/_doc/_search?pretty
|
||||||
|
{
|
||||||
|
"query": {
|
||||||
|
"bool": {
|
||||||
|
"must": [
|
||||||
|
{
|
||||||
|
"term": {
|
||||||
|
"chat_session_id.keyword": "会话ID1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"match": {
|
||||||
|
"content": "盖浇饭"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DELETE /message
|
||||||
170
message/test/message_client.cc
Normal file
170
message/test/message_client.cc
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
#include "etcd.hpp"
|
||||||
|
#include "channel.hpp"
|
||||||
|
#include "utils.hpp"
|
||||||
|
#include <gflags/gflags.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <thread>
|
||||||
|
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||||
|
#include "message.pb.h"
|
||||||
|
#include "base.pb.h"
|
||||||
|
#include "user.pb.h"
|
||||||
|
|
||||||
|
|
||||||
|
DEFINE_bool(run_mode, false, "程序的运行模式,false-调试; true-发布;");
|
||||||
|
DEFINE_string(log_file, "", "发布模式下,用于指定日志的输出文件");
|
||||||
|
DEFINE_int32(log_level, 0, "发布模式下,用于指定日志输出等级");
|
||||||
|
|
||||||
|
DEFINE_string(etcd_host, "http://127.0.0.1:2379", "服务注册中心地址");
|
||||||
|
DEFINE_string(base_service, "/service", "服务监控根目录");
|
||||||
|
DEFINE_string(message_service, "/service/message_service", "服务监控根目录");
|
||||||
|
|
||||||
|
bite_im::ServiceManager::ptr sm;
|
||||||
|
|
||||||
|
void range_test(const std::string &ssid,
|
||||||
|
const boost::posix_time::ptime &stime,
|
||||||
|
const boost::posix_time::ptime &etime) {
|
||||||
|
auto channel = sm->choose(FLAGS_message_service);
|
||||||
|
if (!channel) {
|
||||||
|
std::cout << "获取通信信道失败!" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bite_im::MsgStorageService_Stub stub(channel.get());
|
||||||
|
bite_im::GetHistoryMsgReq req;
|
||||||
|
bite_im::GetHistoryMsgRsp rsp;
|
||||||
|
req.set_request_id(bite_im::uuid());
|
||||||
|
req.set_chat_session_id(ssid);
|
||||||
|
req.set_start_time(boost::posix_time::to_time_t(stime));
|
||||||
|
req.set_over_time(boost::posix_time::to_time_t(etime));
|
||||||
|
brpc::Controller cntl;
|
||||||
|
stub.GetHistoryMsg(&cntl, &req, &rsp, nullptr);
|
||||||
|
ASSERT_FALSE(cntl.Failed());
|
||||||
|
ASSERT_TRUE(rsp.success());
|
||||||
|
|
||||||
|
std::cout << rsp.msg_list_size() << std::endl;
|
||||||
|
|
||||||
|
for (int i = 0; i < rsp.msg_list_size(); i++) {
|
||||||
|
std::cout << "-----------------------获取时间区间消息--------------------------\n";
|
||||||
|
auto msg = rsp.msg_list(i);
|
||||||
|
std::cout << msg.message_id() << std::endl;
|
||||||
|
std::cout << msg.chat_session_id() << std::endl;
|
||||||
|
std::cout << boost::posix_time::to_simple_string(boost::posix_time::from_time_t(msg.timestamp())) << std::endl;
|
||||||
|
std::cout << msg.sender().user_id() << std::endl;
|
||||||
|
std::cout << msg.sender().nickname() << std::endl;
|
||||||
|
std::cout << msg.sender().avatar() << std::endl;
|
||||||
|
if (msg.message().message_type() == bite_im::MessageType::STRING) {
|
||||||
|
std::cout << "文本消息:" << msg.message().string_message().content() << std::endl;
|
||||||
|
}else if (msg.message().message_type() == bite_im::MessageType::IMAGE) {
|
||||||
|
std::cout << "图片消息:" << msg.message().image_message().image_content() << std::endl;
|
||||||
|
}else if (msg.message().message_type() == bite_im::MessageType::FILE) {
|
||||||
|
std::cout << "文件消息:" << msg.message().file_message().file_contents() << std::endl;
|
||||||
|
std::cout << "文件名称:" << msg.message().file_message().file_name() << std::endl;
|
||||||
|
}else if (msg.message().message_type() == bite_im::MessageType::SPEECH) {
|
||||||
|
std::cout << "语音消息:" << msg.message().speech_message().file_contents() << std::endl;
|
||||||
|
}else {
|
||||||
|
std::cout << "类型错误!!\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void recent_test(const std::string &ssid, int count) {
|
||||||
|
auto channel = sm->choose(FLAGS_message_service);
|
||||||
|
if (!channel) {
|
||||||
|
std::cout << "获取通信信道失败!" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bite_im::MsgStorageService_Stub stub(channel.get());
|
||||||
|
bite_im::GetRecentMsgReq req;
|
||||||
|
bite_im::GetRecentMsgRsp rsp;
|
||||||
|
req.set_request_id(bite_im::uuid());
|
||||||
|
req.set_chat_session_id(ssid);
|
||||||
|
req.set_msg_count(count);
|
||||||
|
brpc::Controller cntl;
|
||||||
|
stub.GetRecentMsg(&cntl, &req, &rsp, nullptr);
|
||||||
|
ASSERT_FALSE(cntl.Failed());
|
||||||
|
ASSERT_TRUE(rsp.success());
|
||||||
|
for (int i = 0; i < rsp.msg_list_size(); i++) {
|
||||||
|
std::cout << "----------------------获取最近消息---------------------------\n";
|
||||||
|
auto msg = rsp.msg_list(i);
|
||||||
|
std::cout << msg.message_id() << std::endl;
|
||||||
|
std::cout << msg.chat_session_id() << std::endl;
|
||||||
|
std::cout << boost::posix_time::to_simple_string(boost::posix_time::from_time_t(msg.timestamp())) << std::endl;
|
||||||
|
std::cout << msg.sender().user_id() << std::endl;
|
||||||
|
std::cout << msg.sender().nickname() << std::endl;
|
||||||
|
std::cout << msg.sender().avatar() << std::endl;
|
||||||
|
if (msg.message().message_type() == bite_im::MessageType::STRING) {
|
||||||
|
std::cout << "文本消息:" << msg.message().string_message().content() << std::endl;
|
||||||
|
}else if (msg.message().message_type() == bite_im::MessageType::IMAGE) {
|
||||||
|
std::cout << "图片消息:" << msg.message().image_message().image_content() << std::endl;
|
||||||
|
}else if (msg.message().message_type() == bite_im::MessageType::FILE) {
|
||||||
|
std::cout << "文件消息:" << msg.message().file_message().file_contents() << std::endl;
|
||||||
|
std::cout << "文件名称:" << msg.message().file_message().file_name() << std::endl;
|
||||||
|
}else if (msg.message().message_type() == bite_im::MessageType::SPEECH) {
|
||||||
|
std::cout << "语音消息:" << msg.message().speech_message().file_contents() << std::endl;
|
||||||
|
}else {
|
||||||
|
std::cout << "类型错误!!\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void search_test(const std::string &ssid, const std::string &key) {
|
||||||
|
auto channel = sm->choose(FLAGS_message_service);
|
||||||
|
if (!channel) {
|
||||||
|
std::cout << "获取通信信道失败!" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bite_im::MsgStorageService_Stub stub(channel.get());
|
||||||
|
bite_im::MsgSearchReq req;
|
||||||
|
bite_im::MsgSearchRsp rsp;
|
||||||
|
req.set_request_id(bite_im::uuid());
|
||||||
|
req.set_chat_session_id(ssid);
|
||||||
|
req.set_search_key(key);
|
||||||
|
brpc::Controller cntl;
|
||||||
|
stub.MsgSearch(&cntl, &req, &rsp, nullptr);
|
||||||
|
ASSERT_FALSE(cntl.Failed());
|
||||||
|
ASSERT_TRUE(rsp.success());
|
||||||
|
for (int i = 0; i < rsp.msg_list_size(); i++) {
|
||||||
|
std::cout << "----------------------关键字搜索消息---------------------------\n";
|
||||||
|
auto msg = rsp.msg_list(i);
|
||||||
|
std::cout << msg.message_id() << std::endl;
|
||||||
|
std::cout << msg.chat_session_id() << std::endl;
|
||||||
|
std::cout << boost::posix_time::to_simple_string(boost::posix_time::from_time_t(msg.timestamp())) << std::endl;
|
||||||
|
std::cout << msg.sender().user_id() << std::endl;
|
||||||
|
std::cout << msg.sender().nickname() << std::endl;
|
||||||
|
std::cout << msg.sender().avatar() << std::endl;
|
||||||
|
if (msg.message().message_type() == bite_im::MessageType::STRING) {
|
||||||
|
std::cout << "文本消息:" << msg.message().string_message().content() << std::endl;
|
||||||
|
}else if (msg.message().message_type() == bite_im::MessageType::IMAGE) {
|
||||||
|
std::cout << "图片消息:" << msg.message().image_message().image_content() << std::endl;
|
||||||
|
}else if (msg.message().message_type() == bite_im::MessageType::FILE) {
|
||||||
|
std::cout << "文件消息:" << msg.message().file_message().file_contents() << std::endl;
|
||||||
|
std::cout << "文件名称:" << msg.message().file_message().file_name() << std::endl;
|
||||||
|
}else if (msg.message().message_type() == bite_im::MessageType::SPEECH) {
|
||||||
|
std::cout << "语音消息:" << msg.message().speech_message().file_contents() << std::endl;
|
||||||
|
}else {
|
||||||
|
std::cout << "类型错误!!\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
google::ParseCommandLineFlags(&argc, &argv, true);
|
||||||
|
bite_im::init_logger(FLAGS_run_mode, FLAGS_log_file, FLAGS_log_level);
|
||||||
|
|
||||||
|
|
||||||
|
//1. 先构造Rpc信道管理对象
|
||||||
|
sm = std::make_shared<bite_im::ServiceManager>();
|
||||||
|
sm->declared(FLAGS_message_service);
|
||||||
|
auto put_cb = std::bind(&bite_im::ServiceManager::onServiceOnline, sm.get(), std::placeholders::_1, std::placeholders::_2);
|
||||||
|
auto del_cb = std::bind(&bite_im::ServiceManager::onServiceOffline, sm.get(), std::placeholders::_1, std::placeholders::_2);
|
||||||
|
//2. 构造服务发现对象
|
||||||
|
bite_im::Discovery::ptr dclient = std::make_shared<bite_im::Discovery>(FLAGS_etcd_host, FLAGS_base_service, put_cb, del_cb);
|
||||||
|
|
||||||
|
boost::posix_time::ptime stime(boost::posix_time::time_from_string("2000-08-02 00:00:00"));
|
||||||
|
boost::posix_time::ptime etime(boost::posix_time::time_from_string("2050-08-09 00:00:00"));
|
||||||
|
range_test("会话ID1", stime, etime);
|
||||||
|
recent_test("会话ID1", 2);
|
||||||
|
search_test("会话ID1", "盖浇");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
63
message/test/mysql_test/main.cc
Normal file
63
message/test/mysql_test/main.cc
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#include "mysql_message.hpp"
|
||||||
|
#include <gflags/gflags.h>
|
||||||
|
|
||||||
|
|
||||||
|
DEFINE_bool(run_mode, false, "程序的运行模式,false-调试; true-发布;");
|
||||||
|
DEFINE_string(log_file, "", "发布模式下,用于指定日志的输出文件");
|
||||||
|
DEFINE_int32(log_level, 0, "发布模式下,用于指定日志输出等级");
|
||||||
|
|
||||||
|
void insert_test(bite_im::MessageTable &tb) {
|
||||||
|
bite_im::Message m1("消息ID1", "会话ID1", "用户ID1", 0, boost::posix_time::time_from_string("2002-01-20 23:59:59.000"));
|
||||||
|
tb.insert(m1);
|
||||||
|
bite_im::Message m2("消息ID2", "会话ID1", "用户ID2", 0, boost::posix_time::time_from_string("2002-01-21 23:59:59.000"));
|
||||||
|
tb.insert(m2);
|
||||||
|
bite_im::Message m3("消息ID3", "会话ID1", "用户ID3", 0, boost::posix_time::time_from_string("2002-01-22 23:59:59.000"));
|
||||||
|
tb.insert(m3);
|
||||||
|
|
||||||
|
//另一个会话
|
||||||
|
bite_im::Message m4("消息ID4", "会话ID2", "用户ID4", 0, boost::posix_time::time_from_string("2002-01-20 23:59:59.000"));
|
||||||
|
tb.insert(m4);
|
||||||
|
bite_im::Message m5("消息ID5", "会话ID2", "用户ID5", 0, boost::posix_time::time_from_string("2002-01-21 23:59:59.000"));
|
||||||
|
tb.insert(m5);
|
||||||
|
}
|
||||||
|
void remove_test(bite_im::MessageTable &tb) {
|
||||||
|
tb.remove("会话ID2");
|
||||||
|
}
|
||||||
|
|
||||||
|
void recent_test(bite_im::MessageTable &tb) {
|
||||||
|
auto res = tb.recent("会话ID1", 2);
|
||||||
|
auto begin = res.rbegin();
|
||||||
|
auto end = res.rend();
|
||||||
|
for (; begin != end; ++begin) {
|
||||||
|
std::cout << begin->message_id() << std::endl;
|
||||||
|
std::cout << begin->session_id() << std::endl;
|
||||||
|
std::cout << begin->user_id() << std::endl;
|
||||||
|
std::cout << boost::posix_time::to_simple_string(begin->create_time()) << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void range_test(bite_im::MessageTable &tb) {
|
||||||
|
boost::posix_time::ptime stime(boost::posix_time::time_from_string("2002-01-20 23:59:59.000"));
|
||||||
|
boost::posix_time::ptime etime(boost::posix_time::time_from_string("2002-01-21 23:59:59.000"));
|
||||||
|
auto res = tb.range("会话ID1", stime, etime);
|
||||||
|
for (const auto &m : res) {
|
||||||
|
std::cout << m.message_id() << std::endl;
|
||||||
|
std::cout << m.session_id() << std::endl;
|
||||||
|
std::cout << m.user_id() << std::endl;
|
||||||
|
std::cout << boost::posix_time::to_simple_string(m.create_time()) << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
google::ParseCommandLineFlags(&argc, &argv, true);
|
||||||
|
bite_im::init_logger(FLAGS_run_mode, FLAGS_log_file, FLAGS_log_level);
|
||||||
|
|
||||||
|
auto db = bite_im::ODBFactory::create("root", "123456", "127.0.0.1", "bite_im", "utf8", 0, 1);
|
||||||
|
bite_im::MessageTable tb(db);
|
||||||
|
// insert_test(tb);
|
||||||
|
// remove_test(tb);
|
||||||
|
// recent_test(tb);
|
||||||
|
// range_test(tb);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
68
odb/chat_session.hxx
Normal file
68
odb/chat_session.hxx
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <odb/nullable.hxx>
|
||||||
|
#include <odb/core.hxx>
|
||||||
|
#include "chat_session_member.hxx"
|
||||||
|
|
||||||
|
namespace bite_im {
|
||||||
|
|
||||||
|
enum class ChatSessionType {
|
||||||
|
SINGLE = 1,
|
||||||
|
GROUP = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
#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){}
|
||||||
|
|
||||||
|
std::string chat_session_id() const { return _chat_session_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:
|
||||||
|
friend class odb::access;
|
||||||
|
#pragma db id auto
|
||||||
|
unsigned long _id;
|
||||||
|
#pragma db type("varchar(64)") index unique
|
||||||
|
std::string _chat_session_id;
|
||||||
|
#pragma db type("varchar(64)")
|
||||||
|
std::string _chat_session_name;
|
||||||
|
#pragma db type("tinyint")
|
||||||
|
ChatSessionType _chat_session_type; //1-单聊; 2-群聊
|
||||||
|
};
|
||||||
|
|
||||||
|
// 这里条件必须是指定条件: css::chat_session_type==1 && csm1.user_id=uid && csm2.user_id != csm1.user_id
|
||||||
|
#pragma db view object(ChatSession = css)\
|
||||||
|
object(ChatSessionMember = csm1 : css::_chat_session_id == csm1::_session_id)\
|
||||||
|
object(ChatSessionMember = csm2 : css::_chat_session_id == csm2::_session_id)\
|
||||||
|
query((?))
|
||||||
|
struct SingleChatSession {
|
||||||
|
#pragma db column(css::_chat_session_id)
|
||||||
|
std::string chat_session_id;
|
||||||
|
#pragma db column(csm2::_user_id)
|
||||||
|
std::string friend_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 这里条件必须是指定条件: css::chat_session_type==2 && csm.user_id=uid
|
||||||
|
#pragma db view object(ChatSession = css)\
|
||||||
|
object(ChatSessionMember = csm : css::_chat_session_id == csm::_session_id)\
|
||||||
|
query((?))
|
||||||
|
struct GroupChatSession {
|
||||||
|
#pragma db column(css::_chat_session_id)
|
||||||
|
std::string chat_session_id;
|
||||||
|
#pragma db column(css::_chat_session_name)
|
||||||
|
std::string chat_session_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
32
odb/chat_session_member.hxx
Normal file
32
odb/chat_session_member.hxx
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <odb/core.hxx>
|
||||||
|
|
||||||
|
namespace bite_im {
|
||||||
|
#pragma db object table("chat_session_member")
|
||||||
|
class ChatSessionMember {
|
||||||
|
public:
|
||||||
|
ChatSessionMember(){}
|
||||||
|
ChatSessionMember(const std::string& ssid, const std::string &uid)
|
||||||
|
:_session_id(ssid), _user_id(uid) {}
|
||||||
|
~ChatSessionMember(){}
|
||||||
|
|
||||||
|
std::string session_id() const {return _session_id; }
|
||||||
|
void session_id(std::string &ssid) { _session_id = ssid; }
|
||||||
|
|
||||||
|
std::string user_id() const { return _user_id; }
|
||||||
|
void user_id(std::string &uid) {_user_id = uid; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class odb::access;
|
||||||
|
#pragma db id auto
|
||||||
|
unsigned long _id;
|
||||||
|
#pragma db type("varchar(64)") index
|
||||||
|
std::string _session_id;
|
||||||
|
#pragma db type("varchar(64)")
|
||||||
|
std::string _user_id;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//odb -d mysql --generate-query --generate-schema --profile boost/date-time person.hxx
|
||||||
34
odb/friend_apply.hxx
Normal file
34
odb/friend_apply.hxx
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <odb/core.hxx>
|
||||||
|
|
||||||
|
namespace bite_im {
|
||||||
|
#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;
|
||||||
|
};
|
||||||
|
}
|
||||||
83
odb/message.hxx
Normal file
83
odb/message.hxx
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <odb/nullable.hxx>
|
||||||
|
#include <odb/core.hxx>
|
||||||
|
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||||
|
|
||||||
|
namespace bite_im {
|
||||||
|
#pragma db object table("message")
|
||||||
|
class Message {
|
||||||
|
public:
|
||||||
|
Message(){}
|
||||||
|
Message(const std::string &mid,
|
||||||
|
const std::string &ssid,
|
||||||
|
const std::string &uid,
|
||||||
|
const unsigned char mtype,
|
||||||
|
const boost::posix_time::ptime &ctime):
|
||||||
|
_message_id(mid), _session_id(ssid),
|
||||||
|
_user_id(uid), _message_type(mtype),
|
||||||
|
_create_time(ctime){}
|
||||||
|
|
||||||
|
std::string message_id() const { return _message_id; }
|
||||||
|
void message_id(const std::string &val) { _message_id = val; }
|
||||||
|
|
||||||
|
std::string session_id() const { return _session_id; }
|
||||||
|
void session_id(const std::string &val) { _session_id = val; }
|
||||||
|
|
||||||
|
std::string user_id() const { return _user_id; }
|
||||||
|
void user_id(const std::string &val) { _user_id = val; }
|
||||||
|
|
||||||
|
unsigned char message_type() const { return _message_type; }
|
||||||
|
void message_type(unsigned char val) { _message_type = val; }
|
||||||
|
|
||||||
|
boost::posix_time::ptime create_time() const { return _create_time; }
|
||||||
|
void create_time(const boost::posix_time::ptime &val) { _create_time = val; }
|
||||||
|
|
||||||
|
std::string content() const {
|
||||||
|
if (!_content) return std::string();
|
||||||
|
return *_content;
|
||||||
|
}
|
||||||
|
void content(const std::string &val) { _content = val; }
|
||||||
|
|
||||||
|
std::string file_id() const {
|
||||||
|
if (!_file_id) return std::string();
|
||||||
|
return *_file_id;
|
||||||
|
}
|
||||||
|
void file_id(const std::string &val) { _file_id = val; }
|
||||||
|
|
||||||
|
std::string file_name() const {
|
||||||
|
if (!_file_name) return std::string();
|
||||||
|
return *_file_name;
|
||||||
|
}
|
||||||
|
void file_name(const std::string &val) { _file_name = val; }
|
||||||
|
|
||||||
|
unsigned int file_size() const {
|
||||||
|
if (!_file_size) return 0;
|
||||||
|
return *_file_size;
|
||||||
|
}
|
||||||
|
void file_size(unsigned int val) { _file_size = val; }
|
||||||
|
private:
|
||||||
|
friend class odb::access;
|
||||||
|
#pragma db id auto
|
||||||
|
unsigned long _id;
|
||||||
|
#pragma db type("varchar(64)") index unique
|
||||||
|
std::string _message_id;
|
||||||
|
#pragma db type("varchar(64)") index
|
||||||
|
std::string _session_id; //所属会话ID
|
||||||
|
#pragma db type("varchar(64)")
|
||||||
|
std::string _user_id; //发送者用户ID
|
||||||
|
unsigned char _message_type; //消息类型 0-文本;1-图片;2-文件;3-语音
|
||||||
|
#pragma db type("TIMESTAMP")
|
||||||
|
boost::posix_time::ptime _create_time; //消息的产生时间
|
||||||
|
|
||||||
|
//可空信息字段
|
||||||
|
odb::nullable<std::string> _content; //文本消息内容--非文本消息可以忽略
|
||||||
|
#pragma db type("varchar(64)")
|
||||||
|
odb::nullable<std::string> _file_id; //文件消息的文件ID -- 文本消息忽略
|
||||||
|
#pragma db type("varchar(128)")
|
||||||
|
odb::nullable<std::string> _file_name; //文件消息的文件名称 -- 只针对文件消息有效
|
||||||
|
odb::nullable<unsigned int> _file_size; //文件消息的文件大小 -- 只针对文件消息有效
|
||||||
|
};
|
||||||
|
//odb -d mysql --std c++11 --generate-query --generate-schema --profile boost/date-time message.hxx
|
||||||
|
}
|
||||||
30
odb/relation.hxx
Normal file
30
odb/relation.hxx
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <odb/nullable.hxx>
|
||||||
|
#include <odb/core.hxx>
|
||||||
|
|
||||||
|
namespace bite_im {
|
||||||
|
#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;
|
||||||
|
};
|
||||||
|
//odb -d mysql --std c++11 --generate-query --generate-schema --profile boost/date-time person.hxx
|
||||||
|
}
|
||||||
71
odb/user.hxx
Normal file
71
odb/user.hxx
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||||
|
#include <odb/nullable.hxx>
|
||||||
|
#include <odb/core.hxx>
|
||||||
|
|
||||||
|
typedef boost::posix_time::ptime ptime;
|
||||||
|
#pragma db object table("user")
|
||||||
|
class User
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
User(){}
|
||||||
|
//用户名--新增用户 -- 用户ID, 昵称,密码
|
||||||
|
User(const std::string &uid, const std::string &nickname, const std::string &password):
|
||||||
|
_user_id(uid), _nickname(nickname), _password(password){}
|
||||||
|
//手机号--新增用户 -- 用户ID, 手机号, 随机昵称
|
||||||
|
User(const std::string &uid, const std::string &phone):
|
||||||
|
_user_id(uid), _nickname(uid), _phone(phone){}
|
||||||
|
|
||||||
|
void user_id(const std::string &val) { _user_id = val; }
|
||||||
|
std::string user_id() { return _user_id; }
|
||||||
|
|
||||||
|
std::string nickname() {
|
||||||
|
if (_nickname) return *_nickname;
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
void nickname(const std::string &val) { _nickname = val; }
|
||||||
|
|
||||||
|
std::string description() {
|
||||||
|
if (!_description) return std::string();
|
||||||
|
return *_description;
|
||||||
|
}
|
||||||
|
void description(const std::string &val) { _description = val; }
|
||||||
|
|
||||||
|
std::string password() {
|
||||||
|
if (!_password) return std::string();
|
||||||
|
return *_password;
|
||||||
|
}
|
||||||
|
void password(const std::string &val) { _password = val; }
|
||||||
|
|
||||||
|
std::string phone() {
|
||||||
|
if (!_phone) return std::string();
|
||||||
|
return *_phone;
|
||||||
|
}
|
||||||
|
void phone(const std::string &val) { _phone = val; }
|
||||||
|
|
||||||
|
std::string avatar_id() {
|
||||||
|
if (!_avatar_id) return std::string();
|
||||||
|
return *_avatar_id;
|
||||||
|
}
|
||||||
|
void avatar_id(const std::string &val) { _avatar_id = val; }
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class odb::access;
|
||||||
|
#pragma db id auto
|
||||||
|
unsigned long _id;
|
||||||
|
#pragma db index
|
||||||
|
std::string _user_id;
|
||||||
|
odb::nullable<std::string> _nickname; //用户昵称,不一定存在
|
||||||
|
#pragma db index
|
||||||
|
odb::nullable<std::string> _description; //用户签名 不一定存在
|
||||||
|
odb::nullable<std::string> _password; //用户密码 不一定存在
|
||||||
|
#pragma db index
|
||||||
|
odb::nullable<std::string> _phone; //用户手机号 不一定存在
|
||||||
|
odb::nullable<std::string> _avatar_id; //用户头像文件id 不一定存在
|
||||||
|
};
|
||||||
|
|
||||||
|
//odb -d mysql --std c++11 --generate-query --generate-schema --profile boost/date-time person.hxx
|
||||||
82
proto/base.proto
Normal file
82
proto/base.proto
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
package bite_im;
|
||||||
|
option cc_generic_services = true;
|
||||||
|
|
||||||
|
//用户信息结构
|
||||||
|
message UserInfo {
|
||||||
|
string user_id = 1;//用户ID
|
||||||
|
string nickname = 2;//昵称
|
||||||
|
string description = 3;//个人签名/描述
|
||||||
|
string phone = 4; //绑定手机号
|
||||||
|
bytes avatar = 5;//头像照片,文件内容使用二进制
|
||||||
|
}
|
||||||
|
|
||||||
|
//聊天会话信息
|
||||||
|
message ChatSessionInfo {
|
||||||
|
//群聊会话不需要设置,单聊会话设置为对方用户ID
|
||||||
|
optional string single_chat_friend_id = 1;
|
||||||
|
string chat_session_id = 2; //会话ID
|
||||||
|
string chat_session_name = 3;//会话名称git
|
||||||
|
//会话上一条消息,新建的会话没有最新消息
|
||||||
|
optional MessageInfo prev_message = 4;
|
||||||
|
//会话头像 --群聊会话不需要,直接由前端固定渲染,单聊就是对方的头像
|
||||||
|
optional bytes avatar = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
//消息类型
|
||||||
|
enum MessageType {
|
||||||
|
STRING = 0;
|
||||||
|
IMAGE = 1;
|
||||||
|
FILE = 2;
|
||||||
|
SPEECH = 3;
|
||||||
|
}
|
||||||
|
message StringMessageInfo {
|
||||||
|
string content = 1;//文字聊天内容
|
||||||
|
}
|
||||||
|
message ImageMessageInfo {
|
||||||
|
//图片文件id,客户端发送的时候不用设置,由transmit服务器进行设置后交给storage的时候设置
|
||||||
|
optional string file_id = 1;
|
||||||
|
//图片数据,在ES中存储消息的时候只要id不要文件数据, 服务端转发的时候需要原样转发
|
||||||
|
optional bytes image_content = 2;
|
||||||
|
}
|
||||||
|
message FileMessageInfo {
|
||||||
|
optional string file_id = 1;//文件id,客户端发送的时候不用设置
|
||||||
|
optional int64 file_size = 2;//文件大小
|
||||||
|
optional string file_name = 3;//文件名称
|
||||||
|
//文件数据,在ES中存储消息的时候只要id和元信息,不要文件数据, 服务端转发的时候也不需要填充
|
||||||
|
optional bytes file_contents = 4;
|
||||||
|
}
|
||||||
|
message SpeechMessageInfo {
|
||||||
|
//语音文件id,客户端发送的时候不用设置
|
||||||
|
optional string file_id = 1;
|
||||||
|
//文件数据,在ES中存储消息的时候只要id不要文件数据, 服务端转发的时候也不需要填充
|
||||||
|
optional bytes file_contents = 2;
|
||||||
|
}
|
||||||
|
message MessageContent {
|
||||||
|
MessageType message_type = 1; //消息类型
|
||||||
|
oneof msg_content {
|
||||||
|
StringMessageInfo string_message = 2;//文字消息
|
||||||
|
FileMessageInfo file_message = 3;//文件消息
|
||||||
|
SpeechMessageInfo speech_message = 4;//语音消息
|
||||||
|
ImageMessageInfo image_message = 5;//图片消息
|
||||||
|
};
|
||||||
|
}
|
||||||
|
//消息结构
|
||||||
|
message MessageInfo {
|
||||||
|
string message_id = 1;//消息ID
|
||||||
|
string chat_session_id = 2;//消息所属聊天会话ID
|
||||||
|
int64 timestamp = 3;//消息产生时间
|
||||||
|
UserInfo sender = 4;//消息发送者信息
|
||||||
|
MessageContent message = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message FileDownloadData {
|
||||||
|
string file_id = 1;
|
||||||
|
bytes file_content = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message FileUploadData {
|
||||||
|
string file_name = 1; //文件名称
|
||||||
|
int64 file_size = 2; //文件大小
|
||||||
|
bytes file_content = 3; //文件数据
|
||||||
|
}
|
||||||
64
proto/file.proto
Normal file
64
proto/file.proto
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
package bite_im;
|
||||||
|
import "base.proto";
|
||||||
|
|
||||||
|
option cc_generic_services = true;
|
||||||
|
|
||||||
|
message GetSingleFileReq {
|
||||||
|
string request_id = 1;
|
||||||
|
string file_id = 2;
|
||||||
|
optional string user_id = 3;
|
||||||
|
optional string session_id = 4;
|
||||||
|
}
|
||||||
|
message GetSingleFileRsp {
|
||||||
|
string request_id = 1;
|
||||||
|
bool success = 2;
|
||||||
|
string errmsg = 3;
|
||||||
|
optional FileDownloadData file_data = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetMultiFileReq {
|
||||||
|
string request_id = 1;
|
||||||
|
optional string user_id = 2;
|
||||||
|
optional string session_id = 3;
|
||||||
|
repeated string file_id_list = 4;
|
||||||
|
}
|
||||||
|
message GetMultiFileRsp {
|
||||||
|
string request_id = 1;
|
||||||
|
bool success = 2;
|
||||||
|
string errmsg = 3;
|
||||||
|
map<string, FileDownloadData> file_data = 4;//文件ID与文件数据的映射map
|
||||||
|
}
|
||||||
|
|
||||||
|
message PutSingleFileReq {
|
||||||
|
string request_id = 1; //请求ID,作为处理流程唯一标识
|
||||||
|
optional string user_id = 2;
|
||||||
|
optional string session_id = 3;
|
||||||
|
FileUploadData file_data = 4;
|
||||||
|
}
|
||||||
|
message PutSingleFileRsp {
|
||||||
|
string request_id = 1;
|
||||||
|
bool success = 2;
|
||||||
|
string errmsg = 3;
|
||||||
|
FileMessageInfo file_info = 4; //返回了文件组织的元信息
|
||||||
|
}
|
||||||
|
|
||||||
|
message PutMultiFileReq {
|
||||||
|
string request_id = 1;
|
||||||
|
optional string user_id = 2;
|
||||||
|
optional string session_id = 3;
|
||||||
|
repeated FileUploadData file_data = 4;
|
||||||
|
}
|
||||||
|
message PutMultiFileRsp {
|
||||||
|
string request_id = 1;
|
||||||
|
bool success = 2;
|
||||||
|
string errmsg = 3;
|
||||||
|
repeated FileMessageInfo file_info = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
service FileService {
|
||||||
|
rpc GetSingleFile(GetSingleFileReq) returns (GetSingleFileRsp);
|
||||||
|
rpc GetMultiFile(GetMultiFileReq) returns (GetMultiFileRsp);
|
||||||
|
rpc PutSingleFile(PutSingleFileReq) returns (PutSingleFileRsp);
|
||||||
|
rpc PutMultiFile(PutMultiFileReq) returns (PutMultiFileRsp);
|
||||||
|
}
|
||||||
155
proto/friend.proto
Normal file
155
proto/friend.proto
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
package bite_im;
|
||||||
|
import "base.proto";
|
||||||
|
|
||||||
|
option cc_generic_services = true;
|
||||||
|
|
||||||
|
//--------------------------------------
|
||||||
|
//好友列表获取
|
||||||
|
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; //要返回的用户信息
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------
|
||||||
|
//好友删除
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
//--------------------------------------
|
||||||
|
//添加好友--发送好友申请
|
||||||
|
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
|
||||||
|
}
|
||||||
|
//--------------------------------------
|
||||||
|
//好友申请的处理
|
||||||
|
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; // 被申请人
|
||||||
|
}
|
||||||
|
// +++++++++++++++++++++++++++++++++
|
||||||
|
message FriendAddProcessRsp {
|
||||||
|
string request_id = 1;
|
||||||
|
bool success = 2;
|
||||||
|
string errmsg = 3;
|
||||||
|
// 同意后会创建会话,向网关返回会话信息,用于通知双方会话的建立,这个字段客户端不需要关注
|
||||||
|
optional string new_session_id = 4;
|
||||||
|
}
|
||||||
|
//--------------------------------------
|
||||||
|
//获取待处理的,申请自己好友的信息列表
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------
|
||||||
|
//好友搜索
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------
|
||||||
|
//会话列表获取
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
//--------------------------------------
|
||||||
|
//创建会话
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
//--------------------------------------
|
||||||
|
//获取会话成员列表
|
||||||
|
message GetChatSessionMemberReq {
|
||||||
|
string request_id = 1;
|
||||||
|
optional string session_id = 2;
|
||||||
|
optional string user_id = 3;
|
||||||
|
string chat_session_id = 4;
|
||||||
|
}
|
||||||
|
message GetChatSessionMemberRsp {
|
||||||
|
string request_id = 1;
|
||||||
|
bool success = 2;
|
||||||
|
string errmsg = 3;
|
||||||
|
repeated UserInfo member_info_list = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
service FriendService {
|
||||||
|
rpc GetFriendList(GetFriendListReq) returns (GetFriendListRsp);
|
||||||
|
rpc FriendRemove(FriendRemoveReq) returns (FriendRemoveRsp);
|
||||||
|
rpc FriendAdd(FriendAddReq) returns (FriendAddRsp);
|
||||||
|
rpc FriendAddProcess(FriendAddProcessReq) returns (FriendAddProcessRsp);
|
||||||
|
rpc FriendSearch(FriendSearchReq) returns (FriendSearchRsp);
|
||||||
|
rpc GetChatSessionList(GetChatSessionListReq) returns (GetChatSessionListRsp);
|
||||||
|
rpc ChatSessionCreate(ChatSessionCreateReq) returns (ChatSessionCreateRsp);
|
||||||
|
rpc GetChatSessionMember(GetChatSessionMemberReq) returns (GetChatSessionMemberRsp);
|
||||||
|
rpc GetPendingFriendEventList(GetPendingFriendEventListReq) returns (GetPendingFriendEventListRsp);
|
||||||
|
}
|
||||||
53
proto/gateway.proto
Normal file
53
proto/gateway.proto
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
package bite_im;
|
||||||
|
option cc_generic_services = true;
|
||||||
|
|
||||||
|
message ClientAuthenticationReq {
|
||||||
|
string request_id = 1;
|
||||||
|
string session_id = 2; // 用于向服务器表明当前长连接客户端的身份
|
||||||
|
}
|
||||||
|
|
||||||
|
//在客户端与网关服务器的通信中,使用HTTP协议进行通信
|
||||||
|
// 通信时采用POST请求作为请求方法
|
||||||
|
// 通信时,正文采用protobuf作为正文协议格式,具体内容字段以前边各个文件中定义的字段格式为准
|
||||||
|
/* 以下是HTTP请求的功能与接口路径对应关系:
|
||||||
|
SERVICE HTTP PATH:
|
||||||
|
{
|
||||||
|
获取随机验证码 /service/user/get_random_verify_code
|
||||||
|
获取短信验证码 /service/user/get_phone_verify_code
|
||||||
|
用户名密码注册 /service/user/username_register
|
||||||
|
用户名密码登录 /service/user/username_login
|
||||||
|
手机号码注册 /service/user/phone_register
|
||||||
|
手机号码登录 /service/user/phone_login
|
||||||
|
获取个人信息 /service/user/get_user_info
|
||||||
|
修改头像 /service/user/set_avatar
|
||||||
|
修改昵称 /service/user/set_nickname
|
||||||
|
修改签名 /service/user/set_description
|
||||||
|
修改绑定手机 /service/user/set_phone
|
||||||
|
|
||||||
|
获取好友列表 /service/friend/get_friend_list
|
||||||
|
获取好友信息 /service/friend/get_friend_info
|
||||||
|
发送好友申请 /service/friend/add_friend_apply
|
||||||
|
好友申请处理 /service/friend/add_friend_process
|
||||||
|
删除好友 /service/friend/remove_friend
|
||||||
|
搜索用户 /service/friend/search_friend
|
||||||
|
获取指定用户的消息会话列表 /service/friend/get_chat_session_list
|
||||||
|
创建消息会话 /service/friend/create_chat_session
|
||||||
|
获取消息会话成员列表 /service/friend/get_chat_session_member
|
||||||
|
获取待处理好友申请事件列表 /service/friend/get_pending_friend_events
|
||||||
|
|
||||||
|
获取历史消息/离线消息列表 /service/message_storage/get_history
|
||||||
|
获取最近N条消息列表 /service/message_storage/get_recent
|
||||||
|
搜索历史消息 /service/message_storage/search_history
|
||||||
|
|
||||||
|
发送消息 /service/message_transmit/new_message
|
||||||
|
|
||||||
|
获取单个文件数据 /service/file/get_single_file
|
||||||
|
获取多个文件数据 /service/file/get_multi_file
|
||||||
|
发送单个文件 /service/file/put_single_file
|
||||||
|
发送多个文件 /service/file/put_multi_file
|
||||||
|
|
||||||
|
语音转文字 /service/speech/recognition
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
55
proto/message.proto
Normal file
55
proto/message.proto
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
package bite_im;
|
||||||
|
import "base.proto";
|
||||||
|
|
||||||
|
option cc_generic_services = true;
|
||||||
|
|
||||||
|
message GetHistoryMsgReq {
|
||||||
|
string request_id = 1;
|
||||||
|
string chat_session_id = 2;
|
||||||
|
int64 start_time = 3;
|
||||||
|
int64 over_time = 4;
|
||||||
|
optional string user_id = 5;
|
||||||
|
optional string session_id = 6;
|
||||||
|
}
|
||||||
|
message GetHistoryMsgRsp {
|
||||||
|
string request_id = 1;
|
||||||
|
bool success = 2;
|
||||||
|
string errmsg = 3;
|
||||||
|
repeated MessageInfo msg_list = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetRecentMsgReq {
|
||||||
|
string request_id = 1;
|
||||||
|
string chat_session_id = 2;
|
||||||
|
int64 msg_count = 3;
|
||||||
|
optional int64 cur_time = 4;//用于扩展获取指定时间前的n条消息
|
||||||
|
optional string user_id = 5;
|
||||||
|
optional string session_id = 6;
|
||||||
|
}
|
||||||
|
message GetRecentMsgRsp {
|
||||||
|
string request_id = 1;
|
||||||
|
bool success = 2;
|
||||||
|
string errmsg = 3;
|
||||||
|
repeated MessageInfo msg_list = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MsgSearchReq {
|
||||||
|
string request_id = 1;
|
||||||
|
optional string user_id = 2;
|
||||||
|
optional string session_id = 3;
|
||||||
|
string chat_session_id = 4;
|
||||||
|
string search_key = 5;
|
||||||
|
}
|
||||||
|
message MsgSearchRsp {
|
||||||
|
string request_id = 1;
|
||||||
|
bool success = 2;
|
||||||
|
string errmsg = 3;
|
||||||
|
repeated MessageInfo msg_list = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
service MsgStorageService {
|
||||||
|
rpc GetHistoryMsg(GetHistoryMsgReq) returns (GetHistoryMsgRsp);
|
||||||
|
rpc GetRecentMsg(GetRecentMsgReq) returns (GetRecentMsgRsp);
|
||||||
|
rpc MsgSearch(MsgSearchReq) returns (MsgSearchRsp);
|
||||||
|
}
|
||||||
39
proto/notify.proto
Normal file
39
proto/notify.proto
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
package bite_im;
|
||||||
|
import "base.proto";
|
||||||
|
option cc_generic_services = true;
|
||||||
|
|
||||||
|
enum NotifyType {
|
||||||
|
FRIEND_ADD_APPLY_NOTIFY = 0;
|
||||||
|
FRIEND_ADD_PROCESS_NOTIFY = 1;
|
||||||
|
CHAT_SESSION_CREATE_NOTIFY = 2;
|
||||||
|
CHAT_MESSAGE_NOTIFY = 3;
|
||||||
|
FRIEND_REMOVE_NOTIFY = 4;
|
||||||
|
}
|
||||||
|
message NotifyFriendAddApply {
|
||||||
|
UserInfo user_info = 1; //申请人信息
|
||||||
|
}
|
||||||
|
message NotifyFriendAddProcess {
|
||||||
|
bool agree = 1;
|
||||||
|
UserInfo user_info = 2; //处理人信息
|
||||||
|
}
|
||||||
|
message NotifyFriendRemove {
|
||||||
|
string user_id = 1; //删除自己的用户 ID
|
||||||
|
}
|
||||||
|
message NotifyNewChatSession {
|
||||||
|
ChatSessionInfo chat_session_info = 1; //新建会话信息
|
||||||
|
}
|
||||||
|
message NotifyNewMessage {
|
||||||
|
MessageInfo message_info = 1; //新消息
|
||||||
|
}
|
||||||
|
message NotifyMessage {
|
||||||
|
optional string notify_event_id = 1;//通知事件操作 id(有则填无则忽略)
|
||||||
|
NotifyType notify_type = 2;//通知事件类型
|
||||||
|
oneof notify_remarks { //事件备注信息
|
||||||
|
NotifyFriendAddApply friend_add_apply = 3;
|
||||||
|
NotifyFriendAddProcess friend_process_result = 4;
|
||||||
|
NotifyFriendRemove friend_remove = 7;
|
||||||
|
NotifyNewChatSession new_chat_session_info = 5;//会话信息
|
||||||
|
NotifyNewMessage new_message_info = 6;//消息信息
|
||||||
|
}
|
||||||
|
}
|
||||||
23
proto/speech.proto
Normal file
23
proto/speech.proto
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
package bite_im;
|
||||||
|
|
||||||
|
option cc_generic_services = true;
|
||||||
|
|
||||||
|
message SpeechRecognitionReq {
|
||||||
|
string request_id = 1; //请求ID
|
||||||
|
bytes speech_content = 2; //语音数据
|
||||||
|
optional string user_id = 3; //用户ID
|
||||||
|
optional string session_id = 4; //登录会话ID -- 网关进行身份鉴权
|
||||||
|
}
|
||||||
|
|
||||||
|
message SpeechRecognitionRsp {
|
||||||
|
string request_id = 1; //请求ID
|
||||||
|
bool success = 2; //请求处理结果标志
|
||||||
|
optional string errmsg = 3; //失败原因
|
||||||
|
optional string recognition_result = 4; //识别后的文字数据
|
||||||
|
}
|
||||||
|
|
||||||
|
//语音识别Rpc服务及接口的定义
|
||||||
|
service SpeechService {
|
||||||
|
rpc SpeechRecognition(SpeechRecognitionReq) returns (SpeechRecognitionRsp);
|
||||||
|
}
|
||||||
32
proto/transmite.proto
Normal file
32
proto/transmite.proto
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
package bite_im;
|
||||||
|
import "base.proto";
|
||||||
|
|
||||||
|
option cc_generic_services = true;
|
||||||
|
|
||||||
|
//这个用于和网关进行通信
|
||||||
|
message NewMessageReq {
|
||||||
|
string request_id = 1; //请求ID -- 全链路唯一标识
|
||||||
|
optional string user_id = 2;
|
||||||
|
optional string session_id = 3;//客户端身份识别信息 -- 这就是消息发送者
|
||||||
|
string chat_session_id = 4; //聊天会话ID -- 标识了当前消息属于哪个会话,应该转发给谁
|
||||||
|
MessageContent message = 5; // 消息内容--消息类型+内容
|
||||||
|
}
|
||||||
|
message NewMessageRsp {
|
||||||
|
string request_id = 1;
|
||||||
|
bool success = 2;
|
||||||
|
string errmsg = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
//这个用于内部的通信,生成完整的消息信息,并获取消息的转发人员列表
|
||||||
|
message GetTransmitTargetRsp {
|
||||||
|
string request_id = 1;
|
||||||
|
bool success = 2;
|
||||||
|
string errmsg = 3;
|
||||||
|
MessageInfo message = 4; // 组织好的消息结构 --
|
||||||
|
repeated string target_id_list = 5; //消息的转发目标列表
|
||||||
|
}
|
||||||
|
|
||||||
|
service MsgTransmitService {
|
||||||
|
rpc GetTransmitTarget(NewMessageReq) returns (GetTransmitTargetRsp);
|
||||||
|
}
|
||||||
166
proto/user.proto
Normal file
166
proto/user.proto
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
package bite_im;
|
||||||
|
import "base.proto";
|
||||||
|
option cc_generic_services = true;
|
||||||
|
|
||||||
|
//----------------------------
|
||||||
|
//用户名注册
|
||||||
|
message UserRegisterReq {
|
||||||
|
string request_id = 1;
|
||||||
|
string nickname = 2;
|
||||||
|
string password = 3;
|
||||||
|
optional string verify_code_id = 4; //目前客户端实现了本地验证,该字段没用了
|
||||||
|
optional string verify_code = 5;//目前客户端实现了本地验证,该字段没用了
|
||||||
|
}
|
||||||
|
message UserRegisterRsp {
|
||||||
|
string request_id = 1;
|
||||||
|
bool success = 2;
|
||||||
|
string errmsg = 3;
|
||||||
|
}
|
||||||
|
//----------------------------
|
||||||
|
//用户名登录
|
||||||
|
message UserLoginReq {
|
||||||
|
string request_id = 1;
|
||||||
|
string nickname = 2;
|
||||||
|
string password = 3;
|
||||||
|
optional string verify_code_id = 4;
|
||||||
|
optional string verify_code = 5;
|
||||||
|
}
|
||||||
|
message UserLoginRsp {
|
||||||
|
string request_id = 1;
|
||||||
|
bool success = 2;
|
||||||
|
string errmsg = 3;
|
||||||
|
string login_session_id = 4;
|
||||||
|
}
|
||||||
|
//----------------------------
|
||||||
|
//手机号验证码获取
|
||||||
|
message PhoneVerifyCodeReq {
|
||||||
|
string request_id = 1;
|
||||||
|
string phone_number = 2;
|
||||||
|
}
|
||||||
|
message PhoneVerifyCodeRsp {
|
||||||
|
string request_id = 1;
|
||||||
|
bool success = 2;
|
||||||
|
string errmsg = 3;
|
||||||
|
string verify_code_id = 4;
|
||||||
|
}
|
||||||
|
//----------------------------
|
||||||
|
//手机号注册
|
||||||
|
message PhoneRegisterReq {
|
||||||
|
string request_id = 1;
|
||||||
|
string phone_number = 2;
|
||||||
|
string verify_code_id = 3;
|
||||||
|
string verify_code = 4;
|
||||||
|
}
|
||||||
|
message PhoneRegisterRsp {
|
||||||
|
string request_id = 1;
|
||||||
|
bool success = 2;
|
||||||
|
string errmsg = 3;
|
||||||
|
}
|
||||||
|
//----------------------------
|
||||||
|
//手机号登录
|
||||||
|
message PhoneLoginReq {
|
||||||
|
string request_id = 1;
|
||||||
|
string phone_number = 2;
|
||||||
|
string verify_code_id = 3;
|
||||||
|
string verify_code = 4;
|
||||||
|
}
|
||||||
|
message PhoneLoginRsp {
|
||||||
|
string request_id = 1;
|
||||||
|
bool success = 2;
|
||||||
|
string errmsg = 3;
|
||||||
|
string login_session_id = 4;
|
||||||
|
}
|
||||||
|
//个人信息获取-这个只用于获取当前登录用户的信息
|
||||||
|
// 客户端传递的时候只需要填充session_id即可
|
||||||
|
//其他个人/好友信息的获取在好友操作中完成
|
||||||
|
message GetUserInfoReq {
|
||||||
|
string request_id = 1;
|
||||||
|
optional string user_id = 2; // 这个字段是网关进行身份鉴权之后填入的字段
|
||||||
|
optional string session_id = 3; // 进行客户端身份识别的关键字段
|
||||||
|
}
|
||||||
|
message GetUserInfoRsp {
|
||||||
|
string request_id = 1;
|
||||||
|
bool success = 2;
|
||||||
|
string errmsg = 3;
|
||||||
|
UserInfo user_info = 4;
|
||||||
|
}
|
||||||
|
//内部接口
|
||||||
|
message GetMultiUserInfoReq {
|
||||||
|
string request_id = 1;
|
||||||
|
repeated string users_id = 2;
|
||||||
|
}
|
||||||
|
message GetMultiUserInfoRsp {
|
||||||
|
string request_id = 1;
|
||||||
|
bool success = 2;
|
||||||
|
string errmsg = 3;
|
||||||
|
map<string, UserInfo> users_info = 4;
|
||||||
|
}
|
||||||
|
//----------------------------
|
||||||
|
//用户头像修改
|
||||||
|
message SetUserAvatarReq {
|
||||||
|
string request_id = 1;
|
||||||
|
optional string user_id = 2;
|
||||||
|
optional string session_id = 3;
|
||||||
|
bytes avatar = 4;
|
||||||
|
}
|
||||||
|
message SetUserAvatarRsp {
|
||||||
|
string request_id = 1;
|
||||||
|
bool success = 2;
|
||||||
|
string errmsg = 3;
|
||||||
|
}
|
||||||
|
//----------------------------
|
||||||
|
//用户昵称修改
|
||||||
|
message SetUserNicknameReq {
|
||||||
|
string request_id = 1;
|
||||||
|
optional string user_id = 2;
|
||||||
|
optional string session_id = 3;
|
||||||
|
string nickname = 4;
|
||||||
|
}
|
||||||
|
message SetUserNicknameRsp {
|
||||||
|
string request_id = 1;
|
||||||
|
bool success = 2;
|
||||||
|
string errmsg = 3;
|
||||||
|
}
|
||||||
|
//----------------------------
|
||||||
|
//用户签名修改
|
||||||
|
message SetUserDescriptionReq {
|
||||||
|
string request_id = 1;
|
||||||
|
optional string user_id = 2;
|
||||||
|
optional string session_id = 3;
|
||||||
|
string description = 4;
|
||||||
|
}
|
||||||
|
message SetUserDescriptionRsp {
|
||||||
|
string request_id = 1;
|
||||||
|
bool success = 2;
|
||||||
|
string errmsg = 3;
|
||||||
|
}
|
||||||
|
//----------------------------
|
||||||
|
//用户手机修改
|
||||||
|
message SetUserPhoneNumberReq {
|
||||||
|
string request_id = 1;
|
||||||
|
optional string user_id = 2;
|
||||||
|
optional string session_id = 3;
|
||||||
|
string phone_number = 4;
|
||||||
|
string phone_verify_code_id = 5;
|
||||||
|
string phone_verify_code = 6;
|
||||||
|
}
|
||||||
|
message SetUserPhoneNumberRsp {
|
||||||
|
string request_id = 1;
|
||||||
|
bool success = 2;
|
||||||
|
string errmsg = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
54
speech/CMakeLists.txt
Normal file
54
speech/CMakeLists.txt
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
# 1. 添加cmake版本说明
|
||||||
|
cmake_minimum_required(VERSION 3.1.3)
|
||||||
|
# 2. 声明工程名称
|
||||||
|
project(speech_server)
|
||||||
|
|
||||||
|
set(target "speech_server")
|
||||||
|
set(test_client "speech_client")
|
||||||
|
|
||||||
|
# 3. 检测并生成ODB框架代码
|
||||||
|
# 1. 添加所需的proto映射代码文件名称
|
||||||
|
set(proto_path ${CMAKE_CURRENT_SOURCE_DIR}/../proto)
|
||||||
|
set(proto_files speech.proto)
|
||||||
|
# 2. 检测框架代码文件是否已经生成
|
||||||
|
set(proto_hxx "")
|
||||||
|
set(proto_cxx "")
|
||||||
|
set(proto_srcs "")
|
||||||
|
foreach(proto_file ${proto_files})
|
||||||
|
# 3. 如果没有生成,则预定义生成指令 -- 用于在构建项目之间先生成框架代码
|
||||||
|
string(REPLACE ".proto" ".pb.cc" proto_cc ${proto_file})
|
||||||
|
string(REPLACE ".proto" ".pb.h" proto_hh ${proto_file})
|
||||||
|
if (NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}${proto_cc})
|
||||||
|
add_custom_command(
|
||||||
|
PRE_BUILD
|
||||||
|
COMMAND protoc
|
||||||
|
ARGS --cpp_out=${CMAKE_CURRENT_BINARY_DIR} -I ${proto_path} --experimental_allow_proto3_optional ${proto_path}/${proto_file}
|
||||||
|
DEPENDS ${proto_path}/${proto_file}
|
||||||
|
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${proto_cc}
|
||||||
|
COMMENT "生成Protobuf框架代码文件:" ${CMAKE_CURRENT_BINARY_DIR}/${proto_cc}
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
list(APPEND proto_srcs ${CMAKE_CURRENT_BINARY_DIR}/${proto_cc})
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# 4. 获取源码目录下的所有源码文件
|
||||||
|
set(src_files "")
|
||||||
|
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/source src_files)
|
||||||
|
# 5. 声明目标及依赖
|
||||||
|
add_executable(${target} ${src_files} ${proto_srcs})
|
||||||
|
# 7. 设置需要连接的库
|
||||||
|
target_link_libraries(${target} -lgflags -lspdlog -lfmt -lbrpc -lssl -lcrypto -lprotobuf -lleveldb -letcd-cpp-api -lcpprest -lcurl /usr/lib/x86_64-linux-gnu/libjsoncpp.so.19)
|
||||||
|
|
||||||
|
|
||||||
|
set(test_files "")
|
||||||
|
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/test test_files)
|
||||||
|
add_executable(${test_client} ${test_files} ${proto_srcs})
|
||||||
|
target_link_libraries(${test_client} -lgflags -lspdlog -lfmt -lbrpc -lssl -lcrypto -lprotobuf -lleveldb -letcd-cpp-api -lcpprest -lcurl /usr/lib/x86_64-linux-gnu/libjsoncpp.so.19)
|
||||||
|
|
||||||
|
# 6. 设置头文件默认搜索路径
|
||||||
|
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../common)
|
||||||
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../third/include)
|
||||||
|
|
||||||
|
#8. 设置安装路径
|
||||||
|
INSTALL(TARGETS ${target} ${test_client} RUNTIME DESTINATION bin)
|
||||||
16
speech/dockerfile
Normal file
16
speech/dockerfile
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# 声明基础经镜像来源
|
||||||
|
FROM debian:12
|
||||||
|
|
||||||
|
# 声明工作目录
|
||||||
|
WORKDIR /im
|
||||||
|
RUN mkdir -p /im/logs &&\
|
||||||
|
mkdir -p /im/data &&\
|
||||||
|
mkdir -p /im/conf &&\
|
||||||
|
mkdir -p /im/bin
|
||||||
|
|
||||||
|
# 将可执行程序依赖,拷贝进镜像
|
||||||
|
COPY ./build/speech_server /im/bin/
|
||||||
|
# 将可执行程序文件,拷贝进镜像
|
||||||
|
COPY ./depends /lib/x86_64-linux-gnu/
|
||||||
|
# 设置容器启动的默认操作 ---运行程序
|
||||||
|
CMD /im/bin/speech_server -flagfile=/im/conf/speech_server.conf
|
||||||
33
speech/source/speech_server.cc
Normal file
33
speech/source/speech_server.cc
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#include "speech_server.hpp"
|
||||||
|
//语音识别子服务
|
||||||
|
|
||||||
|
DEFINE_bool(run_mode, false, "程序的运行模式, false-调试;true-发布");
|
||||||
|
DEFINE_string(log_file, "", "发布模式下,用于指定日志的输出文件");
|
||||||
|
DEFINE_int32(log_level, 0, "发布模式下,用于指定日志的输出等级");
|
||||||
|
|
||||||
|
DEFINE_string(registry_host, "http://127.0.0.1:2379", "服务注册中心地址");
|
||||||
|
DEFINE_string(base_service, "/service", "服务监控根目录");
|
||||||
|
DEFINE_string(instance_name, "/speech_service/instance", "当前实例名称");
|
||||||
|
DEFINE_string(access_host, "127.0.0.1:10001", "当前实例的外部访问地址");
|
||||||
|
|
||||||
|
DEFINE_int32(listen_port, 10001, "Rpc服务器监听端口");
|
||||||
|
DEFINE_int32(rpc_timeout, -1, "Rpc调用超时时间");
|
||||||
|
DEFINE_int32(rpc_threads, 1, "Rpc的IO线程数量");
|
||||||
|
|
||||||
|
DEFINE_string(app_id, "118805148", "语音平台应用ID");
|
||||||
|
DEFINE_string(api_key, "tRBBbRWdTOjHgr8xZX0s4Z2d", "语音平台API密钥");
|
||||||
|
DEFINE_string(secret_key, "H2pyXuWi04uKEKK0T8jrTYo7Pj4UUUpC", "语音平台加密密钥");
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
google::ParseCommandLineFlags(&argc, &argv, true);
|
||||||
|
bite_im::init_logger(FLAGS_run_mode, FLAGS_log_file, FLAGS_log_level);
|
||||||
|
|
||||||
|
bite_im::SpeechServerBuilder ssb;
|
||||||
|
ssb.make_asr_object(FLAGS_app_id, FLAGS_api_key, FLAGS_secret_key);
|
||||||
|
ssb.make_rpc_server(FLAGS_listen_port, FLAGS_rpc_timeout, FLAGS_rpc_threads);
|
||||||
|
ssb.make_reg_object(FLAGS_registry_host, FLAGS_base_service + FLAGS_instance_name, FLAGS_access_host);
|
||||||
|
auto server = ssb.build();
|
||||||
|
server->start();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
123
speech/source/speech_server.hpp
Normal file
123
speech/source/speech_server.hpp
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
#include <brpc/server.h>
|
||||||
|
#include <butil/logging.h>
|
||||||
|
|
||||||
|
#include "asr.hpp" //语音识别模块
|
||||||
|
#include "etcd.hpp" //服务注册模块
|
||||||
|
#include "logger.hpp" //日志模块
|
||||||
|
#include "speech.pb.h" //protobuf框架代码
|
||||||
|
|
||||||
|
using namespace bite_im;
|
||||||
|
|
||||||
|
namespace bite_im{
|
||||||
|
class SpeechServiceImpl : public bite_im::SpeechService {
|
||||||
|
public:
|
||||||
|
SpeechServiceImpl(const ASRClient::ptr &asr_client):
|
||||||
|
_asr_client(asr_client){}
|
||||||
|
~SpeechServiceImpl(){}
|
||||||
|
void SpeechRecognition(google::protobuf::RpcController* controller,
|
||||||
|
const ::bite_im::SpeechRecognitionReq* request,
|
||||||
|
::bite_im::SpeechRecognitionRsp* response,
|
||||||
|
::google::protobuf::Closure* done) {
|
||||||
|
LOG_DEBUG("收到语音转文字请求!");
|
||||||
|
brpc::ClosureGuard rpc_guard(done);
|
||||||
|
//1. 取出请求中的语音数据
|
||||||
|
//2. 调用语音sdk模块进行语音识别,得到响应
|
||||||
|
std::string err;
|
||||||
|
std::string res = _asr_client->recognize(request->speech_content(), err);
|
||||||
|
if (res.empty()) {
|
||||||
|
LOG_ERROR("{} 语音识别失败!", request->request_id());
|
||||||
|
response->set_request_id(request->request_id());
|
||||||
|
response->set_success(false);
|
||||||
|
response->set_errmsg("语音识别失败:" + err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//3. 组织响应
|
||||||
|
response->set_request_id(request->request_id());
|
||||||
|
response->set_success(true);
|
||||||
|
response->set_recognition_result(res);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
ASRClient::ptr _asr_client;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SpeechServer {
|
||||||
|
public:
|
||||||
|
using ptr = std::shared_ptr<SpeechServer>;
|
||||||
|
SpeechServer(const ASRClient::ptr asr_client,
|
||||||
|
const Registry::ptr ®_client,
|
||||||
|
const std::shared_ptr<brpc::Server> &server):
|
||||||
|
_asr_client(asr_client),
|
||||||
|
_reg_client(reg_client),
|
||||||
|
_rpc_server(server){}
|
||||||
|
~SpeechServer(){}
|
||||||
|
//搭建RPC服务器,并启动服务器
|
||||||
|
void start() {
|
||||||
|
_rpc_server->RunUntilAskedToQuit();
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
ASRClient::ptr _asr_client;
|
||||||
|
Registry::ptr _reg_client;
|
||||||
|
std::shared_ptr<brpc::Server> _rpc_server;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SpeechServerBuilder {
|
||||||
|
public:
|
||||||
|
//构造语音识别客户端对象
|
||||||
|
void make_asr_object(const std::string &app_id,
|
||||||
|
const std::string &api_key,
|
||||||
|
const std::string &secret_key) {
|
||||||
|
_asr_client = std::make_shared<ASRClient>(app_id, api_key, secret_key);
|
||||||
|
}
|
||||||
|
//用于构造服务注册客户端对象
|
||||||
|
void make_reg_object(const std::string ®_host,
|
||||||
|
const std::string &service_name,
|
||||||
|
const std::string &access_host) {
|
||||||
|
_reg_client = std::make_shared<Registry>(reg_host);
|
||||||
|
_reg_client->registry(service_name, access_host);
|
||||||
|
}
|
||||||
|
//构造RPC服务器对象
|
||||||
|
void make_rpc_server(uint16_t port, int32_t timeout, uint8_t num_threads) {
|
||||||
|
if (!_asr_client) {
|
||||||
|
LOG_ERROR("还未初始化语音识别模块!");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
_rpc_server = std::make_shared<brpc::Server>();
|
||||||
|
SpeechServiceImpl *speech_service = new SpeechServiceImpl(_asr_client);
|
||||||
|
int ret = _rpc_server->AddService(speech_service,
|
||||||
|
brpc::ServiceOwnership::SERVER_OWNS_SERVICE);
|
||||||
|
if (ret == -1) {
|
||||||
|
LOG_ERROR("添加Rpc服务失败!");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
brpc::ServerOptions options;
|
||||||
|
options.idle_timeout_sec = timeout;
|
||||||
|
options.num_threads = num_threads;
|
||||||
|
ret = _rpc_server->Start(port, &options);
|
||||||
|
if (ret == -1) {
|
||||||
|
LOG_ERROR("服务启动失败!");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SpeechServer::ptr build() {
|
||||||
|
if (!_asr_client) {
|
||||||
|
LOG_ERROR("还未初始化语音识别模块!");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
if (!_reg_client) {
|
||||||
|
LOG_ERROR("还未初始化服务注册模块!");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
if (!_rpc_server) {
|
||||||
|
LOG_ERROR("还未初始化RPC服务器模块!");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
SpeechServer::ptr server = std::make_shared<SpeechServer>(
|
||||||
|
_asr_client, _reg_client, _rpc_server);
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
ASRClient::ptr _asr_client;
|
||||||
|
Registry::ptr _reg_client;
|
||||||
|
std::shared_ptr<brpc::Server> _rpc_server;
|
||||||
|
};
|
||||||
|
}
|
||||||
13
speech/speech_server.conf
Normal file
13
speech/speech_server.conf
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
-run_mode=true
|
||||||
|
-log_file=/im/logs/speech.log
|
||||||
|
-log_level=0
|
||||||
|
-registry_host=http://10.0.0.235:2379
|
||||||
|
-instance_name=/speech_service/instance
|
||||||
|
-access_host=10.0.0.235:10001
|
||||||
|
-base_service=/service
|
||||||
|
-listen_port=10001
|
||||||
|
-rpc_timeout=-1
|
||||||
|
-rpc_threads=1
|
||||||
|
-app_id=60694095
|
||||||
|
-api_key=PWn6zlsxym8VwpBW8Or4PPGe
|
||||||
|
-secret_key=Bl0mn74iyAkr3FzCo5TZV7lBq7NYoms9
|
||||||
73
speech/test/speech_client.cc
Normal file
73
speech/test/speech_client.cc
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
|
||||||
|
//speech_server的测试客户端实现
|
||||||
|
//进行服务的发现,发现speech_server服务器节点地址信息并实例化通信信道
|
||||||
|
//读取语音文件数据
|
||||||
|
//发起语音识别rpc调用
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include "etcd.hpp"
|
||||||
|
#include "channel.hpp"
|
||||||
|
#include <gflags/gflags.h>
|
||||||
|
#include <thread>
|
||||||
|
#include "aip-cpp-sdk/speech.h"
|
||||||
|
#include "aip-cpp-sdk/base/utils.h"
|
||||||
|
#include "speech.pb.h"
|
||||||
|
|
||||||
|
|
||||||
|
DEFINE_bool(run_mode, false, "程序的运行模式,false-调试; true-发布;");
|
||||||
|
DEFINE_string(log_file, "", "发布模式下,用于指定日志的输出文件");
|
||||||
|
DEFINE_int32(log_level, 0, "发布模式下,用于指定日志输出等级");
|
||||||
|
|
||||||
|
DEFINE_string(etcd_host, "http://127.0.0.1:2379", "服务注册中心地址");
|
||||||
|
DEFINE_string(base_service, "/service", "服务监控根目录");
|
||||||
|
DEFINE_string(speech_service, "/service/speech_service", "服务监控根目录");
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
google::ParseCommandLineFlags(&argc, &argv, true);
|
||||||
|
bite_im::init_logger(FLAGS_run_mode, FLAGS_log_file, FLAGS_log_level);
|
||||||
|
|
||||||
|
|
||||||
|
//1. 先构造Rpc信道管理对象
|
||||||
|
auto sm = std::make_shared<bite_im::ServiceManager>();
|
||||||
|
sm->declared(FLAGS_speech_service);
|
||||||
|
auto put_cb = std::bind(&bite_im::ServiceManager::onServiceOnline, sm.get(), std::placeholders::_1, std::placeholders::_2);
|
||||||
|
auto del_cb = std::bind(&bite_im::ServiceManager::onServiceOffline, sm.get(), std::placeholders::_1, std::placeholders::_2);
|
||||||
|
//2. 构造服务发现对象
|
||||||
|
bite_im::Discovery::ptr dclient = std::make_shared<bite_im::Discovery>(FLAGS_etcd_host, FLAGS_base_service, put_cb, del_cb);
|
||||||
|
|
||||||
|
//3. 通过Rpc信道管理对象,获取提供Echo服务的信道
|
||||||
|
auto channel = sm->choose(FLAGS_speech_service);
|
||||||
|
if (!channel) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
//读取语音文件数据
|
||||||
|
std::string file_content;
|
||||||
|
aip::get_file_content("16k.pcm", &file_content);
|
||||||
|
std::cout << file_content.size() << std::endl;
|
||||||
|
|
||||||
|
//4. 发起EchoRpc调用
|
||||||
|
bite_im::SpeechService_Stub stub(channel.get());
|
||||||
|
bite_im::SpeechRecognitionReq req;
|
||||||
|
req.set_speech_content(file_content);
|
||||||
|
req.set_request_id("111111");
|
||||||
|
|
||||||
|
brpc::Controller *cntl = new brpc::Controller();
|
||||||
|
bite_im::SpeechRecognitionRsp *rsp = new bite_im::SpeechRecognitionRsp();
|
||||||
|
stub.SpeechRecognition(cntl, &req, rsp, nullptr);
|
||||||
|
if (cntl->Failed() == true) {
|
||||||
|
std::cout << "Rpc调用失败:" << cntl->ErrorText() << std::endl;
|
||||||
|
delete cntl;
|
||||||
|
delete rsp;
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (rsp->success() == false) {
|
||||||
|
std::cout << rsp->errmsg() << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
std::cout << "收到响应: " << rsp->request_id() << std::endl;
|
||||||
|
std::cout << "收到响应: " << rsp->recognition_result() << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
35
third/include/aip-cpp-sdk/README.md
Normal file
35
third/include/aip-cpp-sdk/README.md
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# 安装百度AI开放平台 C++ SDK
|
||||||
|
|
||||||
|
**C++ SDK目录结构**
|
||||||
|
|
||||||
|
├── base
|
||||||
|
│ ├── base.h // 授权相关类
|
||||||
|
│ ├── base64.h // base64加密类
|
||||||
|
│ ├── http.h // http请求类
|
||||||
|
│ └── utils.h // 工具类
|
||||||
|
├── face.h // 人脸识别交互类
|
||||||
|
├── image_censor.h // 图像审核交互类
|
||||||
|
├── image_classify.h // 图像识别交互类
|
||||||
|
├── image_search.h // 图像搜索交互类
|
||||||
|
├── kg.h // 人脸识别交互类
|
||||||
|
├── nlp.h // 人脸识别交互类
|
||||||
|
├── ocr.h // 人脸识别交互类
|
||||||
|
└── speech.h // 语音识别交互类
|
||||||
|
|
||||||
|
**支持 C++ 11+**
|
||||||
|
|
||||||
|
**直接使用开发包步骤如下:**
|
||||||
|
|
||||||
|
1.在[官方网站](http://ai.baidu.com/sdk)下载C++ SDK压缩包。
|
||||||
|
|
||||||
|
2.将下载的`aip-cpp-sdk-version.zip`解压, 其中文件为包含实现代码的头文件。
|
||||||
|
|
||||||
|
3.安装依赖库curl(需要支持ssl) openssl jsoncpp(>1.6.2版本,0.x版本将不被支持)。
|
||||||
|
|
||||||
|
4.编译工程时添加 C++11 支持 (gcc/clang 添加编译参数 -std=c++11), 添加第三方库链接参数 lcurl, lcrypto, ljsoncpp。
|
||||||
|
|
||||||
|
5.在源码中include 您需要使用的交互类头文件(face.h image_censor.h image_classify.h kg.h nlp.h ocr.h speech.h等),引入压缩包中的头文即可使用aip命名空间下的类和方法。
|
||||||
|
|
||||||
|
# 详细使用文档
|
||||||
|
|
||||||
|
参考[百度AI开放平台官方文档](http://ai.baidu.com/docs)
|
||||||
311
third/include/aip-cpp-sdk/base/base.h
Normal file
311
third/include/aip-cpp-sdk/base/base.h
Normal file
@ -0,0 +1,311 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2017 Baidu.com, Inc. All Rights Reserved
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*
|
||||||
|
* @author baidu aip
|
||||||
|
*/
|
||||||
|
#ifndef __AIP_BASE_H__
|
||||||
|
#define __AIP_BASE_H__
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <cstring>
|
||||||
|
#include "http.h"
|
||||||
|
#include "json/json.h"
|
||||||
|
#include "base64.h"
|
||||||
|
#include "curl/curl.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
namespace aip {
|
||||||
|
|
||||||
|
static const char* AIP_SDK_VERSION = "0.3.3";
|
||||||
|
static const char* CURL_ERROR_CODE = "curl_error_code";
|
||||||
|
static const std::string ACCESS_TOKEN_URL = "https://aip.baidubce.com/oauth/2.0/token";
|
||||||
|
static const std::map<std::string, std::string> null;
|
||||||
|
static const Json::Value json_null;
|
||||||
|
|
||||||
|
class AipBase
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::string _app_id;
|
||||||
|
int _expired_time;
|
||||||
|
bool _is_bce;
|
||||||
|
bool _has_decide_type;
|
||||||
|
std::string _scope;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string getAccessToken()
|
||||||
|
{
|
||||||
|
time_t now = time(NULL);
|
||||||
|
if (!access_token.empty())
|
||||||
|
{
|
||||||
|
return this->access_token;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (now < this->_expired_time - 60 * 60 * 24)
|
||||||
|
{
|
||||||
|
return this->access_token;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string response;
|
||||||
|
std::map<std::string, std::string> params;
|
||||||
|
|
||||||
|
params["grant_type"] = "client_credentials";
|
||||||
|
params["client_id"] = this->ak;
|
||||||
|
params["client_secret"] = this->sk;
|
||||||
|
int status_code = this->client.get(
|
||||||
|
ACCESS_TOKEN_URL,
|
||||||
|
¶ms,
|
||||||
|
nullptr,
|
||||||
|
&response
|
||||||
|
);
|
||||||
|
Json::Value obj;
|
||||||
|
if (status_code != CURLcode::CURLE_OK) {
|
||||||
|
obj[CURL_ERROR_CODE] = status_code;
|
||||||
|
return obj.toStyledString();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string error;
|
||||||
|
std::unique_ptr<Json::CharReader> reader(crbuilder.newCharReader());
|
||||||
|
reader->parse(response.data(), response.data() + response.size(), &obj, &error);
|
||||||
|
this->access_token = obj["access_token"].asString();
|
||||||
|
this->_expired_time = obj["expires_in"].asInt() + (int) now;
|
||||||
|
this->_scope = obj["scope"].asString();
|
||||||
|
return this->access_token;
|
||||||
|
}
|
||||||
|
|
||||||
|
void merge_json(Json::Value& data, const Json::Value& options) {
|
||||||
|
Json::Value::Members mem = options.getMemberNames();
|
||||||
|
for (auto & iter : mem) {
|
||||||
|
data[iter.c_str()] = options[iter];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::string ak;
|
||||||
|
std::string sk;
|
||||||
|
HttpClient client;
|
||||||
|
Json::CharReaderBuilder crbuilder;
|
||||||
|
std::string access_token;
|
||||||
|
AipBase(const std::string & app_id, const std::string & ak, const std::string & sk):
|
||||||
|
_app_id(app_id),
|
||||||
|
_is_bce(false),
|
||||||
|
_has_decide_type(false),
|
||||||
|
ak(ak),
|
||||||
|
sk(sk)
|
||||||
|
{
|
||||||
|
if (_app_id == "")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setConnectionTimeoutInMillis(int connect_timeout)
|
||||||
|
{
|
||||||
|
this->client.setConnectTimeout(connect_timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSocketTimeoutInMillis(int socket_timeout)
|
||||||
|
{
|
||||||
|
this->client.setSocketTimeout(socket_timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setDebug(bool debug)
|
||||||
|
{
|
||||||
|
this->client.setDebug(debug);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string getAk() {
|
||||||
|
return ak;
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value request(
|
||||||
|
std::string url,
|
||||||
|
std::map<std::string, std::string> const & params,
|
||||||
|
std::string const & data,
|
||||||
|
std::map<std::string, std::string> const & headers)
|
||||||
|
{
|
||||||
|
std::string response;
|
||||||
|
Json::Value obj;
|
||||||
|
std::string body;
|
||||||
|
auto headers_for_sign = headers;
|
||||||
|
|
||||||
|
auto temp_params = params;
|
||||||
|
|
||||||
|
temp_params["charset"] = "UTF-8";
|
||||||
|
|
||||||
|
this->prepare_request(url, temp_params, headers_for_sign);
|
||||||
|
|
||||||
|
int status_code = this->client.post(url, &temp_params, data, &headers_for_sign, &response);
|
||||||
|
|
||||||
|
if (status_code != CURLcode::CURLE_OK) {
|
||||||
|
obj[CURL_ERROR_CODE] = status_code;
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string error;
|
||||||
|
std::unique_ptr<Json::CharReader> reader(crbuilder.newCharReader());
|
||||||
|
reader->parse(response.data(), response.data() + response.size(), &obj, &error);
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value request(
|
||||||
|
std::string url,
|
||||||
|
std::map<std::string, std::string> const & params,
|
||||||
|
std::map<std::string, std::string> const & data,
|
||||||
|
std::map<std::string, std::string> const & headers)
|
||||||
|
{
|
||||||
|
std::string response;
|
||||||
|
Json::Value obj;
|
||||||
|
|
||||||
|
auto headers_for_sign = headers;
|
||||||
|
auto temp_params = params;
|
||||||
|
|
||||||
|
this->prepare_request(url, temp_params, headers_for_sign);
|
||||||
|
|
||||||
|
int status_code = this->client.post(url, &temp_params, data, &headers_for_sign, &response);
|
||||||
|
|
||||||
|
if (status_code != CURLcode::CURLE_OK) {
|
||||||
|
obj[CURL_ERROR_CODE] = status_code;
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string error;
|
||||||
|
std::unique_ptr<Json::CharReader> reader(crbuilder.newCharReader());
|
||||||
|
reader->parse(response.data(), response.data() + response.size(), &obj, &error);
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
void prepare_request(std::string url,
|
||||||
|
std::map<std::string, std::string> & params,
|
||||||
|
std::map<std::string, std::string> & headers)
|
||||||
|
{
|
||||||
|
|
||||||
|
params["aipSdk"] = "C";
|
||||||
|
params["aipSdkVersion"] = AIP_SDK_VERSION;
|
||||||
|
|
||||||
|
if (_has_decide_type) {
|
||||||
|
if (_is_bce) {
|
||||||
|
std::string method = "POST";
|
||||||
|
sign(method, url, params, headers, ak, sk);
|
||||||
|
} else {
|
||||||
|
params["access_token"] = this->getAccessToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getAccessToken() == "") {
|
||||||
|
_is_bce = true;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
const char * t = std::strstr(this->_scope.c_str(), "brain_all_scope");
|
||||||
|
|
||||||
|
if (t == NULL)
|
||||||
|
{
|
||||||
|
_is_bce = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_has_decide_type = true;
|
||||||
|
prepare_request(url, params, headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Json::Value requestjson(
|
||||||
|
std::string url,
|
||||||
|
Json::Value & data,
|
||||||
|
std::map<std::string, std::string> & params,
|
||||||
|
std::map<std::string, std::string> const & headers)
|
||||||
|
{
|
||||||
|
|
||||||
|
std::string response;
|
||||||
|
Json::Value obj;
|
||||||
|
auto headers_for_sign = headers;
|
||||||
|
auto temp_params = params;
|
||||||
|
this->prepare_request(url, temp_params, headers_for_sign);
|
||||||
|
int status_code = this->client.post(url, nullptr, data, nullptr, &response);
|
||||||
|
if (status_code != CURLcode::CURLE_OK) {
|
||||||
|
obj[aip::CURL_ERROR_CODE] = status_code;
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string error;
|
||||||
|
std::unique_ptr<Json::CharReader> reader(crbuilder.newCharReader());
|
||||||
|
reader->parse(response.data(), response.data() + response.size(), &obj, &error);
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Json::Value request_com(
|
||||||
|
// std::string const & url,
|
||||||
|
// Json::Value & data)
|
||||||
|
// {
|
||||||
|
// std::string response;
|
||||||
|
// Json::Value obj;
|
||||||
|
// int status_code = this->client.post(url, nullptr, data, nullptr, &response);
|
||||||
|
//
|
||||||
|
// if (status_code != CURLcode::CURLE_OK) {
|
||||||
|
// obj[aip::CURL_ERROR_CODE] = status_code;
|
||||||
|
// return obj;
|
||||||
|
// }
|
||||||
|
// std::string error;
|
||||||
|
// std::unique_ptr<Json::CharReader> reader(crbuilder.newCharReader());
|
||||||
|
// reader->parse(response.data(), response.data() + response.size(), &obj, &error);
|
||||||
|
//
|
||||||
|
// return obj;
|
||||||
|
// }
|
||||||
|
|
||||||
|
Json::Value request_com(
|
||||||
|
std::string const & url,
|
||||||
|
Json::Value & data,
|
||||||
|
std::map<std::string, std::string>* headers = nullptr,
|
||||||
|
std::map<std::string, std::string>* params = nullptr)
|
||||||
|
{
|
||||||
|
std::string response;
|
||||||
|
Json::Value obj;
|
||||||
|
|
||||||
|
std::map<std::string, std::string> headers_for_sign;
|
||||||
|
if (headers != nullptr) {
|
||||||
|
headers_for_sign = *headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<std::string, std::string> temp_params;
|
||||||
|
if (params != nullptr) {
|
||||||
|
temp_params = *params;
|
||||||
|
}
|
||||||
|
this->prepare_request(url, temp_params, headers_for_sign);
|
||||||
|
|
||||||
|
int status_code = CURLcode::CURLE_OK;
|
||||||
|
if (headers == nullptr || headers->find("Content-Type") == headers->end()
|
||||||
|
|| (*headers)["Content-Type"] == "application/json") {
|
||||||
|
status_code = this->client.post(url, &temp_params, data, &headers_for_sign, &response);
|
||||||
|
} else if ((*headers)["Content-Type"] == "application/x-www-form-urlencoded") {
|
||||||
|
status_code = this->client.post_form(url, &temp_params, data, &headers_for_sign, &response);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status_code != CURLcode::CURLE_OK) {
|
||||||
|
obj[aip::CURL_ERROR_CODE] = status_code;
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
std::string error;
|
||||||
|
std::unique_ptr<Json::CharReader> reader(crbuilder.newCharReader());
|
||||||
|
reader->parse(response.data(), response.data() + response.size(), &obj, &error);
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
||||||
130
third/include/aip-cpp-sdk/base/base64.h
Normal file
130
third/include/aip-cpp-sdk/base/base64.h
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2017 Baidu.com, Inc. All Rights Reserved
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*
|
||||||
|
* @author baidu aip
|
||||||
|
*/
|
||||||
|
#ifndef __AIP_BASE64_H__
|
||||||
|
#define __AIP_BASE64_H__
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace aip {
|
||||||
|
|
||||||
|
static const std::string base64_chars =
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
"abcdefghijklmnopqrstuvwxyz"
|
||||||
|
"0123456789+/";
|
||||||
|
|
||||||
|
|
||||||
|
static inline bool is_base64(const char c)
|
||||||
|
{
|
||||||
|
return (isalnum(c) || (c == '+') || (c == '/'));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string base64_encode(const char * bytes_to_encode, unsigned int in_len)
|
||||||
|
{
|
||||||
|
std::string ret;
|
||||||
|
int i = 0;
|
||||||
|
int j = 0;
|
||||||
|
unsigned char char_array_3[3];
|
||||||
|
unsigned char char_array_4[4];
|
||||||
|
|
||||||
|
while (in_len--)
|
||||||
|
{
|
||||||
|
char_array_3[i++] = *(bytes_to_encode++);
|
||||||
|
if(i == 3)
|
||||||
|
{
|
||||||
|
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
|
||||||
|
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
|
||||||
|
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
|
||||||
|
char_array_4[3] = char_array_3[2] & 0x3f;
|
||||||
|
|
||||||
|
for(i = 0; (i <4) ; i++)
|
||||||
|
{
|
||||||
|
ret += base64_chars[char_array_4[i]];
|
||||||
|
}
|
||||||
|
i = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(i)
|
||||||
|
{
|
||||||
|
for(j = i; j < 3; j++)
|
||||||
|
{
|
||||||
|
char_array_3[j] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
|
||||||
|
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
|
||||||
|
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
|
||||||
|
char_array_4[3] = char_array_3[2] & 0x3f;
|
||||||
|
|
||||||
|
for(j = 0; (j < i + 1); j++)
|
||||||
|
{
|
||||||
|
ret += base64_chars[char_array_4[j]];
|
||||||
|
}
|
||||||
|
|
||||||
|
while((i++ < 3))
|
||||||
|
{
|
||||||
|
ret += '=';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string base64_decode(std::string const & encoded_string)
|
||||||
|
{
|
||||||
|
int in_len = (int) encoded_string.size();
|
||||||
|
int i = 0;
|
||||||
|
int j = 0;
|
||||||
|
int in_ = 0;
|
||||||
|
unsigned char char_array_4[4], char_array_3[3];
|
||||||
|
std::string ret;
|
||||||
|
|
||||||
|
while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
|
||||||
|
char_array_4[i++] = encoded_string[in_]; in_++;
|
||||||
|
if (i ==4) {
|
||||||
|
for (i = 0; i <4; i++)
|
||||||
|
char_array_4[i] = base64_chars.find(char_array_4[i]);
|
||||||
|
|
||||||
|
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
||||||
|
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
||||||
|
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
||||||
|
|
||||||
|
for (i = 0; (i < 3); i++)
|
||||||
|
ret += char_array_3[i];
|
||||||
|
i = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i) {
|
||||||
|
for (j = i; j <4; j++)
|
||||||
|
char_array_4[j] = 0;
|
||||||
|
|
||||||
|
for (j = 0; j <4; j++)
|
||||||
|
char_array_4[j] = base64_chars.find(char_array_4[j]);
|
||||||
|
|
||||||
|
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
||||||
|
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
||||||
|
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
||||||
|
|
||||||
|
for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
||||||
306
third/include/aip-cpp-sdk/base/http.h
Normal file
306
third/include/aip-cpp-sdk/base/http.h
Normal file
@ -0,0 +1,306 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2017 Baidu.com, Inc. All Rights Reserved
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*
|
||||||
|
* @author baidu aip
|
||||||
|
*/
|
||||||
|
#ifndef __AIP_HTTP_H__
|
||||||
|
#define __AIP_HTTP_H__
|
||||||
|
|
||||||
|
#include "curl/curl.h"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
#include <json/json.h>
|
||||||
|
|
||||||
|
namespace aip {
|
||||||
|
|
||||||
|
inline size_t onWriteData(void * buffer, size_t size, size_t nmemb, void * userp)
|
||||||
|
{
|
||||||
|
std::string * str = dynamic_cast<std::string *>((std::string *)userp);
|
||||||
|
str->append((char *)buffer, size * nmemb);
|
||||||
|
return nmemb;
|
||||||
|
}
|
||||||
|
|
||||||
|
class HttpClient
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
bool debug = false;
|
||||||
|
int connect_timeout = 10000;
|
||||||
|
int socket_timeout = 10000;
|
||||||
|
|
||||||
|
void makeUrlencodedForm(std::map<std::string, std::string> const & params, std::string * content) const
|
||||||
|
{
|
||||||
|
content->clear();
|
||||||
|
std::map<std::string, std::string>::const_iterator it;
|
||||||
|
for(it=params.begin(); it!=params.end(); it++)
|
||||||
|
{
|
||||||
|
char * key = curl_escape(it->first.c_str(), (int) it->first.size());
|
||||||
|
char * value = curl_escape(it->second.c_str(),(int) it->second.size());
|
||||||
|
*content += key;
|
||||||
|
*content += '=';
|
||||||
|
*content += value;
|
||||||
|
*content += '&';
|
||||||
|
curl_free(key);
|
||||||
|
curl_free(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void appendUrlParams(std::map<std::string, std::string> const & params, std::string* url) const
|
||||||
|
{
|
||||||
|
if(params.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::string content;
|
||||||
|
this->makeUrlencodedForm(params, &content);
|
||||||
|
bool url_has_param = false;
|
||||||
|
for (const auto& ch : *url) {
|
||||||
|
if (ch == '?') {
|
||||||
|
url_has_param = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (url_has_param) {
|
||||||
|
url->append("&");
|
||||||
|
} else {
|
||||||
|
url->append("?");
|
||||||
|
}
|
||||||
|
url->append(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
void appendHeaders(std::map<std::string, std::string> const & headers, curl_slist ** slist) const
|
||||||
|
{
|
||||||
|
std::ostringstream ostr;
|
||||||
|
std::map<std::string, std::string>::const_iterator it;
|
||||||
|
for(it=headers.begin(); it!=headers.end(); it++)
|
||||||
|
{
|
||||||
|
ostr << it->first << ":" << it->second;
|
||||||
|
*slist = curl_slist_append(*slist, ostr.str().c_str());
|
||||||
|
ostr.str("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
HttpClient() = default;
|
||||||
|
|
||||||
|
HttpClient(const HttpClient &) = delete;
|
||||||
|
HttpClient & operator=(const HttpClient &) = delete;
|
||||||
|
|
||||||
|
void setConnectTimeout(int connect_timeout)
|
||||||
|
{
|
||||||
|
this->connect_timeout = connect_timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSocketTimeout(int socket_timeout)
|
||||||
|
{
|
||||||
|
this->socket_timeout = socket_timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setDebug(bool debug)
|
||||||
|
{
|
||||||
|
this->debug = debug;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get(
|
||||||
|
std::string url,
|
||||||
|
std::map<std::string, std::string> const * params,
|
||||||
|
std::map<std::string, std::string> const * headers,
|
||||||
|
std::string * response) const
|
||||||
|
{
|
||||||
|
CURL * curl = curl_easy_init();
|
||||||
|
struct curl_slist * slist = NULL;
|
||||||
|
if (headers) {
|
||||||
|
this->appendHeaders(*headers, &slist);
|
||||||
|
}
|
||||||
|
if (params) {
|
||||||
|
this->appendUrlParams(*params, &url);
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||||
|
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, onWriteData);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) response);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, true);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, this->connect_timeout);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, this->socket_timeout);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_VERBOSE, this->debug);
|
||||||
|
|
||||||
|
int status_code = curl_easy_perform(curl);
|
||||||
|
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
curl_slist_free_all(slist);
|
||||||
|
|
||||||
|
return status_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
int post(
|
||||||
|
std::string url,
|
||||||
|
std::map<std::string, std::string> const * params,
|
||||||
|
const std::string & body,
|
||||||
|
std::map<std::string, std::string> const * headers,
|
||||||
|
std::string * response) const
|
||||||
|
{
|
||||||
|
struct curl_slist * slist = NULL;
|
||||||
|
CURL * curl = curl_easy_init();
|
||||||
|
if (headers) {
|
||||||
|
this->appendHeaders(*headers, &slist);
|
||||||
|
}
|
||||||
|
if (params) {
|
||||||
|
this->appendUrlParams(*params, &url);
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||||
|
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_POST, true);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());
|
||||||
|
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, body.size());
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, onWriteData);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) response);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, true);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, this->connect_timeout);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, this->socket_timeout);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_VERBOSE, this->debug);
|
||||||
|
|
||||||
|
int status_code = curl_easy_perform(curl);
|
||||||
|
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
curl_slist_free_all(slist);
|
||||||
|
|
||||||
|
return status_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* application/x-www-form-urlencoded
|
||||||
|
* @param url
|
||||||
|
* @param params
|
||||||
|
* @param data
|
||||||
|
* @param headers
|
||||||
|
* @param response
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
int post(
|
||||||
|
std::string url,
|
||||||
|
std::map<std::string, std::string> const * params,
|
||||||
|
std::map<std::string, std::string> const & data,
|
||||||
|
std::map<std::string, std::string> const * headers,
|
||||||
|
std::string * response) const
|
||||||
|
{
|
||||||
|
std::string body;
|
||||||
|
this->makeUrlencodedForm(data, &body);
|
||||||
|
return this->post(std::move(url), params, body, headers, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* application/json
|
||||||
|
* @param url
|
||||||
|
* @param params
|
||||||
|
* @param data
|
||||||
|
* @param headers
|
||||||
|
* @param response
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
int post(
|
||||||
|
std::string url,
|
||||||
|
std::map<std::string, std::string> const * params,
|
||||||
|
Json::Value const & data,
|
||||||
|
std::map<std::string, std::string> const * headers,
|
||||||
|
std::string * response) const
|
||||||
|
{
|
||||||
|
std::string body;
|
||||||
|
Json::StreamWriterBuilder swb;
|
||||||
|
std::unique_ptr<Json::StreamWriter> writer(swb.newStreamWriter());
|
||||||
|
std::ostringstream os;
|
||||||
|
writer->write(data, &os);
|
||||||
|
body = os.str();
|
||||||
|
std::map<std::string, std::string> temp_headers;
|
||||||
|
if (headers != nullptr) {
|
||||||
|
for (const auto & iter : *headers) {
|
||||||
|
temp_headers[iter.first] = iter.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (temp_headers.find("Content-Type") == temp_headers.end()) {
|
||||||
|
temp_headers["Content-Type"] = "application/json";
|
||||||
|
}
|
||||||
|
return this->post(url.c_str(), params, body, &temp_headers, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* application/x-www-form-urlencoded
|
||||||
|
* all type data
|
||||||
|
* @param url
|
||||||
|
* @param params
|
||||||
|
* @param data
|
||||||
|
* @param headers
|
||||||
|
* @param response
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
int post_form(
|
||||||
|
std::string url,
|
||||||
|
std::map<std::string, std::string> const * params,
|
||||||
|
Json::Value const & data,
|
||||||
|
std::map<std::string, std::string> const * headers,
|
||||||
|
std::string * response) const
|
||||||
|
{
|
||||||
|
std::string body;
|
||||||
|
body.clear();
|
||||||
|
Json::Value::Members mem = data.getMemberNames();
|
||||||
|
for (auto iter = mem.begin(); iter != mem.end(); iter++) {
|
||||||
|
std::string str = "";
|
||||||
|
char * curl_escape_value;
|
||||||
|
char * key = curl_escape((*iter).c_str(), (int)((*iter).size()));
|
||||||
|
body += key;
|
||||||
|
body += '=';
|
||||||
|
Json::Value jsonValue = data[*iter];
|
||||||
|
switch(jsonValue.type()) {
|
||||||
|
case Json::realValue:
|
||||||
|
body += std::to_string(data[*iter].asDouble());
|
||||||
|
break;
|
||||||
|
case Json::intValue:
|
||||||
|
body += std::to_string(data[*iter].asInt64());
|
||||||
|
break;
|
||||||
|
case Json::booleanValue:
|
||||||
|
body += std::to_string(data[*iter].asBool());
|
||||||
|
break;
|
||||||
|
case Json::stringValue:
|
||||||
|
str = data[*iter].asString();
|
||||||
|
curl_escape_value = curl_escape(str.c_str(), (int)(str.size()));
|
||||||
|
body += curl_escape_value;
|
||||||
|
curl_free(curl_escape_value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
body += '&';
|
||||||
|
curl_free(key);
|
||||||
|
}
|
||||||
|
return this->post(std::move(url), params, body, headers, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int post(
|
||||||
|
std::string url,
|
||||||
|
std::map<std::string, std::string> const * params,
|
||||||
|
std::map<std::string, std::string> const * headers,
|
||||||
|
std::string * response) const
|
||||||
|
{
|
||||||
|
const static std::string EMPTY_STRING;
|
||||||
|
return this->post(std::move(url), params, EMPTY_STRING, headers, response);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
283
third/include/aip-cpp-sdk/base/utils.h
Normal file
283
third/include/aip-cpp-sdk/base/utils.h
Normal file
@ -0,0 +1,283 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2017 Baidu.com, Inc. All Rights Reserved
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*
|
||||||
|
* @author baidu aip
|
||||||
|
*/
|
||||||
|
#ifndef __AIP_UTILS_H__
|
||||||
|
#define __AIP_UTILS_H__
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <fstream>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
#include <openssl/hmac.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <openssl/md5.h>
|
||||||
|
|
||||||
|
const int __BCE_VERSION__ = 1;
|
||||||
|
const int __BCE_EXPIRE__ = 1800;
|
||||||
|
|
||||||
|
namespace aip {
|
||||||
|
|
||||||
|
template<class CharT, class Traits, class Allocator>
|
||||||
|
std::basic_istream<CharT, Traits>& getall(std::basic_istream<CharT, Traits>& input,
|
||||||
|
std::basic_string<CharT, Traits, Allocator>& str) {
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << input.rdbuf();
|
||||||
|
str.assign(oss.str());
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int get_file_content(const char *filename, std::string* out) {
|
||||||
|
std::ifstream in(filename, std::ios::in | std::ios::binary);
|
||||||
|
if (in) {
|
||||||
|
getall(in, *out);
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string to_upper(std::string src)
|
||||||
|
{
|
||||||
|
std::transform(src.begin(), src.end(), src.begin(), [](unsigned char c) { return std::toupper(c); });
|
||||||
|
return src;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline std::string to_lower(std::string src)
|
||||||
|
{
|
||||||
|
std::transform(src.begin(), src.end(), src.begin(), [](unsigned char c) { return std::tolower(c); });
|
||||||
|
return src;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string to_hex(unsigned char c, bool lower = false)
|
||||||
|
{
|
||||||
|
const std::string hex = "0123456789ABCDEF";
|
||||||
|
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << hex[c >> 4] << hex[c & 0xf];
|
||||||
|
|
||||||
|
return lower ? to_lower(ss.str()) : ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline time_t now()
|
||||||
|
{
|
||||||
|
return time(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string utc_time(time_t timestamp)
|
||||||
|
{
|
||||||
|
struct tm result_tm;
|
||||||
|
char buffer[32];
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
gmtime_s(&result_tm, ×tamp);
|
||||||
|
#else
|
||||||
|
gmtime_r(×tamp, &result_tm);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
size_t size = strftime(buffer, 32, "%Y-%m-%dT%H:%M:%SZ", &result_tm);
|
||||||
|
|
||||||
|
return std::string(buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void url_parse(
|
||||||
|
const std::string & url,
|
||||||
|
std::map<std::string, std::string> & params)
|
||||||
|
{
|
||||||
|
int pos = (int)url.find("?");
|
||||||
|
if (pos != -1)
|
||||||
|
{
|
||||||
|
int key_start = pos + 1,
|
||||||
|
key_len = 0,
|
||||||
|
val_start = 0;
|
||||||
|
for (int i = key_start; i <= (int)url.size(); ++i)
|
||||||
|
{
|
||||||
|
switch (url[i])
|
||||||
|
{
|
||||||
|
case '=':
|
||||||
|
key_len = i - key_start;
|
||||||
|
val_start = i + 1;
|
||||||
|
break;
|
||||||
|
case '\0':
|
||||||
|
case '&':
|
||||||
|
if (key_len != 0)
|
||||||
|
{
|
||||||
|
params[url.substr(key_start, key_len)] = url.substr(val_start, i - val_start);
|
||||||
|
key_start = i + 1;
|
||||||
|
key_len = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string url_encode(const std::string & input, bool encode_slash=true)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
const char *str = input.c_str();
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < input.size(); i++)
|
||||||
|
{
|
||||||
|
unsigned char c = str[i];
|
||||||
|
if (isalnum(c) || c == '_' || c == '-' || c == '~' || c == '.' || (!encode_slash && c == '/'))
|
||||||
|
{
|
||||||
|
ss << c;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ss << "%" << to_hex(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string canonicalize_params(std::map<std::string, std::string> & params)
|
||||||
|
{
|
||||||
|
std::vector<std::string> v;
|
||||||
|
v.reserve(params.size());
|
||||||
|
|
||||||
|
for (auto & it : params) {
|
||||||
|
v.push_back(url_encode(it.first) + "=" + url_encode(it.second));
|
||||||
|
}
|
||||||
|
std::sort(v.begin(), v.end());
|
||||||
|
|
||||||
|
std::string result;
|
||||||
|
for (auto & it : v)
|
||||||
|
{
|
||||||
|
result.append((result.empty() ? "" : "&") + it);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string canonicalize_headers(std::map<std::string, std::string> & headers)
|
||||||
|
{
|
||||||
|
std::vector<std::string> v;
|
||||||
|
v.reserve(headers.size());
|
||||||
|
|
||||||
|
for (auto & it : headers) {
|
||||||
|
v.push_back(url_encode(to_lower(it.first)) + ":" + url_encode(it.second));
|
||||||
|
}
|
||||||
|
std::sort(v.begin(), v.end());
|
||||||
|
|
||||||
|
std::string result;
|
||||||
|
for (auto & it : v)
|
||||||
|
{
|
||||||
|
result.append((result.empty() ? "" : "\n") + it);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_headers_keys(std::map<std::string, std::string> & headers)
|
||||||
|
{
|
||||||
|
std::vector<std::string> v;
|
||||||
|
v.reserve(headers.size());
|
||||||
|
|
||||||
|
for (auto & it : headers) {
|
||||||
|
v.push_back(to_lower(it.first));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string result;
|
||||||
|
for (auto & it : v)
|
||||||
|
{
|
||||||
|
result.append((result.empty() ? "" : ";") + it);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_host(const std::string & url)
|
||||||
|
{
|
||||||
|
int pos = (int)url.find("://") + 3;
|
||||||
|
return url.substr(
|
||||||
|
pos,
|
||||||
|
url.find('/', pos) - pos
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string get_path(const std::string & url)
|
||||||
|
{
|
||||||
|
int path_start = (int)url.find('/', url.find("://") + 3);
|
||||||
|
int path_end = (int)url.find('?');
|
||||||
|
path_end = path_end == -1 ? (int)url.size() : path_end;
|
||||||
|
|
||||||
|
return url.substr(path_start, path_end - path_start);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string hmac_sha256(
|
||||||
|
const std::string & src,
|
||||||
|
const std::string & sk)
|
||||||
|
{
|
||||||
|
const EVP_MD *evp_md = EVP_sha256();
|
||||||
|
unsigned char md[EVP_MAX_MD_SIZE];
|
||||||
|
unsigned int md_len = 0;
|
||||||
|
|
||||||
|
if (HMAC(evp_md,
|
||||||
|
reinterpret_cast<const unsigned char *>(sk.data()), (int)sk.size(),
|
||||||
|
reinterpret_cast<const unsigned char *>(src.data()), src.size(),
|
||||||
|
md, &md_len) == NULL)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::stringstream ss;
|
||||||
|
for (int i = 0; i < (int)md_len; ++i)
|
||||||
|
{
|
||||||
|
ss << to_hex(md[i], true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
void sign(
|
||||||
|
std::string method,
|
||||||
|
std::string & url,
|
||||||
|
std::map<std::string, std::string> & params,
|
||||||
|
std::map<std::string, std::string> & headers,
|
||||||
|
std::string & ak,
|
||||||
|
std::string & sk)
|
||||||
|
{
|
||||||
|
url_parse(url, params);
|
||||||
|
headers["Host"] = get_host(url);
|
||||||
|
std::string timestamp = utc_time(now());
|
||||||
|
headers["x-bce-date"] = timestamp;
|
||||||
|
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "bce-auth-v" << __BCE_VERSION__ << "/" << ak << "/"
|
||||||
|
<< timestamp << "/" << __BCE_EXPIRE__;
|
||||||
|
|
||||||
|
std::string val = ss.str();
|
||||||
|
std::string sign_key = hmac_sha256(val, sk);
|
||||||
|
|
||||||
|
ss.str("");
|
||||||
|
ss << to_upper(method) << '\n' << url_encode(get_path(url), false)
|
||||||
|
<< '\n' << canonicalize_params(params)
|
||||||
|
<< '\n' << canonicalize_headers(headers);
|
||||||
|
|
||||||
|
std::string signature = hmac_sha256(ss.str(), sign_key);
|
||||||
|
|
||||||
|
ss.str("");
|
||||||
|
ss << "bce-auth-v" << __BCE_VERSION__ << "/" << ak << "/"
|
||||||
|
<< timestamp << "/" << __BCE_EXPIRE__ << "/"
|
||||||
|
<< get_headers_keys(headers) << "/" << signature;
|
||||||
|
|
||||||
|
headers["authorization"] = ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
220
third/include/aip-cpp-sdk/body_analysis.h
Normal file
220
third/include/aip-cpp-sdk/body_analysis.h
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2017 Baidu.com, Inc. All Rights Reserved
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*
|
||||||
|
* @author baidu aip
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __AIP_BODY_ANALYSIS_H__
|
||||||
|
#define __AIP_BODY_ANALYSIS_H__
|
||||||
|
|
||||||
|
#include "base/base.h"
|
||||||
|
|
||||||
|
namespace aip {
|
||||||
|
|
||||||
|
class Bodyanalysis : public AipBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
std::string _body_analysis_v1 =
|
||||||
|
"https://aip.baidubce.com/rest/2.0/image-classify/v1/body_analysis";
|
||||||
|
std::string _body_attr_v1 =
|
||||||
|
"https://aip.baidubce.com/rest/2.0/image-classify/v1/body_attr";
|
||||||
|
std::string _body_num_v1 =
|
||||||
|
"https://aip.baidubce.com/rest/2.0/image-classify/v1/body_num";
|
||||||
|
std::string _driver_behavior_v1 =
|
||||||
|
"https://aip.baidubce.com/rest/2.0/image-classify/v1/driver_behavior";
|
||||||
|
std::string _body_seg_v1 =
|
||||||
|
"https://aip.baidubce.com/rest/2.0/image-classify/v1/body_seg";
|
||||||
|
std::string _gesture_v1 =
|
||||||
|
"https://aip.baidubce.com/rest/2.0/image-classify/v1/gesture";
|
||||||
|
std::string _body_tracking_v1 =
|
||||||
|
"https://aip.baidubce.com/rest/2.0/image-classify/v1/body_tracking";
|
||||||
|
std::string _hand_analysis_v1 =
|
||||||
|
"https://aip.baidubce.com/rest/2.0/image-classify/v1/hand_analysis";
|
||||||
|
std::string _body_danger_v1 =
|
||||||
|
"https://aip.baidubce.com/rest/2.0/video-classify/v1/body_danger";
|
||||||
|
std::string _fingertip_v1 =
|
||||||
|
"https://aip.baidubce.com/rest/2.0/image-classify/v1/fingertip";
|
||||||
|
|
||||||
|
Bodyanalysis(const std::string & app_id, const std::string & ak, const std::string & sk)
|
||||||
|
: AipBase(app_id, ak, sk)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 人体关键点识别
|
||||||
|
* 接口使用文档链接: https://ai.baidu.com/ai-doc/BODY/0k3cpyxme
|
||||||
|
*/
|
||||||
|
Json::Value body_analysis_v1(
|
||||||
|
std::string const &image)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
data["image"] = base64_encode(image.c_str(), (int) image.size());
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_body_analysis_v1, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 人体检测与属性识别
|
||||||
|
* 接口使用文档链接: https://ai.baidu.com/ai-doc/BODY/Ak3cpyx6v
|
||||||
|
*/
|
||||||
|
Json::Value body_attr_v1(
|
||||||
|
std::string const &image,
|
||||||
|
const std::map<std::string, std::string> &options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
data["image"] = base64_encode(image.c_str(), (int) image.size());
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_body_attr_v1, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 人流量统计
|
||||||
|
* 接口使用文档链接: https://ai.baidu.com/ai-doc/BODY/7k3cpyy1t
|
||||||
|
*/
|
||||||
|
Json::Value body_num_v1(
|
||||||
|
std::string const &image,
|
||||||
|
const std::map<std::string, std::string> &options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
data["image"] = base64_encode(image.c_str(), (int) image.size());
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_body_num_v1, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 驾驶行为分析
|
||||||
|
* 接口使用文档链接: https://ai.baidu.com/ai-doc/BODY/Nk3cpywct
|
||||||
|
*/
|
||||||
|
Json::Value driver_behavior_v1(
|
||||||
|
std::string const &image,
|
||||||
|
const std::map<std::string, std::string> &options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
data["image"] = base64_encode(image.c_str(), (int) image.size());
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_driver_behavior_v1, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 人像分割
|
||||||
|
* 接口使用文档链接: https://ai.baidu.com/ai-doc/BODY/Fk3cpyxua
|
||||||
|
*/
|
||||||
|
Json::Value body_seg_v1(
|
||||||
|
std::string const &image,
|
||||||
|
const std::map<std::string, std::string> &options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
data["image"] = base64_encode(image.c_str(), (int) image.size());
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_body_seg_v1, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手势识别
|
||||||
|
* 接口使用文档链接: https://ai.baidu.com/ai-doc/BODY/4k3cpywrv
|
||||||
|
*/
|
||||||
|
Json::Value gesture_v1(
|
||||||
|
std::string const &image)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
data["image"] = base64_encode(image.c_str(), (int) image.size());
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_gesture_v1, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 人流量统计(动态版)
|
||||||
|
* 接口使用文档链接: https://ai.baidu.com/ai-doc/BODY/wk3cpyyog
|
||||||
|
*/
|
||||||
|
Json::Value body_tracking_v1(
|
||||||
|
std::string const &dynamic,
|
||||||
|
std::string const &image,
|
||||||
|
Json::Value & options)
|
||||||
|
{
|
||||||
|
Json::Value data;
|
||||||
|
data["image"] = base64_encode(image.c_str(), (int) image.size());
|
||||||
|
data["dynamic"] = dynamic;
|
||||||
|
merge_json(data, options);
|
||||||
|
|
||||||
|
std::map<std::string, std::string> headers;
|
||||||
|
headers["Content-Type"] = "application/x-www-form-urlencoded";
|
||||||
|
Json::Value result = this->request_com(_body_tracking_v1, data, &headers);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手部关键点识别
|
||||||
|
* 接口使用文档链接: https://ai.baidu.com/ai-doc/BODY/Kk3cpyxeu
|
||||||
|
*/
|
||||||
|
Json::Value hand_analysis_v1(
|
||||||
|
std::string const &image)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
data["image"] = base64_encode(image.c_str(), (int) image.size());
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_hand_analysis_v1, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 危险行为识别
|
||||||
|
* 接口使用文档链接: https://ai.baidu.com/ai-doc/BODY/uk3cpywke
|
||||||
|
*/
|
||||||
|
Json::Value body_danger_v1(
|
||||||
|
std::string const &video_data)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
data["data"] = base64_encode(video_data.c_str(), (int) video_data.size());
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_body_danger_v1, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指尖检测
|
||||||
|
* 接口使用文档链接: https://ai.baidu.com/ai-doc/BODY/Jk7ir38ut
|
||||||
|
*/
|
||||||
|
Json::Value fingertip_v1(
|
||||||
|
std::string const &image)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
data["image"] = base64_encode(image.c_str(), (int) image.size());
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_fingertip_v1, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif
|
||||||
327
third/include/aip-cpp-sdk/content_censor.h
Normal file
327
third/include/aip-cpp-sdk/content_censor.h
Normal file
@ -0,0 +1,327 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2017 Baidu.com, Inc. All Rights Reserved
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*
|
||||||
|
* @author baidu aip
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __AIP_CONTENTCENSOR_H__
|
||||||
|
#define __AIP_CONTENTCENSOR_H__
|
||||||
|
|
||||||
|
#include "base/base.h"
|
||||||
|
|
||||||
|
namespace aip {
|
||||||
|
|
||||||
|
class Contentcensor: public AipBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::string _img_censor_user_defined_v2 =
|
||||||
|
"https://aip.baidubce.com/rest/2.0/solution/v1/img_censor/v2/user_defined";
|
||||||
|
std::string _text_censor_user_defined_v2 =
|
||||||
|
"https://aip.baidubce.com/rest/2.0/solution/v1/text_censor/v2/user_defined";
|
||||||
|
std::string _live_save_v1 = "https://aip.baidubce.com/rest/2.0/solution/v1/live/v1/config/save";
|
||||||
|
std::string _live_stop_v1 = "https://aip.baidubce.com/rest/2.0/solution/v1/live/v1/config/stop";
|
||||||
|
std::string _live_view_v1 = "https://aip.baidubce.com/rest/2.0/solution/v1/live/v1/config/view";
|
||||||
|
std::string _live_pull_v1 = "https://aip.baidubce.com/rest/2.0/solution/v1/live/v1/audit/pull";
|
||||||
|
std::string _video_censor_submit_v1 = "https://aip.baidubce.com/rest/2.0/solution/v1/video_censor/v1/video/submit";
|
||||||
|
std::string _video_censor_pull_v1 = "https://aip.baidubce.com/rest/2.0/solution/v1/video_censor/v1/video/pull";
|
||||||
|
std::string _async_voice_submit_v1 = "https://aip.baidubce.com/rest/2.0/solution/v1/async_voice/submit";
|
||||||
|
std::string _async_voice_pull_v1 = "https://aip.baidubce.com/rest/2.0/solution/v1/async_voice/pull";
|
||||||
|
std::string _document_censor_submit_url = "https://aip.baidubce.com/rest/2.0/solution/v1/solution/document/v1/submit";
|
||||||
|
std::string _document_censor_pull_url = "https://aip.baidubce.com/rest/2.0/solution/v1/solution/document/v1/pull";
|
||||||
|
|
||||||
|
Contentcensor(const std::string & app_id, const std::string & ak, const std::string & sk)
|
||||||
|
: AipBase(app_id, ak, sk)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内容审核平台-图像
|
||||||
|
* 接口使用文档链接: https://ai.baidu.com/ai-doc/ANTIPORN/jk42xep4e
|
||||||
|
*/
|
||||||
|
Json::Value img_censor_user_defined_v2_img(std::string const &image, const Json::Value & options)
|
||||||
|
{
|
||||||
|
Json::Value data;
|
||||||
|
data["image"] = base64_encode(image.c_str(), (int) image.size());
|
||||||
|
merge_json(data, options);
|
||||||
|
|
||||||
|
std::map<std::string, std::string> headers;
|
||||||
|
headers["Content-Type"] = "application/x-www-form-urlencoded";
|
||||||
|
Json::Value result = this->request_com(_img_censor_user_defined_v2, data, &headers);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内容审核平台-图像
|
||||||
|
* 接口使用文档链接: https://ai.baidu.com/ai-doc/ANTIPORN/jk42xep4e
|
||||||
|
*/
|
||||||
|
Json::Value img_censor_user_defined_v2_url(std::string const &imgUrl, const Json::Value & options)
|
||||||
|
{
|
||||||
|
Json::Value data;
|
||||||
|
data["imgUrl"] = imgUrl;
|
||||||
|
merge_json(data, options);
|
||||||
|
|
||||||
|
std::map<std::string, std::string> headers;
|
||||||
|
headers["Content-Type"] = "application/x-www-form-urlencoded";
|
||||||
|
Json::Value result = this->request_com(_img_censor_user_defined_v2, data, &headers);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内容审核平台-文本
|
||||||
|
* 接口使用文档链接: https://ai.baidu.com/ai-doc/ANTIPORN/Rk3h6xb3i
|
||||||
|
*/
|
||||||
|
Json::Value text_censor_user_defined_v2(std::string const &text)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
data["text"] = text;
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_text_censor_user_defined_v2, null, data, null);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内容审核平台-直播流(新增任务)
|
||||||
|
* 接口使用文档链接: https://ai.baidu.com/ai-doc/ANTIPORN/mkxlraoz5
|
||||||
|
*/
|
||||||
|
Json::Value live_save_v1(std::string const &streamUrl, std::string const &streamType,
|
||||||
|
std::string const &extId, long long const &startTime,
|
||||||
|
long long const &endTime, std::string const &streamName, const Json::Value & options)
|
||||||
|
{
|
||||||
|
Json::Value data;
|
||||||
|
data["streamUrl"] = streamUrl;
|
||||||
|
data["streamType"] = streamType;
|
||||||
|
data["extId"] = extId;
|
||||||
|
data["startTime"] = startTime;
|
||||||
|
data["endTime"] = endTime;
|
||||||
|
data["streamName"] = streamName;
|
||||||
|
merge_json(data, options);
|
||||||
|
|
||||||
|
std::map<std::string, std::string> headers;
|
||||||
|
headers["Content-Type"] = "application/x-www-form-urlencoded";
|
||||||
|
Json::Value result = this->request_com(_live_save_v1, data, &headers);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内容审核平台-直播流(删除任务)
|
||||||
|
* 接口使用文档链接: https://ai.baidu.com/ai-doc/ANTIPORN/Ckxls2owb
|
||||||
|
*/
|
||||||
|
Json::Value live_stop_v1(
|
||||||
|
std::string const &taskId,
|
||||||
|
const Json::Value & options)
|
||||||
|
{
|
||||||
|
Json::Value data;
|
||||||
|
data["taskId"] = taskId;
|
||||||
|
merge_json(data, options);
|
||||||
|
|
||||||
|
std::map<std::string, std::string> headers;
|
||||||
|
headers["Content-Type"] = "application/x-www-form-urlencoded";
|
||||||
|
Json::Value result = this->request_com(_live_stop_v1, data, &headers);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内容审核平台-直播流(查看配置)
|
||||||
|
* 接口使用文档链接: https://ai.baidu.com/ai-doc/ANTIPORN/ckxls6tl1
|
||||||
|
*/
|
||||||
|
Json::Value live_view_v1(
|
||||||
|
std::string const &taskId,
|
||||||
|
const Json::Value & options)
|
||||||
|
{
|
||||||
|
Json::Value data;
|
||||||
|
data["taskId"] = taskId;
|
||||||
|
merge_json(data, options);
|
||||||
|
|
||||||
|
std::map<std::string, std::string> headers;
|
||||||
|
headers["Content-Type"] = "application/x-www-form-urlencoded";
|
||||||
|
Json::Value result = this->request_com(_live_view_v1, data, &headers);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内容审核平台-直播流(获取结果)
|
||||||
|
* 接口使用文档链接: https://ai.baidu.com/ai-doc/ANTIPORN/Pkxlshd1s
|
||||||
|
*/
|
||||||
|
Json::Value live_pull_v1(
|
||||||
|
std::string const &taskId,
|
||||||
|
const Json::Value & options)
|
||||||
|
{
|
||||||
|
Json::Value data;
|
||||||
|
data["taskId"] = taskId;
|
||||||
|
merge_json(data, options);
|
||||||
|
|
||||||
|
std::map<std::string, std::string> headers;
|
||||||
|
headers["Content-Type"] = "application/x-www-form-urlencoded";
|
||||||
|
Json::Value result = this->request_com(_live_view_v1, data, &headers);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内容审核平台-长视频(提交任务)
|
||||||
|
* 接口使用文档链接: https://ai.baidu.com/ai-doc/ANTIPORN/bksy7ak30
|
||||||
|
*/
|
||||||
|
Json::Value video_censor_submit_v1(
|
||||||
|
std::string const &url,
|
||||||
|
std::string const &extId,
|
||||||
|
const Json::Value & options)
|
||||||
|
{
|
||||||
|
Json::Value data;
|
||||||
|
data["url"] = url;
|
||||||
|
data["extId"] = extId;
|
||||||
|
merge_json(data, options);
|
||||||
|
|
||||||
|
std::map<std::string, std::string> headers;
|
||||||
|
headers["Content-Type"] = "application/x-www-form-urlencoded";
|
||||||
|
Json::Value result = this->request_com(_video_censor_submit_v1, data, &headers);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内容审核平台-长视频(获取结果)
|
||||||
|
* 接口使用文档链接: https://ai.baidu.com/ai-doc/ANTIPORN/jksy7j3jv
|
||||||
|
*/
|
||||||
|
Json::Value video_censor_pull_v1(
|
||||||
|
std::string const &taskId,
|
||||||
|
const Json::Value & options)
|
||||||
|
{
|
||||||
|
Json::Value data;
|
||||||
|
data["taskId"] = taskId;
|
||||||
|
merge_json(data, options);
|
||||||
|
|
||||||
|
std::map<std::string, std::string> headers;
|
||||||
|
headers["Content-Type"] = "application/x-www-form-urlencoded";
|
||||||
|
Json::Value result = this->request_com(_video_censor_pull_v1, data, &headers);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 音频文件异步审核
|
||||||
|
* 接口使用文档链接: https://ai.baidu.com/ai-doc/ANTIPORN/akxlple3t
|
||||||
|
*/
|
||||||
|
Json::Value async_voice_submit_v1(
|
||||||
|
std::string const &url, std::string const &fmt, int rate,
|
||||||
|
const Json::Value & options)
|
||||||
|
{
|
||||||
|
Json::Value data;
|
||||||
|
data["url"] = url;
|
||||||
|
data["fmt"] = fmt;
|
||||||
|
data["rate"] = rate;
|
||||||
|
merge_json(data, options);
|
||||||
|
|
||||||
|
std::map<std::string, std::string> headers;
|
||||||
|
headers["Content-Type"] = "application/x-www-form-urlencoded";
|
||||||
|
Json::Value result = this->request_com(_async_voice_submit_v1, data, &headers);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 音频文件异步审核-查询
|
||||||
|
* 接口使用文档链接: https://ai.baidu.com/ai-doc/ANTIPORN/jkxlpxllo
|
||||||
|
*/
|
||||||
|
Json::Value async_voice_pull_v1_taskid(
|
||||||
|
std::string const &taskId)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
data["taskId"] = taskId;
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_async_voice_pull_v1, null, data, null);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 音频文件异步审核-查询
|
||||||
|
* 接口使用文档链接: https://ai.baidu.com/ai-doc/ANTIPORN/jkxlpxllo
|
||||||
|
*/
|
||||||
|
Json::Value async_voice_pull_v1_audioid(
|
||||||
|
std::string const &audioId)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
data["audioId"] = audioId;
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_async_voice_pull_v1, null, data, null);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文档审核-提交任务
|
||||||
|
* https://ai.baidu.com/ai-doc/ANTIPORN/2l8484xvl
|
||||||
|
*/
|
||||||
|
Json::Value document_censor_file_submit(
|
||||||
|
std::string const & file_name,
|
||||||
|
std::string const & document,
|
||||||
|
const std::map<std::string, std::string> & options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
|
||||||
|
data["fileBase64"] = base64_encode(document.c_str(), (int) document.size());
|
||||||
|
data["fileName"] = file_name;
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_document_censor_submit_url, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文档审核-提交任务
|
||||||
|
* https://ai.baidu.com/ai-doc/ANTIPORN/2l8484xvl
|
||||||
|
*/
|
||||||
|
Json::Value document_censor_url_submit(
|
||||||
|
std::string const & file_name,
|
||||||
|
std::string const & url,
|
||||||
|
const std::map<std::string, std::string> & options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
|
||||||
|
data["url"] = url;
|
||||||
|
data["fileName"] = file_name;
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_document_censor_submit_url, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文档审核-拉取结果
|
||||||
|
* https://ai.baidu.com/ai-doc/ANTIPORN/4l848df5n
|
||||||
|
*/
|
||||||
|
Json::Value document_censor_pull(
|
||||||
|
std::string const & task_id,
|
||||||
|
const std::map<std::string, std::string> & options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
|
||||||
|
data["taskId"] = task_id;
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_document_censor_pull_url, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif
|
||||||
1074
third/include/aip-cpp-sdk/face.h
Normal file
1074
third/include/aip-cpp-sdk/face.h
Normal file
File diff suppressed because it is too large
Load Diff
111
third/include/aip-cpp-sdk/image_censor.h
Normal file
111
third/include/aip-cpp-sdk/image_censor.h
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2017 Baidu.com, Inc. All Rights Reserved
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*
|
||||||
|
* @author baidu aip
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __AIP_IMAGECENSOR_H__
|
||||||
|
#define __AIP_IMAGECENSOR_H__
|
||||||
|
|
||||||
|
#include "base/base.h"
|
||||||
|
|
||||||
|
namespace aip {
|
||||||
|
|
||||||
|
class Imagecensor: public AipBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
|
||||||
|
std::string _anti_porn =
|
||||||
|
"https://aip.baidubce.com/rest/2.0/antiporn/v1/detect";
|
||||||
|
|
||||||
|
std::string _anti_porn_gif =
|
||||||
|
"https://aip.baidubce.com/rest/2.0/antiporn/v1/detect_gif";
|
||||||
|
|
||||||
|
std::string _anti_terror =
|
||||||
|
"https://aip.baidubce.com/rest/2.0/antiterror/v1/detect";
|
||||||
|
|
||||||
|
|
||||||
|
Imagecensor(const std::string & app_id, const std::string & ak, const std::string & sk): AipBase(app_id, ak, sk)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* anti_porn
|
||||||
|
* 该请求用于鉴定图片的色情度。即对于输入的一张图片(可正常解码,且长宽比适宜),输出图片的色情度。目前支持三个维度:色情、性感、正常。
|
||||||
|
* @param image 图像文件二进制内容,可以使用aip::get_file_content函数获取
|
||||||
|
* options 可选参数:
|
||||||
|
*/
|
||||||
|
Json::Value anti_porn(
|
||||||
|
std::string const & image,
|
||||||
|
const std::map<std::string, std::string> & options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
|
||||||
|
data["image"] = base64_encode(image.c_str(), (int) image.size());
|
||||||
|
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_anti_porn, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* anti_porn_gif
|
||||||
|
* 该请求用于鉴定GIF图片的色情度,对于非gif接口,请使用色情识别接口。接口会对图片中每一帧进行识别,并返回所有检测结果中色情值最大的为结果。目前支持三个维度:色情、性感、正常。
|
||||||
|
* @param image 图像文件二进制内容,可以使用aip::get_file_content函数获取
|
||||||
|
* options 可选参数:
|
||||||
|
*/
|
||||||
|
Json::Value anti_porn_gif(
|
||||||
|
std::string const & image,
|
||||||
|
const std::map<std::string, std::string> & options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
|
||||||
|
data["image"] = base64_encode(image.c_str(), (int) image.size());
|
||||||
|
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_anti_porn_gif, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* anti_terror
|
||||||
|
* 该请求用于鉴定图片是否涉暴涉恐。即对于输入的一张图片(可正常解码,且长宽比适宜),输出图片的涉暴涉恐程度。
|
||||||
|
* @param image 图像文件二进制内容,可以使用aip::get_file_content函数获取
|
||||||
|
* options 可选参数:
|
||||||
|
*/
|
||||||
|
Json::Value anti_terror(
|
||||||
|
std::string const & image,
|
||||||
|
const std::map<std::string, std::string> & options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
|
||||||
|
data["image"] = base64_encode(image.c_str(), (int) image.size());
|
||||||
|
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_anti_terror, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif
|
||||||
1003
third/include/aip-cpp-sdk/image_classify.h
Normal file
1003
third/include/aip-cpp-sdk/image_classify.h
Normal file
File diff suppressed because it is too large
Load Diff
693
third/include/aip-cpp-sdk/image_process.h
Normal file
693
third/include/aip-cpp-sdk/image_process.h
Normal file
@ -0,0 +1,693 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2017 Baidu.com, Inc. All Rights Reserved
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*
|
||||||
|
* @author baidu aip
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __AIP_IMAGEPROCESS_H__
|
||||||
|
#define __AIP_IMAGEPROCESS_H__
|
||||||
|
|
||||||
|
#include "base/base.h"
|
||||||
|
|
||||||
|
namespace aip {
|
||||||
|
|
||||||
|
class Imageprocess: public AipBase
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::string _image_definition_enhance =
|
||||||
|
"https://aip.baidubce.com/rest/2.0/image-process/v1/image_definition_enhance";
|
||||||
|
|
||||||
|
std::string _sky_seg =
|
||||||
|
"https://aip.baidubce.com/rest/2.0/image-process/v1/sky_seg";
|
||||||
|
|
||||||
|
std::string _image_tyle_trans =
|
||||||
|
"https://aip.baidubce.com/rest/2.0/image-process/v1/style_trans";
|
||||||
|
|
||||||
|
std::string _selfie_anime =
|
||||||
|
"https://aip.baidubce.com/rest/2.0/image-process/v1/selfie_anime";
|
||||||
|
|
||||||
|
std::string _color_enhance =
|
||||||
|
"https://aip.baidubce.com/rest/2.0/image-process/v1/color_enhance";
|
||||||
|
|
||||||
|
std::string _image_inpainting =
|
||||||
|
"https://aip.baidubce.com/rest/2.0/image-process/v1/inpainting";
|
||||||
|
|
||||||
|
std::string _image_quality_enhance_v1 =
|
||||||
|
"https://aip.baidubce.com/rest/2.0/image-process/v1/image_quality_enhance";
|
||||||
|
|
||||||
|
std::string _contrast_enhance_v1 =
|
||||||
|
"https://aip.baidubce.com/rest/2.0/image-process/v1/contrast_enhance";
|
||||||
|
|
||||||
|
std::string _dehaze_v1 =
|
||||||
|
"https://aip.baidubce.com/rest/2.0/image-process/v1/dehaze";
|
||||||
|
|
||||||
|
std::string _colourize_v1 =
|
||||||
|
"https://aip.baidubce.com/rest/2.0/image-process/v1/colourize";
|
||||||
|
|
||||||
|
std::string _stretch_restore_v1 =
|
||||||
|
"https://aip.baidubce.com/rest/2.0/image-process/v1/stretch_restore";
|
||||||
|
|
||||||
|
std::string _remove_moire_v1 = "https://aip.baidubce.com/rest/2.0/image-process/v1/remove_moire";
|
||||||
|
std::string _customize_stylization_v1 =
|
||||||
|
"https://aip.baidubce.com/rest/2.0/image-process/v1/customize_stylization";
|
||||||
|
std::string _doc_repair_v1 = "https://aip.baidubce.com/rest/2.0/image-process/v1/doc_repair";
|
||||||
|
std::string _denoise_v1 = "https://aip.baidubce.com/rest/2.0/image-process/v1/denoise";
|
||||||
|
|
||||||
|
Imageprocess(const std::string & app_id, const std::string & ak, const std::string & sk):
|
||||||
|
AipBase(app_id, ak, sk)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图像修复
|
||||||
|
* 去除图片中不需要的遮挡物,并用背景内容填充,提高图像质量。
|
||||||
|
* @param image 二进制图像数据
|
||||||
|
* @param rectangle 要去除的位置为规则矩形时,给出坐标信息.每个元素包含left, top, width, height,int 类型
|
||||||
|
* options 可选参数:
|
||||||
|
*/
|
||||||
|
Json::Value imageinpainting(
|
||||||
|
std::string const & image,
|
||||||
|
Json::Value & rectangle,
|
||||||
|
std::map<std::string, std::string> options)
|
||||||
|
{
|
||||||
|
Json::Value data;
|
||||||
|
std::string access_token = this->getAccessToken();
|
||||||
|
data["image"] = base64_encode(image.c_str(), (int) image.size());
|
||||||
|
data["rectangle"] = rectangle;
|
||||||
|
|
||||||
|
std::map< std::string,std::string >::iterator it ;
|
||||||
|
for(it = options.begin(); it != options.end(); it++)
|
||||||
|
{
|
||||||
|
data[it->first] = it->second;
|
||||||
|
}
|
||||||
|
std::string mid = "?access_token=";
|
||||||
|
std::string url = _image_inpainting+mid+access_token;
|
||||||
|
Json::Value result =
|
||||||
|
this->request_com(url, data);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图像修复
|
||||||
|
* 去除图片中不需要的遮挡物,并用背景内容填充,提高图像质量。
|
||||||
|
* @param image 二进制图像数据
|
||||||
|
* @param rectangle 要去除的位置为规则矩形时,给出坐标信息.每个元素包含left, top, width, height,int 类型
|
||||||
|
* options 可选参数:
|
||||||
|
*/
|
||||||
|
Json::Value imageinpainting_url(
|
||||||
|
std::string const & url,
|
||||||
|
Json::Value & rectangle,
|
||||||
|
std::map<std::string, std::string> options)
|
||||||
|
{
|
||||||
|
Json::Value data;
|
||||||
|
data["url"] = url;
|
||||||
|
data["rectangle"] = rectangle;
|
||||||
|
|
||||||
|
std::map< std::string,std::string >::iterator it ;
|
||||||
|
for(it = options.begin(); it != options.end(); it++)
|
||||||
|
{
|
||||||
|
data[it->first] = it->second;
|
||||||
|
}
|
||||||
|
Json::Value result =
|
||||||
|
this->request_com(_image_inpainting, data);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图像色彩增强
|
||||||
|
* 可智能调节图片的色彩饱和度、亮度、对比度,使得图片内容细节、色彩更加逼真,可用于提升网站图片、手机相册图片、视频封面图片的质量
|
||||||
|
* @param image 二进制图像数据
|
||||||
|
* options 可选参数:
|
||||||
|
*/
|
||||||
|
Json::Value colorenhance(
|
||||||
|
std::string const & image,
|
||||||
|
const std::map<std::string, std::string> & options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
|
||||||
|
data["image"] = base64_encode(image.c_str(), (int) image.size());
|
||||||
|
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_color_enhance, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 人像动漫化接口
|
||||||
|
* 运用世界领先的对抗生成网络,结合人脸检测、头发分割、人像分割等技术,为用户量身定制千人千面的二次元动漫形象,并且可通过参数设置,生成戴口罩的二次元动漫人像
|
||||||
|
* @param image 二进制图像数据
|
||||||
|
* options 可选参数:
|
||||||
|
* type anime或者anime_mask。前者生成二次元动漫图,后者生成戴口罩的二次元动漫人像
|
||||||
|
* mask_id 在type参数填入anime_mask时生效,1~8之间的整数,用于指定所使用的口罩的编码。type参数没有填入anime_mask,或mask_id 为空时,生成不戴口罩的二次元动漫图。
|
||||||
|
*/
|
||||||
|
Json::Value selfieanime(
|
||||||
|
std::string const & image,
|
||||||
|
const std::map<std::string, std::string> & options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
|
||||||
|
data["image"] = base64_encode(image.c_str(), (int) image.size());
|
||||||
|
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_selfie_anime, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图像风格转换
|
||||||
|
* 可将图像转化成卡通画、铅笔画、彩色铅笔画,或者哥特油画、彩色糖块油画、呐喊油画、神奈川冲浪里油画、奇异油画、薰衣草油画等共计9种风格,可用于开展趣味活动,或集成到美图应用中对图像进行风格转换
|
||||||
|
* @param image 二进制图像数据
|
||||||
|
* @param option 转换的风格
|
||||||
|
* options 可选参数:
|
||||||
|
*/
|
||||||
|
Json::Value imagestyletrans(
|
||||||
|
std::string const & image,
|
||||||
|
std::string const & option,
|
||||||
|
const std::map<std::string, std::string> & options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
|
||||||
|
data["image"] = base64_encode(image.c_str(), (int) image.size());
|
||||||
|
data["option"] = option;
|
||||||
|
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_image_tyle_trans, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 天空分割
|
||||||
|
* 可智能分割出天空边界位置,输出天空和其余背景的灰度图和二值图,可用于图像二次处理,进行天空替换、抠图等图片编辑场景。
|
||||||
|
* @param image 二进制图像数据
|
||||||
|
* options 可选参数:
|
||||||
|
*/
|
||||||
|
Json::Value skyseg(
|
||||||
|
std::string const & image,
|
||||||
|
const std::map<std::string, std::string> & options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
|
||||||
|
data["image"] = base64_encode(image.c_str(), (int) image.size());
|
||||||
|
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_sky_seg, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图像清晰增强
|
||||||
|
* 对压缩后的模糊图像实现智能快速去噪,优化图像纹理细节,使画面更加自然清晰
|
||||||
|
* @param image 二进制图像数据
|
||||||
|
* options 可选参数:
|
||||||
|
*/
|
||||||
|
Json::Value imagedefinitionenhance(
|
||||||
|
std::string const & image,
|
||||||
|
const std::map<std::string, std::string> & options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
|
||||||
|
data["image"] = base64_encode(image.c_str(), (int) image.size());
|
||||||
|
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_image_definition_enhance, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图像风格转换
|
||||||
|
* 图像风格转换
|
||||||
|
* @param url 图片完整url
|
||||||
|
* @param option 转换的风格
|
||||||
|
* options 可选参数:
|
||||||
|
*/
|
||||||
|
Json::Value imagestyletransurl(
|
||||||
|
std::string const & url,
|
||||||
|
std::string const & option,
|
||||||
|
const std::map<std::string, std::string> & options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
|
||||||
|
data["url"] = url;
|
||||||
|
data["option"] = option;
|
||||||
|
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_image_tyle_trans, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图像色彩增强
|
||||||
|
* @param url 图片完整url
|
||||||
|
* options 可选参数:
|
||||||
|
*/
|
||||||
|
Json::Value colorenhanceurl(
|
||||||
|
std::string const & url,
|
||||||
|
const std::map<std::string, std::string> & options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
|
||||||
|
data["url"] = url;
|
||||||
|
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_color_enhance, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 人像动漫化接口
|
||||||
|
* 人像动漫化接口
|
||||||
|
* @param url 图片完整url
|
||||||
|
* options 可选参数:
|
||||||
|
* type anime或者anime_mask。前者生成二次元动漫图,后者生成戴口罩的二次元动漫人像
|
||||||
|
* mask_id 在type参数填入anime_mask时生效,1~8之间的整数,用于指定所使用的口罩的编码。type参数没有填入anime_mask,或mask_id 为空时,生成不戴口罩的二次元动漫图。
|
||||||
|
*/
|
||||||
|
Json::Value selfieanimeurl(
|
||||||
|
std::string const & url,
|
||||||
|
const std::map<std::string, std::string> & options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
|
||||||
|
data["url"] = url;
|
||||||
|
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_selfie_anime, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 天空分割
|
||||||
|
* @param url 图片完整url
|
||||||
|
* options 可选参数:
|
||||||
|
*/
|
||||||
|
Json::Value skysegurl(
|
||||||
|
std::string const & url,
|
||||||
|
const std::map<std::string, std::string> & options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
|
||||||
|
data["url"] = url;
|
||||||
|
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_sky_seg, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图像清晰增强
|
||||||
|
* @param url 图片完整url
|
||||||
|
* options 可选参数:
|
||||||
|
*/
|
||||||
|
Json::Value imagedefinitionenhanceurl(
|
||||||
|
std::string const & url,
|
||||||
|
const std::map<std::string, std::string> & options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
|
||||||
|
data["url"] = url;
|
||||||
|
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_image_definition_enhance, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图像无损放大
|
||||||
|
* @param image 二进制图像数据
|
||||||
|
* options 可选参数:
|
||||||
|
*/
|
||||||
|
Json::Value image_quality_enhance_v1(
|
||||||
|
std::string const & image,
|
||||||
|
const std::map<std::string, std::string> & options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
data["image"] = base64_encode(image.c_str(), (int) image.size());
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_image_quality_enhance_v1, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图像无损放大
|
||||||
|
* @param url 图片完整url
|
||||||
|
* options 可选参数:
|
||||||
|
*/
|
||||||
|
Json::Value image_quality_enhance_v1_url(
|
||||||
|
std::string const & url,
|
||||||
|
const std::map<std::string, std::string> & options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
data["url"] = url;
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_image_quality_enhance_v1, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图像对比度增强
|
||||||
|
* @param image 二进制图像数据
|
||||||
|
* options 可选参数:
|
||||||
|
*/
|
||||||
|
Json::Value contrast_enhance_v1(
|
||||||
|
std::string const & image,
|
||||||
|
const std::map<std::string, std::string> & options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
data["image"] = base64_encode(image.c_str(), (int) image.size());
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_contrast_enhance_v1, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图像对比度增强
|
||||||
|
* @param url 图片完整url
|
||||||
|
* options 可选参数:
|
||||||
|
*/
|
||||||
|
Json::Value contrast_enhance_v1_url(
|
||||||
|
std::string const & url,
|
||||||
|
const std::map<std::string, std::string> & options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
data["url"] = url;
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_contrast_enhance_v1, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图像去雾
|
||||||
|
* @param image 二进制图像数据
|
||||||
|
* options 可选参数:
|
||||||
|
*/
|
||||||
|
Json::Value dehaze_v1(
|
||||||
|
std::string const & image,
|
||||||
|
const std::map<std::string, std::string> & options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
data["image"] = base64_encode(image.c_str(), (int) image.size());
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_dehaze_v1, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图像去雾
|
||||||
|
* @param url 图片完整url
|
||||||
|
* options 可选参数:
|
||||||
|
*/
|
||||||
|
Json::Value dehaze_v1_url(
|
||||||
|
std::string const & url,
|
||||||
|
const std::map<std::string, std::string> & options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
data["url"] = url;
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_dehaze_v1, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 黑白图像上色
|
||||||
|
* @param image 二进制图像数据
|
||||||
|
* options 可选参数:
|
||||||
|
*/
|
||||||
|
Json::Value colourize_v1(
|
||||||
|
std::string const & image,
|
||||||
|
const std::map<std::string, std::string> & options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
data["image"] = base64_encode(image.c_str(), (int) image.size());
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_colourize_v1, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 黑白图像上色
|
||||||
|
* @param url 图片完整url
|
||||||
|
* options 可选参数:
|
||||||
|
*/
|
||||||
|
Json::Value colourize_v1_url(
|
||||||
|
std::string const & url,
|
||||||
|
const std::map<std::string, std::string> & options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
data["url"] = url;
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_colourize_v1, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拉伸图像恢复
|
||||||
|
* @param image 二进制图像数据
|
||||||
|
* options 可选参数:
|
||||||
|
*/
|
||||||
|
Json::Value stretch_restore_v1(
|
||||||
|
std::string const & image,
|
||||||
|
const std::map<std::string, std::string> & options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
data["image"] = base64_encode(image.c_str(), (int) image.size());
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_stretch_restore_v1, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拉伸图像恢复
|
||||||
|
* @param url 图片完整url
|
||||||
|
* options 可选参数:
|
||||||
|
*/
|
||||||
|
Json::Value stretch_restore_v1_url(
|
||||||
|
std::string const & url,
|
||||||
|
const std::map<std::string, std::string> & options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
data["url"] = url;
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_stretch_restore_v1, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图片去摩尔纹
|
||||||
|
* 接口使用文档链接: https://ai.baidu.com/ai-doc/IMAGEPROCESS/ql4wdlnc0
|
||||||
|
*/
|
||||||
|
Json::Value remove_moire_v1(
|
||||||
|
std::string const & image,
|
||||||
|
const std::map<std::string, std::string> &options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
data["image"] = base64_encode(image.c_str(), (int) image.size());
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_remove_moire_v1, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图片去摩尔纹 - url
|
||||||
|
* 接口使用文档链接: https://ai.baidu.com/ai-doc/IMAGEPROCESS/ql4wdlnc0
|
||||||
|
*/
|
||||||
|
Json::Value remove_moire_v1_url(
|
||||||
|
std::string const & url,
|
||||||
|
const std::map<std::string, std::string> &options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
data["url"] = url;
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_remove_moire_v1, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图片去摩尔纹 - pdf
|
||||||
|
* 接口使用文档链接: https://ai.baidu.com/ai-doc/IMAGEPROCESS/ql4wdlnc0
|
||||||
|
*/
|
||||||
|
Json::Value remove_moire_v1_pdf(
|
||||||
|
std::string const & pdf,
|
||||||
|
const std::map<std::string, std::string> &options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
data["pdf_file"] = base64_encode(pdf.c_str(), (int) pdf.size());
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_remove_moire_v1, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图像风格自定义
|
||||||
|
* 接口使用文档链接: https://ai.baidu.com/ai-doc/IMAGEPROCESS/al50vf6bq
|
||||||
|
*/
|
||||||
|
Json::Value customize_stylization_v1(std::string const & image, Json::Value & options)
|
||||||
|
{
|
||||||
|
Json::Value data;
|
||||||
|
data["image"] = base64_encode(image.c_str(), (int) image.size());
|
||||||
|
merge_json(data, options);
|
||||||
|
|
||||||
|
std::map<std::string, std::string> headers;
|
||||||
|
headers["Content-Type"] = "application/x-www-form-urlencoded";
|
||||||
|
Json::Value result = this->request_com(_customize_stylization_v1, data, &headers);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图像风格自定义 - url
|
||||||
|
* 接口使用文档链接: https://ai.baidu.com/ai-doc/IMAGEPROCESS/al50vf6bq
|
||||||
|
*/
|
||||||
|
Json::Value customize_stylization_v1_url(std::string const & url, Json::Value & options)
|
||||||
|
{
|
||||||
|
Json::Value data;
|
||||||
|
data["url"] = url;
|
||||||
|
merge_json(data, options);
|
||||||
|
|
||||||
|
std::map<std::string, std::string> headers;
|
||||||
|
headers["Content-Type"] = "application/x-www-form-urlencoded";
|
||||||
|
Json::Value result = this->request_com(_customize_stylization_v1, data, &headers);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文档图片去底纹
|
||||||
|
* 接口使用文档链接: https://ai.baidu.com/ai-doc/IMAGEPROCESS/Nl6os53ab
|
||||||
|
*/
|
||||||
|
Json::Value doc_repair_v1(
|
||||||
|
std::string const & image,
|
||||||
|
const std::map<std::string, std::string> &options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
data["image"] = base64_encode(image.c_str(), (int) image.size());
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_doc_repair_v1, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文档图片去底纹 - url
|
||||||
|
* 接口使用文档链接: https://ai.baidu.com/ai-doc/IMAGEPROCESS/Nl6os53ab
|
||||||
|
*/
|
||||||
|
Json::Value doc_repair_v1_url(
|
||||||
|
std::string const &url,
|
||||||
|
const std::map<std::string, std::string> &options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
data["url"] = url;
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_doc_repair_v1, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图像去噪
|
||||||
|
* 接口使用文档链接: https://ai.baidu.com/ai-doc/IMAGEPROCESS/Tl78sby7g
|
||||||
|
*/
|
||||||
|
Json::Value denoise_v1(
|
||||||
|
std::string const & image, int option)
|
||||||
|
{
|
||||||
|
Json::Value data;
|
||||||
|
data["image"] = base64_encode(image.c_str(), (int) image.size());
|
||||||
|
data["option"] = option;
|
||||||
|
|
||||||
|
std::map<std::string, std::string> headers;
|
||||||
|
headers["Content-Type"] = "application/x-www-form-urlencoded";
|
||||||
|
Json::Value result = this->request_com(_denoise_v1, data, &headers);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图像去噪 - url
|
||||||
|
* 接口使用文档链接: https://ai.baidu.com/ai-doc/IMAGEPROCESS/Tl78sby7g
|
||||||
|
*/
|
||||||
|
Json::Value denoise_v1_url(
|
||||||
|
std::string const &url, int option)
|
||||||
|
{
|
||||||
|
Json::Value data;
|
||||||
|
data["url"] = url;
|
||||||
|
data["option"] = option;
|
||||||
|
|
||||||
|
std::map<std::string, std::string> headers;
|
||||||
|
headers["Content-Type"] = "application/x-www-form-urlencoded";
|
||||||
|
Json::Value result = this->request_com(_denoise_v1, data, &headers);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif
|
||||||
1001
third/include/aip-cpp-sdk/image_search.h
Normal file
1001
third/include/aip-cpp-sdk/image_search.h
Normal file
File diff suppressed because it is too large
Load Diff
206
third/include/aip-cpp-sdk/kg.h
Normal file
206
third/include/aip-cpp-sdk/kg.h
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2017 Baidu.com, Inc. All Rights Reserved
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*
|
||||||
|
* @author baidu aip
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __AIP_KG_H__
|
||||||
|
#define __AIP_KG_H__
|
||||||
|
|
||||||
|
#include "base/base.h"
|
||||||
|
|
||||||
|
namespace aip {
|
||||||
|
|
||||||
|
class Kg: public AipBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
|
||||||
|
std::string _create_task =
|
||||||
|
"https://aip.baidubce.com/rest/2.0/kg/v1/pie/task_create";
|
||||||
|
|
||||||
|
std::string _update_task =
|
||||||
|
"https://aip.baidubce.com/rest/2.0/kg/v1/pie/task_update";
|
||||||
|
|
||||||
|
std::string _task_info =
|
||||||
|
"https://aip.baidubce.com/rest/2.0/kg/v1/pie/task_info";
|
||||||
|
|
||||||
|
std::string _task_query =
|
||||||
|
"https://aip.baidubce.com/rest/2.0/kg/v1/pie/task_query";
|
||||||
|
|
||||||
|
std::string _task_start =
|
||||||
|
"https://aip.baidubce.com/rest/2.0/kg/v1/pie/task_start";
|
||||||
|
|
||||||
|
std::string _task_status =
|
||||||
|
"https://aip.baidubce.com/rest/2.0/kg/v1/pie/task_status";
|
||||||
|
|
||||||
|
|
||||||
|
Kg(const std::string & app_id, const std::string & ak, const std::string & sk): AipBase(app_id, ak, sk)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create_task
|
||||||
|
* 创建一个新的信息抽取任务
|
||||||
|
* @param name 任务名字
|
||||||
|
* @param template_content json string 解析模板内容
|
||||||
|
* @param input_mapping_file 抓取结果映射文件的路径
|
||||||
|
* @param url_pattern url pattern
|
||||||
|
* @param output_file 输出文件名字
|
||||||
|
* options 可选参数:
|
||||||
|
* limit_count 限制解析数量limit_count为0时进行全量任务,limit_count>0时只解析limit_count数量的页面
|
||||||
|
*/
|
||||||
|
Json::Value create_task(
|
||||||
|
std::string const & name,
|
||||||
|
std::string const & template_content,
|
||||||
|
std::string const & input_mapping_file,
|
||||||
|
std::string const & url_pattern,
|
||||||
|
std::string const & output_file,
|
||||||
|
const std::map<std::string, std::string> & options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
|
||||||
|
data["name"] = name;
|
||||||
|
data["template_content"] = template_content;
|
||||||
|
data["input_mapping_file"] = input_mapping_file;
|
||||||
|
data["url_pattern"] = url_pattern;
|
||||||
|
data["output_file"] = output_file;
|
||||||
|
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_create_task, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* update_task
|
||||||
|
* 更新任务配置,在任务重新启动后生效
|
||||||
|
* @param id 任务ID
|
||||||
|
* options 可选参数:
|
||||||
|
* name 任务名字
|
||||||
|
* template_content json string 解析模板内容
|
||||||
|
* input_mapping_file 抓取结果映射文件的路径
|
||||||
|
* url_pattern url pattern
|
||||||
|
* output_file 输出文件名字
|
||||||
|
*/
|
||||||
|
Json::Value update_task(
|
||||||
|
const int & id,
|
||||||
|
const std::map<std::string, std::string> & options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
|
||||||
|
data["id"] = std::to_string(id);
|
||||||
|
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_update_task, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* task_info
|
||||||
|
* 根据任务id获取单个任务的详细信息
|
||||||
|
* @param id 任务ID
|
||||||
|
* options 可选参数:
|
||||||
|
*/
|
||||||
|
Json::Value task_info(
|
||||||
|
const int & id,
|
||||||
|
const std::map<std::string, std::string> & options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
|
||||||
|
data["id"] = std::to_string(id);
|
||||||
|
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_task_info, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* task_query
|
||||||
|
* 该请求用于菜品识别。即对于输入的一张图片(可正常解码,且长宽比适宜),输出图片的菜品名称、卡路里信息、置信度。
|
||||||
|
* options 可选参数:
|
||||||
|
* id 任务ID,精确匹配
|
||||||
|
* name 中缀模糊匹配,abc可以匹配abc,aaabc,abcde等
|
||||||
|
* status 要筛选的任务状态
|
||||||
|
* page 页码
|
||||||
|
* per_page 页码
|
||||||
|
*/
|
||||||
|
Json::Value task_query(
|
||||||
|
const std::map<std::string, std::string> & options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
|
||||||
|
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_task_query, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* task_start
|
||||||
|
* 启动一个已经创建的信息抽取任务
|
||||||
|
* @param id 任务ID
|
||||||
|
* options 可选参数:
|
||||||
|
*/
|
||||||
|
Json::Value task_start(
|
||||||
|
const int & id,
|
||||||
|
const std::map<std::string, std::string> & options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
|
||||||
|
data["id"] = std::to_string(id);
|
||||||
|
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_task_start, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* task_status
|
||||||
|
* 查询指定的任务的最新执行状态
|
||||||
|
* @param id 任务ID
|
||||||
|
* options 可选参数:
|
||||||
|
*/
|
||||||
|
Json::Value task_status(
|
||||||
|
const int & id,
|
||||||
|
const std::map<std::string, std::string> & options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
|
||||||
|
data["id"] = std::to_string(id);
|
||||||
|
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_task_status, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif
|
||||||
144
third/include/aip-cpp-sdk/machine_translation.h
Normal file
144
third/include/aip-cpp-sdk/machine_translation.h
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2017 Baidu.com, Inc. All Rights Reserved
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*
|
||||||
|
* @author baidu aip
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __AIP_MACHINE_TRANSLATION_H__
|
||||||
|
#define __AIP_MACHINE_TRANSLATION_H__
|
||||||
|
|
||||||
|
#include "base/base.h"
|
||||||
|
|
||||||
|
namespace aip {
|
||||||
|
|
||||||
|
class Machinetranslation : public AipBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
std::string _pictrans_v1 =
|
||||||
|
"https://aip.baidubce.com/file/2.0/mt/pictrans/v1";
|
||||||
|
std::string _texttrans_v1 =
|
||||||
|
"https://aip.baidubce.com/rpc/2.0/mt/texttrans/v1";
|
||||||
|
std::string _texttrans_with_dict_v1 =
|
||||||
|
"https://aip.baidubce.com/rpc/2.0/mt/texttrans-with-dict/v1";
|
||||||
|
std::string _doc_translation_create_v2 =
|
||||||
|
"https://aip.baidubce.com/rpc/2.0/mt/v2/doc-translation/create";
|
||||||
|
std::string _doc_translation_query_v2 =
|
||||||
|
"https://aip.baidubce.com/rpc/2.0/mt/v2/doc-translation/query";
|
||||||
|
std::string _speech_translation_v2 =
|
||||||
|
"https://aip.baidubce.com/rpc/2.0/mt/v2/speech-translation";
|
||||||
|
|
||||||
|
Machinetranslation(const std::string & app_id, const std::string & ak, const std::string & sk)
|
||||||
|
: AipBase(app_id, ak, sk)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文本翻译-通用版
|
||||||
|
* 接口使用文档链接: https://ai.baidu.com/ai-doc/MT/4kqryjku9
|
||||||
|
*/
|
||||||
|
Json::Value texttrans_v1(
|
||||||
|
std::string const &from,
|
||||||
|
std::string const &to,
|
||||||
|
std::string const &q,
|
||||||
|
const Json::Value & options)
|
||||||
|
{
|
||||||
|
Json::Value data;
|
||||||
|
data["from"] = from;
|
||||||
|
data["to"] = to;
|
||||||
|
data["q"] = q;
|
||||||
|
merge_json(data, options);
|
||||||
|
|
||||||
|
Json::Value result = this->request_com(_texttrans_v1, data);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文本翻译-词典版
|
||||||
|
* 接口使用文档链接: https://ai.baidu.com/ai-doc/MT/nkqrzmbpc
|
||||||
|
*/
|
||||||
|
Json::Value texttrans_with_dict_v1(
|
||||||
|
std::string const &from,
|
||||||
|
std::string const &to,
|
||||||
|
std::string const &q,
|
||||||
|
const Json::Value & options)
|
||||||
|
{
|
||||||
|
Json::Value data;
|
||||||
|
data["from"] = from;
|
||||||
|
data["to"] = to;
|
||||||
|
data["q"] = q;
|
||||||
|
merge_json(data, options);
|
||||||
|
|
||||||
|
Json::Value result = this->request_com(_texttrans_with_dict_v1, data);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文档翻译
|
||||||
|
* 接口使用文档链接: https://ai.baidu.com/ai-doc/MT/Xky9x5xub
|
||||||
|
*/
|
||||||
|
Json::Value doc_translation_create_v2(
|
||||||
|
std::string const &from,
|
||||||
|
std::string const &to,
|
||||||
|
Json::Value & options)
|
||||||
|
{
|
||||||
|
Json::Value data;
|
||||||
|
data["from"] = from;
|
||||||
|
data["to"] = to;
|
||||||
|
merge_json(data, options);
|
||||||
|
|
||||||
|
Json::Value result =
|
||||||
|
this->request_com(_doc_translation_create_v2, data);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文档翻译-文档状态查询
|
||||||
|
* 接口使用文档链接: https://ai.baidu.com/ai-doc/MT/Xky9x5xub
|
||||||
|
*/
|
||||||
|
Json::Value doc_translation_query_v2(
|
||||||
|
std::string const &id)
|
||||||
|
{
|
||||||
|
Json::Value data;
|
||||||
|
data["id"] = id;
|
||||||
|
Json::Value result = this->request_com(_doc_translation_query_v2, data);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 语音翻译
|
||||||
|
* 接口使用文档链接: https://ai.baidu.com/ai-doc/MT/el4cmi76f
|
||||||
|
*/
|
||||||
|
Json::Value speech_translation_v2(
|
||||||
|
std::string const &from,
|
||||||
|
std::string const &to,
|
||||||
|
std::string const &voice,
|
||||||
|
std::string const &format)
|
||||||
|
{
|
||||||
|
Json::Value data;
|
||||||
|
data["from"] = from;
|
||||||
|
data["to"] = to;
|
||||||
|
data["voice"] = base64_encode(voice.c_str(), (int) voice.size());
|
||||||
|
data["format"] = format;
|
||||||
|
Json::Value result =
|
||||||
|
this->request_com(_speech_translation_v2, data);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif
|
||||||
1034
third/include/aip-cpp-sdk/nlp.h
Normal file
1034
third/include/aip-cpp-sdk/nlp.h
Normal file
File diff suppressed because it is too large
Load Diff
3167
third/include/aip-cpp-sdk/ocr.h
Normal file
3167
third/include/aip-cpp-sdk/ocr.h
Normal file
File diff suppressed because it is too large
Load Diff
135
third/include/aip-cpp-sdk/speech.h
Normal file
135
third/include/aip-cpp-sdk/speech.h
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
#ifndef __AIP_SPEECH_H__
|
||||||
|
#define __AIP_SPEECH_H__
|
||||||
|
|
||||||
|
#include "base/base.h"
|
||||||
|
|
||||||
|
#include <json/json.h>
|
||||||
|
|
||||||
|
namespace aip {
|
||||||
|
|
||||||
|
class Speech : public AipBase {
|
||||||
|
public:
|
||||||
|
|
||||||
|
std::string _asr = "https://vop.baidu.com/server_api";
|
||||||
|
|
||||||
|
std::string _tts = "http://tsn.baidu.com/text2audio";
|
||||||
|
|
||||||
|
Speech(const std::string app_id, const std::string &ak, const std::string &sk) : AipBase(app_id, ak, sk) {
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value request_asr(
|
||||||
|
std::string const &url,
|
||||||
|
Json::Value &data) {
|
||||||
|
std::string response;
|
||||||
|
Json::Value obj;
|
||||||
|
int status_code = this->client.post(url, nullptr, data, nullptr, &response);
|
||||||
|
|
||||||
|
if (status_code != CURLcode::CURLE_OK) {
|
||||||
|
obj[aip::CURL_ERROR_CODE] = status_code;
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string error;
|
||||||
|
std::unique_ptr<Json::CharReader> reader(crbuilder.newCharReader());
|
||||||
|
reader->parse(response.data(), response.data() + response.size(), &obj, &error);
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value request_tts(
|
||||||
|
const std::string url,
|
||||||
|
std::map<std::string, std::string> &data,
|
||||||
|
std::string &file_content) {
|
||||||
|
std::string response;
|
||||||
|
Json::Value obj;
|
||||||
|
Json::Value file_json;
|
||||||
|
int status_code = this->client.post(url, nullptr, data, nullptr, &response);
|
||||||
|
if (status_code != CURLcode::CURLE_OK) {
|
||||||
|
obj[aip::CURL_ERROR_CODE] = status_code;
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
file_content = response;
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value recognize(const std::string voice_binary, const std::string &format, const int &rate,
|
||||||
|
std::map<std::string, std::string> const &options) {
|
||||||
|
Json::Value data;
|
||||||
|
|
||||||
|
std::map<std::string, std::string>::const_iterator it;
|
||||||
|
for (it = options.begin(); it != options.end(); it++) {
|
||||||
|
data[it->first] = it->second;
|
||||||
|
if (it->first == "dev_pid") {
|
||||||
|
data[it->first] = atoi(it->second.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string token = this->getAccessToken();
|
||||||
|
|
||||||
|
data["speech"] = base64_encode(voice_binary.c_str(), (int) voice_binary.size());
|
||||||
|
data["format"] = format;
|
||||||
|
data["rate"] = std::to_string(rate);
|
||||||
|
data["channel"] = "1";
|
||||||
|
data["token"] = token;
|
||||||
|
data["cuid"] = this->getAk();
|
||||||
|
data["len"] = (int) voice_binary.size();
|
||||||
|
|
||||||
|
Json::Value result = this->request_asr(_asr, data);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value recognize_url(const std::string &url,
|
||||||
|
const std::string &callback, const std::string &format,
|
||||||
|
const int &rate,
|
||||||
|
std::map<std::string, std::string> options) {
|
||||||
|
Json::Value data;
|
||||||
|
std::map<std::string, std::string>::iterator it;
|
||||||
|
|
||||||
|
for (it = options.begin(); it != options.end(); it++) {
|
||||||
|
data[it->first] = it->second;
|
||||||
|
if (it->first == "dev_pid") {
|
||||||
|
data[it->first] = atoi(it->second.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string token = this->getAccessToken();
|
||||||
|
|
||||||
|
data["url"] = url;
|
||||||
|
data["callback"] = callback;
|
||||||
|
data["format"] = format;
|
||||||
|
data["rate"] = std::to_string(rate);
|
||||||
|
data["channel"] = 1;
|
||||||
|
data["token"] = token;
|
||||||
|
data["cuid"] = this->getAk();
|
||||||
|
|
||||||
|
Json::Value result = this->request_asr(_asr, data);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value text2audio(const std::string &text, std::map<std::string, std::string> const &options,
|
||||||
|
std::string &file_content) {
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
std::map<std::string, std::string>::const_iterator it;
|
||||||
|
|
||||||
|
for (it = options.begin(); it != options.end(); it++) {
|
||||||
|
data[it->first] = it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string token = this->getAccessToken();
|
||||||
|
|
||||||
|
data["tex"] = text;
|
||||||
|
data["lan"] = "zh";
|
||||||
|
data["ctp"] = "1";
|
||||||
|
data["tok"] = token;
|
||||||
|
data["cuid"] = this->getAk();
|
||||||
|
|
||||||
|
Json::Value result = this->request_tts(_tts, data, file_content);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
||||||
63
third/include/aip-cpp-sdk/video_censor.h
Normal file
63
third/include/aip-cpp-sdk/video_censor.h
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2017 Baidu.com, Inc. All Rights Reserved
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*
|
||||||
|
* @author baidu aip
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __AIP_VIDEOCENSOR_H__
|
||||||
|
#define __AIP_VIDEOCENSOR_H__
|
||||||
|
|
||||||
|
#include "base/base.h"
|
||||||
|
|
||||||
|
namespace aip {
|
||||||
|
|
||||||
|
class Videocensor: public AipBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
|
||||||
|
std::string _video_url =
|
||||||
|
"https://aip.baidubce.com/rest/2.0/solution/v1/video_censor/v2/user_defined";
|
||||||
|
|
||||||
|
|
||||||
|
Videocensor(const std::string & app_id, const std::string & ak, const std::string & sk): AipBase(app_id, ak, sk)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* voice_censor
|
||||||
|
* 本接口除了支持自定义配置外,还对返回结果进行了总体的包装,按照用户在控制台中配置的规则直接返回是否合规,如果不合规则指出具体不合规的内容。
|
||||||
|
* @param voice 语音文件二进制内容,可以使用aip::get_file_content函数获取
|
||||||
|
* options 可选参数:
|
||||||
|
*/
|
||||||
|
Json::Value video_censor(
|
||||||
|
std::string const & name,
|
||||||
|
std::string const & url,
|
||||||
|
std::string const & extId,
|
||||||
|
const std::map<std::string, std::string> & options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
|
||||||
|
data["videoUrl"] = url;
|
||||||
|
data["name"] = name;
|
||||||
|
data["extId"] = extId;
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_video_url, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif
|
||||||
87
third/include/aip-cpp-sdk/voice_censor.h
Normal file
87
third/include/aip-cpp-sdk/voice_censor.h
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2017 Baidu.com, Inc. All Rights Reserved
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*
|
||||||
|
* @author baidu aip
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __AIP_IMAGECENSOR_H__
|
||||||
|
#define __AIP_IMAGECENSOR_H__
|
||||||
|
|
||||||
|
#include "base/base.h"
|
||||||
|
|
||||||
|
namespace aip {
|
||||||
|
|
||||||
|
class Voicecensor: public AipBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
|
||||||
|
std::string _voice_url =
|
||||||
|
"https://aip.baidubce.com/rest/2.0/solution/v1/voice_censor/v3/user_defined";
|
||||||
|
|
||||||
|
|
||||||
|
Voicecensor(const std::string & app_id, const std::string & ak, const std::string & sk): AipBase(app_id, ak, sk)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* voice_censor
|
||||||
|
* 本接口除了支持自定义配置外,还对返回结果进行了总体的包装,按照用户在控制台中配置的规则直接返回是否合规,如果不合规则指出具体不合规的内容。
|
||||||
|
* @param voice 语音文件二进制内容,可以使用aip::get_file_content函数获取
|
||||||
|
* options 可选参数:
|
||||||
|
*/
|
||||||
|
Json::Value voice_censor(
|
||||||
|
std::string const & voice,
|
||||||
|
std::int32_t const & rate,
|
||||||
|
std::string const & fmt,
|
||||||
|
const std::map<std::string, std::string> & options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
|
||||||
|
data["base64"] = base64_encode(voice.c_str(), (int) voice.size());
|
||||||
|
data["fmt"] = fmt;
|
||||||
|
data["rate"] = rate;
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_voice_url, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* voice_censor
|
||||||
|
* 本接口除了支持自定义配置外,还对返回结果进行了总体的包装,按照用户在控制台中配置的规则直接返回是否合规,如果不合规则指出具体不合规的内容。
|
||||||
|
* @param voice 语音文件二进制内容,可以使用aip::get_file_content函数获取
|
||||||
|
* options 可选参数:
|
||||||
|
*/
|
||||||
|
Json::Value voice_censorUrl(
|
||||||
|
std::string const & url,
|
||||||
|
std::int32_t const & rate,
|
||||||
|
std::string const & fmt,
|
||||||
|
const std::map<std::string, std::string> & options)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> data;
|
||||||
|
|
||||||
|
data["url"] = url;
|
||||||
|
data["fmt"] = fmt;
|
||||||
|
data["rate"] = rate;
|
||||||
|
std::copy(options.begin(), options.end(), std::inserter(data, data.end()));
|
||||||
|
|
||||||
|
Json::Value result =
|
||||||
|
this->request(_voice_url, null, data, null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif
|
||||||
92
transmite/CMakeLists.txt
Normal file
92
transmite/CMakeLists.txt
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
# 1. 添加cmake版本说明
|
||||||
|
cmake_minimum_required(VERSION 3.1.3)
|
||||||
|
# 2. 声明工程名称
|
||||||
|
project(transmite_server)
|
||||||
|
|
||||||
|
set(target "transmite_server")
|
||||||
|
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
|
||||||
|
|
||||||
|
# 3. 检测并生成ODB框架代码
|
||||||
|
# 1. 添加所需的proto映射代码文件名称
|
||||||
|
set(proto_path ${CMAKE_CURRENT_SOURCE_DIR}/../proto)
|
||||||
|
set(proto_files base.proto user.proto transmite.proto)
|
||||||
|
# 2. 检测框架代码文件是否已经生成
|
||||||
|
set(proto_hxx "")
|
||||||
|
set(proto_cxx "")
|
||||||
|
set(proto_srcs "")
|
||||||
|
foreach(proto_file ${proto_files})
|
||||||
|
# 3. 如果没有生成,则预定义生成指令 -- 用于在构建项目之间先生成框架代码
|
||||||
|
string(REPLACE ".proto" ".pb.cc" proto_cc ${proto_file})
|
||||||
|
string(REPLACE ".proto" ".pb.h" proto_hh ${proto_file})
|
||||||
|
if (NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}${proto_cc})
|
||||||
|
add_custom_command(
|
||||||
|
PRE_BUILD
|
||||||
|
COMMAND protoc
|
||||||
|
ARGS --cpp_out=${CMAKE_CURRENT_BINARY_DIR} -I ${proto_path} --experimental_allow_proto3_optional ${proto_path}/${proto_file}
|
||||||
|
DEPENDS ${proto_path}/${proto_file}
|
||||||
|
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${proto_cc}
|
||||||
|
COMMENT "生成Protobuf框架代码文件:" ${CMAKE_CURRENT_BINARY_DIR}/${proto_cc}
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
list(APPEND proto_srcs ${CMAKE_CURRENT_BINARY_DIR}/${proto_cc})
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# 3. 检测并生成ODB框架代码
|
||||||
|
# 1. 添加所需的odb映射代码文件名称
|
||||||
|
set(odb_path ${CMAKE_CURRENT_SOURCE_DIR}/../odb)
|
||||||
|
set(odb_files chat_session_member.hxx)
|
||||||
|
# 2. 检测框架代码文件是否已经生成
|
||||||
|
set(odb_hxx "")
|
||||||
|
set(odb_cxx "")
|
||||||
|
set(odb_srcs "")
|
||||||
|
foreach(odb_file ${odb_files})
|
||||||
|
# 3. 如果没有生成,则预定义生成指令 -- 用于在构建项目之间先生成框架代码
|
||||||
|
string(REPLACE ".hxx" "-odb.hxx" odb_hxx ${odb_file})
|
||||||
|
string(REPLACE ".hxx" "-odb.cxx" odb_cxx ${odb_file})
|
||||||
|
if (NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}${odb_cxx})
|
||||||
|
add_custom_command(
|
||||||
|
PRE_BUILD
|
||||||
|
COMMAND odb
|
||||||
|
ARGS -d mysql --std c++11 --generate-query --generate-schema --profile boost/date-time ${odb_path}/${odb_file}
|
||||||
|
DEPENDS ${odb_path}/${odb_file}
|
||||||
|
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${odb_cxx}
|
||||||
|
COMMENT "生成ODB框架代码文件:" ${CMAKE_CURRENT_BINARY_DIR}/${odb_cxx}
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
# 4. 将所有生成的框架源码文件名称保存起来 student-odb.cxx classes-odb.cxx
|
||||||
|
list(APPEND odb_srcs ${CMAKE_CURRENT_BINARY_DIR}/${odb_cxx})
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# 4. 获取源码目录下的所有源码文件
|
||||||
|
set(src_files "")
|
||||||
|
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/source src_files)
|
||||||
|
# 5. 声明目标及依赖
|
||||||
|
add_executable(${target} ${src_files} ${proto_srcs} ${odb_srcs})
|
||||||
|
# 7. 设置需要连接的库
|
||||||
|
target_link_libraries(${target} -lgflags
|
||||||
|
-lspdlog -lfmt -lbrpc -lssl -lcrypto
|
||||||
|
-lprotobuf -lleveldb -letcd-cpp-api
|
||||||
|
-lcpprest -lcurl -lodb-mysql -lodb -lodb-boost
|
||||||
|
-lamqpcpp -lev)
|
||||||
|
|
||||||
|
|
||||||
|
set(trans_user_client "trans_user_client")
|
||||||
|
set(trans_user_files ${CMAKE_CURRENT_SOURCE_DIR}/test/user_client.cc)
|
||||||
|
add_executable(${trans_user_client} ${trans_user_files} ${proto_srcs})
|
||||||
|
target_link_libraries(${trans_user_client} -pthread -lgtest -lgflags -lspdlog -lfmt -lbrpc -lssl -lcrypto -lprotobuf -lleveldb -letcd-cpp-api -lcpprest -lcurl /usr/lib/x86_64-linux-gnu/libjsoncpp.so.19)
|
||||||
|
|
||||||
|
set(transmite_client "transmite_client")
|
||||||
|
set(transmite_files ${CMAKE_CURRENT_SOURCE_DIR}/test/transmite_client.cc)
|
||||||
|
add_executable(${transmite_client} ${transmite_files} ${proto_srcs})
|
||||||
|
target_link_libraries(${transmite_client} -pthread -lgtest -lgflags -lspdlog -lfmt -lbrpc -lssl -lcrypto -lprotobuf -lleveldb -letcd-cpp-api -lcpprest -lcurl /usr/lib/x86_64-linux-gnu/libjsoncpp.so.19)
|
||||||
|
|
||||||
|
|
||||||
|
# 6. 设置头文件默认搜索路径
|
||||||
|
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../common)
|
||||||
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../odb)
|
||||||
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../third/include)
|
||||||
|
|
||||||
|
#8. 设置安装路径
|
||||||
|
INSTALL(TARGETS ${target} ${trans_user_client} ${transmite_client} RUNTIME DESTINATION bin)
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user