#!/usr/bin/env node /** * 查看最近 3 条已完成课程的 outline 与正文样本,检查单引号 '' 出现位置 * 用法:node scripts/inspect-quotes-server.js */ const { PrismaClient } = require('@prisma/client'); const prisma = new PrismaClient(); function hasSingleQuotes(s) { if (!s || typeof s !== 'string') return false; const single = ["'", "'", "'", "''", "''", "`"]; return single.some((c) => s.includes(c)); } function showQuotes(s, label) { if (!s) return; const str = String(s); if (!hasSingleQuotes(str)) return; const idx = str.search(/[''`]/); if (idx === -1) return; const slice = str.slice(Math.max(0, idx - 20), idx + 30); console.log(` [单引号] ${label}: ...${slice}...`); } async function main() { const tasks = await prisma.courseGenerationTask.findMany({ where: { status: 'completed' }, orderBy: { createdAt: 'desc' }, take: 3, include: { course: { select: { id: true, title: true } } }, }); if (tasks.length === 0) { console.log('没有已完成的课程'); return; } console.log('=== 最近 3 条已完成课程:单引号检查 ===\n'); for (let t = 0; t < tasks.length; t++) { const task = tasks[t]; const outline = task.outline; console.log(`--- 课程 ${t + 1}: ${task.course?.title ?? task.courseId} ---`); console.log('courseId:', task.courseId); console.log('createdAt:', task.createdAt?.toISOString?.() ?? task.createdAt); if (outline && outline.chapters && Array.isArray(outline.chapters)) { for (let c = 0; c < outline.chapters.length; c++) { const ch = outline.chapters[c]; const parentTitle = ch.parent_title ?? ch.title ?? ''; if (hasSingleQuotes(parentTitle)) { console.log(` 章节标题含单引号: "${parentTitle}"`); showQuotes(parentTitle, '章节'); } if (ch.nodes && Array.isArray(ch.nodes)) { for (let n = 0; n < ch.nodes.length; n++) { const node = ch.nodes[n]; const nodeTitle = node.title ?? ''; if (hasSingleQuotes(nodeTitle)) { console.log(` 节点标题含单引号: "${nodeTitle}"`); showQuotes(nodeTitle, '节点'); } const content = node.suggestedContent ?? ''; if (hasSingleQuotes(content)) { showQuotes(content, '正文'); } } } } // 只打印第一个小节的正文前 400 字,便于看单引号 let firstContent = null; for (const ch of outline.chapters) { if (ch.nodes && ch.nodes[0] && ch.nodes[0].suggestedContent) { firstContent = ch.nodes[0].suggestedContent; break; } } if (firstContent && hasSingleQuotes(firstContent)) { console.log(' 首段正文中含单引号,片段:'); const singleIdx = firstContent.search(/[''`]/); if (singleIdx !== -1) { console.log(' ', firstContent.slice(Math.max(0, singleIdx - 25), singleIdx + 40)); } } } console.log(''); } } main() .then(() => process.exit(0)) .catch((e) => { console.error(e); process.exit(1); }) .finally(() => prisma.$disconnect());