首页 / 应用软件 / 一步步教你,为WordPress网站添加访客行为热力图与录屏分析

一步步教你,为WordPress网站添加访客行为热力图与录屏分析

一步步教你,为WordPress网站添加访客行为热力图与录屏分析,通过WordPress程序的代码二次开发实现常用互联网小工具功能

引言:为什么你的WordPress网站需要行为分析工具?

在当今竞争激烈的数字环境中,仅仅拥有一个美观的WordPress网站已经远远不够。了解访客如何与你的网站互动,哪些内容吸引他们,哪些元素导致他们离开,这些洞察对于优化用户体验、提高转化率至关重要。传统的数据分析工具如Google Analytics提供了页面浏览量和用户数量的宏观数据,但却无法回答一些关键问题:用户究竟在哪里点击?他们是否看到了重要的行动号召按钮?他们在离开前在页面上做了什么?

这就是热力图和录屏分析的价值所在。热力图通过色彩可视化展示用户在页面上的点击、滚动和注意力分布,而录屏分析则像“数字监控摄像头”一样,记录真实用户在网站上的完整操作过程。本文将详细指导你如何通过WordPress代码二次开发,为你的网站集成这两项强大的分析工具,无需依赖昂贵的第三方服务。

第一部分:理解热力图与录屏分析的技术原理

1.1 热力图的三种类型及其实现原理

热力图主要分为三种类型:点击热力图、滚动热力图和注意力热力图。点击热力图记录用户在页面上的所有点击位置,包括链接、按钮甚至非链接区域。实现原理是通过JavaScript事件监听器捕获点击事件,记录点击元素的坐标、尺寸和页面URL,然后将这些数据发送到服务器进行处理和可视化。

滚动热力图显示用户在不同页面深度的停留比例,揭示有多少用户滚动到页面特定位置。这通过监听滚动事件,记录视窗位置和页面高度比例来实现。注意力热力图则基于视线追踪研究,假设用户的注意力与鼠标移动轨迹相关,通过跟踪鼠标移动和停留时间来模拟实现。

1.2 录屏分析的技术实现方式

录屏分析(也称为会话回放)的实现比热力图更为复杂。现代实现通常采用以下技术组合:

  1. DOM序列化:记录页面初始的DOM状态,然后监听所有可能改变DOM的事件(如点击、输入、滚动等)
  2. 突变观察器(MutationObserver):监控DOM元素的变化
  3. 事件监听:捕获用户交互事件
  4. 时间戳同步:确保所有事件按正确顺序和时序记录
  5. 数据压缩:将记录的数据高效压缩后发送到服务器

录屏数据通常以增量方式记录,而不是完整视频,这大大减少了数据量。回放时,通过重建DOM状态和应用记录的事件来“重现”用户会话。

1.3 数据收集与隐私保护的平衡

实现这些工具时,必须考虑隐私保护。敏感数据(如密码字段、个人身份信息)应在客户端就被过滤或遮蔽。GDPR、CCPA等法规要求对用户数据进行适当处理,因此你的实现应包含:

  • 明确的隐私政策和使用告知
  • 敏感数据自动遮蔽功能
  • 用户选择退出机制
  • 数据匿名化处理

第二部分:准备工作与开发环境搭建

2.1 开发环境配置

在开始编码前,确保你已准备好以下环境:

  1. 本地WordPress开发环境:可以使用Local by Flywheel、XAMPP或Docker配置
  2. 代码编辑器:VS Code、Sublime Text或PHPStorm
  3. 浏览器开发者工具:熟悉Chrome DevTools或Firefox开发者工具
  4. 版本控制系统:Git初始化你的插件目录
  5. 测试网站:不要在正式网站直接开发,使用测试网站或本地环境

2.2 创建自定义WordPress插件框架

我们将创建一个独立的WordPress插件来实现功能,而不是直接修改主题文件。这样做的好处是:

  • 功能与主题分离,更换主题不影响分析工具
  • 便于更新和维护
  • 可以轻松在其他网站上复用

