Older/Database/Database.cpp

235 lines
8.6 KiB
C++
Raw Normal View History

2024-01-03 22:44:36 +08:00
#include "Database.h"
#include "BoostLog.h"
2024-11-26 22:58:54 +08:00
#include "Session.h"
#include <Wt/Dbo/FixedSqlConnectionPool.h>
#include <Wt/Dbo/SqlConnectionPool.h>
#include <Wt/Dbo/backend/Sqlite3.h>
2024-01-03 22:44:36 +08:00
#include <sqlite3.h>
#include <sstream>
2024-11-26 22:58:54 +08:00
std::unique_ptr<Session> Database::session() {
if (!m_sqlConnectionPool) return {};
return std::make_unique<Session>(*m_sqlConnectionPool);
2024-01-03 22:44:36 +08:00
}
2024-11-26 22:58:54 +08:00
bool Database::open(const std::string &path) {
try {
auto connection = std::make_unique<Wt::Dbo::backend::Sqlite3>(path);
connection->setProperty("show-queries", "true");
connection->setDateTimeStorage(Wt::Dbo::SqlDateTimeType::DateTime,
Wt::Dbo::backend::DateTimeStorage::PseudoISO8601AsText);
m_sqlConnectionPool = std::make_unique<Wt::Dbo::FixedSqlConnectionPool>(std::move(connection), 10);
session()->createTables();
} catch (const Wt::Dbo::Exception &e) {
LOG(error) << e.code() << ": " << e.what();
2024-01-03 22:44:36 +08:00
}
bool ret = true;
2024-11-26 22:58:54 +08:00
int result = sqlite3_open(path.c_str(), &m_sqlite3);
2024-01-03 22:44:36 +08:00
if (result != SQLITE_OK) {
ret = false;
2024-11-26 22:58:54 +08:00
LOG(error) << "open database failed.";
2024-01-03 22:44:36 +08:00
}
2024-11-26 22:58:54 +08:00
initialize();
2024-01-03 22:44:36 +08:00
return ret;
}
2024-07-31 22:28:05 +08:00
void Database::updateVisitCount(const std::string &url, const std::string &visitorUuid, const std::string &userAgent,
int64_t time) {
sqlite3_stmt *stmt;
2024-08-05 23:01:19 +08:00
auto strippedUrl = url;
if (strippedUrl.size() > 1 && strippedUrl.back() == '/') {
strippedUrl.pop_back();
}
2024-07-31 22:28:05 +08:00
const char *query = "SELECT id, page_view_count FROM visit_analysis WHERE url = ? AND visitor_uuid = ?";
if (sqlite3_prepare_v2(m_sqlite3, query, -1, &stmt, 0) != SQLITE_OK) {
2024-07-29 00:46:11 +08:00
LOG(error) << "Failed to prepare statement: " << sqlite3_errmsg(m_sqlite3);
}
2024-08-05 23:01:19 +08:00
sqlite3_bind_text(stmt, 1, strippedUrl.c_str(), -1, SQLITE_STATIC);
2024-07-29 00:46:11 +08:00
sqlite3_bind_text(stmt, 2, visitorUuid.c_str(), -1, SQLITE_STATIC);
2024-07-31 22:28:05 +08:00
int id = -1;
int page_view_count = 0;
if (sqlite3_step(stmt) == SQLITE_ROW) {
id = sqlite3_column_int(stmt, 0);
page_view_count = sqlite3_column_int(stmt, 1);
}
sqlite3_finalize(stmt);
if (id != -1) { // 更新记录
const char *updateQuery =
"UPDATE visit_analysis SET last_user_agent = ?, last_view_time = ?, page_view_count = ? WHERE id = ?";
if (sqlite3_prepare_v2(m_sqlite3, updateQuery, -1, &stmt, 0) != SQLITE_OK) {
LOG(error) << "Failed to prepare update statement: " << sqlite3_errmsg(m_sqlite3);
2024-07-29 00:46:11 +08:00
}
2024-07-31 22:28:05 +08:00
sqlite3_bind_text(stmt, 1, userAgent.c_str(), -1, SQLITE_STATIC);
sqlite3_bind_int64(stmt, 2, time);
sqlite3_bind_int(stmt, 3, page_view_count + 1);
sqlite3_bind_int(stmt, 4, id);
2024-07-29 00:46:11 +08:00
if (sqlite3_step(stmt) != SQLITE_DONE) {
2024-07-31 22:28:05 +08:00
LOG(error) << "Failed to update record: " << sqlite3_errmsg(m_sqlite3);
2024-07-29 00:46:11 +08:00
}
2024-07-31 22:28:05 +08:00
sqlite3_finalize(stmt);
} else { // 插入新记录
const char *insertQuery = "INSERT INTO visit_analysis (url, visitor_uuid, last_user_agent, last_view_time, "
"page_view_count) VALUES (?, ?, ?, ?, 1)";
if (sqlite3_prepare_v2(m_sqlite3, insertQuery, -1, &stmt, 0) != SQLITE_OK) {
LOG(error) << "Failed to prepare insert statement: " << sqlite3_errmsg(m_sqlite3);
2024-07-29 00:46:11 +08:00
}
2024-08-05 23:01:19 +08:00
sqlite3_bind_text(stmt, 1, strippedUrl.c_str(), -1, SQLITE_STATIC);
2024-07-29 00:46:11 +08:00
sqlite3_bind_text(stmt, 2, visitorUuid.c_str(), -1, SQLITE_STATIC);
2024-07-31 22:28:05 +08:00
sqlite3_bind_text(stmt, 3, userAgent.c_str(), -1, SQLITE_STATIC);
sqlite3_bind_int64(stmt, 4, time);
if (sqlite3_step(stmt) != SQLITE_DONE) {
LOG(error) << "Failed to insert record: " << sqlite3_errmsg(m_sqlite3) << std::endl;
2024-07-29 00:46:11 +08:00
}
2024-07-31 22:28:05 +08:00
sqlite3_finalize(stmt);
2024-07-29 00:46:11 +08:00
}
}
2024-07-30 23:17:42 +08:00
void Database::clearVisitRecord() {
2024-07-29 22:41:57 +08:00
char *message = nullptr;
2024-07-30 23:17:42 +08:00
constexpr auto sql = "DELETE FROM visit_analysis";
2024-07-29 22:41:57 +08:00
int rc = sqlite3_exec(m_sqlite3, sql, nullptr, nullptr, &message);
if (rc != SQLITE_OK) {
LOG(error) << "SQL error: " << message;
sqlite3_free(message);
}
}
2024-07-30 23:17:42 +08:00
VisitAnalysis Database::siteVisitAnalysisData() {
VisitAnalysis ret;
2024-08-05 23:01:19 +08:00
sqlite3_stmt *stmt = nullptr;
2024-07-30 23:17:42 +08:00
const char *sql = "SELECT COUNT(DISTINCT visitor_uuid) as unique_visitors, SUM(page_view_count) as "
"total_page_views FROM visit_analysis";
if (sqlite3_prepare_v2(m_sqlite3, sql, -1, &stmt, nullptr) != SQLITE_OK) {
2024-07-29 22:41:57 +08:00
LOG(error) << "Failed to prepare statement: " << sqlite3_errmsg(m_sqlite3);
2024-07-30 23:17:42 +08:00
return ret;
2024-07-29 22:41:57 +08:00
}
2024-07-30 23:17:42 +08:00
if (sqlite3_step(stmt) == SQLITE_ROW) {
ret.uniqueVisitorCount = sqlite3_column_int(stmt, 0);
ret.pageViewCount = sqlite3_column_int(stmt, 1);
} else {
LOG(error) << "Failed to execute query: " << sqlite3_errmsg(m_sqlite3);
2024-07-29 22:41:57 +08:00
}
sqlite3_finalize(stmt);
2024-07-30 23:17:42 +08:00
return ret;
2024-07-29 22:41:57 +08:00
}
2024-08-06 09:48:25 +08:00
static std::vector<std::string> urlFilter = {
"/",
2024-08-13 22:21:40 +08:00
"/search",
2024-08-06 09:48:25 +08:00
"/LoginPage",
2024-08-13 22:21:40 +08:00
"/MessageBoard",
2024-08-06 09:48:25 +08:00
"/我的笔记",
"/我的博客",
};
2024-08-05 23:01:19 +08:00
std::list<VisitAnalysis> Database::mostViewedUrls(int size) {
const char *sql = "SELECT url, SUM(page_view_count) AS total_page_view_count "
"FROM visit_analysis "
"GROUP BY url "
"ORDER BY total_page_view_count DESC "
"LIMIT ?";
sqlite3_stmt *stmt = nullptr;
std::list<VisitAnalysis> ret;
auto rc = sqlite3_prepare_v2(m_sqlite3, sql, -1, &stmt, nullptr);
if (rc != SQLITE_OK) {
LOG(error) << "Failed to prepare statement: " << sqlite3_errmsg(m_sqlite3);
return ret;
}
2024-08-06 09:48:25 +08:00
sqlite3_bind_int(stmt, 1, size + urlFilter.size());
2024-08-05 23:01:19 +08:00
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
VisitAnalysis pv;
pv.url = reinterpret_cast<const char *>(sqlite3_column_text(stmt, 0));
2024-08-06 09:48:25 +08:00
if (std::find(urlFilter.cbegin(), urlFilter.cend(), pv.url) != urlFilter.cend()) continue;
2024-08-05 23:01:19 +08:00
pv.pageViewCount = sqlite3_column_int(stmt, 1);
2024-08-07 15:47:30 +08:00
if (ret.size() < size) {
ret.push_back(pv);
}
2024-08-05 23:01:19 +08:00
}
if (rc != SQLITE_DONE) {
LOG(error) << "Failed to execute statement: " << sqlite3_errmsg(m_sqlite3);
}
sqlite3_finalize(stmt);
return ret;
}
2024-08-06 09:48:25 +08:00
std::list<VisitAnalysis> Database::latestViewedUrls(int size) {
std::list<VisitAnalysis> ret;
sqlite3_stmt *stmt;
const char *sql = R"(
SELECT url, MAX(last_view_time) as last_view_time
FROM visit_analysis
GROUP BY url
ORDER BY last_view_time DESC
LIMIT ?
)";
if (sqlite3_prepare_v2(m_sqlite3, sql, -1, &stmt, nullptr) != SQLITE_OK) {
LOG(error) << "Failed to prepare statement: " << sqlite3_errmsg(m_sqlite3);
return ret;
}
if (sqlite3_bind_int(stmt, 1, size + urlFilter.size()) != SQLITE_OK) {
LOG(error) << "Failed to bind parameter: " << sqlite3_errmsg(m_sqlite3);
sqlite3_finalize(stmt);
return ret;
}
while (sqlite3_step(stmt) == SQLITE_ROW) {
VisitAnalysis visit;
visit.url = reinterpret_cast<const char *>(sqlite3_column_text(stmt, 0));
if (std::find(urlFilter.cbegin(), urlFilter.cend(), visit.url) != urlFilter.cend()) continue;
visit.lastViewTime = sqlite3_column_int64(stmt, 1);
2024-08-07 15:47:30 +08:00
if (ret.size() < size) {
ret.push_back(visit);
}
2024-08-06 09:48:25 +08:00
}
return ret;
}
2024-07-30 22:17:55 +08:00
VisitAnalysis Database::visitAnalysisData(const std::string &url) {
VisitAnalysis ret;
sqlite3_stmt *stmt;
2024-07-30 23:17:42 +08:00
std::string query =
"SELECT SUM(page_view_count), COUNT(DISTINCT visitor_uuid) FROM visit_analysis WHERE url = '" + url + "';";
if (sqlite3_prepare_v2(m_sqlite3, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
2024-07-30 22:17:55 +08:00
if (sqlite3_step(stmt) == SQLITE_ROW) {
ret.pageViewCount = sqlite3_column_int(stmt, 0);
ret.uniqueVisitorCount = sqlite3_column_int(stmt, 1);
}
sqlite3_finalize(stmt);
} else {
LOG(error) << "Failed to execute query: " << sqlite3_errmsg(m_sqlite3);
}
return ret;
}
2024-01-03 22:44:36 +08:00
void Database::initialize() {
2024-07-29 00:46:11 +08:00
char *message = nullptr;
2024-11-26 22:58:54 +08:00
auto sql = R"(
2024-07-30 23:17:42 +08:00
CREATE TABLE IF NOT EXISTS visit_analysis (
2024-07-31 22:28:05 +08:00
id INTEGER PRIMARY KEY AUTOINCREMENT,
2024-07-29 00:46:11 +08:00
url TEXT NOT NULL,
visitor_uuid TEXT NOT NULL,
2024-07-31 22:28:05 +08:00
last_user_agent TEXT NOT NULL,
last_view_time INTEGER NOT NULL,
page_view_count INTEGER NOT NULL
2024-07-29 00:46:11 +08:00
);
)";
2024-11-26 22:58:54 +08:00
int result = sqlite3_exec(m_sqlite3, sql, 0, 0, &message);
2024-07-29 00:46:11 +08:00
if (result != SQLITE_OK) {
LOG(error) << "Failed to create table: " << message << std::endl;
sqlite3_free(message);
}
2024-01-03 22:44:36 +08:00
}
Database::~Database() {
if (m_sqlite3 != nullptr) {
sqlite3_close(m_sqlite3);
m_sqlite3 = nullptr;
}
}