首页 / 应用软件 / WordPress高级教程,开发网站用户行为分析工具

WordPress高级教程,开发网站用户行为分析工具

WordPress高级教程:开发网站用户行为分析工具,通过WordPress程序的代码二次开发实现常用互联网小工具功能

引言:当WordPress遇见数据驱动决策

在当今数字化时代,网站已不仅仅是信息展示的平台,更是企业与用户互动、转化和建立关系的关键枢纽。对于使用WordPress构建的数百万个网站而言,理解访问者如何与网站互动,哪些内容受欢迎,用户在何处流失,以及他们来自何方,已成为优化用户体验、提升转化率和实现业务增长的核心需求。虽然市面上存在Google Analytics、Hotjar等强大的第三方分析工具,但它们往往存在数据隐私顾虑、功能泛化无法完全贴合特定业务场景、以及可能影响网站性能等问题。

本高级教程将深入探讨如何通过WordPress程序的代码二次开发,自主构建一个轻量级、高度定制化的网站用户行为分析工具。这不仅能将数据控制权牢牢掌握在自己手中,还能根据具体业务需求,灵活集成各种实用的互联网小工具功能,如热点图、会话回放、表单分析、自定义事件追踪等,从而打造一个真正“懂你业务”的数据分析中枢。

第一部分:基石构建——设计用户行为数据模型与存储方案

任何分析工具的核心都是数据。在WordPress环境中,我们需要设计一个高效、可扩展的数据模型来捕获和存储用户行为数据。

1.1 定义关键行为事件与数据结构

首先,明确需要追踪哪些用户行为。常见的事件包括:

  • 页面浏览: 访问的URL、页面标题、停留时间。
  • 点击事件: 被点击元素的ID、类名、文本内容、坐标位置。
  • 表单交互: 表单提交、字段聚焦、放弃填写。
  • 自定义事件: 如视频播放、文件下载、特定按钮点击(“立即咨询”、“加入购物车”)。
  • 会话信息: 用户ID(匿名或已登录)、会话ID、IP地址(需匿名化处理)、用户代理、引荐来源。

在数据库中,我们可以创建自定义表来存储这些数据。虽然WordPress提供了wp_postswp_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 APIMutationObserver和事件委托来监听页面加载、点击、表单变化等。
    • 将行为数据通过fetch API发送到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_pageadd_submenu_page函数。利用wpdb类查询数据库,计算并展示关键指标:

  • 实时在线用户: 统计过去5分钟内有过活动的独立会话数。
  • 今日/本月PV/UV: 页面浏览量和独立访客数。
  • 平均会话时长: 总会话时长 / 会话数。
  • 热门页面: 按PV排序的页面列表。
  • 来源分析: 引荐来源域名统计。

可以使用Chart.js或WP自带的图表库进行可视化。为了提高大量数据下的查询性能,应考虑对汇总数据使用定时任务(WP-Cron)进行预计算并存储到缓存或汇总表中。

2.2 点击热图(Heatmap)生成

热图是直观展示用户点击密度的强大工具。实现原理如下:

  1. 数据收集: 我们已经在前端收集了每次点击的坐标(clientX, clientY)和页面URL。
  2. 数据聚合: 在后端,针对特定URL,查询所有点击事件的坐标。
  3. 坐标标准化: 由于用户屏幕分辨率不同,需要将绝对坐标转换为相对于页面宽度和高度的百分比坐标。更精确的方法是关联收集到的视口(viewport)尺寸。

    $normalized_x = ($click_x / $viewport_width) * 100;
    $normalized_y = ($click_y / $viewport_height) * 100;
  4. 生成热图图层: 使用PHP的GD库或ImageMagick,或者更简单地在前端使用Canvas,根据归一化的坐标和密度,绘制一个半透明的彩色叠加层。也可以使用开源的JavaScript热图库(如heatmap.js)在后端提供聚合好的数据点,由前端渲染。
  5. 在后台预览: 在分析工具后台,选择页面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天),并编写相应的定时清理任务。

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,
本文来自网络,不代表柔性供应链服务中心立场,转载请注明出处:https://mall.org.cn/5064.html

EXCHANGES®作者

上一篇
下一篇

为您推荐

发表回复

联系我们

联系我们

18559313275

在线咨询: QQ交谈

邮箱: vip@exchanges.center

工作时间:周一至周五,9:00-17:30,节假日休息
返回顶部