创建插件基本结构:

wp-content/plugins/visitor-analytics-tool/
├── visitor-analytics-tool.php
├── includes/
│   ├── class-data-collector.php
│   ├── class-heatmap-generator.php
│   ├── class-session-recorder.php
│   └── class-data-viewer.php
├── assets/
│   ├── js/
│   │   ├── frontend.js
│   │   └── admin.js
│   └── css/
│       ├── frontend.css
│       └── admin.css
├── admin/
│   └── admin-pages.php
└── uninstall.php

2.3 插件主文件基本结构

visitor-analytics-tool.php中,设置插件基本信息:

<?php
/**
 * Plugin Name: Visitor Analytics Tool
 * Plugin URI: https://yourwebsite.com/visitor-analytics
 * Description: 添加访客行为热力图与录屏分析功能
 * Version: 1.0.0
 * Author: Your Name
 * License: GPL v2 or later
 * Text Domain: visitor-analytics
 */

// 防止直接访问
if (!defined('ABSPATH')) {
    exit;
}

// 定义插件常量
define('VAT_VERSION', '1.0.0');
define('VAT_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('VAT_PLUGIN_URL', plugin_dir_url(__FILE__));

// 包含必要文件
require_once VAT_PLUGIN_DIR . 'includes/class-data-collector.php';
require_once VAT_PLUGIN_DIR . 'includes/class-heatmap-generator.php';
require_once VAT_PLUGIN_DIR . 'includes/class-session-recorder.php';
require_once VAT_PLUGIN_DIR . 'includes/class-data-viewer.php';

// 初始化插件
function vat_initialize_plugin() {
    // 检查用户权限
    if (current_user_can('manage_options')) {
        // 初始化各个组件
        $data_collector = new VAT_Data_Collector();
        $heatmap_generator = new VAT_Heatmap_Generator();
        $session_recorder = new VAT_Session_Recorder();
        $data_viewer = new VAT_Data_Viewer();
        
        // 注册激活/停用钩子
        register_activation_hook(__FILE__, array($data_collector, 'activate'));
        register_deactivation_hook(__FILE__, array($data_collector, 'deactivate'));
    }
}
add_action('plugins_loaded', 'vat_initialize_plugin');

第三部分:实现前端数据收集系统

3.1 设计高效的数据收集JavaScript

创建assets/js/frontend.js文件,负责收集用户行为数据:

(function() {
    'use strict';
    
    // 配置参数
    const config = {
        sampleRate: 0.3, // 30%的用户被记录
        heatmap: {
            enabled: true,
            click: true,
            scroll: true,
            move: true
        },
        sessionRecord: {
            enabled: false, // 默认关闭,需要时开启
            maxDuration: 300000, // 5分钟
            events: ['click', 'input', 'scroll', 'resize']
        },
        privacy: {
            maskText: true,
            excludeFields: ['password', 'credit-card', 'ssn']
        }
    };
    
    // 检查是否应该记录当前会话
    function shouldRecordSession() {
        if (!Math.floor(Math.random() * 100) < (config.sampleRate * 100)) {
            return false;
        }
        
        // 检查排除条件(如管理员、特定页面等)
        const excludedPaths = ['/wp-admin', '/wp-login'];
        const currentPath = window.location.pathname;
        
        return !excludedPaths.some(path => currentPath.startsWith(path));
    }
    
    // 数据收集器类
    class DataCollector {
        constructor() {
            this.sessionId = this.generateSessionId();
            this.pageId = window.location.pathname + window.location.search;
            this.startTime = Date.now();
            this.events = [];
            this.isRecording = false;
            
            // 初始化
            this.init();
        }
        
        generateSessionId() {
            return 'session_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
        }
        
        init() {
            if (!shouldRecordSession()) return;
            
            // 设置数据收集
            if (config.heatmap.enabled) {
                this.setupHeatmapCollection();
            }
            
            if (config.sessionRecord.enabled) {
                this.setupSessionRecording();
            }
            
            // 发送初始数据
            this.sendInitialData();
            
            this.isRecording = true;
        }
        
        setupHeatmapCollection() {
            // 点击事件收集
            if (config.heatmap.click) {
                document.addEventListener('click', (e) => {
                    this.recordClick(e);
                }, { capture: true });
            }
            
            // 滚动事件收集
            if (config.heatmap.scroll) {
                let scrollTimeout;
                window.addEventListener('scroll', () => {
                    clearTimeout(scrollTimeout);
                    scrollTimeout = setTimeout(() => {
                        this.recordScroll();
                    }, 100);
                });
            }
            
            // 鼠标移动收集(用于注意力热力图)
            if (config.heatmap.move) {
                let moveTimeout;
                document.addEventListener('mousemove', (e) => {
                    clearTimeout(moveTimeout);
                    moveTimeout = setTimeout(() => {
                        this.recordMousePosition(e);
                    }, 50);
                });
            }
        }
        
        recordClick(event) {
            const target = event.target;
            const rect = target.getBoundingClientRect();
            
            const clickData = {
                type: 'click',
                x: event.clientX,
                y: event.clientY,
                element: {
                    tag: target.tagName,
                    id: target.id,
                    classes: target.className,
                    text: this.maskText(target.textContent.trim().substring(0, 100))
                },
                pageX: event.pageX,
                pageY: event.pageY,
                viewport: {
                    width: window.innerWidth,
                    height: window.innerHeight
                },
                timestamp: Date.now() - this.startTime,
                url: window.location.href
            };
            
            this.events.push(clickData);
            this.sendDataIfNeeded();
        }
        
        recordScroll() {
            const scrollData = {
                type: 'scroll',
                position: {
                    x: window.scrollX,
                    y: window.scrollY
                },
                maxScroll: {
                    x: document.documentElement.scrollWidth - window.innerWidth,
                    y: document.documentElement.scrollHeight - window.innerHeight
                },
                percentage: (window.scrollY / (document.documentElement.scrollHeight - window.innerHeight)) * 100,
                timestamp: Date.now() - this.startTime,
                url: window.location.href
            };
            
            this.events.push(scrollData);
            this.sendDataIfNeeded();
        }
        
        recordMousePosition(event) {
            const moveData = {
                type: 'move',
                x: event.clientX,
                y: event.clientY,
                timestamp: Date.now() - this.startTime,
                url: window.location.href
            };
            
            this.events.push(moveData);
            // 鼠标移动数据较多,单独处理发送逻辑
            this.sendMoveData();
        }
        
        maskText(text) {
            if (!config.privacy.maskText) return text;
            
            // 简单文本遮蔽,实际应用中需要更复杂的逻辑
            if (text.length > 5) {
                return text.substring(0, 2) + '***' + text.substring(text.length - 2);
            }
            return '***';
        }
        
        sendDataIfNeeded() {
            // 每10个事件或每5秒发送一次数据
            if (this.events.length >= 10) {
                this.sendBatchData();
            }
        }
        
        sendMoveData() {
            // 鼠标移动数据单独处理,避免数据量过大
            if (this.moveEvents && this.moveEvents.length >= 20) {
                const moveBatch = {
                    sessionId: this.sessionId,
                    type: 'move_batch',
                    events: this.moveEvents,
                    url: window.location.href
                };
                
                this.sendToServer(moveBatch);
                this.moveEvents = [];
            }
        }
        
        sendBatchData() {
            if (this.events.length === 0) return;
            
            const batch = {
                sessionId: this.sessionId,
                events: [...this.events],
                url: window.location.href,
                timestamp: Date.now()
            };
            
            this.sendToServer(batch);
            this.events = [];
        }
        
        sendToServer(data) {
            // 使用navigator.sendBeacon确保页面卸载时也能发送数据
            const blob = new Blob([JSON.stringify(data)], { type: 'application/json' });
            
            if (navigator.sendBeacon) {
                navigator.sendBeacon(vat_ajax.ajax_url + '?action=vat_track', blob);
            } else {
                // 回退方案
                const xhr = new XMLHttpRequest();
                xhr.open('POST', vat_ajax.ajax_url, false);
                xhr.setRequestHeader('Content-Type', 'application/json');
                xhr.send(JSON.stringify(data));
            }
        }
        
        sendInitialData() {
            const initialData = {
                sessionId: this.sessionId,
                type: 'session_start',
                url: window.location.href,
                referrer: document.referrer,
                viewport: {
                    width: window.innerWidth,
                    height: window.innerHeight
                },
                device: {
                    userAgent: navigator.userAgent,
                    platform: navigator.platform
                },
                timestamp: Date.now()
            };
            
            this.sendToServer(initialData);
        }
        
        setupSessionRecording() {
            // 录屏功能需要更复杂的实现
            console.log('Session recording setup would go here');
            // 实际实现需要DOM序列化、突变观察器等
        }
    }
    
    // 初始化数据收集器
    document.addEventListener('DOMContentLoaded', () => {
        window.vatCollector = new DataCollector();
    });
    
    // 页面卸载前发送剩余数据
    window.addEventListener('beforeunload', () => {
        if (window.vatCollector && window.vatCollector.isRecording) {
            window.vatCollector.sendBatchData();
        }
    });
    
})();

3.2 创建PHP后端数据处理类

includes/class-data-collector.php中创建数据处理后端:

<?php
class VAT_Data_Collector {
    
    private $table_events;
    private $table_sessions;
    
    public function __construct() {
        global $wpdb;
        $this->table_events = $wpdb->prefix . 'vat_events';
        $this->table_sessions = $wpdb->prefix . 'vat_sessions';
        
        // 注册AJAX处理
        add_action('wp_ajax_vat_track', array($this, 'handle_tracking_data'));
        add_action('wp_ajax_nopriv_vat_track', array($this, 'handle_tracking_data'));
        
        // 注册前端脚本
        add_action('wp_enqueue_scripts', array($this, 'enqueue_frontend_scripts'));
    }
    
    public function activate() {
        $this->create_tables();
        $this->set_default_options();
    }
    
    private function create_tables() {
        global $wpdb;
        
        $charset_collate = $wpdb->get_charset_collate();
        
        // 创建事件表
        $sql_events = "CREATE TABLE IF NOT EXISTS {$this->table_events} (
            id bigint(20) NOT NULL AUTO_INCREMENT,
            session_id varchar(100) NOT NULL,
            event_type varchar(50) NOT NULL,
            event_data longtext NOT NULL,
            page_url varchar(500) NOT NULL,
            created_at datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
            PRIMARY KEY (id),
            KEY session_id (session_id),
            KEY event_type (event_type),
            KEY page_url (page_url(200)),
            KEY created_at (created_at)
        ) $charset_collate;";
        
        // 创建会话表
        $sql_sessions = "CREATE TABLE IF NOT EXISTS {$this->table_sessions} (
            session_id varchar(100) NOT NULL,
            start_time datetime NOT NULL,
            end_time datetime,
            page_views int(11) DEFAULT 1,
            duration int(11),
            referrer varchar(500),
            device_info text,
            user_id bigint(20),
            PRIMARY KEY (session_id),
            KEY start_time (start_time),
            KEY user_id (user_id)
        ) $charset_collate;";
        
        require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
        dbDelta($sql_events);
        dbDelta($sql_sessions);
    }
    
    private function set_default_options() {
        if (!get_option('vat_settings')) {
            $default_settings = array(
                'sample_rate' => 0.3,
                'heatmap_enabled' => true,
                'session_record_enabled' => false,
                'data_retention_days' => 30,
                'exclude_roles' => array('administrator'),
                'privacy_mask' => true
            );
            
            update_option('vat_settings', $default_settings);
        }
    }
    
    public function enqueue_frontend_scripts() {
        // 只有在前端页面加载
        if (is_admin()) return;
        
        // 获取设置
        $settings = get_option('vat_settings', array());
        
        // 检查是否应该加载跟踪脚本
        if (!$this->should_track_current_user()) return;
        
        // 计算采样率
        $sample_rate = isset($settings['sample_rate']) ? floatval($settings['sample_rate']) : 0.3;
        
        // 只有部分用户加载跟踪脚本
        if (mt_rand(1, 100) > ($sample_rate * 100)) return;
        
        wp_enqueue_script(
            'vat-frontend',
            VAT_PLUGIN_URL . 'assets/js/frontend.js',
            array(),
            VAT_VERSION,
            true
        );
        
        // 传递AJAX URL给前端脚本
        wp_localize_script('vat-frontend', 'vat_ajax', array(
            'ajax_url' => admin_url('admin-ajax.php'),
            'sample_rate' => $sample_rate
        ));
    }
    

