add site visit analysis.
This commit is contained in:
parent
9c47e0f2c5
commit
fee13aebe7
11
Readme.md
11
Readme.md
@ -2,16 +2,7 @@
|
|||||||
|
|
||||||
### 访问统计
|
### 访问统计
|
||||||
|
|
||||||
表名:visit_count
|
表名:visit_analysis
|
||||||
|
|
||||||
| 字段名 | 数据类型 | 空值约束 | 约束条件 |
|
|
||||||
| -------------------- | -------- | -------- | ----------- |
|
|
||||||
| 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
|
|
||||||
|
|
||||||
| 字段名 | 数据类型 | 空值约束 | PRIMARY KEY |
|
| 字段名 | 数据类型 | 空值约束 | PRIMARY KEY |
|
||||||
| --------------- | -------- | -------- | ----------- |
|
| --------------- | -------- | -------- | ----------- |
|
||||||
|
@ -119,7 +119,7 @@ Application::Application(const std::string &path)
|
|||||||
session.reply(
|
session.reply(
|
||||||
ServiceLogic::make_200<boost::beast::http::string_body>(request, "notify successed.\n", "text/html"));
|
ServiceLogic::make_200<boost::beast::http::string_body>(request, "notify successed.\n", "text/html"));
|
||||||
});
|
});
|
||||||
|
|
||||||
m_router->insert("/api/v1/visit_analysis", [this](HttpSession &session, const Request &request,
|
m_router->insert("/api/v1/visit_analysis", [this](HttpSession &session, const Request &request,
|
||||||
const boost::urls::matches &matches) {
|
const boost::urls::matches &matches) {
|
||||||
using namespace boost::beast;
|
using namespace boost::beast;
|
||||||
@ -135,12 +135,16 @@ Application::Application(const std::string &path)
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto database = Amass::Singleton<Database>::instance();
|
auto database = Amass::Singleton<Database>::instance();
|
||||||
database->updateTodayVisitCount(url, visitorUuid);
|
database->updateVisitCount(url, visitorUuid);
|
||||||
auto data = database->visitAnalysisData(std::string(url));
|
auto data = database->visitAnalysisData(std::string(url));
|
||||||
|
auto total = database->siteVisitAnalysisData();
|
||||||
|
|
||||||
boost::json::object reply;
|
boost::json::object reply;
|
||||||
reply["page_view_count"] = data.pageViewCount;
|
reply["page_view_count"] = data.pageViewCount;
|
||||||
reply["unique_visitor_count"] = data.uniqueVisitorCount;
|
reply["unique_visitor_count"] = data.uniqueVisitorCount;
|
||||||
|
reply["site_page_view_count"] = total.pageViewCount;
|
||||||
|
reply["site_unique_visitor_count"] = total.uniqueVisitorCount;
|
||||||
|
|
||||||
http::response<boost::beast::http::string_body> s{boost::beast::http::status::ok, request.version()};
|
http::response<boost::beast::http::string_body> s{boost::beast::http::status::ok, request.version()};
|
||||||
s.set(http::field::server, BOOST_BEAST_VERSION_STRING);
|
s.set(http::field::server, BOOST_BEAST_VERSION_STRING);
|
||||||
s.set(http::field::content_type, "application/json;charset=UTF-8");
|
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));
|
session.reply(std::move(s));
|
||||||
});
|
});
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
m_ioContext = Amass::Singleton<IoContext>::instance<Amass::Construct>(getThreads());
|
m_ioContext = Amass::Singleton<IoContext>::instance<Amass::Construct>(getThreads());
|
||||||
m_timer = std::make_shared<boost::asio::system_timer>(*m_ioContext->ioContext());
|
m_timer = std::make_shared<boost::asio::system_timer>(*m_ioContext->ioContext());
|
||||||
|
|
||||||
|
@ -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;
|
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) {
|
if (sqlite3_prepare_v2(m_sqlite3, sql_select, -1, &stmt, 0) != SQLITE_OK) {
|
||||||
LOG(error) << "Failed to prepare statement: " << sqlite3_errmsg(m_sqlite3);
|
LOG(error) << "Failed to prepare statement: " << sqlite3_errmsg(m_sqlite3);
|
||||||
return;
|
return;
|
||||||
@ -119,7 +119,7 @@ void Database::updateTodayVisitCount(const std::string &url, const std::string &
|
|||||||
if (rc == SQLITE_ROW) { // 记录存在,执行UPDATE语句
|
if (rc == SQLITE_ROW) { // 记录存在,执行UPDATE语句
|
||||||
sqlite3_finalize(stmt); // 释放SELECT语句的资源
|
sqlite3_finalize(stmt); // 释放SELECT语句的资源
|
||||||
const char *sql_update =
|
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) {
|
if (sqlite3_prepare_v2(m_sqlite3, sql_update, -1, &stmt, 0) != SQLITE_OK) {
|
||||||
LOG(error) << "Failed to prepare statement: " << sqlite3_errmsg(m_sqlite3);
|
LOG(error) << "Failed to prepare statement: " << sqlite3_errmsg(m_sqlite3);
|
||||||
return;
|
return;
|
||||||
@ -133,7 +133,7 @@ void Database::updateTodayVisitCount(const std::string &url, const std::string &
|
|||||||
}
|
}
|
||||||
} else { // 记录不存在,执行INSERT语句
|
} else { // 记录不存在,执行INSERT语句
|
||||||
sqlite3_finalize(stmt); // 释放SELECT语句的资源
|
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) {
|
if (sqlite3_prepare_v2(m_sqlite3, sql_insert, -1, &stmt, 0) != SQLITE_OK) {
|
||||||
LOG(error) << "Failed to prepare statement: " << sqlite3_errmsg(m_sqlite3);
|
LOG(error) << "Failed to prepare statement: " << sqlite3_errmsg(m_sqlite3);
|
||||||
return;
|
return;
|
||||||
@ -150,9 +150,9 @@ void Database::updateTodayVisitCount(const std::string &url, const std::string &
|
|||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Database::clearTodayVisitRecord() {
|
void Database::clearVisitRecord() {
|
||||||
char *message = nullptr;
|
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);
|
int rc = sqlite3_exec(m_sqlite3, sql, nullptr, nullptr, &message);
|
||||||
if (rc != SQLITE_OK) {
|
if (rc != SQLITE_OK) {
|
||||||
LOG(error) << "SQL error: " << message;
|
LOG(error) << "SQL error: " << message;
|
||||||
@ -160,85 +160,36 @@ void Database::clearTodayVisitRecord() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool urlExists(sqlite3 *db, const std::string &url) {
|
VisitAnalysis Database::siteVisitAnalysisData() {
|
||||||
std::string sql = "SELECT 1 FROM visit_count WHERE url = '" + url + "'";
|
VisitAnalysis ret;
|
||||||
sqlite3_stmt *stmt;
|
sqlite3_stmt *stmt;
|
||||||
int rc = sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, nullptr);
|
const char *sql = "SELECT COUNT(DISTINCT visitor_uuid) as unique_visitors, SUM(page_view_count) as "
|
||||||
if (rc != SQLITE_OK) {
|
"total_page_views FROM visit_analysis";
|
||||||
LOG(error) << "Failed to prepare statement: " << sqlite3_errmsg(db);
|
if (sqlite3_prepare_v2(m_sqlite3, sql, -1, &stmt, nullptr) != SQLITE_OK) {
|
||||||
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);
|
LOG(error) << "Failed to prepare statement: " << sqlite3_errmsg(m_sqlite3);
|
||||||
return;
|
return ret;
|
||||||
}
|
}
|
||||||
|
if (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||||
std::unordered_map<std::string, std::pair<int, int>> urlData;
|
ret.uniqueVisitorCount = sqlite3_column_int(stmt, 0);
|
||||||
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
|
ret.pageViewCount = sqlite3_column_int(stmt, 1);
|
||||||
std::string url = reinterpret_cast<const char *>(sqlite3_column_text(stmt, 0));
|
} else {
|
||||||
int uniqueVisitors = sqlite3_column_int(stmt, 1);
|
LOG(error) << "Failed to execute query: " << sqlite3_errmsg(m_sqlite3);
|
||||||
int totalPageViews = sqlite3_column_int(stmt, 2);
|
|
||||||
urlData[url] = {uniqueVisitors, totalPageViews};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
for (const auto &[url, data] : urlData) {
|
return ret;
|
||||||
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 Database::visitAnalysisData(const std::string &url) {
|
||||||
VisitAnalysis ret;
|
VisitAnalysis ret;
|
||||||
std::string query1 = "SELECT page_view_count, unique_visitor_count FROM visit_count WHERE url = '" + url + "';";
|
|
||||||
sqlite3_stmt *stmt;
|
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) {
|
if (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||||
ret.pageViewCount = sqlite3_column_int(stmt, 0);
|
ret.pageViewCount = sqlite3_column_int(stmt, 0);
|
||||||
ret.uniqueVisitorCount = sqlite3_column_int(stmt, 1);
|
ret.uniqueVisitorCount = sqlite3_column_int(stmt, 1);
|
||||||
}
|
}
|
||||||
sqlite3_finalize(stmt);
|
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 {
|
} else {
|
||||||
LOG(error) << "Failed to execute query: " << sqlite3_errmsg(m_sqlite3);
|
LOG(error) << "Failed to execute query: " << sqlite3_errmsg(m_sqlite3);
|
||||||
}
|
}
|
||||||
@ -309,24 +260,9 @@ void Database::initialize() {
|
|||||||
return;
|
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;
|
char *message = nullptr;
|
||||||
result = sqlite3_exec(m_sqlite3, sql_create_visit_count, 0, 0, &message);
|
const char *sql_create_visit_analysis = R"(
|
||||||
if (result != SQLITE_OK) {
|
CREATE TABLE IF NOT EXISTS visit_analysis (
|
||||||
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,
|
id INTEGER NOT NULL,
|
||||||
url TEXT NOT NULL,
|
url TEXT NOT NULL,
|
||||||
visitor_uuid TEXT NOT NULL,
|
visitor_uuid TEXT NOT NULL,
|
||||||
@ -334,7 +270,7 @@ void Database::initialize() {
|
|||||||
PRIMARY KEY (id)
|
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) {
|
if (result != SQLITE_OK) {
|
||||||
LOG(error) << "Failed to create table: " << message << std::endl;
|
LOG(error) << "Failed to create table: " << message << std::endl;
|
||||||
sqlite3_free(message);
|
sqlite3_free(message);
|
||||||
|
@ -27,10 +27,10 @@ public:
|
|||||||
bool removeTask(int id);
|
bool removeTask(int id);
|
||||||
void setTaskFinished(int id, bool finished, uint64_t finishedTime);
|
void setTaskFinished(int id, bool finished, uint64_t finishedTime);
|
||||||
|
|
||||||
void updateTodayVisitCount(const std::string &url, const std::string &visitorUuid);
|
void updateVisitCount(const std::string &url, const std::string &visitorUuid);
|
||||||
void clearTodayVisitRecord();
|
void clearVisitRecord();
|
||||||
void updateVisitCount();
|
|
||||||
VisitAnalysis visitAnalysisData(const std::string &url);
|
VisitAnalysis visitAnalysisData(const std::string &url);
|
||||||
|
VisitAnalysis siteVisitAnalysisData();
|
||||||
|
|
||||||
HomeBox::Items homeBoxItems();
|
HomeBox::Items homeBoxItems();
|
||||||
bool addHomeBoxItem(const std::string &name, const std::string &location, int cost);
|
bool addHomeBoxItem(const std::string &name, const std::string &location, int cost);
|
||||||
|
@ -24,10 +24,8 @@ BOOST_AUTO_TEST_CASE(DatabaseTest) {
|
|||||||
auto now = duration_cast<seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
auto now = duration_cast<seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||||
database.setTaskFinished(1, true, now);
|
database.setTaskFinished(1, true, now);
|
||||||
|
|
||||||
database.updateTodayVisitCount("/note", "uuid_123");
|
database.updateVisitCount("/note", "uuid_123");
|
||||||
database.updateTodayVisitCount("/note/1", "uuid_1232");
|
database.updateVisitCount("/note/1", "uuid_1232");
|
||||||
database.updateTodayVisitCount("/note", "uuid_123");
|
database.updateVisitCount("/note", "uuid_123");
|
||||||
database.updateTodayVisitCount("/note", "uuid_1234");
|
database.updateVisitCount("/note", "uuid_1234");
|
||||||
|
|
||||||
database.updateVisitCount();
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user