layui.define(['form', 'table', 'http', 'auth', 'laydate'], function (exports) {
var $ = layui.jquery,
form = layui.form,
table = layui.table,
layer = layui.layer,
http = layui.http,
laydate = layui.laydate,
auth = layui.auth;
var _savedPerms = (layui.data('backend').permission) || [];
auth.init(_savedPerms);
auth.setController('spider');
// 日期选择
laydate.render({ elem: '#dateInput' });
// 汇总 + 趋势(纯 CSS 迷你条形图,不依赖 echarts)
function loadStats() {
http.get('stats').then(function (res) {
if (res.code !== 0) { return; }
var summary = (res.data && res.data.summary) || [];
var trend = (res.data && res.data.trend) || { dates: [], series: [] };
var $body = $('#summaryBody').empty();
// DNS 熔断提示
if (res.data && res.data.dns_down) {
$('#dnsWarn').show().html('DNS 查询连续失败,真伪校验已临时降级(5 分钟后自动恢复)。请检查服务器 DNS 配置或 /etc/resolv.conf。');
} else {
$('#dnsWarn').hide();
}
if (summary.length === 0) {
$body.append('
| 暂无蜘蛛来访记录 |
');
return;
}
var seriesMap = {};
(trend.series || []).forEach(function (s) { seriesMap[s.name] = s.data; });
// 蜘蛛下拉(去重,避免重复追加)
var $sel = $('#spiderSelect');
if ($sel.find('option').length <= 1) {
summary.forEach(function (row) {
$sel.append('');
});
form.render('select');
}
summary.forEach(function (row) {
var line = seriesMap[row.label] || [];
var max = Math.max.apply(null, line.concat([1]));
var bars = line.map(function (v, i) {
var h = v > 0 ? Math.max(2, Math.round(v / max * 28)) : 1;
return '';
}).join('');
// 不支持 rDNS 校验的蜘蛛,真伪列显示占位说明
var realCell, fakeCell;
if (row.verifiable === false) {
realCell = '—';
fakeCell = '—';
} else {
realCell = '' + (row.real || 0) + '';
fakeCell = (row.fake > 0)
? '' + row.fake + ''
: '0';
}
$body.append(''
+ '| ' + row.label + ' (' + row.spider + ') | '
+ '' + row.today + ' | '
+ '' + row.week + ' | '
+ '' + row.month + ' | '
+ '' + row.total + ' | '
+ '' + realCell + ' | '
+ '' + fakeCell + ' | '
+ '' + bars + ' | '
+ '
');
});
});
}
loadStats();
// 明细表
table.render({
elem: '#dataTable',
url: 'index',
toolbar: '#tableBar',
defaultToolbar: ['filter', 'exports', 'print'],
parseData: function (res) {
return {
code: res.code === 0 ? 0 : 1,
msg: res.message || '',
count: res.count || (res.data ? res.data.length : 0),
data: res.data || []
};
},
cols: [[
{ field: 'id', width: 90, title: 'ID', sort: true },
{ field: 'spider_text', width: 110, title: '蜘蛛' },
{ field: 'verified_text', width: 100, title: '真伪', templet: function (d) {
if (d.verified === 1) {
return '真蜘蛛';
}
if (d.verified === 2) {
return '假蜘蛛';
}
return '待验证';
}},
{ field: 'rdns', width: 220, title: 'rDNS', templet: function (d) {
return d.rdns ? '' + d.rdns + '' : '—';
}},
{ field: 'url', title: '抓取URL', minWidth: 220 },
{ field: 'ip', width: 140, title: '来源IP' },
{ field: 'app', width: 90, title: '应用' },
{ field: 'http_code', width: 80, title: '状态码' },
{ field: 'time_text', width: 170, title: '抓取时间' }
]],
limits: [15, 30, 50, 100],
limit: 15,
page: true,
skin: 'line',
done: function () {
var view = $('#dataTable').next('.layui-table-view');
auth.filterDom(view);
}
});
// 搜索
form.on('submit(data-search-btn)', function (data) {
table.reload('dataTable', { page: { curr: 1 }, where: data.field });
return false;
});
$('#btn-reset').on('click', function () {
$('input[name="ip"],input[name="url"],input[name="date"]').val('');
$('#spiderSelect').val('');
$('#verifiedSelect').val('');
form.render('select');
table.reload('dataTable', { page: { curr: 1 }, where: { spider: '', verified: '', ip: '', url: '', date: '' } });
});
// 工具条:重新校验 / 清理旧日志
table.on('toolbar(dataTable)', function (elem) {
if (elem.event === 'dataRecheck') {
layer.confirm('将对历史上「待验证」的蜘蛛记录重新做 rDNS 双向校验(每 IP 一次 DNS 查询,最多 200 个 IP),可能耗时数秒。是否继续?', {
title: '重新校验真伪',
btn: ['开始校验', '取消']
}, function (index) {
layer.close(index);
var loadIndex = layer.load(2, { shade: [0.3, '#000'] });
http.post('recheck', { limit: 200 }).then(function (res) {
layer.close(loadIndex);
if (res.code === 0) {
layer.msg(res.message || '校验完成', { icon: 1, time: 5000 });
table.reload('dataTable', {}, true);
loadStats();
} else {
layer.msg(res.msg || res.message || '校验失败', { icon: 2 });
}
});
});
return;
}
if (elem.event === 'dataClear') {
layer.prompt({ title: '保留最近多少天的明细?(聚合报表不受影响)', value: '30' }, function (value, index) {
layer.close(index);
var days = parseInt(value, 10) || 30;
http.post('clear', { days: days }).then(function (res) {
if (res.code === 0) {
layer.msg(res.message || '清理完成');
table.reload('dataTable', {}, true);
} else {
layer.msg(res.msg || res.message || '清理失败');
}
});
});
}
});
exports('spider', {});
});