/** * 埋点验证脚本 * * 检查 simple_logs 表中是否存在各关键埋点, * 并汇总每个模块/功能的记录数量。 * * 用法: npx tsx scripts/verify-activity-tracking.ts [--days N] */ import { PrismaClient } from '@prisma/client'; const prisma = new PrismaClient(); interface TrackingPoint { module: string; feature: string; label: string; } const EXPECTED_TRACKING_POINTS: TrackingPoint[] = [ // 系统级 { module: 'SYSTEM', feature: '用户登录', label: '用户登录' }, { module: 'SYSTEM', feature: '顶部导航点击', label: '顶部导航点击' }, // ASL { module: 'ASL', feature: '意图识别', label: 'ASL 意图识别(需求扩写)' }, { module: 'ASL', feature: 'Deep Research', label: 'ASL 启动 Deep Research' }, // AIA (各智能体名称作为 feature) { module: 'AIA', feature: '科学问题梳理', label: 'AIA 智能体 - 科学问题梳理(示例)' }, // PKB { module: 'PKB', feature: '创建知识库', label: 'PKB 创建知识库' }, // DC { module: 'DC', feature: '智能数据清洗', label: 'DC 智能数据清洗' }, // IIT/CRA { module: 'IIT', feature: 'CRA质控', label: 'IIT CRA质控' }, // RVW { module: 'RVW', feature: '稿', label: 'RVW 稿件审查相关' }, ]; async function main() { const daysArg = process.argv.findIndex(a => a === '--days'); const days = daysArg >= 0 ? parseInt(process.argv[daysArg + 1], 10) || 7 : 7; const since = new Date(); since.setDate(since.getDate() - days); console.log(`\n📊 埋点验证报告 (最近 ${days} 天,since ${since.toISOString().slice(0, 10)})\n`); console.log('='.repeat(80)); // 1. 总记录数 const totalCount = await prisma.simple_logs.count({ where: { created_at: { gte: since } }, }); console.log(`\n📈 总记录数: ${totalCount}\n`); // 2. 按模块统计 const moduleStats = await prisma.$queryRaw` SELECT module, COUNT(*) as count FROM admin_schema.simple_logs WHERE created_at >= ${since} GROUP BY module ORDER BY count DESC ` as Array<{ module: string; count: bigint }>; console.log('📦 模块统计:'); console.log('-'.repeat(40)); for (const row of moduleStats) { console.log(` ${row.module.padEnd(12)} ${Number(row.count).toString().padStart(6)} 条`); } // 3. 检查每个关键埋点是否存在 console.log('\n🔍 关键埋点覆盖检查:'); console.log('-'.repeat(80)); let coveredCount = 0; let missingCount = 0; for (const tp of EXPECTED_TRACKING_POINTS) { const count = await prisma.simple_logs.count({ where: { module: tp.module, feature: { contains: tp.feature }, created_at: { gte: since }, }, }); const status = count > 0 ? '✅' : '❌'; if (count > 0) coveredCount++; else missingCount++; console.log(` ${status} ${tp.label.padEnd(35)} ${count > 0 ? `${count} 条` : '缺失'}`); } console.log('\n' + '='.repeat(80)); console.log(`\n📋 结果: ${coveredCount}/${EXPECTED_TRACKING_POINTS.length} 已覆盖, ${missingCount} 缺失\n`); // 4. 按 feature 统计 Top 20 console.log('🏆 Top 20 Feature (按记录数):'); console.log('-'.repeat(80)); const topFeatures = await prisma.$queryRaw` SELECT module, feature, action, COUNT(*) as count FROM admin_schema.simple_logs WHERE created_at >= ${since} GROUP BY module, feature, action ORDER BY count DESC LIMIT 20 ` as Array<{ module: string; feature: string; action: string; count: bigint }>; for (const row of topFeatures) { console.log(` ${row.module.padEnd(10)} ${row.feature.padEnd(25)} ${row.action.padEnd(10)} ${Number(row.count).toString().padStart(6)} 条`); } // 5. DAU/MAU const dauResult = await prisma.$queryRaw` SELECT COUNT(DISTINCT user_id) as dau FROM admin_schema.simple_logs WHERE created_at >= CURRENT_DATE ` as Array<{ dau: bigint }>; const mauResult = await prisma.$queryRaw` SELECT COUNT(DISTINCT user_id) as mau FROM admin_schema.simple_logs WHERE created_at >= CURRENT_DATE - INTERVAL '30 days' ` as Array<{ mau: bigint }>; console.log(`\n👤 DAU (今日活跃用户): ${Number(dauResult[0]?.dau || 0)}`); console.log(`👥 MAU (30日活跃用户): ${Number(mauResult[0]?.mau || 0)}`); console.log('\n✅ 验证完成\n'); } main() .then(() => prisma.$disconnect()) .catch(async (e) => { console.error('❌ 验证失败:', e); await prisma.$disconnect(); process.exit(1); });