第三部分:实现前端数据收集系统(续)

3.2 创建PHP后端数据处理类(续)

    private function should_track_current_user() {
        $settings = get_option('vat_settings', array());
        $exclude_roles = isset($settings['exclude_roles']) ? $settings['exclude_roles'] : array();
        
        // 如果用户已登录,检查其角色
        if (is_user_logged_in()) {
            $user = wp_get_current_user();
            $user_roles = $user->roles;
            
            // 检查用户角色是否在排除列表中
            foreach ($user_roles as $role) {
                if (in_array($role, $exclude_roles)) {
                    return false;
                }
            }
        }
        
        // 排除特定页面
        $excluded_pages = array('/wp-admin', '/wp-login.php', '/cart', '/checkout');
        $current_path = $_SERVER['REQUEST_URI'];
        
        foreach ($excluded_pages as $page) {
            if (strpos($current_path, $page) === 0) {
                return false;
            }
        }
        
        return true;
    }
    
    public function handle_tracking_data() {
        // 验证请求
        if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
            wp_die('Invalid request method', 400);
        }
        
        // 获取原始POST数据
        $raw_data = file_get_contents('php://input');
        $data = json_decode($raw_data, true);
        
        if (!$data || !isset($data['sessionId'])) {
            wp_die('Invalid data format', 400);
        }
        
        // 处理不同类型的数据
        if (isset($data['type']) && $data['type'] === 'session_start') {
            $this->record_session_start($data);
        } elseif (isset($data['type']) && $data['type'] === 'move_batch') {
            $this->record_move_batch($data);
        } elseif (isset($data['events'])) {
            $this->record_events($data);
        }
        
        // 返回成功响应
        wp_send_json_success(array('message' => 'Data recorded'));
    }
    
    private function record_session_start($data) {
        global $wpdb;
        
        $session_data = array(
            'session_id' => sanitize_text_field($data['sessionId']),
            'start_time' => current_time('mysql'),
            'referrer' => isset($data['referrer']) ? sanitize_text_field($data['referrer']) : '',
            'device_info' => isset($data['device']) ? json_encode($data['device']) : '',
            'user_id' => is_user_logged_in() ? get_current_user_id() : 0
        );
        
        $wpdb->insert($this->table_sessions, $session_data);
    }
    
    private function record_events($data) {
        global $wpdb;
        
        $session_id = sanitize_text_field($data['sessionId']);
        $events = $data['events'];
        
        foreach ($events as $event) {
            $event_type = isset($event['type']) ? sanitize_text_field($event['type']) : 'unknown';
            
            $event_data = array(
                'session_id' => $session_id,
                'event_type' => $event_type,
                'event_data' => json_encode($event),
                'page_url' => isset($event['url']) ? sanitize_text_field($event['url']) : ''
            );
            
            $wpdb->insert($this->table_events, $event_data);
        }
        
        // 更新会话的最后活动时间
        $this->update_session_activity($session_id);
    }
    
    private function record_move_batch($data) {
        global $wpdb;
        
        $session_id = sanitize_text_field($data['sessionId']);
        $events = $data['events'];
        
        // 鼠标移动数据较多,进行聚合处理
        $aggregated_data = array(
            'session_id' => $session_id,
            'event_type' => 'move_aggregated',
            'event_data' => json_encode(array(
                'count' => count($events),
                'positions' => array_map(function($e) {
                    return array($e['x'], $e['y']);
                }, $events)
            )),
            'page_url' => isset($data['url']) ? sanitize_text_field($data['url']) : ''
        );
        
        $wpdb->insert($this->table_events, $aggregated_data);
    }
    
    private function update_session_activity($session_id) {
        global $wpdb;
        
        $wpdb->update(
            $this->table_sessions,
            array('end_time' => current_time('mysql')),
            array('session_id' => $session_id)
        );
    }
    
    public function cleanup_old_data() {
        global $wpdb;
        
        $settings = get_option('vat_settings', array());
        $retention_days = isset($settings['data_retention_days']) ? intval($settings['data_retention_days']) : 30;
        
        $cutoff_date = date('Y-m-d H:i:s', strtotime("-$retention_days days"));
        
        // 删除旧事件数据
        $wpdb->query($wpdb->prepare(
            "DELETE FROM {$this->table_events} WHERE created_at < %s",
            $cutoff_date
        ));
        
        // 删除旧会话数据
        $wpdb->query($wpdb->prepare(
            "DELETE FROM {$this->table_sessions} WHERE start_time < %s",
            $cutoff_date
        ));
    }
}

