修复日志页面卡顿我呢提

This commit is contained in:
root
2026-06-15 22:42:59 +08:00
parent 1a52201ec7
commit bcb9f293a6
151 changed files with 2461 additions and 471 deletions
+119 -47
View File
@@ -348,6 +348,7 @@ if(empty($_SESSION['password']) || $_SESSION['password'] != LOGIN_PASSWORD) {
</div>
<div class="log-nav">
<a href="table.php" class="active">访问日志</a>
<a href="simulate_test.php">模拟测试</a>
<a href="log_analytics.php">数据分析</a>
<a href="dashboard.php">配置中心</a>
</div>
@@ -406,6 +407,7 @@ if(empty($_SESSION['password']) || $_SESSION['password'] != LOGIN_PASSWORD) {
</div>
<div class="log-actions">
<button class="layui-btn layui-btn-sm" id="btn-refresh" onclick="resetTable()" lay-event="footerDemoBtn1">刷新</button>
<a href="simulate_test.php" class="layui-btn layui-btn-sm layui-btn-normal">模拟测试</a>
<button class="layui-btn layui-btn-sm layui-btn-normal" id="btn-col-settings" type="button">列设置</button>
<button class="layui-btn layui-btn-sm btn-danger" id="btn-clear" lay-on="confirm" lay-event="footerDemoBtn2">清空数据库</button>
<span class="log-count">当前查询数量:<strong id="total_nums">—</strong></span>
@@ -509,7 +511,13 @@ layui.use(function(){
}
}
function cellApiAudit(val, title) {
function cellApiAudit(val, title, d, field) {
if (val === '__LAZY__' && d && d.id && field) {
return '<div class="fp-hdata-cell">'
+ '<span class="fp-hdata-summary text-muted">已记录</span>'
+ '<span class="fp-hdata-actions"><span class="fp-hdata-btn fp-hdata-btn-json" data-log-id="' + escapeAttr(String(d.id)) + '" data-field="' + escapeAttr(field) + '" data-title="' + escapeAttr(title || 'API 数据') + '">查看 JSON</span></span>'
+ '</div>';
}
if (val == null || val === '' || val === '-') {
return '<span class="text-muted">—</span>';
}
@@ -522,30 +530,23 @@ layui.use(function(){
}
function cellFpHdata(d) {
if (!d.fp_hdata && !d.fp_hdata_text) {
if (!d.fp_hdata_text) {
return '<span class="text-muted">—</span>';
}
var summary = d.fp_hdata_explain_summary || d.fp_hdata_text || '';
var html = '';
html += '<div class="fp-hdata-cell">';
var summary = d.fp_hdata_text || '';
var html = '<div class="fp-hdata-cell">';
if (summary) {
html += '<span class="fp-hdata-summary" title="' + escapeAttr(summary) + '">' + escapeHtml(summary.length > 48 ? summary.slice(0, 48) + '…' : summary) + '</span>';
}
html += '<span class="fp-hdata-actions">';
if (d.fp_hdata_explain_html) {
html += '<span class="fp-hdata-btn" data-explain-id="' + escapeAttr(String(d.id)) + '">判定详情</span>';
window.__fpExplainCache = window.__fpExplainCache || {};
window.__fpExplainCache[d.id] = d.fp_hdata_explain_html;
}
if (d.fp_hdata || d.fp_hdata_text) {
html += '<span class="fp-hdata-btn fp-hdata-btn-raw" data-full="' + escapeAttr(d.fp_hdata || d.fp_hdata_text) + '">原始 hdata</span>';
}
html += '<span class="fp-hdata-btn" data-explain-id="' + escapeAttr(String(d.id)) + '">判定详情</span>';
html += '<span class="fp-hdata-btn fp-hdata-btn-raw" data-log-id="' + escapeAttr(String(d.id)) + '" data-field="fp_hdata">原始 hdata</span>';
html += '</span></div>';
return html;
}
var COLUMN_DEFS = [
{ field: 'id', title: 'ID', width: 56, fixed: true, defaultVisible: true },
{ field: 'id', title: 'ID', width: 56, fixed: 'left', defaultVisible: true },
{ field: 'domain', title: '域名', width: 120, defaultVisible: true, templet: function(d){ return cellEllipsis(d.domain, 18); } },
{ field: 'visit_date', title: '访问日期', width: 130, defaultVisible: true, templet: function(d){
return '<span class="cell-mono" title="' + escapeHtml(d.visit_date) + '">' + escapeHtml(d.visit_date) + '</span>';
@@ -570,10 +571,20 @@ layui.use(function(){
{ field: 'http_referer', title: 'HTTP-Referer', width: 260, defaultVisible: false, templet: function(d){ return cellLinkEllipsis(d.http_referer, LINK_CELL_MAX); } },
{ field: 'accept_language_raw', title: 'Accept-Language', width: 220, defaultVisible: false, templet: function(d){ return cellEllipsis(d.accept_language_raw, 32); } },
{ field: 'fp_hdata_text', title: '指纹 hdata', width: 360, defaultVisible: false, templet: function(d){ return cellFpHdata(d); } },
{ field: 'api_by_request_text', title: 'byApi 请求参数', width: 300, defaultVisible: false, templet: function(d){ return cellApiAudit(d.api_by_request_text, 'byApi 请求参数 #' + d.id); } },
{ field: 'api_by_response_text', title: 'byApi 返回值', width: 300, defaultVisible: false, templet: function(d){ return cellApiAudit(d.api_by_response_text, 'byApi 返回值 #' + d.id); } },
{ field: 'api_risk_request_text', title: 'byApiRisk 请求参数', width: 300, defaultVisible: false, templet: function(d){ return cellApiAudit(d.api_risk_request_text, 'byApiRisk 请求参数 #' + d.id); } },
{ field: 'api_risk_response_text', title: 'byApiRisk 返回值', width: 300, defaultVisible: false, templet: function(d){ return cellApiAudit(d.api_risk_response_text, 'byApiRisk 返回值 #' + d.id); } },
{ field: 'api_by_request_text', title: 'byApi 请求参数', width: 300, defaultVisible: false, templet: function(d){ return cellApiAudit(d.api_by_request_text, 'byApi 请求参数 #' + d.id, d, 'api_by_request'); } },
{ field: 'api_by_response_text', title: 'byApi 返回值', width: 300, defaultVisible: false, templet: function(d){ return cellApiAudit(d.api_by_response_text, 'byApi 返回值 #' + d.id, d, 'api_by_response'); } },
{ field: 'api_risk_request_text', title: 'byApiRisk 请求参数', width: 300, defaultVisible: false, templet: function(d){ return cellApiAudit(d.api_risk_request_text, 'byApiRisk 请求参数 #' + d.id, d, 'api_risk_request'); } },
{ field: 'api_risk_response_text', title: 'byApiRisk 返回值', width: 300, defaultVisible: false, templet: function(d){ return cellApiAudit(d.api_risk_response_text, 'byApiRisk 返回值 #' + d.id, d, 'api_risk_response'); } },
{ field: 'sim_tag', title: '模拟', width: 88, defaultVisible: true, templet: function(d){
if (String(d.is_simulation) === '1') {
var src = d.sim_source_log_id ? ' #' + d.sim_source_log_id : '';
return '<span class="cloak-badge badge-info">是' + escapeHtml(String(src)) + '</span>';
}
return '<span class="text-muted">—</span>';
}},
{ field: 'sim_action', title: '操作', width: 108, defaultVisible: true, templet: function(d){
return '<a class="layui-btn layui-btn-xs layui-btn-normal" href="simulate_test.php?from_log=' + encodeURIComponent(String(d.id)) + '">模拟测试</a>';
}},
];
var COLUMN_DEF_MAP = {};
@@ -681,14 +692,35 @@ layui.use(function(){
});
}
function applyTableRowStyles(res) {
if (!res || !Array.isArray(res.data)) {
return;
}
var $rows = $('.log-table-wrap .layui-table-body table tbody tr');
res.data.forEach(function(row, idx) {
var $tr = $rows.eq(idx);
if (!$tr.length) {
return;
}
if (row.reason && String(row.reason).indexOf('未确定流量来源') !== -1) {
$tr.css({ color: '#856404', backgroundColor: '#fff8e6' });
}
});
}
function onTableDataRendered(res, count) {
if (typeof count === 'number') {
$("#total_nums").text(count);
}
applyTableRowStyles(res);
fixRichTableCellHeights();
}
function reloadTableCols() {
table.reload(TABLE_ID, {
cols: buildCols(colConfig),
done: function(res, curr, count) {
if (typeof count === 'number') {
$("#total_nums").text(count);
}
fixRichTableCellHeights();
onTableDataRendered(res, count);
}
});
}
@@ -822,20 +854,8 @@ layui.use(function(){
size: "sm",
skin: "line",
even: true,
rowStyle: function(row) {
if (row.reason && row.reason.indexOf('未确定流量来源') !== -1) {
return 'color:#856404;background-color:#fff8e6;';
}
},
lineStyle: function(row) {
if (row.fp_hdata || row.fp_hdata_text || row.fp_hdata_explain_html) {
return 'height:auto;';
}
return '';
},
done: function(res, curr, count, origin){
$("#total_nums").text(count);
fixRichTableCellHeights();
onTableDataRendered(res, count);
},
});
@@ -844,28 +864,80 @@ layui.use(function(){
$(document).on('click', '.fp-hdata-btn', function(e) {
e.stopPropagation();
var id = this.getAttribute('data-explain-id');
if (id && window.__fpExplainCache && window.__fpExplainCache[id]) {
layer.open({
type: 1,
title: '二次风控判定解析 #' + id,
area: ['720px', '85vh'],
shadeClose: true,
content: '<div style="padding:16px 18px;max-height:calc(85vh - 60px);overflow:auto;">' + window.__fpExplainCache[id] + '</div>'
var logId = this.getAttribute('data-log-id');
var field = this.getAttribute('data-field');
var full = this.getAttribute('data-full') || '';
if (id) {
if (window.__fpExplainCache && window.__fpExplainCache[id]) {
layer.open({
type: 1,
title: '二次风控判定解析 #' + id,
area: ['720px', '85vh'],
shadeClose: true,
content: '<div style="padding:16px 18px;max-height:calc(85vh - 60px);overflow:auto;">' + window.__fpExplainCache[id] + '</div>'
});
return;
}
var loadIdx = layer.load(1);
$.getJSON('datatable/fp_explain.php', { id: id }, function(res) {
layer.close(loadIdx);
if (!res || res.code !== 0 || !res.html) {
layer.msg((res && res.msg) ? res.msg : '加载判定详情失败');
return;
}
window.__fpExplainCache = window.__fpExplainCache || {};
window.__fpExplainCache[id] = res.html;
layer.open({
type: 1,
title: '二次风控判定解析 #' + id,
area: ['720px', '85vh'],
shadeClose: true,
content: '<div style="padding:16px 18px;max-height:calc(85vh - 60px);overflow:auto;">' + res.html + '</div>'
});
}).fail(function() {
layer.close(loadIdx);
layer.msg('加载判定详情失败');
});
return;
}
var full = this.getAttribute('data-full') || '';
if (logId && field) {
var popupTitle = this.getAttribute('data-title') || (field === 'fp_hdata' ? '指纹 hdata' : field);
var isJson = this.classList.contains('fp-hdata-btn-json') || field.indexOf('api_') === 0 || field === 'fp_hdata';
var loadIdx2 = layer.load(1);
$.getJSON('datatable/log_blob.php', { id: logId, field: field }, function(res) {
layer.close(loadIdx2);
if (!res || res.code !== 0 || !res.data) {
layer.msg((res && res.msg) ? res.msg : '加载失败');
return;
}
var body = isJson ? escapeHtml(formatJsonPretty(res.data)) : escapeHtml(res.data);
layer.open({
type: 1,
title: popupTitle + ' #' + logId,
area: ['680px', '70vh'],
shadeClose: true,
content: '<pre style="padding:14px;margin:0;max-height:calc(70vh - 50px);overflow:auto;font-size:12px;white-space:pre-wrap;word-break:break-all;">' + body + '</pre>'
});
}).fail(function() {
layer.close(loadIdx2);
layer.msg('加载失败');
});
return;
}
if (full) {
var popupTitle = this.getAttribute('data-title') || '指纹 hdata';
var body = this.classList.contains('fp-hdata-btn-json')
var popupTitleStatic = this.getAttribute('data-title') || '指纹 hdata';
var bodyStatic = this.classList.contains('fp-hdata-btn-json')
? escapeHtml(formatJsonPretty(full))
: escapeHtml(full);
layer.open({
type: 1,
title: popupTitle,
title: popupTitleStatic,
area: ['680px', '70vh'],
shadeClose: true,
content: '<pre style="padding:14px;margin:0;max-height:calc(70vh - 50px);overflow:auto;font-size:12px;white-space:pre-wrap;word-break:break-all;">' + body + '</pre>'
content: '<pre style="padding:14px;margin:0;max-height:calc(70vh - 50px);overflow:auto;font-size:12px;white-space:pre-wrap;word-break:break-all;">' + bodyStatic + '</pre>'
});
}
});