diff --git a/Readme.md b/Readme.md index 0ae6936..e5d8c29 100644 --- a/Readme.md +++ b/Readme.md @@ -2,16 +2,7 @@ ### 访问统计 -表名:visit_count - -| 字段名 | 数据类型 | 空值约束 | 约束条件 | -| -------------------- | -------- | -------- | ----------- | -| id | INTEGER | NOT NULL | PRIMARY KEY | -| url | TEXT | NOT NULL | | -| page_view_count | INTEGER | NOT NULL | | -| unique_visitor_count | INTEGER | NOT NULL | | - -表名:today_visit_count +表名:visit_analysis | 字段名 | 数据类型 | 空值约束 | PRIMARY KEY | | --------------- | -------- | -------- | ----------- | diff --git a/Server/Application.cpp b/Server/Application.cpp index e65a20b..a45aedb 100644 --- a/Server/Application.cpp +++ b/Server/Application.cpp @@ -119,7 +119,7 @@ Application::Application(const std::string &path) session.reply( ServiceLogic::make_200(request, "notify successed.\n", "text/html")); }); - + m_router->insert("/api/v1/visit_analysis", [this](HttpSession &session, const Request &request, const boost::urls::matches &matches) { using namespace boost::beast; @@ -135,12 +135,16 @@ Application::Application(const std::string &path) } auto database = Amass::Singleton::instance(); - database->updateTodayVisitCount(url, visitorUuid); + database->updateVisitCount(url, visitorUuid); auto data = database->visitAnalysisData(std::string(url)); + auto total = database->siteVisitAnalysisData(); boost::json::object reply; reply["page_view_count"] = data.pageViewCount; reply["unique_visitor_count"] = data.uniqueVisitorCount; + reply["site_page_view_count"] = total.pageViewCount; + reply["site_unique_visitor_count"] = total.uniqueVisitorCount; + http::response s{boost::beast::http::status::ok, request.version()}; s.set(http::field::server, BOOST_BEAST_VERSION_STRING); s.set(http::field::content_type, "application/json;charset=UTF-8"); @@ -150,7 +154,7 @@ Application::Application(const std::string &path) session.reply(std::move(s)); }); // clang-format on - + m_ioContext = Amass::Singleton::instance(getThreads()); m_timer = std::make_shared(*m_ioContext->ioContext()); diff --git a/Server/Database/Database.cpp b/Server/Database/Database.cpp index d236292..b320baf 100644 --- a/Server/Database/Database.cpp +++ b/Server/Database/Database.cpp @@ -106,9 +106,9 @@ void Database::setTaskFinished(int id, bool finished, uint64_t finishedTime) { } } -void Database::updateTodayVisitCount(const std::string &url, const std::string &visitorUuid) { +void Database::updateVisitCount(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 = ?"; + const char *sql_select = "SELECT page_view_count FROM visit_analysis 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; @@ -119,7 +119,7 @@ void Database::updateTodayVisitCount(const std::string &url, const std::string & 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 = ?"; + "UPDATE visit_analysis 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; @@ -133,7 +133,7 @@ void Database::updateTodayVisitCount(const std::string &url, const std::string & } } else { // 记录不存在,执行INSERT语句 sqlite3_finalize(stmt); // 释放SELECT语句的资源 - const char *sql_insert = "INSERT INTO today_visit_count (url, visitor_uuid, page_view_count) VALUES (?, ?, 1)"; + const char *sql_insert = "INSERT INTO visit_analysis (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; @@ -150,9 +150,9 @@ void Database::updateTodayVisitCount(const std::string &url, const std::string & sqlite3_finalize(stmt); } -void Database::clearTodayVisitRecord() { +void Database::clearVisitRecord() { char *message = nullptr; - constexpr auto sql = "DELETE FROM today_visit_count"; + 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; @@ -160,85 +160,36 @@ void Database::clearTodayVisitRecord() { } } -static bool urlExists(sqlite3 *db, const std::string &url) { - std::string sql = "SELECT 1 FROM visit_count WHERE url = '" + url + "'"; +VisitAnalysis Database::siteVisitAnalysisData() { + VisitAnalysis ret; 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) { + 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; + return ret; } - - 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}; + 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); - 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); - } - } + return ret; } 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) { + 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; - } - - 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); } @@ -309,24 +260,9 @@ void Database::initialize() { 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 ( + const char *sql_create_visit_analysis = R"( + CREATE TABLE IF NOT EXISTS visit_analysis ( id INTEGER NOT NULL, url TEXT NOT NULL, visitor_uuid TEXT NOT NULL, @@ -334,7 +270,7 @@ void Database::initialize() { PRIMARY KEY (id) ); )"; - result = sqlite3_exec(m_sqlite3, sql_create_today_visit_count, 0, 0, &message); + result = sqlite3_exec(m_sqlite3, sql_create_visit_analysis, 0, 0, &message); if (result != SQLITE_OK) { LOG(error) << "Failed to create table: " << message << std::endl; sqlite3_free(message); diff --git a/Server/Database/Database.h b/Server/Database/Database.h index c8131b2..96d1ae0 100644 --- a/Server/Database/Database.h +++ b/Server/Database/Database.h @@ -27,10 +27,10 @@ public: bool removeTask(int id); void setTaskFinished(int id, bool finished, uint64_t finishedTime); - void updateTodayVisitCount(const std::string &url, const std::string &visitorUuid); - void clearTodayVisitRecord(); - void updateVisitCount(); + void updateVisitCount(const std::string &url, const std::string &visitorUuid); + void clearVisitRecord(); VisitAnalysis visitAnalysisData(const std::string &url); + VisitAnalysis siteVisitAnalysisData(); HomeBox::Items homeBoxItems(); bool addHomeBoxItem(const std::string &name, const std::string &location, int cost); diff --git a/UnitTest/DatabaseTest.cpp b/UnitTest/DatabaseTest.cpp index f378947..666ecf4 100644 --- a/UnitTest/DatabaseTest.cpp +++ b/UnitTest/DatabaseTest.cpp @@ -24,10 +24,8 @@ BOOST_AUTO_TEST_CASE(DatabaseTest) { auto now = duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); database.setTaskFinished(1, true, now); - database.updateTodayVisitCount("/note", "uuid_123"); - database.updateTodayVisitCount("/note/1", "uuid_1232"); - database.updateTodayVisitCount("/note", "uuid_123"); - database.updateTodayVisitCount("/note", "uuid_1234"); - - database.updateVisitCount(); + database.updateVisitCount("/note", "uuid_123"); + database.updateVisitCount("/note/1", "uuid_1232"); + database.updateVisitCount("/note", "uuid_123"); + database.updateVisitCount("/note", "uuid_1234"); } \ No newline at end of file