第四部分:构建热力图生成与可视化系统

4.1 创建热力图数据处理类

includes/class-heatmap-generator.php中:

<?php
class VAT_Heatmap_Generator {
    
    private $data_collector;
    
    public function __construct() {
        global $wpdb;
        $this->table_events = $wpdb->prefix . 'vat_events';
        
        // 注册管理页面
        add_action('admin_menu', array($this, 'add_admin_menu'));
        
        // 注册AJAX端点
        add_action('wp_ajax_vat_get_heatmap_data', array($this, 'get_heatmap_data'));
    }
    
    public function add_admin_menu() {
        add_menu_page(
            '访客行为分析',
            '行为分析',
            'manage_options',
            'vat-analytics',
            array($this, 'render_admin_page'),
            'dashicons-chart-area',
            30
        );
        
        add_submenu_page(
            'vat-analytics',
            '热力图分析',
            '热力图',
            'manage_options',
            'vat-heatmaps',
            array($this, 'render_heatmap_page')
        );
    }
    
    public function render_admin_page() {
        ?>
        <div class="wrap">
            <h1>访客行为分析仪表板</h1>
            <div class="vat-dashboard">
                <div class="vat-stats-grid">
                    <div class="vat-stat-card">
                        <h3>今日会话</h3>
                        <p class="vat-stat-number"><?php echo $this->get_today_sessions(); ?></p>
                    </div>
                    <div class="vat-stat-card">
                        <h3>平均点击次数</h3>
                        <p class="vat-stat-number"><?php echo $this->get_average_clicks(); ?></p>
                    </div>
                    <div class="vat-stat-card">
                        <h3>热门页面</h3>
                        <p class="vat-stat-number"><?php echo $this->get_top_page(); ?></p>
                    </div>
                </div>
            </div>
        </div>
        <?php
    }
    
