新增骗量排行榜: 按account统计>500点击且0转化的campaign占比,降序
This commit is contained in:
+60
@@ -119,6 +119,29 @@
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>骗量排行榜 <span class="tag">前一天 · 按 account · 占比降序</span></h2>
|
||||
<ul class="note-list">
|
||||
<li>按 <b>campaign_id</b> 重新聚合(同 campaign_id 的所有 adping_id 的 clicks、conversion 累加)</li>
|
||||
<li><b>可疑 campaign</b>:clicks 总数 > 500 且 conversion = 0 的 campaign</li>
|
||||
<li><b>骗量占比</b> = 可疑 campaign 数 / campaign 总数,占比越高越靠前</li>
|
||||
</ul>
|
||||
<div style="overflow:auto;">
|
||||
<table id="fraudTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>排名</th>
|
||||
<th class="txt">account</th>
|
||||
<th>campaign 总数</th>
|
||||
<th>可疑 campaign 数<br><span style="font-weight:normal;font-size:11px;">(>500点击 & 0转化)</span></th>
|
||||
<th>骗量占比</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
@@ -230,6 +253,42 @@ function renderDetail(){
|
||||
updateSortArrows();
|
||||
}
|
||||
|
||||
function renderFraud(){
|
||||
// 取"前一天" = 明细里最新的 dt
|
||||
if(!ALL.length){ document.querySelector('#fraudTable tbody').innerHTML='<tr><td colspan="5" class="empty">暂无数据</td></tr>'; return; }
|
||||
var maxDt = ALL.reduce(function(m,d){ return dtKey(d.dt)>dtKey(m)?d.dt:m; }, ALL[0].dt);
|
||||
var rows = ALL.filter(function(d){ return d.dt===maxDt; });
|
||||
// 按 account -> campaign_id 聚合 clicks/conversion
|
||||
var byAcc = {}; // acc -> { campId -> {clicks,conv} }
|
||||
rows.forEach(function(d){
|
||||
var acc=d.account||'未知', cid=String(d.campaign_id||'');
|
||||
if(!byAcc[acc]) byAcc[acc]={};
|
||||
if(!byAcc[acc][cid]) byAcc[acc][cid]={clicks:0,conv:0};
|
||||
byAcc[acc][cid].clicks += (+d.clicks||0);
|
||||
byAcc[acc][cid].conv += (+d.conversion||0);
|
||||
});
|
||||
var list = Object.keys(byAcc).map(function(acc){
|
||||
var camps = byAcc[acc];
|
||||
var ids = Object.keys(camps);
|
||||
var total = ids.length;
|
||||
var suspicious = ids.filter(function(cid){ return camps[cid].clicks>500 && camps[cid].conv===0; }).length;
|
||||
return { account:acc, total:total, suspicious:suspicious, ratio: total? suspicious/total : 0 };
|
||||
});
|
||||
list.sort(function(a,b){ return b.ratio-a.ratio || b.suspicious-a.suspicious || b.total-a.total; });
|
||||
var tb=document.querySelector('#fraudTable tbody');
|
||||
if(!list.length){ tb.innerHTML='<tr><td colspan="5" class="empty">暂无数据</td></tr>'; return; }
|
||||
tb.innerHTML = list.map(function(r,i){
|
||||
var hot = r.ratio>=0.5 ? 'neg' : '';
|
||||
return '<tr>'
|
||||
+'<td>'+(i+1)+'</td>'
|
||||
+'<td class="txt">'+r.account+'</td>'
|
||||
+'<td>'+num(r.total)+'</td>'
|
||||
+'<td>'+num(r.suspicious)+'</td>'
|
||||
+'<td class="'+hot+'">'+pct(r.ratio)+'</td>'
|
||||
+'</tr>';
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function renderTotal(rows){
|
||||
var c=0,cv=0,rev=0,cost=0;
|
||||
rows.forEach(function(d){ c+=(+d.clicks||0); cv+=(+d.conversion||0); rev+=(+d.revenue||0); cost+=(+d.cost||0); });
|
||||
@@ -267,6 +326,7 @@ fetch('data.json?_='+Date.now()).then(function(r){return r.json();}).then(functi
|
||||
ALL = d.detail||[];
|
||||
document.getElementById('meta').textContent='数据更新时间:'+(d.updated_at||'—')+' | 总表 '+SUMMARY.length+' 天 | 明细 '+ALL.length+' 行';
|
||||
renderSummary();
|
||||
renderFraud();
|
||||
fillSelects();
|
||||
// 默认 dt 范围 = 数据里的最早~最新
|
||||
if(ALL.length){
|
||||
|
||||
Reference in New Issue
Block a user