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());
|