import { PrismaClient } from "@prisma/client"; import { PrismaMariaDb } from "@prisma/adapter-mariadb"; import OpenAI from "openai"; import "dotenv/config"; const base = (process.env.DATABASE_URL || "mysql://localhost:3306/zhuiguang_ai?charset=utf8mb4").replace("mysql://", "mariadb://"); const sep = base.includes("?") ? "&" : "?"; const connectionString = `${base}${sep}connection_limit=20&pool_timeout=30`; const adapter = new PrismaMariaDb(connectionString); const prisma = new PrismaClient({ adapter }); const openai = new OpenAI({ apiKey: process.env.DEEPSEEK_API_KEY, baseURL: "https://api.deepseek.com", }); function extractJSON(str) { const trimmed = str.trim(); const codeBlockMatch = trimmed.match(/```(?:json)?\s*([\s\S]*?)```/); let jsonStr = codeBlockMatch ? codeBlockMatch[1].trim() : trimmed; const braceMatch = jsonStr.match(/\{[\s\S]*\}/); if (braceMatch) jsonStr = braceMatch[0]; return JSON.parse(jsonStr); } const LONG_ARTICLE_PROMPT = `你是AI工具评测专家。基于以下评测数据,撰写一篇800-1200字的深度评测文章。 项目:{name} 简介:{desc} 综合评分:{overall}/5 五维评分:能力{cap}/5 | 开发体验{dev}/5 | 成本许可{cost}/5 | 社区{comm}/5 | 性能{perf}/5 要求: 1. 标题醒目,20字以内 2. 开头用一句话概括项目核心价值 3. 分5个段落分别深入分析五个维度 4. 每段包含具体使用场景和优缺点 5. 结尾给出适用人群和推荐指数 6. 语言专业但易懂,避免空洞套话 返回纯文本,不要JSON格式。`; const VIDEO_SCRIPT_PROMPT = `为GitHub开源AI项目{name}(⭐{stars} stars)创作一个60秒短视频脚本。 项目简介:{desc} 综合评分:{overall}/5 要求5个镜头: - 镜头1:开场hook(5秒) - 镜头2:项目定位(10秒) - 镜头3:核心功能展示(20秒) - 镜头4:使用场景(15秒) - 镜头5:CTA号召(10秒) 每个镜头包含:[画面描述] + 旁白文案 返回纯文本格式。`; async function generateLongArticle(review, skill) { const prompt = LONG_ARTICLE_PROMPT .replace("{name}", skill.name) .replace("{desc}", skill.description || "暂无描述") .replace("{overall}", review.overall.toString()) .replace("{cap}", review.capability.toString()) .replace("{dev}", review.devExp.toString()) .replace("{cost}", review.costLicense.toString()) .replace("{comm}", review.community.toString()) .replace("{perf}", review.performance.toString()); const { choices } = await openai.chat.completions.create({ model: "deepseek-v4-pro", messages: [{ role: "user", content: prompt }], temperature: 0.7, }); return choices[0]?.message?.content || ""; } async function generateVideoScript(review, skill) { const reviewsData = review.reviews || {}; const prompt = VIDEO_SCRIPT_PROMPT .replace("{name}", skill.name) .replace("{desc}", skill.description || "暂无描述") .replace("{overall}", review.overall.toString()) .replace("{stars}", String(reviewsData.stars || "N/A")); const { choices } = await openai.chat.completions.create({ model: "deepseek-v4-pro", messages: [{ role: "user", content: prompt }], temperature: 0.7, }); return choices[0]?.message?.content || ""; } async function main() { console.log("📝 批量生成评测内容\n"); const reviews = await prisma.skillReview.findMany({ where: { status: "PUBLISHED", longArticle: null }, include: { skill: { select: { id: true, name: true, description: true } } }, }); console.log(`找到 ${reviews.length} 个需要生成内容的评测\n`); for (let i = 0; i < reviews.length; i++) { const r = reviews[i]; console.log(`\n[${i + 1}/${reviews.length}] ${r.skill.name} (overall: ${r.overall})`); try { console.log(" 生成长文..."); const article = await generateLongArticle(r, r.skill); console.log(` ✅ 长文: ${article.length}字`); console.log(" 生成视频脚本..."); const script = await generateVideoScript(r, r.skill); console.log(` ✅ 脚本: ${script.length}字`); await prisma.skillReview.update({ where: { id: r.id }, data: { longArticle: article, videoScript: script, }, }); console.log(" 💾 已保存"); if (i < reviews.length - 1) { await new Promise((resolve) => setTimeout(resolve, 2000)); } } catch (e) { console.error(` ❌ 失败: ${e.message}`); } } console.log("\n🏁 内容生成完成"); } main() .catch((e) => { console.error("💥", e.message); process.exit(1); }) .finally(() => prisma.$disconnect());