hkuserver continuue

This commit is contained in:
fasiondog 2021-04-14 01:49:02 +08:00
parent 84857ea909
commit 203885c8c8
15 changed files with 239 additions and 75 deletions

View File

@ -92,7 +92,7 @@ bool SQLiteConnect::tableExist(const string& tablename) {
}
void SQLiteConnect::transaction() {
exec("BEGIN TRANSACTION");
exec("BEGIN IMMEDIATE");
}
void SQLiteConnect::commit() {

View File

@ -0,0 +1,109 @@
/*
* Copyright(C) 2021 hikyuu.org
*
* The code comes from: https://github.com/sniper00/snowflake-cpp
* Thanks sniper00
*
* Create on: 2021-04-13
* Author: fasiondog
*/
#pragma once
#include <cstdint>
#include <chrono>
#include <stdexcept>
#include <mutex>
namespace hku {
class snowflake_nonlock {
public:
void lock() {}
void unlock() {}
};
template <int64_t Twepoch, typename Lock = snowflake_nonlock>
class snowflake {
using lock_type = Lock;
static constexpr int64_t TWEPOCH = Twepoch;
static constexpr int64_t WORKER_ID_BITS = 5L;
static constexpr int64_t DATACENTER_ID_BITS = 5L;
static constexpr int64_t MAX_WORKER_ID = (1 << WORKER_ID_BITS) - 1;
static constexpr int64_t MAX_DATACENTER_ID = (1 << DATACENTER_ID_BITS) - 1;
static constexpr int64_t SEQUENCE_BITS = 12L;
static constexpr int64_t WORKER_ID_SHIFT = SEQUENCE_BITS;
static constexpr int64_t DATACENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
static constexpr int64_t TIMESTAMP_LEFT_SHIFT =
SEQUENCE_BITS + WORKER_ID_BITS + DATACENTER_ID_BITS;
static constexpr int64_t SEQUENCE_MASK = (1 << SEQUENCE_BITS) - 1;
using time_point = std::chrono::time_point<std::chrono::steady_clock>;
time_point start_time_point_ = std::chrono::steady_clock::now();
int64_t start_millsecond_ = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch())
.count();
int64_t last_timestamp_ = -1;
int64_t workerid_ = 0;
int64_t datacenterid_ = 0;
int64_t sequence_ = 0;
lock_type lock_;
public:
snowflake() = default;
snowflake(const snowflake&) = delete;
snowflake& operator=(const snowflake&) = delete;
void init(int64_t workerid, int64_t datacenterid) {
if (workerid > MAX_WORKER_ID || workerid < 0) {
throw std::runtime_error("worker Id can't be greater than 31 or less than 0");
}
if (datacenterid > MAX_DATACENTER_ID || datacenterid < 0) {
throw std::runtime_error("datacenter Id can't be greater than 31 or less than 0");
}
workerid_ = workerid;
datacenterid_ = datacenterid;
}
int64_t nextid() {
std::lock_guard<lock_type> lock(lock_);
// std::chrono::steady_clock cannot decrease as physical time moves forward
auto timestamp = millsecond();
if (last_timestamp_ == timestamp) {
sequence_ = (sequence_ + 1) & SEQUENCE_MASK;
if (sequence_ == 0) {
timestamp = wait_next_millis(last_timestamp_);
}
} else {
sequence_ = 0;
}
last_timestamp_ = timestamp;
return ((timestamp - TWEPOCH) << TIMESTAMP_LEFT_SHIFT) |
(datacenterid_ << DATACENTER_ID_SHIFT) | (workerid_ << WORKER_ID_SHIFT) | sequence_;
}
private:
int64_t millsecond() const noexcept {
auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - start_time_point_);
return start_millsecond_ + diff.count();
}
int64_t wait_next_millis(int64_t last) const noexcept {
auto timestamp = millsecond();
while (timestamp <= last) {
timestamp = millsecond();
}
return timestamp;
}
};
} // namespace hku

View File

