2026-05-25: 等级体系升级(20级)+论坛二级分类+会员空间

This commit is contained in:
ZhuiGuangAI Dev
2026-05-24 23:05:38 +08:00
parent 6c78c32157
commit 9bf3e7574a
118 changed files with 11842 additions and 3262 deletions
+70 -31
View File
@@ -3,6 +3,7 @@ import { PrismaMariaDb } from "@prisma/adapter-mariadb";
import { Octokit } from "octokit";
import OpenAI from "openai";
import "dotenv/config";
import { withRetry } from "./lib/retry.mjs";
const base = process.env.DATABASE_URL || "mysql://localhost:3306/zhuiguang_ai?charset=utf8mb4";
const sep = base.includes("?") ? "&" : "?";
@@ -165,13 +166,17 @@ function genSlug(name) {
async function searchRepos(query, minStars, max) {
const q = `${query} stars:>${minStars} pushed:>2024-01-01`;
const { data } = await octokit.rest.search.repos({ q, sort: "stars", order: "desc", per_page: max });
const { data } = await withRetry(async () => {
return await octokit.rest.search.repos({ q, sort: "stars", order: "desc", per_page: max });
}, { maxRetries: 3, label: `GitHub Search "${query}"` });
return data.items;
}
async function getReadme(owner, repo) {
try {
const { data } = await octokit.rest.repos.getReadme({ owner, repo });
const { data } = await withRetry(async () => {
return await octokit.rest.repos.getReadme({ owner, repo });
}, { maxRetries: 3, label: `GitHub README ${owner}/${repo}` });
return Buffer.from(data.content, "base64").toString("utf-8");
} catch { return ""; }
}
@@ -181,9 +186,11 @@ async function genReview(repoName, desc, stars, license, readme) {
.replace("{repoName}", repoName).replace("{description}", desc || "暂无描述")
.replace("{stars}", stars.toString()).replace("{license}", license || "未知")
.replace("{readmeSummary}", readme.substring(0, 1500));
const { choices } = await openai.chat.completions.create({
model: "deepseek-v4-pro", messages: [{ role: "user", content: prompt }], temperature: 0.3,
});
const { choices } = await withRetry(async () => {
return await openai.chat.completions.create({
model: "deepseek-v4-pro", messages: [{ role: "user", content: prompt }], temperature: 0.3,
});
}, { maxRetries: 3, label: "DeepSeek Review API" });
const content = choices[0]?.message?.content;
if (!content) throw new Error("Empty response");
let review = extractJSON(content);
@@ -199,15 +206,16 @@ async function genReview(repoName, desc, stars, license, readme) {
}
async function main() {
console.log(`🚀 [Daily Discover] ${new Date().toISOString().split("T")[0]} 开始...\n`);
const DAILY_LIMIT = 10;
const startTime = new Date();
console.log(`🚀 [Task4] ${new Date().toISOString().split("T")[0]} 发现新AI技能...\n`);
const DAILY_LIMIT = 30;
let discovered = 0, skipped = 0, reviewed = 0, failed = 0;
for (const query of SEARCH_QUERIES) {
if (discovered >= DAILY_LIMIT) break;
console.log(`🔍 "${query}"`);
let repos;
try { repos = await searchRepos(query, 500, 5); } catch (e) { console.error(` ❌ 搜索失败: ${e.message}`); failed++; continue; }
try { repos = await searchRepos(query, 1000, 5); } catch (e) { console.error(` ❌ 搜索失败: ${e.message}`); failed++; continue; }
for (const repo of repos) {
if (discovered >= DAILY_LIMIT) break;
@@ -225,36 +233,31 @@ async function main() {
const [owner, name] = repo.full_name.split("/");
const readme = await getReadme(owner, name);
let reviewData = null;
try { reviewData = await genReview(repo.full_name, repo.description || "", repo.stargazers_count, repo.license?.spdx_id || "Unknown", readme); console.log(` ✅ 评测: ${reviewData.overall}`); reviewed++; }
catch (e) { console.log(` ⚠️ 评测失败: ${e.message}`); }
try { reviewData = await genReview(repo.full_name, repo.description || "", repo.stargazers_count, repo.license?.spdx_id || "Unknown", readme); console.log(` ✅ 预评测: ${reviewData.overall}`); reviewed++; }
catch (e) { console.log(` ⚠️ 预评测失败: ${e.message}`); }
const categoryId = await guessCategory(repo.full_name, repo.description || "", reviewData?.tags || []);
const skill = await prisma.skill.create({
const pendingSkill = await prisma.pendingSkill.create({
data: {
name: repo.full_name.substring(0, 100), slug, categoryId,
description: repo.description || "", sourceUrl: repo.html_url, sourceType: "github",
rating: reviewData?.overall || 0,
features: { forks: repo.forks_count, language: repo.language, license: repo.license?.spdx_id || null, topics: repo.topics || [] },
tags: reviewData?.tags || [], status: "draft",
lastReviewedAt: reviewData ? new Date() : null,
githubStarsHistory: [{ date: new Date().toISOString().split("T")[0], stars: repo.stargazers_count }],
features: {
forks: repo.forks_count,
language: repo.language,
license: repo.license?.spdx_id || null,
topics: repo.topics || [],
stars: repo.stargazers_count,
reviewData: reviewData || null,
},
tags: reviewData?.tags || [],
status: "pending",
},
});
console.log(` ✅ Skill #${skill.id}`);
if (reviewData) {
await prisma.skillReview.create({
data: {
skillId: skill.id, capability: reviewData.capability.score, devExp: reviewData.devExp.score,
costLicense: reviewData.costLicense.score, community: reviewData.community.score,
performance: reviewData.performance.score, overall: reviewData.overall,
reviews: reviewData, status: "DRAFT",
},
});
}
console.log(` ✅ PendingSkill #${pendingSkill.id}`);
await prisma.reviewGenerationLog.create({
data: { skillId: skill.id, status: "SUCCESS", prompt: `daily-discover: ${repo.full_name}`, response: reviewData ? JSON.stringify(reviewData) : null },
data: { skillId: pendingSkill.id, status: "SUCCESS", prompt: `task4-discover: ${repo.full_name}`, response: reviewData ? JSON.stringify(reviewData) : null },
});
discovered++;
} catch (e) {
@@ -262,14 +265,50 @@ async function main() {
failed++;
try {
await prisma.reviewGenerationLog.create({
data: { skillId: 0, status: "FAILED", prompt: `daily-discover: ${repo.full_name}`, error: e.message?.substring(0, 500) || "Unknown" },
data: { skillId: 0, status: "FAILED", prompt: `task4-discover: ${repo.full_name}`, error: e.message?.substring(0, 500) || "Unknown" },
});
} catch {}
}
}
}
console.log(`\n🏁 [Daily Discover] 完成: 发现${discovered} / 跳过${skipped} / 评测${reviewed} / 失败${failed}`);
const endTime = new Date();
const duration = endTime.getTime() - startTime.getTime();
const result = { discovered, skipped, reviewed, failed, duration: `${(duration / 1000).toFixed(1)}s` };
console.log(`\n🏁 [Task4] 完成: 发现${discovered} / 跳过${skipped} / 评测${reviewed} / 失败${failed}`);
await prisma.taskLog.create({
data: {
taskKey: "task4-discover-skills",
taskName: "发现AI技能",
status: "completed",
startedAt: startTime,
finishedAt: endTime,
duration,
result,
error: failed > discovered * 2 ? `失败率过高: ${failed}/${discovered}` : null,
},
});
}
main().catch((e) => { console.error("💥 异常:", e); process.exit(1); }).finally(() => prisma.$disconnect());
main().catch(async (e) => {
console.error("💥 异常:", e);
if (prisma) {
try {
await prisma.taskLog.create({
data: {
taskKey: "task4-discover-skills",
taskName: "发现AI技能",
status: "failed",
startedAt: new Date(),
finishedAt: new Date(),
duration: 0,
error: e.message,
},
});
} catch {}
await prisma.$disconnect();
}
process.exit(1);
}).finally(() => prisma.$disconnect());