文章目录[隐藏]
网络传媒柔性内容A/B测试WordPress插件应用教程
引言:为什么需要A/B测试
在当今竞争激烈的网络传媒领域,内容优化已成为提升用户参与度和转化率的关键手段。A/B测试(也称为分割测试)是一种科学的优化方法,通过对比两个或多个版本的内容表现,帮助内容创作者和营销人员做出数据驱动的决策。
对于WordPress网站管理员而言,实施A/B测试通常需要专业的技术知识或昂贵的第三方服务。本教程将介绍如何通过一个自定义的柔性内容A/B测试插件,在WordPress中轻松实现内容测试,无需依赖外部服务,同时保持对数据的完全控制。
插件设计与架构
核心功能规划
我们的柔性内容A/B测试插件将具备以下核心功能:
- 创建和管理A/B测试实验
- 定义测试变量(不同版本的内容)
- 随机分配访客到不同测试组
- 跟踪和记录用户交互数据
- 提供统计分析结果
- 自动选择优胜版本
数据库结构设计
<?php
/**
* A/B测试插件数据库表结构
* 安装插件时创建所需数据表
*/
function ab_testing_plugin_create_tables() {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
// 实验表:存储A/B测试实验的基本信息
$experiments_table = $wpdb->prefix . 'ab_experiments';
$experiments_sql = "CREATE TABLE IF NOT EXISTS $experiments_table (
experiment_id INT(11) NOT NULL AUTO_INCREMENT,
experiment_name VARCHAR(255) NOT NULL,
description TEXT,
status ENUM('active', 'paused', 'completed') DEFAULT 'active',
start_date DATETIME DEFAULT CURRENT_TIMESTAMP,
end_date DATETIME,
primary_metric VARCHAR(100) DEFAULT 'click_rate',
confidence_level DECIMAL(4,3) DEFAULT 0.95,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (experiment_id)
) $charset_collate;";
// 变量表:存储每个实验的不同版本内容
$variations_table = $wpdb->prefix . 'ab_variations';
$variations_sql = "CREATE TABLE IF NOT EXISTS $variations_table (
variation_id INT(11) NOT NULL AUTO_INCREMENT,
experiment_id INT(11) NOT NULL,
variation_name VARCHAR(255) NOT NULL,
content_type ENUM('shortcode', 'widget', 'post_content') DEFAULT 'shortcode',
content_key VARCHAR(255) NOT NULL,
content_value LONGTEXT,
is_control TINYINT(1) DEFAULT 0,
weight INT(11) DEFAULT 1,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (variation_id),
FOREIGN KEY (experiment_id) REFERENCES $experiments_table(experiment_id) ON DELETE CASCADE
) $charset_collate;";
// 结果表:存储用户交互数据
$results_table = $wpdb->prefix . 'ab_results';
$results_sql = "CREATE TABLE IF NOT EXISTS $results_table (
result_id BIGINT(20) NOT NULL AUTO_INCREMENT,
experiment_id INT(11) NOT NULL,
variation_id INT(11) NOT NULL,
user_id BIGINT(20),
session_id VARCHAR(255) NOT NULL,
action_type VARCHAR(100) NOT NULL,
action_value DECIMAL(10,2),
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
ip_address VARCHAR(45),
user_agent TEXT,
PRIMARY KEY (result_id),
INDEX exp_var_idx (experiment_id, variation_id),
INDEX session_idx (session_id)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($experiments_sql);
dbDelta($variations_sql);
dbDelta($results_sql);
}
?>
插件核心功能实现
实验管理类
<?php
/**
* A/B测试实验管理类
* 处理实验的创建、更新、删除和状态管理
*/
class AB_Experiment_Manager {
private $wpdb;
private $experiments_table;
private $variations_table;
private $results_table;
public function __construct() {
global $wpdb;
$this->wpdb = $wpdb;
$this->experiments_table = $wpdb->prefix . 'ab_experiments';
$this->variations_table = $wpdb->prefix . 'ab_variations';
$this->results_table = $wpdb->prefix . 'ab_results';
}
/**
* 创建新的A/B测试实验
* @param string $name 实验名称
* @param string $description 实验描述
* @param array $variations 变量数组
* @return int|false 成功返回实验ID,失败返回false
*/
public function create_experiment($name, $description, $variations = []) {
// 验证输入数据
if (empty($name) || empty($variations)) {
return false;
}
// 插入实验记录
$result = $this->wpdb->insert(
$this->experiments_table,
[
'experiment_name' => sanitize_text_field($name),
'description' => sanitize_textarea_field($description),
'status' => 'active'
],
['%s', '%s', '%s']
);
if (!$result) {
return false;
}
$experiment_id = $this->wpdb->insert_id;
// 插入变量数据
$control_added = false;
foreach ($variations as $index => $variation) {
$is_control = (!$control_added && $index === 0) ? 1 : 0;
if ($is_control) $control_added = true;
$this->wpdb->insert(
$this->variations_table,
[
'experiment_id' => $experiment_id,
'variation_name' => sanitize_text_field($variation['name']),
'content_type' => sanitize_text_field($variation['type']),
'content_key' => sanitize_text_field($variation['key']),
'content_value' => wp_kses_post($variation['value']),
'is_control' => $is_control,
'weight' => isset($variation['weight']) ? intval($variation['weight']) : 1
],
['%d', '%s', '%s', '%s', '%s', '%d', '%d']
);
}
return $experiment_id;
}
/**
* 为访客分配测试变量
* @param int $experiment_id 实验ID
* @param string $session_id 会话ID
* @return int 分配的变量ID
*/
public function assign_variation($experiment_id, $session_id) {
// 检查是否已有分配
$existing = $this->wpdb->get_var($this->wpdb->prepare(
"SELECT variation_id FROM $this->results_table
WHERE experiment_id = %d AND session_id = %s
LIMIT 1",
$experiment_id, $session_id
));
if ($existing) {
return $existing;
}
// 获取实验的所有变量及其权重
$variations = $this->wpdb->get_results($this->wpdb->prepare(
"SELECT variation_id, weight FROM $this->variations_table
WHERE experiment_id = %d AND weight > 0",
$experiment_id
), ARRAY_A);
if (empty($variations)) {
return 0;
}
// 基于权重随机选择变量
$weight_sum = array_sum(array_column($variations, 'weight'));
$random = mt_rand(1, $weight_sum);
$current = 0;
foreach ($variations as $variation) {
$current += $variation['weight'];
if ($random <= $current) {
$assigned_variation = $variation['variation_id'];
break;
}
}
// 记录分配结果
$this->wpdb->insert(
$this->results_table,
[
'experiment_id' => $experiment_id,
'variation_id' => $assigned_variation,
'session_id' => $session_id,
'action_type' => 'assigned'
],
['%d', '%d', '%s', '%s']
);
return $assigned_variation;
}
}
?>
短代码处理器
<?php
/**
* A/B测试短代码处理器
* 通过短代码在文章和页面中插入测试内容
*/
class AB_Shortcode_Handler {
private $experiment_manager;
public function __construct() {
$this->experiment_manager = new AB_Experiment_Manager();
add_shortcode('ab_test', [$this, 'render_ab_test']);
add_action('wp_enqueue_scripts', [$this, 'enqueue_tracking_script']);
}
/**
* 渲染A/B测试短代码
* @param array $atts 短代码属性
* @param string $content 短代码内容(备用内容)
* @return string 渲染后的内容
*/
public function render_ab_test($atts, $content = null) {
// 解析短代码属性
$atts = shortcode_atts([
'experiment' => 0,
'default' => ''
], $atts, 'ab_test');
$experiment_id = intval($atts['experiment']);
if (!$experiment_id) {
return $atts['default'] ?: $content;
}
// 生成或获取会话ID
$session_id = $this->get_session_id();
// 分配测试变量
$variation_id = $this->experiment_manager->assign_variation($experiment_id, $session_id);
if (!$variation_id) {
return $atts['default'] ?: $content;
}
// 获取变量内容
global $wpdb;
$variation = $wpdb->get_row($wpdb->prepare(
"SELECT content_value FROM {$wpdb->prefix}ab_variations
WHERE variation_id = %d",
$variation_id
));
if (!$variation) {
return $atts['default'] ?: $content;
}
// 添加跟踪属性
$output = '<div class="ab-test-content"
data-experiment="' . esc_attr($experiment_id) . '"
data-variation="' . esc_attr($variation_id) . '"
data-session="' . esc_attr($session_id) . '">';
$output .= do_shortcode($variation->content_value);
$output .= '</div>';
return $output;
}
/**
* 获取或创建会话ID
* @return string 会话ID
*/
private function get_session_id() {
if (isset($_COOKIE['ab_session_id']) && $_COOKIE['ab_session_id']) {
return $_COOKIE['ab_session_id'];
}
$session_id = wp_generate_uuid4();
setcookie('ab_session_id', $session_id, time() + (30 * DAY_IN_SECONDS), COOKIEPATH, COOKIE_DOMAIN);
return $session_id;
}
/**
* 加载跟踪脚本
*/
public function enqueue_tracking_script() {
wp_enqueue_script(
'ab-testing-tracker',
plugins_url('js/tracker.js', __FILE__),
['jquery'],
'1.0.0',
true
);
wp_localize_script('ab-testing-tracker', 'abTesting', [
'ajaxurl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('ab_testing_track')
]);
}
}
?>
前端交互跟踪
/**
* A/B测试前端跟踪脚本
* 跟踪用户与测试内容的交互
*/
(function($) {
'use strict';
$(document).ready(function() {
// 跟踪链接点击
$(document).on('click', '.ab-test-content a', function(e) {
var $content = $(this).closest('.ab-test-content');
trackInteraction($content, 'click', $(this).attr('href'));
});
// 跟踪表单提交
$(document).on('submit', '.ab-test-content form', function(e) {
var $content = $(this).closest('.ab-test-content');
trackInteraction($content, 'form_submit', $(this).attr('action') || '');
});
// 跟踪按钮点击
$(document).on('click', '.ab-test-content button, .ab-test-content .button, .ab-test-content input[type="submit"]', function(e) {
var $content = $(this).closest('.ab-test-content');
trackInteraction($content, 'button_click', $(this).text() || $(this).val() || '');
});
// 跟踪内容可见性(当50%内容进入视口时)
if ('IntersectionObserver' in window) {
var observer = new IntersectionObserver(function(entries) {
entries.forEach(function(entry) {
if (entry.isIntersecting && entry.intersectionRatio >= 0.5) {
var $content = $(entry.target);
if (!$content.data('view-tracked')) {
trackInteraction($content, 'view', '');
$content.data('view-tracked', true);
}
}
});
}, {
threshold: [0.5]
});
$('.ab-test-content').each(function() {
observer.observe(this);
});
}
/**
* 发送交互数据到服务器
* @param {jQuery} $content 内容元素
* @param {string} actionType 交互类型
* @param {string} actionValue 交互值
*/
function trackInteraction($content, actionType, actionValue) {
var data = {
action: 'ab_testing_track',
nonce: abTesting.nonce,
experiment: $content.data('experiment'),
variation: $content.data('variation'),
session: $content.data('session'),
action_type: actionType,
action_value: actionValue
};
$.post(abTesting.ajaxurl, data, function(response) {
if (!response.success) {
console.error('Tracking failed:', response.data);
}
});
}
});
})(jQuery);
管理界面实现
实验列表页面
<?php
/**
* A/B测试插件管理界面
* 提供实验管理、数据查看和统计分析功能
*/
class AB_Testing_Admin {
public function __construct() {
add_action('admin_menu', [$this, 'add_admin_menu']);
add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_scripts']);
}
/**
* 添加管理菜单
*/
public function add_admin_menu() {
add_menu_page(
'A/B测试管理',
'A/B测试',
'manage_options',
'ab-testing',
[$this, 'render_main_page'],
'dashicons-chart-area',
30
);
add_submenu_page(
'ab-testing',
'新建实验',
'新建实验',
'manage_options',
'ab-testing-new',
[$this, 'render_new_experiment_page']
);
add_submenu_page(
'ab-testing',
'统计分析',
'统计分析',
'manage_options',
'ab-testing-analytics',
[$this, 'render_analytics_page']
);
}
/**
* 渲染主管理页面
*/
public function render_main_page() {
global $wpdb;
// 获取所有实验
$experiments = $wpdb->get_results(
"SELECT * FROM {$wpdb->prefix}ab_experiments
ORDER BY created_at DESC"
);
?>
<div class="wrap">
<h1 class="wp-heading-inline">A/B测试实验</h1>
<a href="<?php echo admin_url('admin.php?page=ab-testing-new'); ?>" class="page-title-action">新建实验</a>
<div class="tablenav top">
<div class="alignleft actions">
<select name="filter_status">
<option value="">所有状态</option>
<option value="active">进行中</option>
<option value="paused">已暂停</option>
<option value="completed">已完成</option>
</select>
<button class="button" id="filter-button">筛选</button>
</div>
</div>
<table class="wp-list-table widefat fixed striped">
<thead>
<tr>
<th>ID</th>
<th>实验名称</th>
<th>状态</th>
<th>开始时间</th>
<th>参与人数</th>
<th>转化率</th>
<th>置信度</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<?php if (empty($experiments)): ?>
<tr>
<td colspan="8" style="text-align: center;">暂无实验数据</td>
</tr>
<?php else: ?>
<?php foreach ($experiments as $exp):
// 获取实验统计数据
$stats = $this->get_experiment_stats($exp->experiment_id);
?>
<tr>
<td><?php echo $exp->experiment_id; ?></td>
<td>
<strong><?php echo esc_html($exp->experiment_name); ?></strong>
<p class="description"><?php echo esc_html($exp->description); ?></p>
</td>
<td>
<span class="ab-status status-<?php echo $exp->status; ?>">
<?php
$status_labels = [
'active' => '进行中',
'paused' => '已暂停',
'completed' => '已完成'
];
echo $status_labels[$exp->status] ?? $exp->status;
?>
</span>
</td>
<td><?php echo date('Y-m-d H:i', strtotime($exp->start_date)); ?></td>
<td><?php echo number_format($stats['total_participants']); ?></td>
<td><?php echo $stats['conversion_rate'] ? number_format($stats['conversion_rate'] * 100, 2) . '%' : 'N/A'; ?></td>
<td>
<?php if ($stats['confidence']): ?>
<span class="confidence-<?php echo ($stats['confidence'] >= 0.95) ? 'high' : 'low'; ?>">
<?php echo number_format($stats['confidence'] * 100, 1); ?>%
</span>
<?php else: ?>
N/A
<?php endif; ?>
</td>
<td>
<div class="row-actions">
<a href="<?php echo admin_url('admin.php?page=ab-testing-analytics&experiment=' . $exp->experiment_id); ?>">查看数据</a> |
<a href="#" class="edit-experiment" data-id="<?php echo $exp->experiment_id; ?>">编辑</a> |
<?php if ($exp->status == 'active'): ?>
<a href="#" class="pause-experiment" data-id="<?php echo $exp->experiment_id; ?>">暂停</a> |
<?php elseif ($exp->status == 'paused'): ?>
<a href="#" class="resume-experiment" data-id="<?php echo $exp->experiment_id; ?>">继续</a> |
<?php endif; ?>
<a href="#" class="delete-experiment" data-id="<?php echo $exp->experiment_id; ?>" style="color: #a00;">删除</a>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
<script>
jQuery(document).ready(function($) {
// 实验状态切换
$('.pause-experiment, .resume-experiment').on('click', function(e) {
e.preventDefault();
var experimentId = $(this).data('id');
var action = $(this).hasClass('pause-experiment') ? 'pause' : 'resume';
$.post(ajaxurl, {
action: 'ab_testing_toggle_status',
experiment_id: experimentId,
status: action,
nonce: '<?php echo wp_create_nonce('ab_testing_admin'); ?>'
}, function(response) {
if (response.success) {
location.reload();
} else {
alert('操作失败: ' + response.data);
}
});
});
// 删除实验
$('.delete-experiment').on('click', function(e) {
e.preventDefault();
if (!confirm('确定要删除这个实验吗?此操作不可恢复。')) {
return;
}
var experimentId = $(this).data('id');
$.post(ajaxurl, {
action: 'ab_testing_delete_experiment',
experiment_id: experimentId,
nonce: '<?php echo wp_create_nonce('ab_testing_admin'); ?>'
}, function(response) {
if (response.success) {
location.reload();
} else {
alert('删除失败: ' + response.data);
}
});
});
});
</script>
<style>
.ab-status {
padding: 3px 8px;
border-radius: 3px;
font-size: 12px;
font-weight: bold;
}
.status-active { background: #d1ecf1; color: #0c5460; }
.status-paused { background: #fff3cd; color: #856404; }
.status-completed { background: #d4edda; color: #155724; }
.confidence-high { color: #28a745; font-weight: bold; }
.confidence-low { color: #dc3545; }
.row-actions { font-size: 12px; }
</style>
<?php
}
/**
* 获取实验统计数据
*/
private function get_experiment_stats($experiment_id) {
global $wpdb;
$stats = [
'total_participants' => 0,
'conversion_rate' => 0,
'confidence' => 0
];
// 获取总参与人数
$stats['total_participants'] = $wpdb->get_var($wpdb->prepare(
"SELECT COUNT(DISTINCT session_id)
FROM {$wpdb->prefix}ab_results
WHERE experiment_id = %d",
$experiment_id
));
// 获取转化数据
$conversions = $wpdb->get_results($wpdb->prepare(
"SELECT v.variation_id, v.variation_name, v.is_control,
COUNT(DISTINCT r.session_id) as participants,
SUM(CASE WHEN r.action_type IN ('click', 'form_submit', 'button_click') THEN 1 ELSE 0 END) as conversions
FROM {$wpdb->prefix}ab_variations v
LEFT JOIN {$wpdb->prefix}ab_results r ON v.variation_id = r.variation_id
AND r.experiment_id = %d
WHERE v.experiment_id = %d
GROUP BY v.variation_id",
$experiment_id, $experiment_id
));
if ($conversions && count($conversions) >= 2) {
// 计算转化率和置信区间
$control = null;
$variations = [];
foreach ($conversions as $row) {
if ($row->is_control) {
$control = $row;
} else {
$variations[] = $row;
}
}
if ($control && $control->participants > 0) {
$control_rate = $control->conversions / $control->participants;
$stats['conversion_rate'] = $control_rate;
// 计算最佳变体的置信度(简化版)
if (!empty($variations)) {
$best_variation = null;
$best_improvement = 0;
foreach ($variations as $var) {
if ($var->participants > 0) {
$var_rate = $var->conversions / $var->participants;
$improvement = ($var_rate - $control_rate) / $control_rate;
if ($improvement > $best_improvement) {
$best_improvement = $improvement;
$best_variation = $var;
}
}
}
if ($best_variation && $best_variation->participants >= 100) {
// 简化置信度计算(实际应使用统计检验)
$stats['confidence'] = min(0.95 + ($best_improvement * 0.1), 0.99);
}
}
}
}
return $stats;
}
/**
* 渲染新建实验页面
*/
public function render_new_experiment_page() {
?>
<div class="wrap">
<h1>新建A/B测试实验</h1>
<form id="ab-experiment-form" method="post">
<?php wp_nonce_field('ab_testing_create_experiment', 'ab_testing_nonce'); ?>
<table class="form-table">
<tr>
<th scope="row"><label for="experiment_name">实验名称 *</label></th>
<td>
<input type="text" id="experiment_name" name="experiment_name"
class="regular-text" required>
<p class="description">为实验起一个描述性的名称</p>
</td>
</tr>
<tr>
<th scope="row"><label for="description">实验描述</label></th>
<td>
<textarea id="description" name="description"
rows="3" class="large-text"></textarea>
<p class="description">描述实验的目的和测试内容</p>
</td>
</tr>
<tr>
<th scope="row"><label>内容类型</label></th>
<td>
<select id="content_type" name="content_type">
<option value="shortcode">短代码内容</option>
<option value="post_content">文章内容</option>
<option value="widget">小工具</option>
</select>
<p class="description">选择要测试的内容类型</p>
</td>
</tr>
<tr>
<th scope="row"><label for="content_key">内容标识 *</label></th>
<td>
<input type="text" id="content_key" name="content_key"
class="regular-text" required>
<p class="description">
对于短代码:输入短代码标识(如:cta_button)<br>
对于文章内容:输入文章ID或slug<br>
对于小工具:输入小工具ID
</p>
</td>
</tr>
<tr>
<th scope="row"><label>测试变量</label></th>
<td>
<div id="variations-container">
<div class="variation-group">
<h3>对照组(原始版本)</h3>
<div>
<label>变量名称:</label>
<input type="text" name="variations[0][name]"
value="对照组" class="regular-text" required>
</div>
<div>
<label>内容:</label>
<?php
wp_editor('', 'variation_0_content', [
'textarea_name' => 'variations[0][value]',
'textarea_rows' => 6,
'media_buttons' => true,
'teeny' => true
]);
?>
</div>
</div>
<div class="variation-group">
<h3>变体A</h3>
<div>
<label>变量名称:</label>
<input type="text" name="variations[1][name]"
value="变体A" class="regular-text" required>
</div>
<div>
<label>内容:</label>
<?php
wp_editor('', 'variation_1_content', [
'textarea_name' => 'variations[1][value]',
'textarea_rows' => 6,
'media_buttons' => true,
'teeny' => true
]);
?>
</div>
</div>
</div>
<button type="button" id="add-variation" class="button">添加更多变体</button>
<p class="description">至少需要两个版本(对照组和一个变体)</p>
</td>
</tr>
<tr>
<th scope="row"><label>流量分配</label></th>
<td>
<div id="traffic-allocation">
<p>默认情况下,流量将均匀分配给所有变体</p>
<p>您可以在实验开始后调整流量分配</p>
</div>
</td>
</tr>
</table>
<p class="submit">
<button type="submit" class="button button-primary">创建实验</button>
<a href="<?php echo admin_url('admin.php?page=ab-testing'); ?>" class="button">取消</a>
</p>
</form>
</div>
<script>
jQuery(document).ready(function($) {
var variationCount = 2;
// 添加新变体
$('#add-variation').on('click', function() {
variationCount++;
var editorId = 'variation_' + variationCount + '_content';
var variationHtml = `
<div class="variation-group">
<h3>变体${String.fromCharCode(64 + variationCount)}</h3>
<div>
<label>变量名称:</label>
<input type="text" name="variations[${variationCount}][name]"
value="变体${String.fromCharCode(64 + variationCount)}" class="regular-text" required>
</div>
<div>
<label>内容:</label>
<textarea id="${editorId}" name="variations[${variationCount}][value]"
rows="6" class="large-text"></textarea>
</div>
<button type="button" class="button remove-variation">删除此变体</button>
</div>
`;
$('#variations-container').append(variationHtml);
// 初始化文本编辑器
if (typeof tinymce !== 'undefined') {
tinymce.execCommand('mceAddEditor', false, editorId);
}
});
// 删除变体
$(document).on('click', '.remove-variation', function() {
if ($('.variation-group').length > 2) {
$(this).closest('.variation-group').remove();
// 重新标记变体名称
$('.variation-group').each(function(index) {
if (index > 0) {
var letter = String.fromCharCode(64 + index);
$(this).find('h3').text('变体' + letter);
$(this).find('input[name*="[name]"]').val('变体' + letter);
}
});
} else {
alert('至少需要两个变体');
}
});
// 表单提交
$('#ab-experiment-form').on('submit', function(e) {
e.preventDefault();
var formData = new FormData(this);
$.ajax({
url: ajaxurl,
type: 'POST',
data: formData,
processData: false,
contentType: false,
success: function(response) {
if (response.success) {
alert('实验创建成功!');
window.location.href = '<?php echo admin_url('admin.php?page=ab-testing'); ?>';
} else {
alert('创建失败:' + response.data);
}
}
});
});
});
</script>
<style>
.variation-group {
border: 1px solid #ddd;
padding: 15px;
margin-bottom: 20px;
background: #f9f9f9;
}
.variation-group h3 {
margin-top: 0;
padding-bottom: 10px;
border-bottom: 1px solid #ddd;
}
.variation-group > div {
margin-bottom: 10px;
}
.variation-group label {
display: inline-block;
width: 100px;
font-weight: bold;
}
.remove-variation {
margin-top: 10px;
color: #dc3232;
}
</style>
<?php
}
/**
* 渲染统计分析页面
*/
public function render_analytics_page() {
global $wpdb;
$experiment_id = isset($_GET['experiment']) ? intval($_GET['experiment']) : 0;
if (!$experiment_id) {
echo '<div class="wrap"><p>请选择一个实验查看统计数据</p></div>';
return;
}
// 获取实验详情
$experiment = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}ab_experiments WHERE experiment_id = %d",
$experiment_id
));
if (!$experiment) {
echo '<div class="wrap"><p>实验不存在</p></div>';
return;
}
// 获取详细统计数据
$stats = $this->get_detailed_stats($experiment_id);
?>
<div class="wrap">
<h1>实验分析:<?php echo esc_html($experiment->experiment_name); ?></h1>
<div class="ab-analytics-overview">
<div class="ab-stats-grid">
<div class="ab-stat-card">
<h3>总参与人数</h3>
<div class="ab-stat-value"><?php echo number_format($stats['total_participants']); ?></div>
</div>
<div class="ab-stat-card">
<h3>测试时长</h3>
<div class="ab-stat-value">
<?php
$start = new DateTime($experiment->start_date);
$end = $experiment->end_date ? new DateTime($experiment->end_date) : new DateTime();
$interval = $start->diff($end);
echo $interval->format('%a天 %h小时');
?>
</div>
</div>
<div class="ab-stat-card">
<h3>总转化次数</h3>
<div class="ab-stat-value"><?php echo number_format($stats['total_conversions']); ?></div>
</div>
<div class="ab-stat-card">
<h3>整体转化率</h3>
<div class="ab-stat-value"><?php echo number_format($stats['overall_conversion_rate'] * 100, 2); ?>%</div>
</div>
</div>
</div>
<h2>变体表现对比</h2>
<table class="wp-list-table widefat fixed striped">
<thead>
<tr>
<th>变体</th>
<th>参与人数</th>
<th>转化次数</th>
<th>转化率</th>
<th>相对提升</th>
<th>
