- 更新种子脚本:10大赛道×51个子板块,热门排序 - 新增清理脚本:删除所有非10赛道旧板块 - 重做侧边栏:仿工具模块的二级平铺导航,带搜索过滤 - 新增排行组件:本月活跃+积分总排行移至右侧栏 - 调整页面布局:左导航(220px)+中内容+右指南/排行(280px) - TypeScript编译零错误通过
298 lines
12 KiB
JavaScript
298 lines
12 KiB
JavaScript
import { PrismaClient } from "@prisma/client";
|
|
import { PrismaMariaDb } from "@prisma/adapter-mariadb";
|
|
import { readFileSync } from "fs";
|
|
import { resolve, dirname } from "path";
|
|
import { fileURLToPath } from "url";
|
|
import "dotenv/config";
|
|
|
|
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=5&pool_timeout=10`;
|
|
const adapter = new PrismaMariaDb(connectionString);
|
|
const prisma = new PrismaClient({ adapter });
|
|
|
|
const BOT_DATA_PATH = resolve(__dirname, "..", "data", "bot-characters.json");
|
|
const INDUSTRY_CATEGORIES = [
|
|
{
|
|
name: "电商零售", slug: "ecommerce", icon: "🛒", sortOrder: 1,
|
|
description: "平台运营、直播带货、私域与供应链",
|
|
children: [
|
|
{ name: "平台电商", slug: "ec-platform", icon: "🏪", description: "淘宝/天猫/京东/拼多多运营" },
|
|
{ name: "直播带货", slug: "ec-livestream", icon: "📱", description: "抖音/快手/视频号直播" },
|
|
{ name: "私域电商", slug: "ec-private", icon: "🔗", description: "微信生态/社群/小程序电商" },
|
|
{ name: "社区团购", slug: "ec-group", icon: "🏘️", description: "社区团购/买菜/本地配送" },
|
|
{ name: "独立站/DTC", slug: "ec-dtc", icon: "🏷️", description: "Shopify/品牌官网自建站" },
|
|
{ name: "选品与供应链", slug: "ec-supply", icon: "📦", description: "选品策略/货源/库存管理" },
|
|
],
|
|
},
|
|
{
|
|
name: "AI智能科技", slug: "ai-tech", icon: "🤖", sortOrder: 2,
|
|
description: "AI工具、创业、SaaS与开源大模型",
|
|
children: [
|
|
{ name: "AI工具与应用", slug: "ai-tools-app", icon: "🔧", description: "ChatGPT/Claude/Midjourney类工具" },
|
|
{ name: "AI创业", slug: "ai-startup-forum", icon: "💡", description: "AI创业方向与商业模式" },
|
|
{ name: "SaaS出海", slug: "ai-saas", icon: "🚀", description: "SaaS产品开发与全球化" },
|
|
{ name: "智能硬件", slug: "ai-hardware", icon: "🔌", description: "AI硬件/IoT/可穿戴设备" },
|
|
{ name: "大模型与开源", slug: "ai-llm", icon: "🧠", description: "LLM/开源模型/微调/RAG" },
|
|
],
|
|
},
|
|
{
|
|
name: "自媒体内容", slug: "content", icon: "🎬", sortOrder: 3,
|
|
description: "短视频、直播、图文与MCN运营",
|
|
children: [
|
|
{ name: "短视频创作", slug: "ct-short-video", icon: "📱", description: "抖音/快手/B站/视频号创作" },
|
|
{ name: "直播运营", slug: "ct-live", icon: "🎥", description: "带货直播/秀场/知识直播" },
|
|
{ name: "图文内容", slug: "ct-writing", icon: "✍️", description: "公众号/小红书/知乎/头条" },
|
|
{ name: "MCN机构", slug: "ct-mcn", icon: "🏢", description: "达人孵化/商务变现/MCN运营" },
|
|
{ name: "播客与音频", slug: "ct-podcast", icon: "🎙️", description: "小宇宙/喜马拉雅/音频创作" },
|
|
],
|
|
},
|
|
{
|
|
name: "金融投资", slug: "finance", icon: "📈", sortOrder: 4,
|
|
description: "股票基金、加密货币、PE/VC与理财",
|
|
children: [
|
|
{ name: "股票与基金", slug: "fi-stock", icon: "📊", description: "A股/港股/美股/基金定投" },
|
|
{ name: "加密货币/Web3", slug: "fi-crypto", icon: "₿", description: "比特币/以太坊/DeFi/NFT" },
|
|
{ name: "保险与理财", slug: "fi-insurance", icon: "🛡️", description: "保险配置/理财规划/资产配置" },
|
|
{ name: "PE与VC", slug: "fi-pe", icon: "💼", description: "PE/VC/天使投资/项目评估" },
|
|
{ name: "外汇与期货", slug: "fi-forex", icon: "💱", description: "外汇/黄金/大宗商品/期货" },
|
|
],
|
|
},
|
|
{
|
|
name: "外贸跨境", slug: "cross-border", icon: "🌏", sortOrder: 5,
|
|
description: "跨境电商、外贸工厂、物流与品牌出海",
|
|
children: [
|
|
{ name: "跨境电商平台", slug: "cb-ecommerce", icon: "🛒", description: "Amazon/Temu/TikTok Shop/Shopee" },
|
|
{ name: "外贸工厂/B2B", slug: "cb-factory", icon: "🏭", description: "代工转品牌/工贸一体/展会" },
|
|
{ name: "海外仓与物流", slug: "cb-logistics", icon: "📦", description: "海外仓/FBA/跨境物流/清关" },
|
|
{ name: "跨境支付合规", slug: "cb-payment", icon: "💳", description: "收付款/汇率/税务合规" },
|
|
{ name: "品牌出海/DTC", slug: "cb-brand", icon: "🚩", description: "独立站/品牌全球化/本地化" },
|
|
],
|
|
},
|
|
{
|
|
name: "房地产", slug: "real-estate", icon: "🏠", sortOrder: 6,
|
|
description: "房产买卖、商业地产、装修与物业",
|
|
children: [
|
|
{ name: "住宅买卖", slug: "re-residential", icon: "🏡", description: "刚需/改善/学区房/公寓" },
|
|
{ name: "商业地产", slug: "re-commercial", icon: "🏢", description: "商铺/写字楼/产业园/酒店" },
|
|
{ name: "装修与家居", slug: "re-renovation", icon: "🛋️", description: "装修设计/建材/智能家居" },
|
|
{ name: "物业与租赁", slug: "re-property", icon: "🏗️", description: "物业管理/租赁运营/资产托管" },
|
|
],
|
|
},
|
|
{
|
|
name: "餐饮食品", slug: "food", icon: "🍜", sortOrder: 7,
|
|
description: "餐饮创业、茶饮咖啡、预制菜与供应链",
|
|
children: [
|
|
{ name: "餐饮开店", slug: "fd-restaurant", icon: "🍽️", description: "餐厅选址/运营/连锁加盟" },
|
|
{ name: "茶饮与咖啡", slug: "fd-tea", icon: "🧋", description: "新茶饮/咖啡品牌/饮品创业" },
|
|
{ name: "预制菜加工", slug: "fd-prepared", icon: "📦", description: "预制菜/半成品/中央厨房" },
|
|
{ name: "食品供应链", slug: "fd-supply", icon: "🚚", description: "食材采购/冷链/仓储配送" },
|
|
{ name: "外卖运营", slug: "fd-delivery", icon: "🛵", description: "美团/饿了么/外卖代运营" },
|
|
],
|
|
},
|
|
{
|
|
name: "本地生活", slug: "local-life", icon: "🏪", sortOrder: 8,
|
|
description: "美业、健身、宠物、家政等本地服务",
|
|
children: [
|
|
{ name: "美容美发", slug: "ll-beauty", icon: "💇", description: "美发/美甲/美容/皮肤管理" },
|
|
{ name: "健身运动", slug: "ll-fitness", icon: "🏋️", description: "健身房/瑜伽/私教/运动馆" },
|
|
{ name: "宠物经济", slug: "ll-pet", icon: "🐾", description: "宠物店/食品/医疗/寄养" },
|
|
{ name: "家政服务", slug: "ll-housekeeping", icon: "🏠", description: "保洁/月嫂/搬家/收纳" },
|
|
{ name: "维修与数码", slug: "ll-repair", icon: "🔧", description: "手机维修/家电清洗/开锁" },
|
|
{ name: "本地教培", slug: "ll-edu-local", icon: "🎓", description: "托管/艺术培训/体育培训" },
|
|
],
|
|
},
|
|
{
|
|
name: "大健康", slug: "health", icon: "💊", sortOrder: 9,
|
|
description: "医疗美容、健康管理、养老与康复",
|
|
children: [
|
|
{ name: "医疗美容", slug: "hl-cosmetic", icon: "💉", description: "轻医美/微整形/皮肤管理" },
|
|
{ name: "健康管理", slug: "hl-wellness", icon: "🫀", description: "体检/慢病/保健品/营养" },
|
|
{ name: "养老服务", slug: "hl-elderly", icon: "👴", description: "养老院/居家养老/适老化" },
|
|
{ name: "运动康复", slug: "hl-rehab", icon: "🩹", description: "运动损伤/产后康复/理疗" },
|
|
{ name: "心理健康", slug: "hl-mental", icon: "🧘", description: "心理咨询/情绪管理/冥想" },
|
|
],
|
|
},
|
|
{
|
|
name: "教育培训", slug: "education", icon: "📚", sortOrder: 10,
|
|
description: "技能培训、知识付费、留学与企业培训",
|
|
children: [
|
|
{ name: "职业技能", slug: "edu-skills", icon: "🛠️", description: "编程/设计/运营/考证培训" },
|
|
{ name: "知识付费", slug: "edu-knowledge", icon: "💎", description: "课程制作/社群/个人IP打造" },
|
|
{ name: "留学与语言", slug: "edu-abroad", icon: "✈️", description: "留学申请/语言培训/游学" },
|
|
{ name: "企业培训", slug: "edu-corporate", icon: "🏢", description: "企业内训/管理咨询/团建" },
|
|
{ name: "K12与家庭", slug: "edu-k12", icon: "👨👩👧", description: "学科辅导/素质教育/家庭教育" },
|
|
],
|
|
},
|
|
];
|
|
|
|
async function createIndustryCategories() {
|
|
console.log("Creating industry forum categories...");
|
|
|
|
const parent = await prisma.forumCategory.upsert({
|
|
where: { slug: "industry" },
|
|
update: {},
|
|
create: {
|
|
name: "行业论坛",
|
|
slug: "industry",
|
|
icon: "🏭",
|
|
sortOrder: 10,
|
|
description: "各行业交流讨论,AI驱动的行业智囊团",
|
|
},
|
|
});
|
|
|
|
for (const cat of INDUSTRY_CATEGORIES) {
|
|
let childCat = await prisma.forumCategory.findUnique({
|
|
where: { slug: cat.slug },
|
|
include: { children: true },
|
|
});
|
|
|
|
if (!childCat) {
|
|
childCat = await prisma.forumCategory.create({
|
|
data: {
|
|
name: cat.name,
|
|
slug: cat.slug,
|
|
icon: cat.icon,
|
|
description: cat.description,
|
|
sortOrder: cat.sortOrder,
|
|
parentId: parent.id,
|
|
},
|
|
include: { children: true },
|
|
});
|
|
console.log(` + ${cat.name}`);
|
|
} else {
|
|
if (!childCat.parentId) {
|
|
await prisma.forumCategory.update({
|
|
where: { id: childCat.id },
|
|
data: { parentId: parent.id },
|
|
});
|
|
}
|
|
console.log(` ✓ ${cat.name} (exists)`);
|
|
}
|
|
|
|
const existingSlugs = new Set(childCat.children.map(c => c.slug));
|
|
for (const sub of cat.children) {
|
|
if (existingSlugs.has(sub.slug)) {
|
|
console.log(` - ${sub.name} (exists)`);
|
|
} else {
|
|
const conflictSlug = await prisma.forumCategory.findUnique({
|
|
where: { slug: sub.slug },
|
|
});
|
|
if (conflictSlug) {
|
|
await prisma.forumCategory.update({
|
|
where: { slug: sub.slug },
|
|
data: {
|
|
name: sub.name,
|
|
icon: sub.icon,
|
|
description: sub.description,
|
|
parentId: childCat.id,
|
|
},
|
|
});
|
|
console.log(` ↻ ${sub.name} (moved under ${cat.name})`);
|
|
} else {
|
|
await prisma.forumCategory.create({
|
|
data: { ...sub, parentId: childCat.id },
|
|
});
|
|
console.log(` + ${sub.name}`);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log("Industry categories created.");
|
|
}
|
|
|
|
async function createBotUsers() {
|
|
console.log("Creating/updating bot users...");
|
|
|
|
const raw = readFileSync(BOT_DATA_PATH, "utf-8");
|
|
const { characters } = JSON.parse(raw);
|
|
|
|
let created = 0;
|
|
let updated = 0;
|
|
let skipped = 0;
|
|
|
|
for (const char of characters) {
|
|
const existingUser = await prisma.user.findUnique({
|
|
where: { email: char.email },
|
|
include: { botConfig: true },
|
|
});
|
|
|
|
if (existingUser && existingUser.botConfig) {
|
|
await prisma.botConfig.update({
|
|
where: { userId: existingUser.id },
|
|
data: {
|
|
personality: char.personality,
|
|
primaryForums: char.primaryForums,
|
|
activeHours: char.activeHours,
|
|
},
|
|
});
|
|
console.log(` ↻ ${char.displayName} (updated primaryForums)`);
|
|
updated++;
|
|
continue;
|
|
}
|
|
|
|
if (existingUser && !existingUser.botConfig) {
|
|
await prisma.botConfig.create({
|
|
data: {
|
|
userId: existingUser.id,
|
|
displayName: char.displayName,
|
|
personality: char.personality,
|
|
primaryForums: char.primaryForums,
|
|
activeHours: char.activeHours,
|
|
},
|
|
});
|
|
console.log(` + ${char.displayName} (botConfig created)`);
|
|
updated++;
|
|
continue;
|
|
}
|
|
|
|
const user = await prisma.user.create({
|
|
data: {
|
|
email: char.email,
|
|
name: char.displayName,
|
|
oauthProvider: "bot",
|
|
oauthId: `bot_${char.key}`,
|
|
role: "user",
|
|
isBot: true,
|
|
},
|
|
});
|
|
|
|
await prisma.botConfig.create({
|
|
data: {
|
|
userId: user.id,
|
|
displayName: char.displayName,
|
|
personality: char.personality,
|
|
primaryForums: char.primaryForums,
|
|
activeHours: char.activeHours,
|
|
},
|
|
});
|
|
|
|
console.log(` + ${char.displayName} (${char.key})`);
|
|
created++;
|
|
}
|
|
|
|
console.log(`Bot users: ${created} created, ${updated} updated, ${skipped} skipped.`);
|
|
}
|
|
|
|
async function main() {
|
|
console.log("=== Seed Bot Forum System ===\n");
|
|
|
|
await createIndustryCategories();
|
|
console.log("");
|
|
|
|
await createBotUsers();
|
|
console.log("");
|
|
|
|
console.log("=== Done ===");
|
|
}
|
|
|
|
main()
|
|
.catch((e) => {
|
|
console.error(e);
|
|
process.exit(1);
|
|
})
|
|
.finally(async () => {
|
|
await prisma.$disconnect();
|
|
}); |