    public function render_heatmap_page() {
        $pages = $this->get_pages_with_data();
        ?>
        <div class="wrap">
            <h1>热力图分析</h1>
            
            <div class="vat-heatmap-controls">
                <div class="vat-control-group">
                    <label for="vat-page-select">选择页面:</label>
                    <select id="vat-page-select" class="vat-select">
                        <option value="">选择页面...</option>
                        <?php foreach ($pages as $page): ?>
                        <option value="<?php echo esc_attr($page->page_url); ?>">
                            <?php echo esc_html($this->truncate_url($page->page_url)); ?> 
                            (<?php echo $page->event_count; ?> 事件)
                        </option>
                        <?php endforeach; ?>
                    </select>
                </div>
                
                <div class="vat-control-group">
                    <label for="vat-date-range">日期范围:</label>
                    <select id="vat-date-range" class="vat-select">
                        <option value="today">今天</option>
                        <option value="yesterday">昨天</option>
                        <option value="7days" selected>最近7天</option>
                        <option value="30days">最近30天</option>
                        <option value="custom">自定义</option>
                    </select>
                    
                    <div id="vat-custom-dates" style="display: none; margin-top: 10px;">
                        <input type="date" id="vat-date-from" class="vat-date-input">
                        <span>至</span>
                        <input type="date" id="vat-date-to" class="vat-date-input">
                    </div>
                </div>
                
                <div class="vat-control-group">
                    <label for="vat-heatmap-type">热力图类型:</label>
                    <select id="vat-heatmap-type" class="vat-select">
                        <option value="click">点击热力图</option>
                        <option value="scroll">滚动热力图</option>
                        <option value="attention">注意力热力图</option>
                    </select>
                </div>
                
                <button id="vat-generate-heatmap" class="button button-primary">生成热力图</button>
            </div>
            
            <div class="vat-heatmap-container">
                <div id="vat-page-preview" style="border: 1px solid #ddd; margin: 20px 0; position: relative; overflow: hidden;">
                    <!-- 页面预览将在这里显示 -->
                </div>
                
                <div id="vat-heatmap-canvas" style="position: absolute; top: 0; left: 0; pointer-events: none;">
                    <!-- 热力图将在这里绘制 -->
                </div>
                
                <div class="vat-heatmap-legend">
                    <div class="vat-legend-title">热力强度</div>
                    <div class="vat-legend-gradient">
                        <span>低</span>
                        <div class="vat-gradient-bar"></div>
                        <span>高</span>
                    </div>
                </div>
            </div>
            
            <div class="vat-heatmap-stats">
                <h3>页面分析数据</h3>
                <div id="vat-page-stats">
                    <!-- 统计数据将动态加载 -->
                </div>
            </div>
        </div>
        
        <script>
        jQuery(document).ready(function($) {
            // 初始化热力图页面
            vatHeatmap.init();
        });
        </script>
        <?php
    }
    
