问题排查与修复: 1. 🔴 bot-activity从未运行 — 新增cron任务(9-23点每小时15轮) 2. 🔴 task7报错'板块不存在: ai-tools' — 更新所有slug为新的51子板块格式 3. 🟡 install-cron.sh旧cron清理不完整 — 改为覆盖模式 4. 🟡 Google News RSS被墙 — 优化fallback提示词用DeepSeek知识+当前日期 效果: - 8个定时任务全部正常运行 - 首次运行产出10个话题(7+3),社区从12→24帖 - 每小时15轮持续产出,预计每天~45帖
225 lines
6.9 KiB
JavaScript
225 lines
6.9 KiB
JavaScript
import { PrismaClient } from "@prisma/client";
|
|
import { PrismaMariaDb } from "@prisma/adapter-mariadb";
|
|
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 });
|
|
|
|
function ts() {
|
|
return `[${new Date().toISOString()}]`;
|
|
}
|
|
|
|
function generateSlug(title) {
|
|
return title
|
|
.toLowerCase()
|
|
.replace(/[^a-z0-9\u4e00-\u9fa5]+/g, "-")
|
|
.replace(/^-|-$/g, "");
|
|
}
|
|
|
|
function ensureUniqueSlug(title) {
|
|
const baseSlug = generateSlug(title) || "topic";
|
|
const clean = baseSlug.substring(0, 80);
|
|
const suffix = Date.now().toString(36);
|
|
return `${clean}-${suffix}`;
|
|
}
|
|
|
|
const CATEGORY_MAP = {
|
|
"产品发布": "ai-tools-app",
|
|
"product": "ai-tools-app",
|
|
"融资投资": "ai-startup-forum",
|
|
"business": "ai-startup-forum",
|
|
"技术突破": "ai-llm",
|
|
"model": "ai-llm",
|
|
"政策法规": "ai-startup-forum",
|
|
"policy": "ai-startup-forum",
|
|
"开源项目": "ai-llm",
|
|
"opensource": "ai-llm",
|
|
"行业事件": "ai-startup-forum",
|
|
"insight": "ai-startup-forum",
|
|
};
|
|
|
|
async function main() {
|
|
if (!process.env.DATABASE_URL) throw new Error("缺少环境变量: DATABASE_URL");
|
|
|
|
const startTime = new Date();
|
|
const now = new Date();
|
|
const cnOffset = 8 * 60 * 60 * 1000;
|
|
const cnTime = new Date(now.getTime() + cnOffset);
|
|
const today = cnTime.toISOString().split("T")[0];
|
|
|
|
const yesterdayDate = new Date(cnTime.getTime() - 24 * 60 * 60 * 1000);
|
|
const yesterday = yesterdayDate.toISOString().split("T")[0];
|
|
|
|
console.log(`${ts()} 📰 [Task7] 开始将AI日报分发到社区板块 ${today}`);
|
|
|
|
let report = await prisma.dailyReport.findFirst({
|
|
where: { date: new Date(today), status: "published" },
|
|
include: { items: { orderBy: { sortOrder: "asc" } } },
|
|
});
|
|
|
|
if (!report) {
|
|
console.log(`${ts()} ⚠ 今日(${today})无日报,尝试昨天(${yesterday})...`);
|
|
report = await prisma.dailyReport.findFirst({
|
|
where: { date: new Date(yesterday), status: "published" },
|
|
include: { items: { orderBy: { sortOrder: "asc" } } },
|
|
});
|
|
}
|
|
|
|
if (!report || report.items.length === 0) {
|
|
console.log(`${ts()} ⚠ 没有可用的日报条目,跳过`);
|
|
await prisma.taskLog.create({
|
|
data: {
|
|
taskKey: "task7-news-to-community",
|
|
taskName: "AI日报分发到社区",
|
|
status: "completed",
|
|
startedAt: startTime,
|
|
finishedAt: new Date(),
|
|
duration: new Date().getTime() - startTime.getTime(),
|
|
result: { created: 0, skipped: 0, message: "没有可用的日报条目" },
|
|
},
|
|
});
|
|
await prisma.$disconnect();
|
|
return;
|
|
}
|
|
|
|
console.log(`${ts()} 📋 日报ID=${report.id}, 共${report.items.length}条新闻`);
|
|
|
|
const forumCategories = await prisma.forumCategory.findMany({
|
|
where: {
|
|
slug: { in: ["ai-tools-app", "ai-startup-forum", "ai-llm"] },
|
|
},
|
|
});
|
|
const categoryBySlug = {};
|
|
for (const c of forumCategories) {
|
|
categoryBySlug[c.slug] = c;
|
|
}
|
|
console.log(`${ts()} 📂 已加载${forumCategories.length}个社区板块: ${Object.keys(categoryBySlug).join(", ")}`);
|
|
|
|
const requiredSlugs = ["ai-tools-app", "ai-startup-forum", "ai-llm"];
|
|
for (const slug of requiredSlugs) {
|
|
if (!categoryBySlug[slug]) {
|
|
throw new Error(`社区板块不存在: ${slug},请先在管理后台创建社区分类`);
|
|
}
|
|
}
|
|
|
|
const adminUser = await prisma.user.findFirst({
|
|
where: { role: "ADMIN" },
|
|
orderBy: { createdAt: "asc" },
|
|
});
|
|
if (!adminUser) {
|
|
throw new Error("管理员用户不存在,请先创建管理员账号");
|
|
}
|
|
console.log(`${ts()} 👤 管理员用户: ${adminUser.name || adminUser.email} (ID:${adminUser.id})`);
|
|
|
|
const topicCount = await prisma.forumTopic.count();
|
|
console.log(`${ts()} 📊 社区现有话题数: ${topicCount}`);
|
|
|
|
let created = 0;
|
|
let skipped = 0;
|
|
const errors = [];
|
|
|
|
for (const item of report.items) {
|
|
const forumSlug = CATEGORY_MAP[item.category];
|
|
if (!forumSlug) {
|
|
skipped++;
|
|
console.log(`${ts()} ⏭ 跳过(未映射分类): [${item.category}] ${item.title}`);
|
|
continue;
|
|
}
|
|
|
|
const forumCategory = categoryBySlug[forumSlug];
|
|
if (!forumCategory) {
|
|
skipped++;
|
|
console.log(`${ts()} ⏭ 跳过(板块不存在): [${item.category}] ${item.title} → ${forumSlug}`);
|
|
continue;
|
|
}
|
|
|
|
const existing = await prisma.forumTopic.findFirst({
|
|
where: { title: item.title },
|
|
});
|
|
if (existing) {
|
|
skipped++;
|
|
console.log(`${ts()} ⏭ 跳过(已存在): ${item.title}`);
|
|
continue;
|
|
}
|
|
|
|
const slug = ensureUniqueSlug(item.title);
|
|
const content = item.summary
|
|
? `${item.summary}\n\n${item.detail || ""}\n\n来源: ${item.sourceUrl || "AI日报"}`
|
|
: `${item.detail || ""}\n\n来源: ${item.sourceUrl || "AI日报"}`;
|
|
|
|
try {
|
|
const topic = await prisma.forumTopic.create({
|
|
data: {
|
|
categoryId: forumCategory.id,
|
|
userId: adminUser.id,
|
|
title: item.title,
|
|
slug,
|
|
content: content.trim(),
|
|
},
|
|
});
|
|
|
|
await prisma.forumCategory.update({
|
|
where: { id: forumCategory.id },
|
|
data: { topicCount: { increment: 1 } },
|
|
});
|
|
|
|
created++;
|
|
console.log(`${ts()} ✅ 已创建话题: [${forumCategory.name}] ${item.title} (ID:${topic.id})`);
|
|
} catch (e) {
|
|
errors.push(`${item.title}: ${e.message}`);
|
|
console.error(`${ts()} ❌ 创建话题失败: ${item.title} - ${e.message}`);
|
|
}
|
|
}
|
|
|
|
const endTime = new Date();
|
|
const duration = endTime.getTime() - startTime.getTime();
|
|
const result = {
|
|
reportId: report.id,
|
|
reportDate: report.date.toISOString().split("T")[0],
|
|
totalItems: report.items.length,
|
|
created,
|
|
skipped,
|
|
duration: `${(duration / 1000).toFixed(1)}s`,
|
|
};
|
|
|
|
console.log(`\n${ts()} 🏁 [Task7] 完成: 创建${created}个话题, 跳过${skipped}个, 耗时${result.duration}`);
|
|
|
|
await prisma.taskLog.create({
|
|
data: {
|
|
taskKey: "task7-news-to-community",
|
|
taskName: "AI日报分发到社区",
|
|
status: errors.length > 0 && created === 0 ? "failed" : "completed",
|
|
startedAt: startTime,
|
|
finishedAt: endTime,
|
|
duration,
|
|
result,
|
|
error: errors.length > 0 ? errors.slice(0, 10).join("\n") : null,
|
|
},
|
|
});
|
|
|
|
await prisma.$disconnect();
|
|
}
|
|
|
|
main().catch(async (e) => {
|
|
console.error(`${ts()} ❌ 任务七失败:`, e.message);
|
|
if (prisma) {
|
|
try {
|
|
await prisma.taskLog.create({
|
|
data: {
|
|
taskKey: "task7-news-to-community",
|
|
taskName: "AI日报分发到社区",
|
|
status: "failed",
|
|
startedAt: new Date(),
|
|
finishedAt: new Date(),
|
|
duration: 0,
|
|
error: e.message,
|
|
},
|
|
});
|
|
} catch {}
|
|
await prisma.$disconnect();
|
|
}
|
|
process.exit(1);
|
|
}); |