#include "Database.h" #include "BoostLog.h" #include #include 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(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 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> urlData; while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) { std::string url = reinterpret_cast(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(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; } }