mirror of
https://gitee.com/fasiondog/hikyuu.git
synced 2024-11-29 18:39:10 +08:00
hkuserver continuue
This commit is contained in:
parent
84857ea909
commit
203885c8c8
@ -92,7 +92,7 @@ bool SQLiteConnect::tableExist(const string& tablename) {
|
||||
}
|
||||
|
||||
void SQLiteConnect::transaction() {
|
||||
exec("BEGIN TRANSACTION");
|
||||
exec("BEGIN IMMEDIATE");
|
||||
}
|
||||
|
||||
void SQLiteConnect::commit() {
|
||||
|
109
hikyuu_cpp/hikyuu_server/common/snowflake.h
Normal file
109
hikyuu_cpp/hikyuu_server/common/snowflake.h
Normal 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
|
@ -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__)); \
|
||||
|
@ -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
|
@ -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);
|
||||
|
@ -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"})");
|
||||
|
@ -9,6 +9,6 @@
|
||||
|
||||
namespace hku {
|
||||
|
||||
enum TradeErrorCode { TD_ACCOUNT_REPETITION = 20000 };
|
||||
enum TradeErrorCode { TD_ACCOUNT_REPETITION = 20000, TD_ACCOUNT_INVALD_TYPE };
|
||||
|
||||
}
|
@ -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!");
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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() {}
|
||||
|
@ -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
|
@ -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 ¶m);
|
||||
static void initTradeServiceMysql(const Parameter ¶m);
|
||||
@ -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
|
@ -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
|
||||
);
|
||||
|
||||
)"};
|
||||
|
||||
|
@ -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.
Loading…
Reference in New Issue
Block a user