v0.11.1: 权限体系重构 + AI管线优化 + 内容整理
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
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";
|
||||
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/v1",
|
||||
});
|
||||
|
||||
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());
|
||||
Reference in New Issue
Block a user