feat(admin): track capture-to-report conversion funnel

This commit is contained in:
xsl
2026-10-16 15:45:13 +08:00
parent 03a8907b4e
commit 9ff7a582af
8 changed files with 222 additions and 0 deletions
+9
View File
@@ -29,6 +29,7 @@ const ReportUI = require('./js/report-ui.js');
const BeijingTime = require('./js/beijing-time.js');
const CaptureDiagnostics = require('./lib/capture-diagnostics.js');
const WrinkleViews = require('./lib/wrinkle-views.js');
const captureFunnel = require('./lib/capture-funnel.js')(path.join(DATA_ROOT, 'capture-funnel.jsonl'));
const accounts = require('./lib/accounts.js')(path.join(DATA_ROOT, 'accounts'));
/* 服务端评分引擎:与 C 端同一份公式(禁止复制公式,直接复用) */
@@ -751,6 +752,7 @@ function handleStats(req, res) {
fail_total: failed.length,
success_rate: finished.length ? Math.round(succeeded.length * 1000 / finished.length) / 10 : 0,
avg_cost_ms: avgCost,
capture_funnel: captureFunnel.stats(now),
fail_dist,
dim_dist,
group_dist,
@@ -874,6 +876,13 @@ async function handleAdmin(req, res, pathname, url) {
http.createServer(async (req, res) => {
const pathname = req.url.split('?')[0];
if (await accounts.route(req, res, pathname)) return;
if (req.method === 'POST' && pathname === '/api/capture-funnel') {
if (!accounts.sameOrigin(req) || !accounts.session(req)) return sendJson(res, 403, { ok: false });
try {
const accepted = captureFunnel.ingest(JSON.parse((await readBody(req, 4096)).toString('utf8')));
return sendJson(res, accepted ? 200 : 400, { ok: accepted });
} catch (e) { return sendJson(res, e.message === 'TOO_LARGE' ? 413 : e instanceof SyntaxError ? 400 : 500, { ok: false }); }
}
if (req.method === 'POST' && pathname === '/api/skin-analyze') return handleAnalyze(req, res);
if (req.method === 'GET' && pathname === '/api/health') {
res.writeHead(200, { 'Content-Type': 'application/json' });