Older/Server/Database/Database.cpp
amass 2b45dad26e
All checks were successful
Deploy / PullDocker (push) Successful in 12s
Deploy / Build (push) Successful in 53s
add extra info for analysis.
2024-07-31 22:28:05 +08:00

290 lines
11 KiB
C++

#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::updateVisitCount(const std::string &url, const std::string &visitorUuid, const std::string &userAgent,
int64_t time) {
sqlite3_stmt *stmt;
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) {
LOG(error) << "Failed to prepare statement: " << sqlite3_errmsg(m_sqlite3);
}
sqlite3_bind_text(stmt, 1, url.c_str(), -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 2, visitorUuid.c_str(), -1, SQLITE_STATIC);
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);
}
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);
if (sqlite3_step(stmt) != SQLITE_DONE) {
LOG(error) << "Failed to update record: " << sqlite3_errmsg(m_sqlite3);
}
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);
}
sqlite3_bind_text(stmt, 1, url.c_str(), -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 2, visitorUuid.c_str(), -1, SQLITE_STATIC);
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;
}
sqlite3_finalize(stmt);
}
}
void Database::clearVisitRecord() {
char *message = nullptr;
constexpr auto sql = "DELETE FROM visit_analysis";
int rc = sqlite3_exec(m_sqlite3, sql, nullptr, nullptr, &message);
if (rc != SQLITE_OK) {
LOG(error) << "SQL error: " << message;
sqlite3_free(message);
}
}
VisitAnalysis Database::siteVisitAnalysisData() {
VisitAnalysis ret;
sqlite3_stmt *stmt;
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) {
LOG(error) << "Failed to prepare statement: " << sqlite3_errmsg(m_sqlite3);
return ret;
}
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);
}
sqlite3_finalize(stmt);
return ret;
}
VisitAnalysis Database::visitAnalysisData(const std::string &url) {
VisitAnalysis ret;
sqlite3_stmt *stmt;
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) {
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;
}
char *message = nullptr;
sql = 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
);
)";
result = sqlite3_exec(m_sqlite3, sql, 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;
}
}