Older/Server/Database/Database.cpp
amass 9c47e0f2c5
All checks were successful
Deploy / PullDocker (push) Successful in 27s
Deploy / Build (push) Successful in 55s
add visit analysis.
2024-07-30 22:17:55 +08:00

350 lines
14 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "Database.h"
#include "BoostLog.h"
#include <sqlite3.h>
#include <sstream>
bool Database::open(const std::string &path) {
bool ret = true;
int result = sqlite3_open(path.c_str(), &m_sqlite3);
if (result != SQLITE_OK) {
ret = false;
LOG(error) << "open database failed.";
}
initialize();
return ret;
}
static int selectTaskCallback(void *data, int argc, char **argv, char **columnName) {
auto tasks = reinterpret_cast<Tasks *>(data);
Task task;
for (int i = 0; i < argc; i++) {
if (argv[i] == nullptr) continue;
if (strcmp(columnName[i], "id") == 0) {
task.id = std::atol(argv[i]);
} else if (strcmp(columnName[i], "create_time") == 0) {
task.createTime = std::atol(argv[i]);
} else if (strcmp(columnName[i], "content") == 0) {
task.content = argv[i];
} else if (strcmp(columnName[i], "comment") == 0) {
task.comment = argv[i];
} else if (strcmp(columnName[i], "finished") == 0) {
task.finished = std::atol(argv[i]);
} else if (strcmp(columnName[i], "parent_id") == 0) {
task.parentId = std::atol(argv[i]);
}
}
tasks->push_back(task);
return 0;
}
Tasks Database::tasks() {
Tasks ret;
char *error = nullptr;
if (sqlite3_exec(m_sqlite3, "select * from tasks", selectTaskCallback, &ret, &error) != SQLITE_OK) {
LOG(error) << "sqlite3_exec() failed: " << error << std::endl;
sqlite3_free(error);
}
std::unordered_map<int, Task *> tasks;
for (auto iterator = ret.begin(); iterator != ret.end();) {
if (iterator->parentId >= 0) {
if (tasks.count(iterator->parentId) > 0) {
auto parentTask = tasks.at(iterator->parentId);
parentTask->children.push_back(*iterator);
tasks.insert({iterator->id, &parentTask->children.back()});
} else {
LOG(warning) << "task`s parent id " << iterator->parentId << " not existed.";
}
iterator = ret.erase(iterator);
} else {
tasks.insert({iterator->id, &(*iterator)});
++iterator;
}
}
return ret;
}
bool Database::addTask(uint64_t createTime, const std::string &content, const std::string &comment, int parentId,
bool finished) {
bool ret = true;
std::ostringstream oss;
oss << "INSERT INTO tasks (create_time,content,comment,parent_id,finished) VALUES (" << createTime << ",\""
<< content << "\",\"" << comment << "\"," << parentId << "," << finished << ");";
auto sql = oss.str();
char *error = nullptr;
int result = sqlite3_exec(m_sqlite3, sql.c_str(), NULL, NULL, &error);
if (result != SQLITE_OK) {
LOG(error) << "add task failed: " << error << ", sql: " << sql;
sqlite3_free(error);
ret = false;
}
return ret;
}
bool Database::removeTask(int id) {
bool ret = true;
std::ostringstream oss;
oss << "DELETE FROM tasks WHERE id = " << id << ";";
auto sql = oss.str();
char *error = nullptr;
int result = sqlite3_exec(m_sqlite3, sql.c_str(), NULL, NULL, &error);
if (result != SQLITE_OK) {
LOG(error) << "add task failed: " << error << ", sql: " << sql;
sqlite3_free(error);
ret = false;
}
return ret;
}
void Database::setTaskFinished(int id, bool finished, uint64_t finishedTime) {
std::ostringstream oss;
oss << "UPDATE tasks SET finished = " << finished << ", finished_time = " << finishedTime << " WHERE id = " << id;
auto sql = oss.str();
int result = sqlite3_exec(m_sqlite3, sql.c_str(), NULL, NULL, NULL);
if (result != SQLITE_OK) {
LOG(error) << "add task failed: " << sqlite3_errmsg(m_sqlite3) << ", sql: " << sql;
return;
}
}
void Database::updateTodayVisitCount(const std::string &url, const std::string &visitorUuid) {
sqlite3_stmt *stmt = nullptr;
const char *sql_select = "SELECT page_view_count FROM today_visit_count WHERE url = ? AND visitor_uuid = ?";
if (sqlite3_prepare_v2(m_sqlite3, sql_select, -1, &stmt, 0) != SQLITE_OK) {
LOG(error) << "Failed to prepare statement: " << sqlite3_errmsg(m_sqlite3);
return;
}
sqlite3_bind_text(stmt, 1, url.c_str(), -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 2, visitorUuid.c_str(), -1, SQLITE_STATIC);
int rc = sqlite3_step(stmt);
if (rc == SQLITE_ROW) { // 记录存在执行UPDATE语句
sqlite3_finalize(stmt); // 释放SELECT语句的资源
const char *sql_update =
"UPDATE today_visit_count SET page_view_count = page_view_count + 1 WHERE url = ? AND visitor_uuid = ?";
if (sqlite3_prepare_v2(m_sqlite3, sql_update, -1, &stmt, 0) != SQLITE_OK) {
LOG(error) << "Failed to prepare statement: " << sqlite3_errmsg(m_sqlite3);
return;
}
sqlite3_bind_text(stmt, 1, url.c_str(), -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 2, visitorUuid.c_str(), -1, SQLITE_STATIC);
if (sqlite3_step(stmt) != SQLITE_DONE) {
LOG(error) << "Failed to execute statement: " << sqlite3_errmsg(m_sqlite3);
sqlite3_finalize(stmt);
return;
}
} else { // 记录不存在执行INSERT语句
sqlite3_finalize(stmt); // 释放SELECT语句的资源
const char *sql_insert = "INSERT INTO today_visit_count (url, visitor_uuid, page_view_count) VALUES (?, ?, 1)";
if (sqlite3_prepare_v2(m_sqlite3, sql_insert, -1, &stmt, 0) != SQLITE_OK) {
LOG(error) << "Failed to prepare statement: " << sqlite3_errmsg(m_sqlite3);
return;
}
sqlite3_bind_text(stmt, 1, url.c_str(), -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 2, visitorUuid.c_str(), -1, SQLITE_STATIC);
if (sqlite3_step(stmt) != SQLITE_DONE) { // 执行INSERT语句
LOG(error) << "Failed to execute statement: " << sqlite3_errmsg(m_sqlite3);
sqlite3_finalize(stmt);
return;
}
}
// 释放语句资源
sqlite3_finalize(stmt);
}
void Database::clearTodayVisitRecord() {
char *message = nullptr;
constexpr auto sql = "DELETE FROM today_visit_count";
int rc = sqlite3_exec(m_sqlite3, sql, nullptr, nullptr, &message);
if (rc != SQLITE_OK) {
LOG(error) << "SQL error: " << message;
sqlite3_free(message);
}
}
static bool urlExists(sqlite3 *db, const std::string &url) {
std::string sql = "SELECT 1 FROM visit_count WHERE url = '" + url + "'";
sqlite3_stmt *stmt;
int rc = sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, nullptr);
if (rc != SQLITE_OK) {
LOG(error) << "Failed to prepare statement: " << sqlite3_errmsg(db);
return false;
}
rc = sqlite3_step(stmt);
bool exists = (rc == SQLITE_ROW);
sqlite3_finalize(stmt);
return exists;
}
void Database::updateVisitCount() {
constexpr auto sql = "SELECT url, COUNT(DISTINCT visitor_uuid) AS unique_visitors, SUM(page_view_count) AS "
"total_page_views FROM today_visit_count GROUP BY url";
sqlite3_stmt *stmt;
int 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;
}
std::unordered_map<std::string, std::pair<int, int>> urlData;
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
std::string url = reinterpret_cast<const char *>(sqlite3_column_text(stmt, 0));
int uniqueVisitors = sqlite3_column_int(stmt, 1);
int totalPageViews = sqlite3_column_int(stmt, 2);
urlData[url] = {uniqueVisitors, totalPageViews};
}
sqlite3_finalize(stmt);
for (const auto &[url, data] : urlData) {
int uniqueVisitors = data.first;
int totalPageViews = data.second;
std::string updateSql;
if (urlExists(m_sqlite3, url)) {
updateSql = "UPDATE visit_count SET unique_visitor_count = unique_visitor_count + " +
std::to_string(uniqueVisitors) + ", page_view_count = page_view_count + " +
std::to_string(totalPageViews) + " WHERE url = '" + url + "'";
} else {
updateSql = "INSERT INTO visit_count (url, unique_visitor_count, page_view_count) VALUES ('" + url + "', " +
std::to_string(uniqueVisitors) + ", " + std::to_string(totalPageViews) + ")";
}
char *message = nullptr;
LOG(info) << updateSql;
rc = sqlite3_exec(m_sqlite3, updateSql.c_str(), nullptr, nullptr, &message);
if (rc != SQLITE_OK) {
LOG(error) << "SQL error: " << message;
sqlite3_free(message);
}
}
}
VisitAnalysis Database::visitAnalysisData(const std::string &url) {
VisitAnalysis ret;
std::string query1 = "SELECT page_view_count, unique_visitor_count FROM visit_count WHERE url = '" + url + "';";
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(m_sqlite3, query1.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
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;
}
std::string query2 =
"SELECT SUM(page_view_count), COUNT(DISTINCT visitor_uuid) FROM today_visit_count WHERE url = '" + url + "';";
if (sqlite3_prepare_v2(m_sqlite3, query2.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
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;
}
bool Database::addHomeBoxItem(const std::string &name, const std::string &location, int cost) {
bool ret = true;
std::ostringstream oss;
oss << "INSERT INTO homebox (name,location,cost) VALUES (\"" << name << "\",\"" << location << "\"," << cost
<< ");";
auto sql = oss.str();
char *error = nullptr;
int result = sqlite3_exec(m_sqlite3, sql.c_str(), NULL, NULL, &error);
if (result != SQLITE_OK) {
LOG(error) << "add task failed: " << error << ", sql: " << sql;
sqlite3_free(error);
ret = false;
}
return ret;
}
static int selectHomeBoxItemCallback(void *data, int argc, char **argv, char **columnName) {
auto items = reinterpret_cast<HomeBox::Items *>(data);
HomeBox::Item item;
for (int i = 0; i < argc; i++) {
if (argv[i] == nullptr) continue;
if (strcmp(columnName[i], "id") == 0) {
item.id = std::atol(argv[i]);
} else if (strcmp(columnName[i], "name") == 0) {
item.name = argv[i];
} else if (strcmp(columnName[i], "location") == 0) {
item.location = argv[i];
} else if (strcmp(columnName[i], "cost") == 0) {
item.cost = std::atol(argv[i]);
}
}
items->push_back(item);
return 0;
}
HomeBox::Items Database::homeBoxItems() {
HomeBox::Items ret;
char *error = nullptr;
if (sqlite3_exec(m_sqlite3, "select * from homebox", selectHomeBoxItemCallback, &ret, &error) != SQLITE_OK) {
LOG(error) << "sqlite3_exec() failed: " << error << std::endl;
sqlite3_free(error);
}
return ret;
}
void Database::initialize() {
const char *sql =
"CREATE TABLE IF NOT EXISTS tasks (id INTEGER PRIMARY KEY AUTOINCREMENT, create_time INTEGER NOT NULL, "
"parent_id INTEGER, content VARCHAR(512) NOT NULL, comment VARCHAR(512) NOT NULL, finished BOLL, finished_time "
"INTEGER);";
int result = sqlite3_exec(m_sqlite3, sql, NULL, NULL, NULL);
if (result != SQLITE_OK) {
LOG(error) << "Failed to create table: " << sqlite3_errmsg(m_sqlite3);
return;
}
const char *homeBoxSql = "CREATE TABLE IF NOT EXISTS homebox (id INTEGER PRIMARY KEY AUTOINCREMENT, "
"name VARCHAR(512) NOT NULL, location VARCHAR(512) NOT NULL, cost INTEGER);";
result = sqlite3_exec(m_sqlite3, homeBoxSql, NULL, NULL, NULL);
if (result != SQLITE_OK) {
LOG(error) << "Failed to create table: " << sqlite3_errmsg(m_sqlite3);
return;
}
const char *sql_create_visit_count = R"(
CREATE TABLE IF NOT EXISTS visit_count (
id INTEGER NOT NULL PRIMARY KEY,
url TEXT NOT NULL,
page_view_count INTEGER NOT NULL,
unique_visitor_count INTEGER NOT NULL
);
)";
char *message = nullptr;
result = sqlite3_exec(m_sqlite3, sql_create_visit_count, 0, 0, &message);
if (result != SQLITE_OK) {
LOG(error) << "Failed to create table: " << message << std::endl;
sqlite3_free(message);
}
const char *sql_create_today_visit_count = R"(
CREATE TABLE IF NOT EXISTS today_visit_count (
id INTEGER NOT NULL,
url TEXT NOT NULL,
visitor_uuid TEXT NOT NULL,
page_view_count INTEGER NOT NULL,
PRIMARY KEY (id)
);
)";
result = sqlite3_exec(m_sqlite3, sql_create_today_visit_count, 0, 0, &message);
if (result != SQLITE_OK) {
LOG(error) << "Failed to create table: " << message << std::endl;
sqlite3_free(message);
}
}
Database::~Database() {
if (m_sqlite3 != nullptr) {
sqlite3_close(m_sqlite3);
m_sqlite3 = nullptr;
}
}