DSP-routing-daily: 每日总表+Kibana监测明细报告

This commit is contained in:
2026-09-10 16:56:38 +08:00
commit 94d711c8c8
4 changed files with 414 additions and 0 deletions
+206
View File
@@ -0,0 +1,206 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DSP Routing Daily 报告</title>
<style>
:root{--bg:#f5f6f8;--card:#fff;--line:#e3e6ea;--head:#1f2937;--sub:#6b7280;--accent:#2563eb;--accent-bg:#eff4ff;}
*{box-sizing:border-box;}
body{margin:0;background:var(--bg);color:#1f2937;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif;font-size:13px;}
.wrap{max-width:1500px;margin:0 auto;padding:24px 20px 60px;}
h1{font-size:20px;margin:0 0 4px;}
.meta{color:var(--sub);font-size:12px;margin-bottom:20px;}
.card{background:var(--card);border:1px solid var(--line);border-radius:10px;padding:16px 18px;margin-bottom:22px;box-shadow:0 1px 2px rgba(0,0,0,.03);}
.card h2{font-size:15px;margin:0 0 12px;display:flex;align-items:center;gap:8px;}
.card h2 .tag{font-size:11px;font-weight:normal;color:var(--sub);background:var(--bg);border:1px solid var(--line);padding:1px 8px;border-radius:20px;}
table{border-collapse:collapse;width:100%;font-variant-numeric:tabular-nums;}
th,td{border:1px solid var(--line);padding:7px 10px;text-align:right;white-space:nowrap;}
th{background:var(--head);color:#fff;font-weight:600;text-align:center;position:sticky;top:0;cursor:default;}
td:first-child,th:first-child{text-align:center;}
.detail-scroll{max-height:620px;overflow:auto;border:1px solid var(--line);border-radius:8px;}
.detail-scroll table th{top:0;}
tbody tr:nth-child(even){background:#fafbfc;}
tbody tr:hover{background:var(--accent-bg);}
.txt{text-align:left;}
.filters{display:flex;flex-wrap:wrap;gap:14px;align-items:flex-end;margin-bottom:14px;}
.fld{display:flex;flex-direction:column;gap:4px;}
.fld label{font-size:11px;color:var(--sub);}
.fld input,.fld select{border:1px solid var(--line);border-radius:6px;padding:6px 8px;font-size:13px;min-width:150px;background:#fff;}
.fld input[type=date]{min-width:140px;}
.btn{border:1px solid var(--accent);background:var(--accent);color:#fff;border-radius:6px;padding:7px 16px;font-size:13px;cursor:pointer;}
.btn.ghost{background:#fff;color:var(--accent);}
.count-note{color:var(--sub);font-size:12px;margin:6px 2px 0;}
.pos{color:#059669;} .neg{color:#dc2626;}
.empty{padding:30px;text-align:center;color:var(--sub);}
.sum-metric{color:var(--accent);font-weight:600;}
</style>
</head>
<body>
<div class="wrap">
<h1>DSP Routing Daily 报告</h1>
<div class="meta" id="meta">加载中…</div>
<div class="card">
<h2>每日总表 <span class="tag">每天累加一行</span></h2>
<div style="overflow:auto;">
<table id="summaryTable">
<thead>
<tr>
<th>日期</th><th>原始订单</th><th>pkg</th><th>中转订单</th>
<th>有点击订单</th><th>无转化订单</th><th>无点击订单</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
<div class="card">
<h2>Kibana 监测明细表 <span class="tag">按 adping_id 聚合 · 近14天</span></h2>
<div class="filters">
<div class="fld"><label>dt 起</label><input type="date" id="fDtFrom"></div>
<div class="fld"><label>dt 止</label><input type="date" id="fDtTo"></div>
<div class="fld"><label>融合后campaign id (routing_campaign_id)</label><input type="text" id="fRc" placeholder="模糊匹配"></div>
<div class="fld"><label>融合前campaign id (campaign_id)</label><input type="text" id="fCid" placeholder="模糊匹配"></div>
<div class="fld"><label>second_channel</label><select id="fSc"><option value="">全部</option></select></div>
<div class="fld"><label>account</label><select id="fAcc"><option value="">全部</option></select></div>
<button class="btn" id="applyBtn">筛选</button>
<button class="btn ghost" id="resetBtn">重置</button>
</div>
<div class="count-note" id="detailNote"></div>
<div class="detail-scroll">
<table id="detailTable">
<thead>
<tr>
<th>dt</th>
<th>融合后campaign id</th>
<th>融合前campaign id</th>
<th>adping_id</th>
<th>second_channel</th>
<th>account</th>
<th>pkg</th>
<th>clicks</th>
<th>conversion</th>
<th>cvr</th>
<th>revenue</th>
<th>cost</th>
<th>GP</th>
<th>ecpc</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
<script>
var ALL = []; // 全部明细
var SUMMARY = [];
function num(n){ return (n==null||n==='')?'':Number(n).toLocaleString('en-US'); }
function pct(n){ if(n==null||n===''||isNaN(n)) return ''; return (Number(n)*100).toFixed(2)+'%'; }
function money(n){ if(n==null||n===''||isNaN(n)) return ''; return Number(n).toLocaleString('en-US',{minimumFractionDigits:2,maximumFractionDigits:2}); }
function dec(n,d){ if(n==null||n===''||isNaN(n)) return ''; return Number(n).toFixed(d==null?4:d); }
function dtKey(s){ // "2026/9/9" -> 20260909 数值,便于比较
var p=String(s).split('/'); return parseInt(p[0])*10000+parseInt(p[1])*100+parseInt(p[2]);
}
function dtToInput(s){ var p=String(s).split('/'); return p[0]+'-'+String(p[1]).padStart(2,'0')+'-'+String(p[2]).padStart(2,'0'); }
function inputToKey(v){ if(!v) return null; var p=v.split('-'); return parseInt(p[0])*10000+parseInt(p[1])*100+parseInt(p[2]); }
function renderSummary(){
var tb=document.querySelector('#summaryTable tbody');
if(!SUMMARY.length){ tb.innerHTML='<tr><td colspan="7" class="empty">暂无数据</td></tr>'; return; }
tb.innerHTML = SUMMARY.slice().sort(function(a,b){return dtKey(b.date)-dtKey(a.date);}).map(function(r){
return '<tr><td>'+r.date+'</td>'
+'<td class="sum-metric">'+num(r.raw_orders)+'</td>'
+'<td>'+num(r.pkg)+'</td>'
+'<td class="sum-metric">'+num(r.relay_orders)+'</td>'
+'<td>'+num(r.has_click_orders)+'</td>'
+'<td>'+num(r.no_conv_orders)+'</td>'
+'<td>'+num(r.no_click_orders)+'</td></tr>';
}).join('');
}
function fillSelects(){
var scSet={}, accSet={};
ALL.forEach(function(d){ if(d.second_channel) scSet[d.second_channel]=1; if(d.account) accSet[d.account]=1; });
var scSel=document.getElementById('fSc'), accSel=document.getElementById('fAcc');
Object.keys(scSet).sort().forEach(function(v){ scSel.insertAdjacentHTML('beforeend','<option>'+v+'</option>'); });
Object.keys(accSet).sort().forEach(function(v){ accSel.insertAdjacentHTML('beforeend','<option>'+v+'</option>'); });
}
function currentFilter(d){
var from=inputToKey(document.getElementById('fDtFrom').value);
var to=inputToKey(document.getElementById('fDtTo').value);
var rc=document.getElementById('fRc').value.trim().toLowerCase();
var cid=document.getElementById('fCid').value.trim().toLowerCase();
var sc=document.getElementById('fSc').value;
var acc=document.getElementById('fAcc').value;
var k=dtKey(d.dt);
if(from!=null && k<from) return false;
if(to!=null && k>to) return false;
if(rc && String(d.routing_campaign_id).toLowerCase().indexOf(rc)<0) return false;
if(cid && String(d.campaign_id).toLowerCase().indexOf(cid)<0) return false;
if(sc && d.second_channel!==sc) return false;
if(acc && d.account!==acc) return false;
return true;
}
function renderDetail(){
var rows = ALL.filter(currentFilter).sort(function(a,b){
return dtKey(b.dt)-dtKey(a.dt) || (b.clicks-a.clicks);
});
var tb=document.querySelector('#detailTable tbody');
if(!rows.length){ tb.innerHTML='<tr><td colspan="14" class="empty">无匹配数据</td></tr>'; }
else {
tb.innerHTML = rows.map(function(d){
var gpCls = (d.gp>=0)?'pos':'neg';
return '<tr>'
+'<td>'+d.dt+'</td>'
+'<td>'+d.routing_campaign_id+'</td>'
+'<td>'+d.campaign_id+'</td>'
+'<td>'+d.adping_id+'</td>'
+'<td class="txt">'+d.second_channel+'</td>'
+'<td class="txt">'+d.account+'</td>'
+'<td class="txt">'+d.pkg+'</td>'
+'<td>'+num(d.clicks)+'</td>'
+'<td>'+num(d.conversion)+'</td>'
+'<td>'+pct(d.cvr)+'</td>'
+'<td>'+money(d.revenue)+'</td>'
+'<td>'+money(d.cost)+'</td>'
+'<td class="'+gpCls+'">'+pct(d.gp)+'</td>'
+'<td>'+dec(d.ecpc,4)+'</td>'
+'</tr>';
}).join('');
}
document.getElementById('detailNote').textContent='显示 '+rows.length+' / '+ALL.length+' 行';
}
document.getElementById('applyBtn').onclick=renderDetail;
document.getElementById('resetBtn').onclick=function(){
['fDtFrom','fDtTo','fRc','fCid'].forEach(function(id){document.getElementById(id).value='';});
document.getElementById('fSc').value=''; document.getElementById('fAcc').value='';
renderDetail();
};
fetch('data.json?_='+Date.now()).then(function(r){return r.json();}).then(function(d){
SUMMARY = d.summary||[];
ALL = d.detail||[];
document.getElementById('meta').textContent='数据更新时间:'+(d.updated_at||'—')+' | 总表 '+SUMMARY.length+' 天 | 明细 '+ALL.length+' 行';
renderSummary();
fillSelects();
// 默认 dt 范围 = 数据里的最早~最新
if(ALL.length){
var ks=ALL.map(function(x){return x.dt;}).sort(function(a,b){return dtKey(a)-dtKey(b);});
document.getElementById('fDtFrom').value=dtToInput(ks[0]);
document.getElementById('fDtTo').value=dtToInput(ks[ks.length-1]);
}
renderDetail();
}).catch(function(e){
document.getElementById('meta').textContent='数据加载失败:'+e;
});
</script>
</body>
</html>