99 lines
3.2 KiB
JavaScript
99 lines
3.2 KiB
JavaScript
#!/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());
|