feat: 在AI查询页面添加统计功能
- 添加AI查询统计卡片,显示总查询次数、成功率、平均响应时间等 - 按场景统计:文章摘要、垃圾评论检测、批量检测、关键词提取 - 按服务商统计:OpenAI、Anthropic、DeepSeek、小米Mimo等 - 使用AJAX动态加载统计数据 - 仅对管理员可见 - 统计数据来自wp_argon_ai_query_log表
This commit is contained in:
@@ -1064,6 +1064,220 @@ if (current_user_can('manage_options')):
|
|||||||
|
|
||||||
<?php
|
<?php
|
||||||
endif; // !empty($all_records)
|
endif; // !empty($all_records)
|
||||||
|
|
||||||
|
// ==========================================================================
|
||||||
|
// AI 查询统计
|
||||||
|
// ==========================================================================
|
||||||
|
?>
|
||||||
|
|
||||||
|
<article class="post card shadow-sm bg-white border-0 ai-verify-card" style="margin-top: 16px;">
|
||||||
|
<h3 class="ai-verify-section-title">
|
||||||
|
<i class="fa fa-bar-chart"></i><?php _e('AI 查询统计', 'argon'); ?>
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<div class="ai-alert ai-alert-info" style="margin-bottom: 20px;">
|
||||||
|
<span class="ai-alert-icon">ℹ</span>
|
||||||
|
<div>
|
||||||
|
<strong><?php _e('管理员专属', 'argon'); ?></strong><br>
|
||||||
|
<?php _e('显示 AI 查询的统计信息,包括总查询次数、成功率、平均响应时间等', 'argon'); ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="aiStatsContainer">
|
||||||
|
<div style="text-align: center; padding: 40px; color: var(--color-text-muted);">
|
||||||
|
<i class="fa fa-spinner fa-spin" style="font-size: 24px; margin-bottom: 12px;"></i>
|
||||||
|
<p><?php _e('加载统计数据中...', 'argon'); ?></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.ai-stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; margin-bottom: 28px; }
|
||||||
|
.ai-stat-card { background: linear-gradient(135deg, rgba(var(--themecolor-rgbstr), 0.08) 0%, rgba(var(--themecolor-rgbstr), 0.03) 100%); border: 1px solid rgba(var(--themecolor-rgbstr), 0.15); border-radius: var(--card-radius); padding: 20px; text-align: center; transition: all var(--animation-fast) var(--ease-standard); }
|
||||||
|
.ai-stat-card:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(var(--themecolor-rgbstr), 0.15); }
|
||||||
|
.ai-stat-value { font-size: 32px; font-weight: 700; color: var(--themecolor); margin-bottom: 8px; font-family: 'Consolas', 'Monaco', monospace; }
|
||||||
|
.ai-stat-label { font-size: 13px; color: var(--color-text-muted); font-weight: 500; text-transform: uppercase; letter-spacing: 0.5px; }
|
||||||
|
|
||||||
|
.ai-stats-table { width: 100%; border-collapse: separate; border-spacing: 0; margin-top: 20px; }
|
||||||
|
.ai-stats-table th { background: linear-gradient(135deg, rgba(var(--themecolor-rgbstr), 0.08) 0%, rgba(var(--themecolor-rgbstr), 0.04) 100%); padding: 12px 16px; text-align: left; font-size: 13px; font-weight: 600; color: var(--color-text-deeper); }
|
||||||
|
.ai-stats-table td { padding: 12px 16px; font-size: 14px; color: var(--color-text); border-bottom: 1px solid var(--color-border-on-foreground); }
|
||||||
|
.ai-stats-table tbody tr:last-child td { border-bottom: none; }
|
||||||
|
.ai-stats-table tbody tr:hover { background: rgba(var(--themecolor-rgbstr), 0.05); }
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.ai-stats-grid { grid-template-columns: repeat(2, 1fr); }
|
||||||
|
.ai-stat-value { font-size: 24px; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script data-pjax>
|
||||||
|
(function() {
|
||||||
|
// 加载统计数据
|
||||||
|
fetch('<?php echo admin_url('admin-ajax.php'); ?>', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
},
|
||||||
|
body: new URLSearchParams({
|
||||||
|
action: 'argon_get_ai_query_stats',
|
||||||
|
nonce: '<?php echo wp_create_nonce('argon_ai_query_stats'); ?>'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
renderStats(data.data);
|
||||||
|
} else {
|
||||||
|
showError(data.data || '<?php _e('加载失败', 'argon'); ?>');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error loading stats:', error);
|
||||||
|
showError('<?php _e('网络错误', 'argon'); ?>');
|
||||||
|
});
|
||||||
|
|
||||||
|
function renderStats(stats) {
|
||||||
|
const container = document.getElementById('aiStatsContainer');
|
||||||
|
|
||||||
|
let html = `
|
||||||
|
<div class="ai-stats-grid">
|
||||||
|
<div class="ai-stat-card">
|
||||||
|
<div class="ai-stat-value">${stats.total_queries || 0}</div>
|
||||||
|
<div class="ai-stat-label"><?php _e('总查询次数', 'argon'); ?></div>
|
||||||
|
</div>
|
||||||
|
<div class="ai-stat-card">
|
||||||
|
<div class="ai-stat-value">${stats.success_queries || 0}</div>
|
||||||
|
<div class="ai-stat-label"><?php _e('成功次数', 'argon'); ?></div>
|
||||||
|
</div>
|
||||||
|
<div class="ai-stat-card">
|
||||||
|
<div class="ai-stat-value">${stats.error_queries || 0}</div>
|
||||||
|
<div class="ai-stat-label"><?php _e('失败次数', 'argon'); ?></div>
|
||||||
|
</div>
|
||||||
|
<div class="ai-stat-card">
|
||||||
|
<div class="ai-stat-value">${stats.success_rate || 0}%</div>
|
||||||
|
<div class="ai-stat-label"><?php _e('成功率', 'argon'); ?></div>
|
||||||
|
</div>
|
||||||
|
<div class="ai-stat-card">
|
||||||
|
<div class="ai-stat-value">${stats.avg_response_time || 0}ms</div>
|
||||||
|
<div class="ai-stat-label"><?php _e('平均响应时间', 'argon'); ?></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// 按场景统计
|
||||||
|
if (stats.by_scenario && stats.by_scenario.length > 0) {
|
||||||
|
html += `
|
||||||
|
<h4 class="ai-section-subtitle"><?php _e('按场景统计', 'argon'); ?></h4>
|
||||||
|
<div style="overflow-x: auto;">
|
||||||
|
<table class="ai-stats-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th><?php _e('场景', 'argon'); ?></th>
|
||||||
|
<th><?php _e('查询次数', 'argon'); ?></th>
|
||||||
|
<th><?php _e('平均响应时间', 'argon'); ?></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const scenarioNames = {
|
||||||
|
'summary': '<?php _e('文章摘要', 'argon'); ?>',
|
||||||
|
'spam_detection': '<?php _e('垃圾评论检测', 'argon'); ?>',
|
||||||
|
'spam_detection_batch': '<?php _e('批量评论检测', 'argon'); ?>',
|
||||||
|
'keyword_extraction': '<?php _e('关键词提取', 'argon'); ?>'
|
||||||
|
};
|
||||||
|
|
||||||
|
stats.by_scenario.forEach(item => {
|
||||||
|
const scenarioName = scenarioNames[item.scenario] || item.scenario;
|
||||||
|
html += `
|
||||||
|
<tr>
|
||||||
|
<td><strong>${escapeHtml(scenarioName)}</strong></td>
|
||||||
|
<td>${item.count || 0}</td>
|
||||||
|
<td>${Math.round(item.avg_time || 0)}ms</td>
|
||||||
|
</tr>
|
||||||
|
`;
|
||||||
|
});
|
||||||
|
|
||||||
|
html += `
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按服务商统计
|
||||||
|
if (stats.by_provider && stats.by_provider.length > 0) {
|
||||||
|
html += `
|
||||||
|
<h4 class="ai-section-subtitle" style="margin-top: 28px;"><?php _e('按服务商统计', 'argon'); ?></h4>
|
||||||
|
<div style="overflow-x: auto;">
|
||||||
|
<table class="ai-stats-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th><?php _e('服务商', 'argon'); ?></th>
|
||||||
|
<th><?php _e('查询次数', 'argon'); ?></th>
|
||||||
|
<th><?php _e('平均响应时间', 'argon'); ?></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const providerNames = {
|
||||||
|
'openai': 'OpenAI',
|
||||||
|
'anthropic': 'Anthropic',
|
||||||
|
'deepseek': 'DeepSeek',
|
||||||
|
'xiaomi': '小米 Mimo',
|
||||||
|
'qianwen': '通义千问',
|
||||||
|
'wenxin': '文心一言',
|
||||||
|
'doubao': '豆包',
|
||||||
|
'kimi': 'Kimi',
|
||||||
|
'zhipu': '智谱',
|
||||||
|
'siliconflow': 'SiliconFlow'
|
||||||
|
};
|
||||||
|
|
||||||
|
stats.by_provider.forEach(item => {
|
||||||
|
const providerName = providerNames[item.provider] || item.provider;
|
||||||
|
html += `
|
||||||
|
<tr>
|
||||||
|
<td><strong>${escapeHtml(providerName)}</strong></td>
|
||||||
|
<td>${item.count || 0}</td>
|
||||||
|
<td>${Math.round(item.avg_time || 0)}ms</td>
|
||||||
|
</tr>
|
||||||
|
`;
|
||||||
|
});
|
||||||
|
|
||||||
|
html += `
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
container.innerHTML = html;
|
||||||
|
}
|
||||||
|
|
||||||
|
function showError(message) {
|
||||||
|
const container = document.getElementById('aiStatsContainer');
|
||||||
|
container.innerHTML = `
|
||||||
|
<div class="ai-alert ai-alert-warning">
|
||||||
|
<span class="ai-alert-icon">⚠</span>
|
||||||
|
<div>
|
||||||
|
<strong><?php _e('加载失败', 'argon'); ?></strong><br>
|
||||||
|
${escapeHtml(message)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeHtml(text) {
|
||||||
|
if (!text) return '';
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.textContent = text;
|
||||||
|
return div.innerHTML;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<?php
|
||||||
endif; // current_user_can('manage_options')
|
endif; // current_user_can('manage_options')
|
||||||
?>
|
?>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user