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