Files
ObsidianHome/小鱼/.cdp/items-1106.mjs
T

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');