    private function get_pages_with_data() {
        global $wpdb;
        
        return $wpdb->get_results($wpdb->prepare(
            "SELECT page_url, COUNT(*) as event_count 
             FROM {$this->table_events} 
             WHERE event_type IN ('click', 'scroll', 'move_aggregated')
             AND created_at >= %s
             GROUP BY page_url 
             ORDER BY event_count DESC 
             LIMIT 50",
            date('Y-m-d H:i:s', strtotime('-30 days'))
        ));
    }
    
    public function get_heatmap_data() {
        // 验证权限
        if (!current_user_can('manage_options')) {
            wp_die('权限不足', 403);
        }
        
        $page_url = isset($_GET['page_url']) ? urldecode($_GET['page_url']) : '';
        $date_range = isset($_GET['date_range']) ? $_GET['date_range'] : '7days';
        $heatmap_type = isset($_GET['heatmap_type']) ? $_GET['heatmap_type'] : 'click';
        
        if (empty($page_url)) {
            wp_send_json_error('请选择页面');
        }
        
        // 计算日期范围
        $date_conditions = $this->get_date_condition($date_range);
        
        // 根据热力图类型获取数据
        switch ($heatmap_type) {
            case 'click':
                $data = $this->get_click_data($page_url, $date_conditions);
                break;
            case 'scroll':
                $data = $this->get_scroll_data($page_url, $date_conditions);
                break;
            case 'attention':
                $data = $this->get_attention_data($page_url, $date_conditions);
                break;
            default:
                $data = array();
        }
        
        // 获取页面统计数据
        $stats = $this->get_page_stats($page_url, $date_conditions);
        
        wp_send_json_success(array(
            'data' => $data,
            'stats' => $stats,
            'page_url' => $page_url
        ));
    }
    