@ -30,7 +30,7 @@ enum HttpErrorCode {
WRONG_PARAMETER_TYPE // 参数类型错误(各个业务接口返回各个接口的参数)
};
#define HTTP_VALID_CHECK(expr, errcode, ...) \
#define HTTP_CHECK(expr, errcode, ...) \
{ \
if (!(expr)) { \
throw HttpError(errcode, fmt::format(__VA_ARGS__)); \

View File

@ -16,12 +16,12 @@ inline void MissContentFilter(HttpHandle *handle) {
void *data = nullptr;
size_t len = 0;
handle->getReqData(&data, &len);
HTTP_VALID_CHECK(data, HttpErrorCode::MISS_CONTENT, "Miss content!");
HTTP_CHECK(data, HttpErrorCode::MISS_CONTENT, "Miss content!");
}
inline void ApiTokenAuthorizeFilter(HttpHandle *handle) {
const char *token = handle->getReqHeader("token");
HTTP_VALID_CHECK(token, HttpErrorCode::MISS_TOKEN, "Miss token!");
HTTP_CHECK(token, HttpErrorCode::MISS_TOKEN, "Miss token!");
}
} // namespace hku

View File

@ -111,7 +111,7 @@ json HttpHandle::getReqJson() {
void* data;
size_t len;
nng_http_req_get_data(m_nng_req, &data, &len);
HTTP_VALID_CHECK(data, INVALID_JSON_REQUEST, "Req data is empty!");
HTTP_CHECK(data, INVALID_JSON_REQUEST, "Req data is empty!");
json result;
try {
result = json::parse((const char*)data);

View File

@ -16,8 +16,8 @@ class LoginHandle : public HttpHandle {
virtual void run() override {
json req = getReqJson();
HTTP_VALID_CHECK(req.contains("user"), HttpErrorCode::MISS_PARAMETER,
"Invalid login request! missing user");
HTTP_CHECK(req.contains("user"), HttpErrorCode::MISS_PARAMETER,
"Invalid login request! missing user");
setResHeader("Content-Type", "application/json; charset=UTF-8");
setResData(
R"({"hku_token":"7c98806c0711cf996d602890e0ab9119d9a86afe04296ba69a16f0d9d76be755"})");

View File

@ -9,6 +9,6 @@
namespace hku {
enum TradeErrorCode { TD_ACCOUNT_REPETITION = 20000 };
enum TradeErrorCode { TD_ACCOUNT_REPETITION = 20000, TD_ACCOUNT_INVALD_TYPE };
}

View File

@ -14,8 +14,8 @@ namespace hku {
inline void AuthorizeFilter(HttpHandle *handle) {
const char *token = handle->getReqHeader("hku_token");
HTTP_VALID_CHECK(token, HttpErrorCode::MISS_TOKEN, "Miss token!");
HTTP_VALID_CHECK(
HTTP_CHECK(token, HttpErrorCode::MISS_TOKEN, "Miss token!");
HTTP_CHECK(
strcmp(token, "7c98806c0711cf996d602890e0ab9119d9a86afe04296ba69a16f0d9d76be755") == 0,
HttpErrorCode::UNAUTHORIZED, "Failed authorize!");
}

View File

@ -16,10 +16,9 @@ class LogLevelHandle : public RestHandle {
virtual void run() override {
json req = getReqJson();
HTTP_VALID_CHECK(req.contains("level"), HttpErrorCode::MISS_PARAMETER,
"Missing key: level");
HTTP_VALID_CHECK(req["level"].is_number_integer(), HttpErrorCode::WRONG_PARAMETER_TYPE,
"level type must be integer");
HTTP_CHECK(req.contains("level"), HttpErrorCode::MISS_PARAMETER, "Missing key: level");
HTTP_CHECK(req["level"].is_number_integer(), HttpErrorCode::WRONG_PARAMETER_TYPE,
"level type must be integer");
int level = req["level"].get<int>();
if (!req.contains("logger")) {
set_logger_level(level);
@ -27,8 +26,8 @@ class LogLevelHandle : public RestHandle {
return;
}
HTTP_VALID_CHECK(req["logger"].is_string(), HttpErrorCode::WRONG_PARAMETER_TYPE,
"logger type must be string");
HTTP_CHECK(req["logger"].is_string(), HttpErrorCode::WRONG_PARAMETER_TYPE,
"logger type must be string");
std::string logger = req["logger"].get<std::string>();
if (have_logger(logger)) {
set_logger_level(logger, level);

View File

@ -13,25 +13,24 @@ namespace hku {
void AddTradeAccountHandle::run() {
json req = getReqJson();
HTTP_VALID_CHECK(req.contains("name"), HttpErrorCode::MISS_PARAMETER,
R"(Missing param "name")");
HTTP_VALID_CHECK(req.contains("type"), HttpErrorCode::MISS_PARAMETER,
R"(Missing param "type")");
HTTP_CHECK(req.contains("name"), HttpErrorCode::MISS_PARAMETER, R"(Missing param "name")");
HTTP_CHECK(req.contains("type"), HttpErrorCode::MISS_PARAMETER, R"(Missing param "type")");
TradeAccountModel account;
std::string name = req["name"].get<std::string>();
std::string td_type = req["type"].get<std::string>();
HTTP_CHECK(TradeService::isValidEumValue(TradeAccountModel::getTableName(), "type", td_type),
TradeErrorCode::TD_ACCOUNT_INVALD_TYPE, "Invalid trade account type: {}", td_type);
account.setName(name);
account.setType(req["type"].get<std::string>());
account.setAccount(UUID());
account.setType(td_type);
account.setTdId(TradeService::newTdId());
auto con = TradeService::getDBConnect();
{
TransAction trans(con);
HTTP_VALID_CHECK(!TradeAccountModel::isExistName(con, name),
TradeErrorCode::TD_ACCOUNT_REPETITION, "Name repetition");
HTTP_CHECK(!TradeAccountModel::isExistName(con, name),
TradeErrorCode::TD_ACCOUNT_REPETITION, "Name repetition");
con->save(account, false);
}
json res;
to_json(res, account);
setResData(res);
setResData(account.json());
}
void GetTradeAccountHandle::run() {}

View File

@ -15,6 +15,29 @@ namespace hku {
std::unique_ptr<ConnectPool<SQLiteConnect>> TradeService::ms_sqlite_pool;
std::unique_ptr<ConnectPool<MySQLConnect>> TradeService::ms_mysql_pool;
TradeService::snowflake_t TradeService::ms_td_id_generator;
TradeService::snowflake_t TradeService::ms_sta_id_generator;
TradeService::TradeService(const char* url, const std::string& config_file) : HttpService(url) {
ms_td_id_generator.init(1, 1);
ms_sta_id_generator.init(1, 1);
if (ms_sqlite_pool || ms_mysql_pool) {
return;
}
IniParser ini;
ini.read(config_file);
Parameter param;
auto options = ini.getOptionList("database");
for (auto& option : *options) {
param.set<string>(option, ini.get("database", option));
}
initTradeServiceDB(param);
}
void TradeService::initTradeServiceSqlite(const Parameter& param) {
Parameter sqlite_param;
sqlite_param.set<string>("db", param.get<string>("db"));
@ -55,21 +78,12 @@ DBConnectPtr TradeService::getDBConnect() {
: DBConnectPtr();
}
TradeService::TradeService(const char* url, const std::string& config_file) : HttpService(url) {
if (ms_sqlite_pool || ms_mysql_pool) {
return;
}
IniParser ini;
ini.read(config_file);
Parameter param;
auto options = ini.getOptionList("database");
for (auto& option : *options) {
param.set<string>(option, ini.get("database", option));
}
initTradeServiceDB(param);
bool TradeService::isValidEumValue(const std::string& table, const std::string& field,
const std::string& val) {
int count = getDBConnect()->queryInt(fmt::format(
R"(select count(1) from td_enum where table_name="{}" and field_name="{}" and value="{}")",
table, field, val));
return count > 0;
}
} // namespace hku

View File

@ -12,6 +12,7 @@
#include <hikyuu/utilities/db_connect/sqlite/SQLiteConnect.h>
#include <hikyuu/utilities/db_connect/mysql/MySQLConnect.h>
#include "http/HttpService.h"
#include "common/snowflake.h"
#include "WalletHandle.h"
#include "TradeAccountHandle.h"
@ -38,6 +39,17 @@ public:
public:
static DBConnectPtr getDBConnect();
static int64_t newTdId() {
return ms_td_id_generator.nextid();
}
static int64_t newStaId() {
return ms_sta_id_generator.nextid();
}
static bool isValidEumValue(const std::string &table, const std::string &field,
const std::string &val);
private:
static void initTradeServiceSqlite(const Parameter &param);
static void initTradeServiceMysql(const Parameter &param);
@ -46,6 +58,10 @@ private:
private:
static std::unique_ptr<ConnectPool<SQLiteConnect>> ms_sqlite_pool;
static std::unique_ptr<ConnectPool<MySQLConnect>> ms_mysql_pool;
using snowflake_t = snowflake<1618243200000L, std::mutex>;
static snowflake_t ms_td_id_generator;
static snowflake_t ms_sta_id_generator;
};
} // namespace hku

View File

@ -13,16 +13,16 @@ const char *g_sqlite_create_db{
R"(
CREATE TABLE "td_account" (
"id" INTEGER NOT NULL UNIQUE,
"account" TEXT NOT NULL UNIQUE,
"td_id" INTEGER NOT NULL UNIQUE,
"name" TEXT NOT NULL UNIQUE,
"type" TEXT NOT NULL,
PRIMARY KEY("id" AUTOINCREMENT)
);
CREATE UNIQUE INDEX "ix_td_account_on_account" ON "td_account" (
"account" ASC
CREATE UNIQUE INDEX "ix_td_account_on_td_id" ON "td_account" (
"td_id"
);
CREATE UNIQUE INDEX "ix_td_account_on_name" ON "td_account" (
"name" ASC
"name"
);
CREATE TABLE "td_funds" (
"id" INTEGER NOT NULL UNIQUE,
@ -33,14 +33,22 @@ CREATE TABLE "td_funds" (
"available" REAL NOT NULL,
PRIMARY KEY("id" AUTOINCREMENT)
);
CREATE INDEX "ix_td_funds_on_td_id" ON "td_funds" (
"td_id"
);
CREATE TABLE "td_enum" (
"id" INTEGER NOT NULL UNIQUE,
"table" TEXT NOT NULL,
"field" TEXT NOT NULL,
"value" INTEGER NOT NULL,
"string" TEXT NOT NULL,
"table_name" TEXT NOT NULL,
"field_name" TEXT NOT NULL,
"value" TEXT NOT NULL,
PRIMARY KEY("id" AUTOINCREMENT)
);
CREATE INDEX "ix_td_enum_on_tabel_field" ON "td_enum" (
"table_name",
"field_name",
"value"
);
INSERT INTO "td_enum" ("id", "table_name", "field_name", "value") VALUES ('1', 'td_account', 'type', 'xq');
CREATE TABLE "td_positions" (
"id" INTEGER NOT NULL UNIQUE,
"td_id" INTEGER NOT NULL,
@ -58,6 +66,10 @@ CREATE TABLE "td_positions" (
"ref_profit" REAL NOT NULL,
PRIMARY KEY("id" AUTOINCREMENT)
);
CREATE INDEX "ix_td_positions_on_td_id_sta_id" ON "td_positions" (
"td_id",
"sta_id"
);
CREATE TABLE "td_orders" (
"id" INTEGER NOT NULL UNIQUE,
"td_id" INTEGER NOT NULL,
@ -74,6 +86,13 @@ CREATE TABLE "td_orders" (
"status" INTEGER NOT NULL,
PRIMARY KEY("id" AUTOINCREMENT)
);
CREATE INDEX "ix_td_orders_on_td_id_sta_id" ON "td_orders" (
"td_id",
"sta_id"
);
CREATE INDEX "ix_td_orders_on_datetime" ON "td_orders" (
"datetime" ASC
);
CREATE TABLE "td_fills" (
"id" INTEGER NOT NULL UNIQUE,
"td_id" INTEGER NOT NULL,
@ -87,17 +106,24 @@ CREATE TABLE "td_fills" (
"order_seq" TEXT NOT NULL,
PRIMARY KEY("id" AUTOINCREMENT)
);
CREATE INDEX "ix_td_fills_on_td_id_sta_id" ON "td_fills" (
"td_id",
"sta_id"
);
CREATE INDEX "ix_td_fills_on_datetime" ON "td_fills" (
"datetime" ASC
);
CREATE TABLE "td_sta_account" (
"id" INTEGER NOT NULL UNIQUE,
"sta_id" INTEGER NOT NULL UNIQUE,
"td_id" INTEGER NOT NULL,
"account" TEXT NOT NULL,
"name" TEXT NOT NULL,
"path" TEXT NOT NULL,
"status" TEXT NOT NULL,
PRIMARY KEY("id" AUTOINCREMENT)
);
CREATE UNIQUE INDEX "ix_td_sta_account_on_account" ON "td_sta_account" (
"account" ASC
CREATE UNIQUE INDEX "ix_td_sta_account_on_sta_id" ON "td_sta_account" (
"sta_id" ASC
);
CREATE TABLE "td_sta_funds" (
"id" INTEGER NOT NULL UNIQUE,
@ -108,6 +134,9 @@ CREATE TABLE "td_sta_funds" (
"available" REAL NOT NULL,
PRIMARY KEY("id" AUTOINCREMENT)
);
CREATE INDEX "ix_td_sta_funds_on_sta_id" ON "td_sta_funds" (
"sta_id"
);
CREATE TABLE "td_account_xq" (
"id" INTEGER NOT NULL UNIQUE,
"td_id" INTEGER NOT NULL,
@ -116,6 +145,9 @@ CREATE TABLE "td_account_xq" (
"portfolio_market" TEXT NOT NULL,
PRIMARY KEY("id" AUTOINCREMENT)
);
CREATE INDEX "ix_td_account_xq_on_td_id" ON "td_account_xq" (
"td_id" ASC
);
)"};

View File

@ -7,15 +7,12 @@
#pragma once
#include <nlohmann/json.hpp>
#include <hikyuu/utilities/db_connect/DBConnect.h>
using nlohmann::json;
namespace hku {
class TradeAccountModel {
TABLE_BIND3(td_account, account, name, type)
TABLE_BIND3(td_account, td_id, name, type)
public:
static bool isExistName(DBConnectPtr con, const std::string& name) {
@ -29,12 +26,12 @@ public:
}
public:
std::string getAccount() const {
return account;
int64_t getTdId() const {
return td_id;
}
void setAccount(const std::string& account) {
this->account = account;
void setTdId(int64_t id) {
td_id = id;
}
std::string getName() const {
@ -53,23 +50,21 @@ public:
this->type = type;
}
private:
string account;
string name;
string type;
string json() const {
return fmt::format(R"({{"td_id":"{}", "name":"{}", "type":"{}"}})", td_id, name, type);
}
friend void to_json(json& j, const TradeAccountModel& p);
friend void from_json(const json& j, TradeAccountModel& p);
private:
int64_t td_id; // 内部交易账户id
string name; // 内部交易账户名称
string type; // 内部交易账户类型xq雪球模拟账户
};
inline void to_json(json& j, const TradeAccountModel& p) {
j = json{{"account", p.account}, {"name", p.name}, {"type", p.type}};
}
inline void from_json(const json& j, TradeAccountModel& p) {
j.at("account").get_to(p.account);
j.at("name").get_to(p.name);
j.at("type").get_to(p.type);
inline std::ostream& operator<<(std::ostream& out, const TradeAccountModel& model) {
string strip(", ");
out << "(" << model.id() << strip << model.getTdId() << strip << model.getName() << strip
<< model.getType() << ")";
return out;
}
} // namespace hku

Binary file not shown.