#!/usr/bin/env node /** * 拉取最近 100 条已完成课程,按模型对比:生成时间、字数 * 用法:node scripts/stats-models-100.js */ const { PrismaClient } = require('@prisma/client'); const prisma = new PrismaClient(); function countOutlineChars(outline) { if (!outline || !outline.chapters || !Array.isArray(outline.chapters)) return 0; let total = 0; for (const ch of outline.chapters) { if (ch.nodes && Array.isArray(ch.nodes)) { for (const node of ch.nodes) { const s = node.suggestedContent; if (s && typeof s === 'string') total += s.length; } } } return total; } function modelLabel(modelId) { if (!modelId) return '未知'; if (modelId.includes('lite')) return 'lite'; if (modelId.includes('flash')) return 'flash'; return modelId; } async function main() { const tasks = await prisma.courseGenerationTask.findMany({ where: { status: 'completed' }, orderBy: { createdAt: 'desc' }, take: 100, include: { course: { select: { id: true, title: true } } }, }); if (tasks.length === 0) { console.log('没有已完成的课程'); return; } const rows = tasks.map((t) => { const durationSec = t.updatedAt && t.createdAt ? Math.round((new Date(t.updatedAt) - new Date(t.createdAt)) / 1000) : null; const chars = countOutlineChars(t.outline); return { courseId: t.courseId, title: (t.course?.title || '课程创建中').slice(0, 28), modelId: t.modelId || null, model: modelLabel(t.modelId), createdAt: t.createdAt.toISOString().replace('T', ' ').slice(0, 19), durationSec, chars, }; }); const byModel = {}; for (const r of rows) { const k = r.model; if (!byModel[k]) byModel[k] = { count: 0, totalDuration: 0, totalChars: 0, durations: [], chars: [] }; byModel[k].count++; if (r.durationSec != null) { byModel[k].totalDuration += r.durationSec; byModel[k].durations.push(r.durationSec); } byModel[k].totalChars += r.chars; byModel[k].chars.push(r.chars); } console.log('=== 最近 100 条已完成课程:按模型对比 ===\n'); console.log('--- 按模型汇总 ---'); console.log('模型\t\t条数\t平均生成时间(秒)\t总字数\t平均字数'); for (const [model, s] of Object.entries(byModel)) { const avgDur = s.count > 0 && s.totalDuration > 0 ? Math.round(s.totalDuration / s.count) : '-'; const avgChars = s.count > 0 ? Math.round(s.totalChars / s.count) : 0; console.log(`${model}\t\t${s.count}\t${avgDur}\t\t\t${s.totalChars}\t${avgChars}`); } console.log('\n--- 明细表(最近 100 条:课程标题、模型、创建时间、生成时间(秒)、字数)---'); console.log('序号\t课程标题\t\t\t模型\t创建时间\t\t生成(秒)\t字数'); rows.forEach((r, i) => { const title = (r.title + '\t').slice(0, 20); const model = (r.model || '未知').padEnd(6); const dur = r.durationSec != null ? String(r.durationSec) : '-'; console.log(`${i + 1}\t${title}\t${model}\t${r.createdAt}\t${dur}\t\t${r.chars}`); }); } main() .then(() => process.exit(0)) .catch((e) => { console.error(e); process.exit(1); }) .finally(() => prisma.$disconnect());