    private function get_click_data($page_url, $date_conditions) {
        global $wpdb;
        
        $events = $wpdb->get_results($wpdb->prepare(
            "SELECT event_data 
             FROM {$this->table_events} 
             WHERE page_url = %s 
             AND event_type = 'click'
             AND created_at >= %s
             ORDER BY created_at DESC
             LIMIT 1000",
            $page_url,
            $date_conditions['start_date']
        ));
        
        $clicks = array();
        foreach ($events as $event) {
            $data = json_decode($event->event_data, true);
            if (isset($data['x']) && isset($data['y'])) {
                $clicks[] = array(
                    'x' => intval($data['x']),
                    'y' => intval($data['y']),
                    'viewport_width' => isset($data['viewport']['width']) ? intval($data['viewport']['width']) : 1920,
                    'viewport_height' => isset($data['viewport']['height']) ? intval($data['viewport']['height']) : 1080
                );
            }
        }
        
        return $clicks;
    }
    
    private function get_scroll_data($page_url, $date_conditions) {
        global $wpdb;
        
        $events = $wpdb->get_results($wpdb->prepare(
            "SELECT event_data 
             FROM {$this->table_events} 
             WHERE page_url = %s 
             AND event_type = 'scroll'
             AND created_at >= %s
             ORDER BY created_at DESC
             LIMIT 5000",
            $page_url,
            $date_conditions['start_date']
        ));
        
        $scroll_depths = array();
        foreach ($events as $event) {
            $data = json_decode($event->event_data, true);
            if (isset($data['percentage'])) {
                $scroll_depths[] = floatval($data['percentage']);
            }
        }
        
        // 计算滚动深度分布
        $distribution = array_fill(0, 10, 0); // 10个区间:0-10%, 10-20%, ..., 90-100%
        
        foreach ($scroll_depths as $depth) {
            $index = min(floor($depth / 10), 9);
            $distribution[$index]++;
        }
        
        return $distribution;
    }
    
