文章目录[隐藏]
WordPress高级教程:开发网站用户行为分析工具,通过WordPress程序的代码二次开发实现常用互联网小工具功能
引言:当WordPress遇见数据驱动决策
在当今数字化时代,网站已不仅仅是信息展示的平台,更是企业与用户互动、转化和建立关系的关键枢纽。对于使用WordPress构建的数百万个网站而言,理解访问者如何与网站互动,哪些内容受欢迎,用户在何处流失,以及他们来自何方,已成为优化用户体验、提升转化率和实现业务增长的核心需求。虽然市面上存在Google Analytics、Hotjar等强大的第三方分析工具,但它们往往存在数据隐私顾虑、功能泛化无法完全贴合特定业务场景、以及可能影响网站性能等问题。
本高级教程将深入探讨如何通过WordPress程序的代码二次开发,自主构建一个轻量级、高度定制化的网站用户行为分析工具。这不仅能将数据控制权牢牢掌握在自己手中,还能根据具体业务需求,灵活集成各种实用的互联网小工具功能,如热点图、会话回放、表单分析、自定义事件追踪等,从而打造一个真正“懂你业务”的数据分析中枢。
第一部分:基石构建——设计用户行为数据模型与存储方案
任何分析工具的核心都是数据。在WordPress环境中,我们需要设计一个高效、可扩展的数据模型来捕获和存储用户行为数据。
1.1 定义关键行为事件与数据结构
首先,明确需要追踪哪些用户行为。常见的事件包括:
- 页面浏览: 访问的URL、页面标题、停留时间。
- 点击事件: 被点击元素的ID、类名、文本内容、坐标位置。
- 表单交互: 表单提交、字段聚焦、放弃填写。
- 自定义事件: 如视频播放、文件下载、特定按钮点击(“立即咨询”、“加入购物车”)。
- 会话信息: 用户ID(匿名或已登录)、会话ID、IP地址(需匿名化处理)、用户代理、引荐来源。
在数据库中,我们可以创建自定义表来存储这些数据。虽然WordPress提供了wp_posts和wp_postmeta表,但对于高频写入的行为数据,使用独立的自定义表能获得更优的性能和查询效率。
-- 示例:创建用户会话表
CREATE TABLE wp_my_analytics_sessions (
session_id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT UNSIGNED DEFAULT 0, -- 关联wp_users.ID,0表示匿名用户
anonymous_id VARCHAR(32) NOT NULL, -- 为匿名用户生成唯一标识
ip_address VARCHAR(45) DEFAULT '', -- 存储匿名化后的IP
user_agent TEXT,
referrer VARCHAR(512) DEFAULT '',
landing_page VARCHAR(512) NOT NULL,
start_time DATETIME DEFAULT CURRENT_TIMESTAMP,
end_time DATETIME NULL,
INDEX idx_user_id (user_id),
INDEX idx_anonymous_id (anonymous_id),
INDEX idx_start_time (start_time)
);
-- 示例:创建页面浏览事件表
CREATE TABLE wp_my_analytics_pageviews (
event_id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
session_id BIGINT UNSIGNED NOT NULL,
page_url VARCHAR(2048) NOT NULL,
page_title VARCHAR(512) DEFAULT '',
view_time DATETIME DEFAULT CURRENT_TIMESTAMP,
time_on_page INT UNSIGNED DEFAULT 0, -- 单位:秒
FOREIGN KEY (session_id) REFERENCES wp_my_analytics_sessions(session_id) ON DELETE CASCADE,
INDEX idx_session_id (session_id),
INDEX idx_view_time (view_time)
);
-- 示例:创建点击事件表
CREATE TABLE wp_my_analytics_clicks (
event_id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
session_id BIGINT UNSIGNED NOT NULL,
element_id VARCHAR(255) DEFAULT '',
element_class TEXT,
element_text TEXT,
page_url VARCHAR(2048) NOT NULL,
coordinates_x INT,
coordinates_y INT,
click_time DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (session_id) REFERENCES wp_my_analytics_sessions(session_id) ON DELETE CASCADE
);
1.2 利用WordPress Hook与REST API进行数据采集
数据采集需要在用户端(浏览器)和服务器端(WordPress)协同完成。前端通过JavaScript监听用户行为,后端通过WordPress的Hook和REST API接收并处理数据。
-
前端数据收集脚本: 编写一个非侵入式的JavaScript文件(如
my-analytics-tracker.js),将其排入WordPress。该脚本负责:- 生成或获取匿名用户ID,并关联会话。
- 使用
Performance API或MutationObserver和事件委托来监听页面加载、点击、表单变化等。 - 将行为数据通过
fetchAPI发送到WordPress自定义的REST API端点。 - 注意节流(throttle)和防抖(debounce),避免过高频率的请求。
// 简化的前端追踪示例
(function() {
'use strict';
const endpoint = '/wp-json/my-analytics/v1/track';
let sessionId = getOrCreateSessionId(); // 从Cookie或LocalStorage获取/创建
// 追踪页面浏览
function trackPageView() {
const data = {
type: 'pageview',
session_id: sessionId,
url: window.location.href,
title: document.title,
referrer: document.referrer
};
sendToEndpoint(data);
}
// 追踪点击事件(事件委托到document)
document.addEventListener('click', function(e) {
const target = e.target;
const data = {
type: 'click',
session_id: sessionId,
url: window.location.href,
element: {
id: target.id,
className: target.className,
tagName: target.tagName,
text: target.textContent.substring(0, 200),
x: e.clientX,
y: e.clientY
}
};
sendToEndpoint(data);
}, { passive: true });
// 发送数据到WordPress REST API
function sendToEndpoint(data) {
navigator.sendBeacon(endpoint, JSON.stringify(data)); // 使用sendBeacon,页面卸载时也能可靠发送
// 或者使用fetch
}
// 初始化
trackPageView();
})();
- 后端REST API与数据处理: 在WordPress插件中,注册一个自定义的REST API路由来接收前端发送的数据。
// 在您的插件主文件中
add_action('rest_api_init', function () {
register_rest_route('my-analytics/v1', '/track', array(
'methods' => 'POST',
'callback' => 'my_analytics_handle_track_event',
'permission_callback' => '__return_true', // 注意:此处需添加非验证,但应有安全措施如nonce或限流
));
});
function my_analytics_handle_track_event(WP_REST_Request $request) {
$data = $request->get_json_params();
$event_type = sanitize_text_field($data['type'] ?? '');
$session_id = absint($data['session_id'] ?? 0);
global $wpdb;
$sessions_table = $wpdb->prefix . 'my_analytics_sessions';
$pageviews_table = $wpdb->prefix . 'my_analytics_pageviews';
$clicks_table = $wpdb->prefix . 'my_analytics_clicks';
switch ($event_type) {
case 'pageview':
// 处理页面浏览数据,插入pageviews表,并更新会话的last_activity等
$wpdb->insert($pageviews_table, array(
'session_id' => $session_id,
'page_url' => esc_url_raw($data['url']),
'page_title' => sanitize_text_field($data['title']),
'view_time' => current_time('mysql'),
));
break;
case 'click':
// 处理点击数据
$wpdb->insert($clicks_table, array(
'session_id' => $session_id,
'element_id' => sanitize_text_field($data['element']['id']),
'element_class' => sanitize_textarea_field($data['element']['className']),
'element_text' => sanitize_textarea_field($data['element']['text']),
'page_url' => esc_url_raw($data['url']),
'coordinates_x' => absint($data['element']['x']),
'coordinates_y' => absint($data['element']['y']),
'click_time' => current_time('mysql'),
));
break;
// ... 处理其他事件类型
}
return new WP_REST_Response(array('success' => true), 200);
}
第二部分:功能深化——实现常用互联网分析小工具
有了数据管道,我们就可以在此基础上构建具体的分析功能模块。
2.1 实时仪表盘与核心指标计算
在WordPress后台创建一个分析仪表盘页面,使用add_menu_page或add_submenu_page函数。利用wpdb类查询数据库,计算并展示关键指标:
- 实时在线用户: 统计过去5分钟内有过活动的独立会话数。
- 今日/本月PV/UV: 页面浏览量和独立访客数。
- 平均会话时长: 总会话时长 / 会话数。
- 热门页面: 按PV排序的页面列表。
- 来源分析: 引荐来源域名统计。
可以使用Chart.js或WP自带的图表库进行可视化。为了提高大量数据下的查询性能,应考虑对汇总数据使用定时任务(WP-Cron)进行预计算并存储到缓存或汇总表中。
2.2 点击热图(Heatmap)生成
热图是直观展示用户点击密度的强大工具。实现原理如下:
- 数据收集: 我们已经在前端收集了每次点击的坐标(
clientX, clientY)和页面URL。 - 数据聚合: 在后端,针对特定URL,查询所有点击事件的坐标。
-
坐标标准化: 由于用户屏幕分辨率不同,需要将绝对坐标转换为相对于页面宽度和高度的百分比坐标。更精确的方法是关联收集到的视口(viewport)尺寸。
$normalized_x = ($click_x / $viewport_width) * 100; $normalized_y = ($click_y / $viewport_height) * 100; - 生成热图图层: 使用PHP的GD库或ImageMagick,或者更简单地在前端使用Canvas,根据归一化的坐标和密度,绘制一个半透明的彩色叠加层。也可以使用开源的JavaScript热图库(如
heatmap.js)在后端提供聚合好的数据点,由前端渲染。 - 在后台预览: 在分析工具后台,选择页面URL后,动态生成或加载该页面的热图叠加层图像。
2.3 会话回放(Session Replay)技术浅析
会话回放是更高级的功能,它近乎“录屏”般地重现单个用户的访问过程。实现思路较为复杂:
- 录制阶段: 前端需要记录初始的DOM快照(可通过
document.documentElement.outerHTML获取,但需清理敏感信息),以及后续所有的DOM变更(使用MutationObserver)、用户交互(点击、输入、滚动)、网络请求和console日志。开源库如rrweb提供了成熟的录制解决方案。 - 数据压缩与发送: 录制数据量巨大,需要进行增量快照和压缩,然后分块发送到后端。
- 存储与播放: 后端将序列化的录制数据与会话关联存储。播放时,在后台管理界面中,使用
rrweb-player等播放器库,根据录制数据重建用户会话过程。
2.4 表单分析与转化漏斗
专门追踪表单的交互细节:
- 表单曝光: 记录表单进入视口的时刻。
- 字段交互: 记录每个字段的聚焦、输入、失焦事件。
- 提交与放弃: 记录成功提交和用户离开未提交表单的行为。
- 漏斗可视化: 定义关键步骤(如:访问落地页 -> 点击表单 -> 填写第一个字段 -> ... -> 提交),计算每一步的转化率和流失率。这需要在前端为特定表单和字段添加额外的监听,并在后端建立漏斗模型进行查询分析。
第三部分:高级优化与安全隐私考量
3.1 性能优化策略
- 数据库索引优化: 确保在
session_id,view_time,page_url等常用查询字段上建立了合适的索引。 - 数据采样与聚合: 对于超高流量网站,可以对原始事件数据进行采样(如只记录10%的会话),并建立每小时/每日的聚合汇总表,供大部分报表查询使用,减轻实时查询压力。
- 前端发送优化: 使用
requestIdleCallback在浏览器空闲时发送数据,或使用批量发送(将多个事件打包成一个请求),减少HTTP请求数。 - 缓存应用: 对仪表盘中非实时的汇总数据使用WordPress Transients API或对象缓存进行缓存。
3.2 安全与隐私保护
- 数据安全: 对所有插入数据库的数据使用
$wpdb->prepare或相应的WordPress清理函数(如sanitize_text_field,esc_url_raw)防止SQL注入。REST API端点应验证nonce或实现简单的令牌验证,防止CSRF和恶意数据灌入。 -
用户隐私: 这是重中之重。必须遵循GDPR、CCPA等法规。
- IP匿名化: 存储IP地址前,将其最后一段或两段置零(例如,
192.168.100.123变为192.168.100.0)。 - Cookie/LocalStorage告知: 在网站首次访问时,显示清晰的横幅,告知用户正在收集匿名行为数据用于改善体验,并提供同意或拒绝的选项。
- “请勿追踪”(DNT)尊重: 检查
navigator.doNotTrack标志,如果用户启用,则停止数据收集。 - 敏感信息过滤: 在前端录制或点击收集时,避免收集密码字段、可能包含个人信息的输入框(如邮箱、地址)的内容。可以通过忽略带有特定
data-*属性(如data-sensitive)的元素来实现。 - 数据保留策略: 提供设置选项,允许管理员配置原始数据的保留时长(如30天、90天),并编写相应的定时清理任务。
- IP匿名化: 存储IP地址前,将其最后一段或两段置零(例如,
3.3 扩展性与模块化设计
将整个分析工具设计成模块化插件。核心模块负责数据收集、存储和API。每个分析小工具(如热图、漏斗、回放)作为独立的子模块,可以通过过滤器(Filter)和动作(Action)钩子与核心交互。这样便于后续的功能增删和定制开发。
结论:从工具到洞察,赋能WordPress网站增长
通过本教程的探讨,我们展示了在WordPress生态内进行深度二次开发,构建一个功能全面、以用户为中心的行为分析工具的完整路径。从设计数据模型、搭建采集管道,到实现热图、漏斗等具体功能,再到关注性能、安全与隐私,每一步都体现了WordPress作为开发框架的强大灵活性和开发者对数据价值的深度挖掘能力。
自主开发的分析工具,其最大优势在于与业务逻辑的深度契合。你可以轻松追踪电子商务网站的“加入购物车-结算-支付成功”全流程,可以分析在线教育平台用户的视频观看完成度与测验得分的关系,可以衡量内容网站中哪些“相关文章”推荐真正带来了深度阅读。
这不仅仅是一个技术实现过程,更是一种思维方式的转变——将你的WordPress网站从一个内容管理系统,升级为一个真正的数据驱动型智能业务平台。通过自主收集和分析的一手用户行为数据,你将获得最直接、最真实的用户反馈,从而做出更精准的优化决策,最终驱动网站与业务的持续增长。
请注意: 本文提供的代码示例为概念性演示,用于说明关键技术和流程。在实际开发中,需要更完善的错误处理、安全加固、性能优化和代码组织。建议在开发前充分测试,并在生产环境中谨慎部署。
第四部分:实战开发——构建模块化用户行为分析插件
4.1 插件架构设计与初始化
让我们从零开始构建一个名为"WP Behavior Analytics Pro"的模块化插件。首先创建插件的基本结构:
wp-behavior-analytics-pro/
├── wp-behavior-analytics-pro.php # 主插件文件
├── includes/
│ ├── class-core.php # 核心功能类
│ ├── class-database.php # 数据库操作类
│ ├── class-tracker.php # 前端追踪类
│ ├── class-api.php # REST API处理类
│ └── class-admin.php # 后台管理类
├── modules/ # 功能模块目录
│ ├── heatmap/
│ ├── session-replay/
│ ├── funnel-analysis/
│ └── form-analytics/
├── assets/
│ ├── js/
│ ├── css/
│ └── images/
├── templates/ # 后台模板文件
└── vendor/ # 第三方库
主插件文件初始化:
<?php
/**
* Plugin Name: WP Behavior Analytics Pro
* Plugin URI: https://yourwebsite.com/
* Description: 高级用户行为分析工具,包含热图、会话回放、转化漏斗等功能
* Version: 1.0.0
* Author: Your Name
* License: GPL v2 or later
*/
// 防止直接访问
if (!defined('ABSPATH')) {
exit;
}
// 定义插件常量
define('WBAP_VERSION', '1.0.0');
define('WBAP_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('WBAP_PLUGIN_URL', plugin_dir_url(__FILE__));
define('WBAP_TABLE_PREFIX', 'wbap_');
// 自动加载类文件
spl_autoload_register(function ($class_name) {
if (strpos($class_name, 'WBAP_') === 0) {
$class_file = str_replace('_', '-', strtolower($class_name));
$file_path = WBAP_PLUGIN_DIR . 'includes/class-' . $class_file . '.php';
if (file_exists($file_path)) {
require_once $file_path;
}
}
});
// 初始化插件
add_action('plugins_loaded', 'wbap_init_plugin');
function wbap_init_plugin() {
// 检查依赖
if (!function_exists('wp_get_current_user')) {
require_once(ABSPATH . 'wp-includes/pluggable.php');
}
// 初始化核心组件
$core = WBAP_Core::get_instance();
$core->init();
// 根据设置加载模块
$core->load_modules();
// 国际化支持
load_plugin_textdomain('wbap', false, dirname(plugin_basename(__FILE__)) . '/languages/');
}
4.2 数据库操作类的优化实现
<?php
class WBAP_Database {
private static $instance = null;
private $wpdb;
private $table_prefix;
public static function get_instance() {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct() {
global $wpdb;
$this->wpdb = $wpdb;
$this->table_prefix = $wpdb->prefix . WBAP_TABLE_PREFIX;
}
/**
* 创建或更新数据库表
*/
public function create_tables() {
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
$charset_collate = $this->wpdb->get_charset_collate();
// 会话表
$sessions_table = $this->table_prefix . 'sessions';
$sql = "CREATE TABLE IF NOT EXISTS $sessions_table (
session_id BIGINT UNSIGNED AUTO_INCREMENT,
visitor_id VARCHAR(32) NOT NULL,
user_id BIGINT UNSIGNED DEFAULT 0,
device_id VARCHAR(64),
ip_hash VARCHAR(64) DEFAULT '',
user_agent_hash VARCHAR(64),
browser VARCHAR(50),
os VARCHAR(50),
device_type VARCHAR(20),
screen_resolution VARCHAR(20),
language VARCHAR(10),
country_code CHAR(2),
region VARCHAR(100),
city VARCHAR(100),
referrer_domain VARCHAR(255),
landing_page VARCHAR(2048),
utm_source VARCHAR(255),
utm_medium VARCHAR(255),
utm_campaign VARCHAR(255),
start_time DATETIME DEFAULT CURRENT_TIMESTAMP,
end_time DATETIME NULL,
page_count INT UNSIGNED DEFAULT 1,
session_duration INT UNSIGNED DEFAULT 0,
is_bounce TINYINT(1) DEFAULT 1,
PRIMARY KEY (session_id),
INDEX idx_visitor_id (visitor_id),
INDEX idx_user_id (user_id),
INDEX idx_start_time (start_time),
INDEX idx_device_id (device_id),
INDEX idx_country_code (country_code),
INDEX idx_utm_source (utm_source(20))
) $charset_collate;";
dbDelta($sql);
// 页面浏览表(分区表设计,提高查询性能)
$pageviews_table = $this->table_prefix . 'pageviews';
$sql = "CREATE TABLE IF NOT EXISTS $pageviews_table (
pageview_id BIGINT UNSIGNED AUTO_INCREMENT,
session_id BIGINT UNSIGNED NOT NULL,
page_url VARCHAR(2048) NOT NULL,
page_url_hash VARCHAR(32) NOT NULL,
page_title VARCHAR(512),
page_type VARCHAR(50),
content_id BIGINT UNSIGNED,
content_type VARCHAR(50),
scroll_depth TINYINT UNSIGNED DEFAULT 0,
time_on_page INT UNSIGNED DEFAULT 0,
view_time DATETIME DEFAULT CURRENT_TIMESTAMP,
exit_time DATETIME NULL,
is_exit_page TINYINT(1) DEFAULT 0,
is_entrance_page TINYINT(1) DEFAULT 0,
PRIMARY KEY (pageview_id, view_time),
INDEX idx_session_id (session_id),
INDEX idx_view_time (view_time),
INDEX idx_page_url_hash (page_url_hash),
INDEX idx_content_id (content_id)
) $charset_collate
PARTITION BY RANGE (YEAR(view_time))
SUBPARTITION BY HASH (MONTH(view_time))
SUBPARTITIONS 12 (
PARTITION p2024 VALUES LESS THAN (2025),
PARTITION p2025 VALUES LESS THAN (2026),
PARTITION p2026 VALUES LESS THAN (2027),
PARTITION p_future VALUES LESS THAN MAXVALUE
);";
dbDelta($sql);
// 事件表(存储所有用户交互事件)
$events_table = $this->table_prefix . 'events';
$sql = "CREATE TABLE IF NOT EXISTS $events_table (
event_id BIGINT UNSIGNED AUTO_INCREMENT,
session_id BIGINT UNSIGNED NOT NULL,
event_type VARCHAR(50) NOT NULL,
event_category VARCHAR(100),
event_action VARCHAR(255),
event_label VARCHAR(512),
event_value DECIMAL(10,2),
element_data JSON,
page_url VARCHAR(2048),
event_time DATETIME DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (event_id, event_time),
INDEX idx_session_id (session_id),
INDEX idx_event_type (event_type),
INDEX idx_event_time (event_time),
INDEX idx_event_category (event_category(20)),
INDEX idx_event_action (event_action(50))
) $charset_collate
PARTITION BY RANGE (YEAR(event_time))
SUBPARTITION BY HASH (MONTH(event_time))
SUBPARTITIONS 12 (
PARTITION p2024 VALUES LESS THAN (2025),
PARTITION p2025 VALUES LESS THAN (2026),
PARTITION p2026 VALUES LESS THAN (2027),
PARTITION p_future VALUES LESS THAN MAXVALUE
);";
dbDelta($sql);
// 聚合表(预计算常用指标,提高查询性能)
$aggregates_table = $this->table_prefix . 'daily_aggregates';
$sql = "CREATE TABLE IF NOT EXISTS $aggregates_table (
aggregate_date DATE NOT NULL,
metric_name VARCHAR(100) NOT NULL,
dimension_1 VARCHAR(255),
dimension_2 VARCHAR(255),
dimension_3 VARCHAR(255),
value_1 DECIMAL(15,4),
value_2 DECIMAL(15,4),
value_3 DECIMAL(15,4),
count_1 BIGINT UNSIGNED,
count_2 BIGINT UNSIGNED,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (aggregate_date, metric_name, dimension_1(100), dimension_2(100)),
INDEX idx_metric_date (metric_name, aggregate_date),
INDEX idx_dimension_1 (dimension_1(50))
) $charset_collate;";
dbDelta($sql);
}
/**
* 插入会话数据
*/
public function insert_session($data) {
$table = $this->table_prefix . 'sessions';
// 数据验证和清理
$clean_data = $this->sanitize_session_data($data);
// 使用INSERT ... ON DUPLICATE KEY UPDATE处理重复会话
$result = $this->wpdb->replace($table, $clean_data);
if ($result === false) {
error_log('WBAP: Failed to insert session data - ' . $this->wpdb->last_error);
return false;
}
return $this->wpdb->insert_id;
}
/**
* 批量插入事件数据(提高性能)
*/
public function bulk_insert_events($events) {
if (empty($events)) {
return true;
}
$table = $this->table_prefix . 'events';
$values = [];
$placeholders = [];
$data = [];
foreach ($events as $event) {
$placeholders[] = "(%d, %s, %s, %s, %s, %f, %s, %s, %s)";
$data[] = $event['session_id'];
$data[] = sanitize_text_field($event['event_type']);
$data[] = sanitize_text_field($event['event_category'] ?? '');
$data[] = sanitize_text_field($event['event_action'] ?? '');
$data[] = sanitize_text_field($event['event_label'] ?? '');
$data[] = floatval($event['event_value'] ?? 0);
$data[] = wp_json_encode($event['element_data'] ?? []);
$data[] = esc_url_raw($event['page_url'] ?? '');
$data[] = current_time('mysql');
}
$query = "INSERT INTO $table
(session_id, event_type, event_category, event_action, event_label,
event_value, element_data, page_url, event_time)
VALUES " . implode(', ', $placeholders);
$this->wpdb->query($this->wpdb->prepare($query, $data));
return $this->wpdb->rows_affected > 0;
}
/**
* 获取实时在线用户
*/
public function get_realtime_visitors($minutes = 5) {
$table = $this->table_prefix . 'sessions';
$cutoff_time = date('Y-m-d H:i:s', strtotime("-$minutes minutes"));
$query = $this->wpdb->prepare(
"SELECT COUNT(DISTINCT session_id) as count,
COUNT(DISTINCT visitor_id) as unique_visitors,
COUNT(DISTINCT user_id) as logged_in_users
FROM $table
WHERE end_time IS NULL
OR end_time > %s",
$cutoff_time
);
return $this->wpdb->get_row($query, ARRAY_A);
}
/**
* 获取热门页面
*/
public function get_top_pages($limit = 10, $date_range = '7days') {
$pageviews_table = $this->table_prefix . 'pageviews';
$date_condition = $this->get_date_condition($date_range, 'view_time');
$query = $this->wpdb->prepare(
"SELECT page_url,
page_title,
COUNT(*) as pageviews,
COUNT(DISTINCT session_id) as unique_visitors,
AVG(time_on_page) as avg_time_on_page,
SUM(CASE WHEN is_exit_page = 1 THEN 1 ELSE 0 END) as exits
FROM $pageviews_table
WHERE $date_condition
GROUP BY page_url_hash
ORDER BY pageviews DESC
LIMIT %d",
$limit
);
return $this->wpdb->get_results($query, ARRAY_A);
}
/**
* 更新聚合数据
*/
public function update_aggregates($date = null) {
if (!$date) {
$date = date('Y-m-d', strtotime('-1 day'));
}
$this->update_daily_traffic_aggregates($date);
$this->update_page_performance_aggregates($date);
$this->update_user_behavior_aggregates($date);
}
private function update_daily_traffic_aggregates($date) {
$sessions_table = $this->table_prefix . 'sessions';
$aggregates_table = $this->table_prefix . 'daily_aggregates';
// 删除旧数据
$this->wpdb->delete($aggregates_table, [
'aggregate_date' => $date,
'metric_name' => 'daily_traffic'
]);
$query = $this->wpdb->prepare(
"INSERT INTO $aggregates_table
(aggregate_date, metric_name, dimension_1, value_1, count_1)
SELECT
DATE(start_time) as aggregate_date,
'daily_traffic' as metric_name,
'sessions' as dimension_1,
COUNT(*) as value_1,
COUNT(DISTINCT visitor_id) as count_1
FROM $sessions_table
WHERE DATE(start_time) = %s
GROUP BY DATE(start_time)",
$date
);
$this->wpdb->query($query);
}
}
4.3 高级前端追踪器的实现
// assets/js/wbap-tracker.js
(function(window, document) {
'use strict';
class WBAPTracker {
constructor(config) {
this.config = {
endpoint: '/wp-json/wbap/v1/track',
batchSize: 10,
batchTimeout: 5000,
sampleRate: 1.0,
respectDNT: true,
...config
};
this.queue = [];
this.isSending = false;
this.sessionId = null;
this.visitorId = null;
this.currentPage = null;
this.lastActivity = Date.now();
this.idleTimeout = 30000; // 30秒无操作视为空闲
this.init();
}
init() {
// 检查DNT
if (this.config.respectDNT && navigator.doNotTrack === '1') {
console.log('WBAP: Do Not Track is enabled, tracking disabled.');
return;
}
// 获取或创建访客ID和会话ID
this.visitorId = this.getVisitorId();
this.sessionId = this.generateSessionId();
// 设置页面卸载前的数据发送
this.setupBeforeUnload();
// 开始追踪
this.trackPageView();
this.setupEventListeners();
this.setupPerformanceTracking();
this.setupIdleDetection();
// 启动批量发送定时器
setInterval(() => this.flushQueue(), this.config.batchTimeout);
}
getVisitorId() {
// 尝试从Cookie获取
let visitorId = this.getCookie('_wbap_vid');
if (!visitorId) {
// 生成新的访客ID
visitorId = this.generateUUID();
this.setCookie('_wbap_vid', visitorId, 365 * 2); // 保存2年
}
return visitorId;
}
generateSessionId() {
const sessionId = this.generateUUID();
this.setCookie('_wbap_sid', sessionId, 0.5); // 会话cookie,浏览器关闭时过期
return sessionId;
}
generateUUID() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
const r = Math.random() * 16 | 0;
const v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
trackPageView() {
const pageData = {
event_type: 'pageview',
session_id: this.sessionId,
visitor_id: this.visitorId,
page_url: window.location.href,
page_title: document.title,
referrer: document.referrer,
viewport_size: {
width: window.innerWidth,
height: window.innerHeight
},
screen_size: {
width: screen.width,
height: screen.height
},
timestamp: Date.now()
};
// 收集性能数据
if ('performance' in window && 'timing' in performance) {
const perf = performance.timing;
pageData.performance = {
dns_lookup: perf.domainLookupEnd - perf.domainLookupStart,
tcp_connect: perf.connectEnd - perf.connectStart,
request_response: perf.responseEnd - perf.requestStart,
dom_loading: perf.domContentLoadedEventStart - perf.domLoading,
