Files
ObsidianHome/小鱼/.cdp/extract2.mjs
T
2026-11-05 15:16:24 +08:00

97 lines
4.0 KiB
JavaScript

// 提取逐日序列 + 指标清单 + 分布,输出到文件
import fs from 'node:fs';
import path from 'node:path';
const dir = path.resolve(process.argv[2] || '.cdp/out/api');
const outFile = path.resolve(process.argv[3] || '.cdp/out/analysis-dump.md');
const files = fs.readdirSync(dir).filter(f => f.endsWith('.txt')).sort();
const load = (f) => {
const raw = fs.readFileSync(path.join(dir, f), 'utf8');
const i = raw.indexOf('\n\n');
const url = raw.slice(5, i).trim();
let body = raw.slice(i + 2).trim();
let json = null;
try { json = JSON.parse(body); } catch {
const m = body.match(/^[^(]*\((.*)\)[;\s]*$/s);
if (m) { try { json = JSON.parse(m[1]); } catch {} }
}
return { url, json, api: (url.match(/datacompass\.[A-Za-z0-9._]+/) || [''])[0] };
};
const deepFind = (o, key, hits = [], p = '') => {
if (o === null || typeof o !== 'object') return hits;
for (const k of Object.keys(o)) {
const np = p ? p + '.' + k : k;
if (k === key) hits.push({ path: np, value: o[k] });
deepFind(o[k], key, hits, np);
}
return hits;
};
const L = [];
const say = (s) => { L.push(s); };
for (const f of files) {
const { json, api } = load(f);
if (!json) continue;
const rr = deepFind(json, 'realDateRange')[0];
const banner = deepFind(json, 'bannerDataList')[0];
const graph = deepFind(json, 'graphDataList')[0];
const cycle = banner?.value?.[0]?.cycle || '';
say('\n' + '='.repeat(90));
say(`FILE ${f}`);
say(` api=${api.replace('datacompass.', '')} range=${JSON.stringify(rr?.value)} cycle=${cycle} bannerN=${banner?.value?.length ?? 0} graphN=${graph?.value?.length ?? 0}`);
if (banner && f.startsWith('30d-') && Number(f.slice(4, 6)) >= 6) {
say(' --- bannerDataList (指标总表) ---');
for (const b of banner.value) {
say(` ${String(b.name).padEnd(22)} now=${String(b.dataStr).padEnd(12)} prev=${String(b.lastDataStr).padEnd(12)} Δ=${String(b.ratioFormat).padEnd(10)}`);
}
}
if (graph && f.startsWith('30d-')) {
const rows = graph.value;
const keys = ['ds', 'payAmt', 'payOrdCnt', 'payByrCnt', 'aov', 'showPv', 'showUv', 'vstPv', 'vstUv', 'ipv', 'ipvUv', 'chatUv', 'favCnt', 'rep3minUvRate', 'rfdAmt', 'rfdOrdCnt', 'onlCnt', 'newItmCnt', 'stItmCnt', 'uctr', 'uctcvr', 'cmtItmCnt'];
say(' --- graphDataList (逐日) ---');
say(' ' + keys.map(k => k.padStart(13)).join(''));
for (const r of rows) {
say(' ' + keys.map(k => {
let v = r[k];
if (k === 'rep3minUvRate' || k === 'uctr' || k === 'uctcvr') v = (v === undefined || v === null) ? '-' : (v * 100).toFixed(1) + '%';
return String(v ?? '-').padStart(13);
}).join(''));
}
// 汇总校验
const sum = (k) => rows.reduce((a, b) => a + (Number(b[k]) || 0), 0);
say(` SUM payAmt=${sum('payAmt').toFixed(2)} payOrdCnt=${sum('payOrdCnt')} showPv=${sum('showPv')} vstPv=${sum('vstPv')} ipv=${sum('ipv')} chatUv=${sum('chatUv')}`);
}
// 分布类
if (/browse/.test(api)) {
const dists = deepFind(json, 'distributionList');
const anyArr = [];
const walkArr = (o, p) => {
if (!o || typeof o !== 'object') return;
for (const k of Object.keys(o)) {
if (Array.isArray(o[k]) && o[k].length && typeof o[k][0] === 'object') anyArr.push([p + '.' + k, o[k]]);
else walkArr(o[k], p + '.' + k);
}
};
walkArr(json.data, 'data');
say(' --- browse 分布数组 ---');
for (const [p, arr] of anyArr.slice(0, 12)) {
say(` ${p} len=${arr.length}`);
say(' ' + JSON.stringify(arr.slice(0, 12)).slice(0, 1200));
}
}
if (/flow\.detail/.test(api)) {
const t = deepFind(json, 'itemFlowTransferData')[0];
say(' --- flow itemFlowTransferData ---');
if (t) for (const k of Object.keys(t.value)) {
const v = t.value[k];
say(` ${k.padEnd(20)} now=${String(v?.dataStr).padEnd(12)} prev=${String(v?.lastDataStr).padEnd(12)} Δ=${String(v?.ratioFormat).padEnd(10)}`);
}
}
}
fs.writeFileSync(outFile, L.join('\n'), 'utf8');
console.log('written: ' + outFile + ' lines=' + L.length);