81 lines
3.6 KiB
JavaScript
81 lines
3.6 KiB
JavaScript
import fs from 'node:fs';
|
|
const o = JSON.parse(fs.readFileSync('.cdp/out/items-1106.json', 'utf8'));
|
|
const v = o.value || o;
|
|
const N = s => Number(s) || 0;
|
|
const d7 = v.data.recent7d.items, d30 = v.data.recent30d.items;
|
|
|
|
const m30 = new Map(d30.map(x => [x.id, x]));
|
|
const rows = d7.map(x => {
|
|
const a = m30.get(x.id) || {};
|
|
const pv7 = N(x.pv), pv30 = N(a.pv);
|
|
return {
|
|
id: x.id, name: x.name, price: x.price, cat: x.c4,
|
|
days: N(x.days), postDt: x.postDt,
|
|
pv7, uv7: N(x.ipvUv), ipv7: N(x.ipv), chat7: N(x.chat), ord7: N(x.ord), amt7: N(x.amt),
|
|
pv30, uv30: N(a.ipvUv), ord30: N(a.ord), amt30: N(a.amt),
|
|
// 近7天日均 vs 近30天日均
|
|
rate7: pv7 / 7,
|
|
rate30: pv30 / 30,
|
|
};
|
|
});
|
|
for (const r of rows) {
|
|
r.ratio = r.rate30 > 0 ? r.rate7 / r.rate30 : (r.pv7 > 0 ? 99 : 0);
|
|
// 曝光流失量(近7天相对30天日均的缺口 × 7)
|
|
r.gap = Math.round((r.rate30 - r.rate7) * 7);
|
|
}
|
|
|
|
const L = [];
|
|
const sum = (arr, f) => arr.reduce((a, b) => a + N(b[f]), 0);
|
|
L.push('商品数: 7天 ' + d7.length + ' / 30天 ' + d30.length);
|
|
L.push('近7天 曝光合计 ' + sum(d7, 'pv') + ' | 近30天 曝光合计 ' + sum(d30, 'pv'));
|
|
L.push('近7天 支付合计 ' + sum(d7, 'ord') + ' 单 / ¥' + sum(d7, 'amt').toFixed(2));
|
|
L.push('近30天 支付合计 ' + sum(d30, 'ord') + ' 单 / ¥' + sum(d30, 'amt').toFixed(2));
|
|
L.push('');
|
|
|
|
// 1) 曝光流失最严重的(30天日均高、7天日均低)
|
|
const bleed = rows.filter(r => r.pv30 >= 300).sort((a, b) => b.gap - a.gap).slice(0, 25);
|
|
L.push('=== 曝光流失 Top25(近30天曝光≥300,按日均缺口排序)===');
|
|
L.push('ID | 曝光30d | 日均30d | 日均7d | 缺口(7天折算) | 单30d | 单7d | 价 | 类目 | 名称');
|
|
L.push('---|---|---:|---:|---:|---:|---:|---:|---:|---|---');
|
|
for (const r of bleed) {
|
|
L.push([r.id, r.pv30, r.rate30.toFixed(1), r.rate7.toFixed(1), r.gap, r.ord30, r.ord7, '¥' + r.price, r.cat, String(r.name).slice(0, 26)].join(' | '));
|
|
}
|
|
L.push('');
|
|
|
|
// 2) 近7天曝光最高的(当前流量在哪)
|
|
const top7 = [...rows].sort((a, b) => b.pv7 - a.pv7).slice(0, 20);
|
|
L.push('=== 近7天曝光 Top20(现在的流量在哪)===');
|
|
L.push('ID | 曝光7d | 浏览人 | 询单 | 单 | 金额 | 价 | 类目 | 名称');
|
|
L.push('---|---|---:|---:|---:|---:|---:|---:|---|---');
|
|
for (const r of top7) {
|
|
L.push([r.id, r.pv7, r.uv7, r.chat7, r.ord7, '¥' + r.amt7, '¥' + r.price, r.cat, String(r.name).slice(0, 26)].join(' | '));
|
|
}
|
|
L.push('');
|
|
|
|
// 3) 近7天出单的
|
|
const sellers7 = rows.filter(r => r.ord7 > 0).sort((a, b) => N(b.amt7) - N(a.amt7));
|
|
L.push('=== 近7天出单商品(共 ' + sellers7.length + ' 个)===');
|
|
L.push('ID | 单 | 金额 | 曝光 | 价 | 类目 | 名称');
|
|
L.push('---|---|---:|---:|---:|---:|---|---');
|
|
for (const r of sellers7) {
|
|
L.push([r.id, r.ord7, '¥' + r.amt7, r.pv7, '¥' + r.price, r.cat, String(r.name).slice(0, 30)].join(' | '));
|
|
}
|
|
L.push('');
|
|
|
|
// 4) 零浏览
|
|
const zero7 = rows.filter(r => N(r.uv7) === 0);
|
|
L.push('零浏览商品(7天): ' + zero7.length + ' / ' + rows.length);
|
|
L.push('零成交商品(7天): ' + rows.filter(r => r.ord7 === 0).length);
|
|
L.push('');
|
|
|
|
// 5) 新品(近30天上架)
|
|
const fresh = rows.filter(r => r.days <= 30).sort((a, b) => b.pv7 - a.pv7);
|
|
L.push('=== 在架≤30天的新品(' + fresh.length + ' 个)===');
|
|
L.push('ID | 在架天 | 曝光7d | 浏览人 | 询单 | 单7d | 单30d | 价 | 名称');
|
|
L.push('---|---|---:|---:|---:|---:|---:|---:|---:|---');
|
|
for (const r of fresh) {
|
|
L.push([r.id, r.days, r.pv7, r.uv7, r.chat7, r.ord7, r.ord30, '¥' + r.price, String(r.name).slice(0, 30)].join(' | '));
|
|
}
|
|
|
|
fs.writeFileSync('.cdp/out/items-1106-analysis.txt', L.join('\n'), 'utf8');
|