新增骗量排行榜: 按account统计>500点击且0转化的campaign占比,降序

This commit is contained in:
2026-09-10 19:12:39 +08:00
parent 8f4134bfac
commit ce7317258a
+60
View File
@@ -119,6 +119,29 @@
</table> </table>
</div> </div>
</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 总数 &gt; 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;">(&gt;500点击 &amp; 0转化)</span></th>
<th>骗量占比</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div> </div>
<script> <script>
@@ -230,6 +253,42 @@ function renderDetail(){
updateSortArrows(); 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){ function renderTotal(rows){
var c=0,cv=0,rev=0,cost=0; 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); }); 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||[]; ALL = d.detail||[];
document.getElementById('meta').textContent='数据更新时间:'+(d.updated_at||'—')+' | 总表 '+SUMMARY.length+' 天 | 明细 '+ALL.length+' 行'; document.getElementById('meta').textContent='数据更新时间:'+(d.updated_at||'—')+' | 总表 '+SUMMARY.length+' 天 | 明细 '+ALL.length+' 行';
renderSummary(); renderSummary();
renderFraud();
fillSelects(); fillSelects();
// 默认 dt 范围 = 数据里的最早~最新 // 默认 dt 范围 = 数据里的最早~最新
if(ALL.length){ if(ALL.length){