add tool。
Some checks failed
Deploy / Build (push) Failing after 1m55s

This commit is contained in:
luocai
2025-06-28 15:09:13 +08:00
parent 73bd900000
commit f5c76b931f
13 changed files with 285 additions and 18 deletions

View File

@ -1,4 +1,5 @@
add_library(Base
Database.h Database.cpp
DataStructure.h DataStructure.cpp
HttpSession.h HttpSession.cpp
)
@ -12,4 +13,5 @@ target_link_libraries(Base
PRIVATE OpenSSL::Crypto
PUBLIC Kylin::Router
PUBLIC Kylin::Core
PRIVATE sqlite3
)

View File

@ -13,6 +13,15 @@ struct VisitorStats {
int64_t lastViewTime = 0;
};
struct VisitRecord {
int id;
std::string url;
std::string visitorUuid;
std::string lastUserAgent;
int lastViewTime;
int pageViewCount;
};
struct SiteStats {
int totalViews = 0;
int totalVisitors = 0;

328
Base/Database.cpp Normal file
View File

@ -0,0 +1,328 @@
#include "Database.h"
#include "Core/Logger.h"
#include <sqlite3.h>
#include <stdexcept>
namespace Older {
bool Database::open(const std::string &path) {
if (sqlite3_open(path.c_str(), &m_sqlite) != SQLITE_OK) {
LOG(error) << "Can't open database: " << sqlite3_errmsg(m_sqlite);
return false;
}
initialize();
return true;
}
void Database::upsertVisitRecord(const std::string &url, const std::string &visitorUuid, const std::string &userAgent,
int64_t viewTime) {
sqlite3_stmt *statement = nullptr;
const char *query = "INSERT INTO visit_analysis (url, visitor_uuid, last_user_agent, last_view_time, page_view_count) "
"VALUES (?, ?, ?, ?, 1) "
"ON CONFLICT(url, visitor_uuid) DO UPDATE SET "
"last_user_agent = excluded.last_user_agent, "
"last_view_time = excluded.last_view_time, "
"page_view_count = page_view_count + 1;";
if (sqlite3_prepare_v2(m_sqlite, query, -1, &statement, nullptr) == SQLITE_OK) {
sqlite3_bind_text(statement, 1, url.c_str(), -1, SQLITE_STATIC);
sqlite3_bind_text(statement, 2, visitorUuid.c_str(), -1, SQLITE_STATIC);
sqlite3_bind_text(statement, 3, userAgent.c_str(), -1, SQLITE_STATIC);
sqlite3_bind_int(statement, 4, viewTime);
if (sqlite3_step(statement) != SQLITE_DONE) {
LOG(error) << "Error inserting/updating record: " << sqlite3_errmsg(m_sqlite);
}
}
sqlite3_finalize(statement);
}
VisitorStats Database::visitorStats(const std::string &url) {
VisitorStats ret;
ret.url = url;
sqlite3_stmt *statement = nullptr;
const char *query = "SELECT COUNT(DISTINCT visitor_uuid) AS visitor_count, SUM(page_view_count) AS total_views "
"FROM visit_analysis WHERE url = ?;";
if (sqlite3_prepare_v2(m_sqlite, query, -1, &statement, nullptr) == SQLITE_OK) {
sqlite3_bind_text(statement, 1, url.c_str(), -1, SQLITE_STATIC);
if (sqlite3_step(statement) == SQLITE_ROW) {
ret.visitorCount = sqlite3_column_int(statement, 0);
ret.totalViews = sqlite3_column_int(statement, 1);
}
}
sqlite3_finalize(statement);
return ret;
}
std::list<VisitorStats> Database::mostViewedUrls(int n) {
std::list<VisitorStats> ret;
sqlite3_stmt *statement = nullptr;
const char *query = R"(
SELECT url, SUM(page_view_count) AS total_page_views
FROM visit_analysis
GROUP BY url
ORDER BY total_page_views DESC
LIMIT ?;
)";
if (sqlite3_prepare_v2(m_sqlite, query, -1, &statement, nullptr) == SQLITE_OK) {
sqlite3_bind_int(statement, 1, n);
while (sqlite3_step(statement) == SQLITE_ROW) {
VisitorStats item;
item.url = reinterpret_cast<const char *>(sqlite3_column_text(statement, 0));
item.totalViews = sqlite3_column_int(statement, 1);
ret.push_back(item);
}
}
sqlite3_finalize(statement);
return ret;
}
std::list<VisitorStats> Database::latestViewedUrls(int n) {
std::list<VisitorStats> ret;
sqlite3_stmt *statement = nullptr;
const char *query = R"(
SELECT url, MAX(last_view_time) AS latest_view_time
FROM visit_analysis
GROUP BY url
ORDER BY latest_view_time DESC
LIMIT ?;
)";
if (sqlite3_prepare_v2(m_sqlite, query, -1, &statement, nullptr) == SQLITE_OK) {
sqlite3_bind_int(statement, 1, n);
while (sqlite3_step(statement) == SQLITE_ROW) {
VisitorStats item;
item.url = reinterpret_cast<const char *>(sqlite3_column_text(statement, 0));
item.lastViewTime = sqlite3_column_int64(statement, 1);
ret.push_back(item);
}
}
sqlite3_finalize(statement);
return ret;
}
SiteStats Database::siteStats() {
SiteStats ret;
sqlite3_stmt *statement;
const char *query = "SELECT COUNT(DISTINCT visitor_uuid) AS total_visitors, SUM(page_view_count) AS total_page_views "
"FROM visit_analysis;";
if (sqlite3_prepare_v2(m_sqlite, query, -1, &statement, nullptr) == SQLITE_OK) {
if (sqlite3_step(statement) == SQLITE_ROW) {
ret.totalVisitors = sqlite3_column_int(statement, 0);
ret.totalViews = sqlite3_column_int(statement, 1);
}
}
sqlite3_finalize(statement);
return ret;
}
void Database::createUser(const Account &account) {
if (!Account::validateEmail(account.email)) {
throw std::runtime_error("Invalid email format");
}
sqlite3_stmt *statement = nullptr;
const char *sql = "INSERT INTO users (username, email, password_hash, salt, created_at) VALUES (?, ?, ?, ?, ?);";
if (sqlite3_prepare_v2(m_sqlite, sql, -1, &statement, nullptr) != SQLITE_OK) {
throw std::runtime_error(sqlite3_errmsg(m_sqlite));
}
sqlite3_bind_text(statement, 1, account.username.c_str(), -1, SQLITE_STATIC);
sqlite3_bind_text(statement, 2, account.email.c_str(), -1, SQLITE_STATIC); // 新增绑定
sqlite3_bind_blob(statement, 3, account.passwordHash.data(), account.passwordHash.size(), SQLITE_STATIC);
sqlite3_bind_blob(statement, 4, account.salt.data(), account.salt.size(), SQLITE_STATIC);
sqlite3_bind_int64(statement, 5, account.createdAt);
int rc = sqlite3_step(statement);
sqlite3_finalize(statement);
if (rc != SQLITE_DONE) {
const char *message = sqlite3_errmsg(m_sqlite);
if (std::string(message).find("UNIQUE") != std::string::npos) {
throw std::runtime_error("username or email already exists");
}
throw std::runtime_error(message);
}
}
Account Database::user(const std::string &identifier) const {
sqlite3_stmt *stmt;
const char *sql = "SELECT id, username, email, password_hash, salt, created_at "
"FROM users WHERE username = ? OR email = ?;";
if (sqlite3_prepare_v2(m_sqlite, sql, -1, &stmt, nullptr) != SQLITE_OK) {
throw std::runtime_error(sqlite3_errmsg(m_sqlite));
}
sqlite3_bind_text(stmt, 1, identifier.c_str(), -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 2, identifier.c_str(), -1, SQLITE_STATIC);
if (sqlite3_step(stmt) != SQLITE_ROW) {
sqlite3_finalize(stmt);
throw std::runtime_error("User not found");
}
Account ret;
ret.id = sqlite3_column_int(stmt, 0);
ret.username = reinterpret_cast<const char *>(sqlite3_column_text(stmt, 1));
ret.email = reinterpret_cast<const char *>(sqlite3_column_text(stmt, 2));
const void *hash_blob = sqlite3_column_blob(stmt, 3);
int hash_size = sqlite3_column_bytes(stmt, 3);
ret.passwordHash.resize(hash_size);
memcpy(ret.passwordHash.data(), hash_blob, hash_size);
const void *salt_blob = sqlite3_column_blob(stmt, 4);
int salt_size = sqlite3_column_bytes(stmt, 4);
ret.salt.resize(salt_size);
memcpy(ret.salt.data(), salt_blob, salt_size);
ret.createdAt = sqlite3_column_int64(stmt, 5);
sqlite3_finalize(stmt);
return ret;
}
Account Database::user(int64_t id) const {
sqlite3_stmt *stmt;
const char *sql = "SELECT id, username, email, password_hash, salt, created_at "
"FROM users WHERE id = ?;";
if (sqlite3_prepare_v2(m_sqlite, sql, -1, &stmt, nullptr) != SQLITE_OK) {
throw std::runtime_error(sqlite3_errmsg(m_sqlite));
}
sqlite3_bind_int64(stmt, 1, id);
if (sqlite3_step(stmt) != SQLITE_ROW) {
sqlite3_finalize(stmt);
throw std::runtime_error("User not found");
}
Account ret;
ret.id = sqlite3_column_int(stmt, 0);
ret.username = reinterpret_cast<const char *>(sqlite3_column_text(stmt, 1));
ret.email = reinterpret_cast<const char *>(sqlite3_column_text(stmt, 2));
const void *hash_blob = sqlite3_column_blob(stmt, 3);
int hash_size = sqlite3_column_bytes(stmt, 3);
ret.passwordHash.resize(hash_size);
memcpy(ret.passwordHash.data(), hash_blob, hash_size);
const void *salt_blob = sqlite3_column_blob(stmt, 4);
int salt_size = sqlite3_column_bytes(stmt, 4);
ret.salt.resize(salt_size);
memcpy(ret.salt.data(), salt_blob, salt_size);
ret.createdAt = sqlite3_column_int64(stmt, 5);
sqlite3_finalize(stmt);
return ret;
}
std::list<VisitRecord> Database::visitRecords() {
constexpr char *sql = "SELECT * FROM visit_analysis;";
sqlite3_stmt *statement = nullptr;
std::list<VisitRecord> ret;
if (sqlite3_prepare_v2(m_sqlite, sql, -1, &statement, nullptr) != SQLITE_OK) {
LOG(error) << "sqlite3_prepare_v2() failed: " << sqlite3_errmsg(m_sqlite);
return ret;
}
while (sqlite3_step(statement) == SQLITE_ROW) {
VisitRecord record;
record.id = sqlite3_column_int(statement, 0);
record.url = reinterpret_cast<const char *>(sqlite3_column_text(statement, 1));
record.visitorUuid = reinterpret_cast<const char *>(sqlite3_column_text(statement, 2));
record.lastUserAgent = reinterpret_cast<const char *>(sqlite3_column_text(statement, 3));
record.lastViewTime = sqlite3_column_int(statement, 4);
record.pageViewCount = sqlite3_column_int(statement, 5);
ret.push_back(record);
}
sqlite3_finalize(statement);
return ret;
}
bool Database::removeVisitRecord(int id) {
constexpr char *sql = "DELETE FROM visit_analysis WHERE id = ?;";
sqlite3_stmt *statement = nullptr;
if (sqlite3_prepare_v2(m_sqlite, sql, -1, &statement, nullptr) != SQLITE_OK) {
LOG(error) << "sqlite3_prepare_v2() failed: " << sqlite3_errmsg(m_sqlite);
return false;
}
if (sqlite3_bind_int(statement, 1, id) != SQLITE_OK) {
LOG(error) << "sqlite3_bind_int() failed: " << sqlite3_errmsg(m_sqlite);
sqlite3_finalize(statement);
return false;
}
bool success = false;
if (sqlite3_step(statement) == SQLITE_DONE) {
if (sqlite3_changes(m_sqlite) > 0) {
success = true;
} else {
LOG(warning) << "cannot find item " << id;
}
} else {
LOG(error) << "sqlite3_step() failed: " << sqlite3_errmsg(m_sqlite);
}
// 清理资源
sqlite3_finalize(statement);
return success;
}
void Database::initialize() {
createVisitAnalysisTable();
createUsersTable();
}
void Database::createVisitAnalysisTable() {
const char *createTableQuery = R"(
CREATE TABLE IF NOT EXISTS visit_analysis (
id INTEGER PRIMARY KEY AUTOINCREMENT,
url TEXT NOT NULL,
visitor_uuid TEXT NOT NULL,
last_user_agent TEXT NOT NULL,
last_view_time INTEGER NOT NULL,
page_view_count INTEGER NOT NULL,
UNIQUE(url, visitor_uuid)
);
)";
char *message = nullptr;
int rc = sqlite3_exec(m_sqlite, createTableQuery, nullptr, nullptr, &message);
if (rc != SQLITE_OK) {
LOG(error) << "Error creating table: " << message;
sqlite3_free(message);
}
}
void Database::createUsersTable() {
const char *sql = R"(
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
email TEXT UNIQUE NOT NULL,
password_hash BLOB NOT NULL,
salt BLOB NOT NULL,
created_at INTEGER NOT NULL
);
)";
char *message = nullptr;
int rc = sqlite3_exec(m_sqlite, sql, nullptr, nullptr, &message);
if (rc != SQLITE_OK) {
LOG(error) << "Error creating table: " << message;
sqlite3_free(message);
}
}
} // namespace Older

37
Base/Database.h Normal file
View File

@ -0,0 +1,37 @@
#ifndef __DATABASE_H__
#define __DATABASE_H__
#include "DataStructure.h"
#include <list>
#include <string>
typedef struct sqlite3 sqlite3;
namespace Older {
class Database {
public:
bool open(const std::string &path);
void upsertVisitRecord(const std::string &url, const std::string &visitorUuid, const std::string &userAgent,
int64_t viewTime);
VisitorStats visitorStats(const std::string &url);
std::list<VisitorStats> mostViewedUrls(int n);
std::list<VisitorStats> latestViewedUrls(int n);
SiteStats siteStats();
std::list<VisitRecord> visitRecords();
bool removeVisitRecord(int id);
void createUser(const Account &account);
Account user(const std::string &identifier) const;
Account user(int64_t id) const;
protected:
void createVisitAnalysisTable();
void createUsersTable();
void initialize();
private:
sqlite3 *m_sqlite = nullptr;
};
} // namespace Older
#endif // __DATABASE_H__