    private function get_attention_data($page_url, $date_conditions) {
        global $wpdb;
        
        $events = $wpdb->get_results($wpdb->prepare(
            "SELECT event_data 
             FROM {$this->table_events} 
             WHERE page_url = %s 
             AND event_type = 'move_aggregated'
             AND created_at >= %s
             ORDER BY created_at DESC
             LIMIT 100",
            $page_url,
            $date_conditions['start_date']
        ));
        
        $positions = array();
        foreach ($events as $event) {
            $data = json_decode($event->event_data, true);
            if (isset($data['positions'])) {
                foreach ($data['positions'] as $pos) {
                    $positions[] = array(
                        'x' => $pos[0],
                        'y' => $pos[1]
                    );
                }
            }
        }
        
        return $positions;
    }
    
    private function get_page_stats($page_url, $date_conditions) {
        global $wpdb;
        
        $stats = array();
        
        // 获取总点击次数
        $stats['total_clicks'] = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) 
             FROM {$this->table_events} 
             WHERE page_url = %s 
             AND event_type = 'click'
             AND created_at >= %s",
            $page_url,
            $date_conditions['start_date']
        ));
        
        // 获取独立访客数
        $stats['unique_visitors'] = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(DISTINCT session_id) 
             FROM {$this->table_events} 
             WHERE page_url = %s 
             AND created_at >= %s",
            $page_url,
            $date_conditions['start_date']
        ));
        
        // 获取平均滚动深度
        $scroll_data = $wpdb->get_results($wpdb->prepare(
            "SELECT event_data 
             FROM {$this->table_events} 
             WHERE page_url = %s 
             AND event_type = 'scroll'
             AND created_at >= %s",
            $page_url,
            $date_conditions['start_date']
        ));
        
        $total_depth = 0;
        $count = 0;
        foreach ($scroll_data as $event) {
            $data = json_decode($event->event_data, true);
            if (isset($data['percentage'])) {
                $total_depth += floatval($data['percentage']);
                $count++;
            }
        }
        
本文来自网络,不代表柔性供应链服务中心立场,转载请注明出处:https://mall.org.cn/5140.html

EXCHANGES®作者

上一篇
下一篇

为您推荐

发表回复

联系我们

联系我们

18559313275

在线咨询: QQ交谈

邮箱: vip@exchanges.center

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