diff --git a/Server/Application.cpp b/Server/Application.cpp index 9785613..e25a32f 100644 --- a/Server/Application.cpp +++ b/Server/Application.cpp @@ -194,6 +194,40 @@ Application::Application(const std::string &path) session.reply(std::move(s)); }); + m_router->insert("/api/v1/latest_viewed_urls", [this](HttpSession &session, const Request &request, + const boost::urls::matches &matches) { + using namespace boost::beast; + int size = 5; + std::error_code error; + if (!request.body().empty()) { + auto rootJson = boost::json::parse(request.body(), error); + if (error) { + LOG(info) << "<" << request.body() << "> parse json error: " << error.message(); + } else { + auto &root = rootJson.as_object(); + if (root.contains("size")) { + size = root.at("size").as_int64(); + } + } + } + + auto data = Amass::Singleton::instance()->latestViewedUrls(size); + boost::json::array reply; + for (auto &d : data) { + boost::json::object object; + object["url"] = d.url; + object["time"] = d.lastViewTime; + reply.push_back(std::move(object)); + } + 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"); + s.keep_alive(request.keep_alive()); + s.body() = boost::json::serialize(reply); + s.prepare_payload(); + session.reply(std::move(s)); + }); + 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 a9adaa3..5c1fc93 100644 --- a/Server/Database/Database.cpp +++ b/Server/Database/Database.cpp @@ -186,13 +186,14 @@ VisitAnalysis Database::siteVisitAnalysisData() { return ret; } +static std::vector urlFilter = { + "/", + "/LoginPage", + "/我的笔记", + "/我的博客", +}; + std::list Database::mostViewedUrls(int size) { - std::vector filter = { - "/", - "/LoginPage", - "/我的笔记", - "/我的博客", - }; const char *sql = "SELECT url, SUM(page_view_count) AS total_page_view_count " "FROM visit_analysis " "GROUP BY url " @@ -206,13 +207,11 @@ std::list Database::mostViewedUrls(int size) { return ret; } - sqlite3_bind_int(stmt, 1, size + filter.size()); - - // Execute the SQL statement and fetch the results + sqlite3_bind_int(stmt, 1, size + urlFilter.size()); while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) { VisitAnalysis pv; pv.url = reinterpret_cast(sqlite3_column_text(stmt, 0)); - if (std::find(filter.cbegin(), filter.cend(), pv.url) != filter.cend()) continue; + if (std::find(urlFilter.cbegin(), urlFilter.cend(), pv.url) != urlFilter.cend()) continue; pv.pageViewCount = sqlite3_column_int(stmt, 1); ret.push_back(pv); } @@ -225,6 +224,38 @@ std::list Database::mostViewedUrls(int size) { return ret; } +std::list Database::latestViewedUrls(int size) { + std::list 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(sqlite3_column_text(stmt, 0)); + if (std::find(urlFilter.cbegin(), urlFilter.cend(), visit.url) != urlFilter.cend()) continue; + visit.lastViewTime = sqlite3_column_int64(stmt, 1); + ret.push_back(visit); + } + return ret; +} + VisitAnalysis Database::visitAnalysisData(const std::string &url) { VisitAnalysis ret; sqlite3_stmt *stmt; diff --git a/Server/Database/Database.h b/Server/Database/Database.h index b8e9f72..363207c 100644 --- a/Server/Database/Database.h +++ b/Server/Database/Database.h @@ -11,6 +11,7 @@ public: std::string url; int pageViewCount = 0; int uniqueVisitorCount = 0; + int lastViewTime = 0; }; struct sqlite3; @@ -33,6 +34,7 @@ public: VisitAnalysis visitAnalysisData(const std::string &url); VisitAnalysis siteVisitAnalysisData(); std::list mostViewedUrls(int size); + std::list latestViewedUrls(int size); HomeBox::Items homeBoxItems(); bool addHomeBoxItem(const std::string &name, const std::string &location, int cost);