全项目扫描修复: Docker数据卷修复+安全requireAdmin+12页SEO+API白名单+脚本超时+常量提取+假数据删除
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
// 数字人 vs 真人"对抗学习" 调度入口
|
||||
// 每周给所有专家 bot 各选 1 篇真实高赞真人帖,提取"为什么火"的洞察
|
||||
// 用法:
|
||||
// node scripts/bot-adversarial-learning-run.mjs # 默认本周
|
||||
// node scripts/bot-adversarial-learning-run.mjs --week=2026-W23 # 指定周
|
||||
// node scripts/bot-adversarial-learning-run.mjs --force # 强制重学(覆盖已有)
|
||||
// node scripts/bot-adversarial-learning-run.mjs --bot=laochen # 单 bot
|
||||
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import { PrismaMariaDb } from "@prisma/adapter-mariadb";
|
||||
import { fileURLToPath } from "url";
|
||||
import { dirname } from "path";
|
||||
import "dotenv/config";
|
||||
import {
|
||||
runWeeklyAdversarialLearning,
|
||||
getISOWeekKey,
|
||||
disconnectAdversarialLearning,
|
||||
} from "./lib/bot-adversarial-learning.mjs";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const base = process.env.DATABASE_URL.replace("mysql://", "mariadb://");
|
||||
const sep = base.includes("?") ? "&" : "?";
|
||||
const connectionString = `${base}${sep}connection_limit=3&pool_timeout=10`;
|
||||
const adapter = new PrismaMariaDb(connectionString);
|
||||
const prisma = new PrismaClient({ adapter });
|
||||
|
||||
function ts() {
|
||||
return `[${new Date().toISOString()}]`;
|
||||
}
|
||||
|
||||
function parseArgs() {
|
||||
const args = process.argv.slice(2);
|
||||
const out = { week: null, force: false, bot: null, lookback: 7 };
|
||||
for (const a of args) {
|
||||
if (a.startsWith("--week=")) out.week = a.split("=")[1];
|
||||
else if (a === "--force") out.force = true;
|
||||
else if (a.startsWith("--bot=")) out.bot = a.split("=")[1];
|
||||
else if (a.startsWith("--lookback=")) out.lookback = parseInt(a.split("=")[1], 10);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const args = parseArgs();
|
||||
const weekKey = args.week || getISOWeekKey();
|
||||
console.log(
|
||||
`${ts()} Adversarial Learning run starting (week=${weekKey}, force=${args.force}, bot=${args.bot || "all"}, lookback=${args.lookback}d)`
|
||||
);
|
||||
|
||||
if (!process.env.DATABASE_URL) {
|
||||
throw new Error("DATABASE_URL 未配置");
|
||||
}
|
||||
|
||||
const startTime = Date.now();
|
||||
|
||||
// --force 时:先把本周已有 learning 删掉(针对限定 bot)
|
||||
if (args.force) {
|
||||
const where = args.bot
|
||||
? { bot: { user: { email: `bot_${args.bot}@zhuiguang.ai` } }, weekKey }
|
||||
: { weekKey };
|
||||
const del = await prisma.botAdversarialLearning.deleteMany({ where });
|
||||
console.log(`${ts()} --force: 删除了 ${del.count} 条已有 learning`);
|
||||
}
|
||||
|
||||
// 单 bot 模式
|
||||
let botConfigIds = null;
|
||||
if (args.bot) {
|
||||
const u = await prisma.user.findFirst({ where: { email: `bot_${args.bot}@zhuiguang.ai` } });
|
||||
if (!u) {
|
||||
console.error(`${ts()} Bot "${args.bot}" 未找到`);
|
||||
process.exit(1);
|
||||
}
|
||||
const c = await prisma.botConfig.findUnique({ where: { userId: u.id } });
|
||||
if (!c) {
|
||||
console.error(`${ts()} Bot "${args.bot}" 没有 BotConfig`);
|
||||
process.exit(1);
|
||||
}
|
||||
botConfigIds = [c.id];
|
||||
}
|
||||
|
||||
const summary = await runWeeklyAdversarialLearning({
|
||||
weekKey,
|
||||
lookbackDays: args.lookback,
|
||||
botConfigIds,
|
||||
});
|
||||
|
||||
const duration = Date.now() - startTime;
|
||||
console.log(
|
||||
`${ts()} Done: bots=${summary.botCount} learned=${summary.learned} skipped=${summary.skipped} errors=${summary.errors} (${duration}ms)`
|
||||
);
|
||||
for (const r of summary.results.slice(0, 10)) {
|
||||
if (r.error) {
|
||||
console.log(`${ts()} ✗ ${r.botName} ${r.error}`);
|
||||
} else if (r.skipped) {
|
||||
console.log(`${ts()} ⤼ ${r.botName} skip=${r.reason}`);
|
||||
} else {
|
||||
const rec = r.record;
|
||||
console.log(
|
||||
`${ts()} ✓ ${r.botName} ← topic#${rec.sourceRefId} "${rec.sourceTitle?.slice(0, 30)}" rel=${rec.relevanceScore} cat=${JSON.stringify(rec.learnCategories)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
await prisma.taskLog.create({
|
||||
data: {
|
||||
taskKey: "bot-adversarial-learning-run",
|
||||
taskName: "Bot 对抗学习(真人高赞帖)",
|
||||
status: summary.errors > 0 ? "partial" : "success",
|
||||
startedAt: new Date(startTime),
|
||||
finishedAt: new Date(),
|
||||
triggerBy: "cron",
|
||||
result: {
|
||||
weekKey,
|
||||
botCount: summary.botCount,
|
||||
learned: summary.learned,
|
||||
skipped: summary.skipped,
|
||||
errors: summary.errors,
|
||||
force: args.force,
|
||||
lookbackDays: args.lookback,
|
||||
durationMs: duration,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
main()
|
||||
.catch(async (e) => {
|
||||
console.error(`${ts()} Fatal:`, e);
|
||||
try {
|
||||
await prisma.taskLog.create({
|
||||
data: {
|
||||
taskKey: "bot-adversarial-learning-run",
|
||||
taskName: "Bot 对抗学习(真人高赞帖)",
|
||||
status: "failed",
|
||||
startedAt: new Date(),
|
||||
finishedAt: new Date(),
|
||||
triggerBy: "cron",
|
||||
error: (e && e.message ? e.message : String(e)).slice(0, 1000),
|
||||
},
|
||||
});
|
||||
} catch {}
|
||||
process.exit(1);
|
||||
})
|
||||
.finally(async () => {
|
||||
await prisma.$disconnect();
|
||||
await disconnectAdversarialLearning();
|
||||
});
|
||||
Reference in New Issue
Block a user