/** * 作者: 阿瑞 * 功能: 数据库填充脚本 (Seeding Script) * 版本: 2.0.0 * * 运行方式: npx tsx scripts/seed.ts */ import mongoose from 'mongoose'; import fs from 'fs'; import path from 'path'; import bcrypt from 'bcryptjs'; import { User, Article, SystemConfig, Comment, Order, MembershipPlan, Category, Tag } from '../src/models/index'; // ------------------------------------------------------------------ // 1. 环境配置加载 (手动读取 .env.local,避免依赖 dotenv) // ------------------------------------------------------------------ const loadEnv = () => { const envPath = path.resolve(process.cwd(), '.env.local'); if (fs.existsSync(envPath)) { console.log('📄 正在加载 .env.local 配置...'); const envConfig = fs.readFileSync(envPath, 'utf8'); envConfig.split('\n').forEach((line) => { const parts = line.split('='); if (parts.length >= 2) { const key = parts[0].trim(); const value = parts.slice(1).join('=').trim(); if (key && value && !key.startsWith('#')) { process.env[key] = value; } } }); } else { console.warn('⚠️ 未找到 .env.local 文件,尝试使用系统环境变量...'); } }; loadEnv(); const MONGODB_URI = process.env.MONGODB_URI; if (!MONGODB_URI) { console.error('❌ 错误: MONGODB_URI 未定义,请检查 .env.local 文件'); process.exit(1); } // ------------------------------------------------------------------ // 2. 辅助函数 // ------------------------------------------------------------------ const getRandomInt = (min: number, max: number) => Math.floor(Math.random() * (max - min + 1)) + min; const getRandomItem = (arr: T[]): T => arr[Math.floor(Math.random() * arr.length)]; const getRandomItems = (arr: T[], count: number): T[] => { const shuffled = [...arr].sort(() => 0.5 - Math.random()); return shuffled.slice(0, count); }; // ------------------------------------------------------------------ // 3. 主逻辑 // ------------------------------------------------------------------ const seed = async () => { try { console.log('🔌 正在连接数据库...'); await mongoose.connect(MONGODB_URI); console.log('✅ 数据库连接成功'); console.log('🧹 正在清理旧数据...'); await Promise.all([ User.deleteMany({}), Article.deleteMany({}), SystemConfig.deleteMany({}), Comment.deleteMany({}), Order.deleteMany({}), MembershipPlan.deleteMany({}), Category.deleteMany({}), Tag.deleteMany({}) ]); console.log('✅ 旧数据清理完成'); // -------------------------------------------------------------- // 4. 创建基础数据 // -------------------------------------------------------------- // --- 4.1 系统配置 --- console.log('⚙️ 创建系统配置...'); await SystemConfig.create({ 配置标识: 'default', 站点设置: { 网站标题: 'AOUN - 独立开发者资源站', 网站副标题: '构建你的数字资产', Logo地址: '/LOGO.png', 全局SEO关键词: 'React, Next.js, AI, 独立开发, 源码下载', 全局SEO描述: 'AOUN 是一个专注于分享高质量编程资源、AI 实战教程和独立开发经验的平台。', 底部版权信息: '© 2025 AounApp. All Rights Reserved.' }, Banner配置: [ { 标题: 'Next.js 15 全栈实战', 描述: '深入探索 Next.js 15 的最新特性,从 App Router 到 Server Actions,构建高性能全栈应用。', 图片地址: 'https://images.unsplash.com/photo-1618477247222-ac59124545da?q=80&w=2070&auto=format&fit=crop', 按钮文本: '立即学习', 按钮链接: '/article/nextjs-15-fullstack', 状态: 'visible' }, { 标题: 'AI Agent 开发指南', 描述: '掌握 LLM 应用开发核心,使用 LangChain 和 OpenAI 构建智能体。', 图片地址: 'https://images.unsplash.com/photo-1677442136019-21780ecad995?q=80&w=2070&auto=format&fit=crop', 按钮文本: '查看教程', 按钮链接: '/article/ai-agent-guide', 状态: 'visible' }, { 标题: '独立开发变现之路', 描述: '分享从 0 到 1 的产品构建、推广与商业化经验。', 图片地址: 'https://images.unsplash.com/photo-1553729459-efe14ef6055d?q=80&w=2070&auto=format&fit=crop', 按钮文本: '阅读专栏', 按钮链接: '/category/indie-hacker', 状态: 'visible' } ], AI配置列表: [ { 名称: '写作助手-Gemini', 接口地址: 'https://generativelanguage.googleapis.com/v1beta/openai', API密钥: process.env.GEMINI_API_KEY || 'dummy-key', 模型: 'gemini-1.5-flash', 系统提示词: '你是一个专业的技术写作助手,擅长撰写清晰、有深度的技术文章。', 是否启用: true, 流式传输: true } ] }); // --- 4.2 会员套餐 --- console.log('💎 创建会员套餐...'); const plans = await MembershipPlan.create([ { 套餐名称: '月度会员', 有效天数: 30, 价格: 29.9, 描述: '适合短期突击学习,畅享一个月无限下载', 特权配置: { 每日下载限制: 20, 购买折扣: 0.9 } }, { 套餐名称: '年度会员', 有效天数: 365, 价格: 299, 描述: '超值推荐,全年无忧学习,低至 0.8 元/天', 特权配置: { 每日下载限制: 100, 购买折扣: 0.7 } }, { 套餐名称: '永久会员', 有效天数: 36500, 价格: 999, 描述: '一次付费,终身受益,尊享所有未来更新', 特权配置: { 每日下载限制: 9999, 购买折扣: 0.5 } } ]); // --- 4.3 分类与标签 --- console.log('🏷️ 创建分类与标签...'); const categories = await Category.create([ { 分类名称: '全栈开发', 别名: 'fullstack', 排序权重: 10 }, { 分类名称: 'AI 工程化', 别名: 'ai-engineering', 排序权重: 9 }, { 分类名称: '系统设计', 别名: 'system-design', 排序权重: 8 }, { 分类名称: '独立开发', 别名: 'indie-hacker', 排序权重: 7 }, { 分类名称: '源码资源', 别名: 'resources', 排序权重: 6 } ]); const tags = await Tag.create([ { 标签名称: 'Next.js' }, { 标签名称: 'React' }, { 标签名称: 'TypeScript' }, { 标签名称: 'TailwindCSS' }, { 标签名称: 'Node.js' }, { 标签名称: 'PostgreSQL' }, { 标签名称: 'MongoDB' }, { 标签名称: 'Docker' }, { 标签名称: 'LangChain' }, { 标签名称: 'OpenAI' }, { 标签名称: 'RAG' }, { 标签名称: 'SaaS' } ]); // --- 4.4 用户 --- console.log('👥 创建用户...'); // 加密密码 const adminPassword = await bcrypt.hash('admin@aoun.ltd', 10); const userPassword = await bcrypt.hash('123456', 10); const adminUser = await User.create({ 用户名: 'Admin', 邮箱: 'admin@aoun.ltd', 密码: adminPassword, 角色: 'admin', 头像: 'https://api.dicebear.com/7.x/avataaars/svg?seed=admin', 钱包: { 当前积分: 99999, 历史总消费: 0 } }); const demoUsers = await User.create([ { 用户名: 'Alex', 邮箱: 'alex@example.com', 密码: userPassword, 角色: 'user', 头像: 'https://api.dicebear.com/7.x/avataaars/svg?seed=Alex', 钱包: { 当前积分: 50, 历史总消费: 100 } }, { 用户名: 'Sarah', 邮箱: 'sarah@example.com', 密码: userPassword, 角色: 'user', 头像: 'https://api.dicebear.com/7.x/avataaars/svg?seed=Sarah', 会员信息: { 当前等级ID: plans[1]._id, // 年度会员 过期时间: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000) } }, { 用户名: 'Developer', 邮箱: 'dev@example.com', 密码: userPassword, 角色: 'editor', 头像: 'https://api.dicebear.com/7.x/avataaars/svg?seed=Dev' } ]); const allUsers = [adminUser, ...demoUsers]; // --- 4.5 文章/资源 --- console.log('📚 创建文章与资源...'); const articlesData = [ { title: 'Next.js 15 App Router 深度解析与最佳实践', summary: '全面解析 Next.js 15 的 App Router 架构,探讨 Server Components、Server Actions 以及流式渲染的最佳实践。', content: ` ## 引言 Next.js 15 带来了革命性的变化,App Router 彻底改变了我们构建 React 应用的方式。本文将带你深入了解其核心概念。 ## Server Components React Server Components (RSC) 允许我们在服务器端渲染组件,减少发送到客户端的 JavaScript 体积。 \`\`\`tsx async function GetData() { const res = await fetch('https://api.example.com/data'); return res.json(); } export default async function Page() { const data = await GetData(); return
{data.title}
; } \`\`\` ## Server Actions 不再需要单独的 API 路由,直接在组件中定义服务器端逻辑。 ## 总结 掌握 App Router 是成为现代 React 全栈开发者的关键。 `, category: '全栈开发', tags: ['Next.js', 'React', 'TypeScript'], isPaid: false }, { title: '构建企业级 RAG 应用:从原理到实战', summary: '基于 LangChain 和 Vector Database,手把手教你搭建一个能够理解私有数据的 AI 知识库问答系统。', content: ` ## 什么是 RAG? 检索增强生成 (Retrieval-Augmented Generation) 是解决 LLM 幻觉和知识过时的有效方案。 ## 架构设计 1. **文档加载与切分**:使用 LangChain Loader。 2. **向量化 (Embedding)**:使用 OpenAI Embedding 模型。 3. **向量数据库**:使用 Pinecone 或 Milvus。 4. **检索与生成**:构建 Prompt,结合检索结果和 LLM。 ## 代码实现 \`\`\`python from langchain.chains import RetrievalQA from langchain.llms import OpenAI qa = RetrievalQA.from_chain_type( llm=OpenAI(), chain_type="stuff", retriever=docsearch.as_retriever() ) \`\`\` `, category: 'AI 工程化', tags: ['LangChain', 'RAG', 'OpenAI'], isPaid: true }, { title: 'SaaS 产品设计与系统架构指南', summary: '如何设计一个高可用、多租户的 SaaS 系统?本文涵盖数据库设计、权限管理及计费系统实现。', content: ` ## 多租户架构 选择合适的隔离策略: - **Database per Tenant**:最高隔离性,成本高。 - **Schema per Tenant**:平衡方案。 - **Discriminator Column**:共享表,成本最低,开发复杂度高。 ## 权限系统 (RBAC) 设计灵活的角色和权限模型是 SaaS 的核心。 ## 计费集成 集成 Stripe 或 Lemon Squeezy 实现订阅支付。 `, category: '系统设计', tags: ['SaaS', 'System Design', 'PostgreSQL'], isPaid: true }, { title: '独立开发者如何寻找 Profitable Niche', summary: '避开红海竞争,发现细分市场的机会。分享 5 个验证产品构想的低成本方法。', content: ` ## 寻找痛点 最好的产品往往源于开发者自己的痛点。 ## 验证想法 1. **Landing Page MVP**:先做落地页,收集邮箱。 2. **Pre-sale**:尝试预售。 3. **社区反馈**:在 Reddit 或 Product Hunt 发布。 ## 案例分析 分析几个成功的独立开发产品案例... `, category: '独立开发', tags: ['Indie Hacker', 'SaaS'], isPaid: false }, { title: 'React 19 Hook 使用指南', summary: 'React 19 带来了 use()、useOptimistic() 等新 Hook,本文详细介绍它们的用法和场景。', content: ` ## use() Hook \`use()\` 是一个新的 API,用于在组件中读取 Promise 或 Context 的值。 \`\`\`tsx import { use } from 'react'; function Message({ messagePromise }) { const message = use(messagePromise); return

{message}

; } \`\`\` ## useOptimistic 用于处理乐观 UI 更新,提升用户体验。 `, category: '全栈开发', tags: ['React', 'Next.js'], isPaid: false } ]; const articles = []; for (let i = 0; i < articlesData.length; i++) { const data = articlesData[i]; const cat = categories.find(c => c.分类名称 === data.category) || categories[0]; const articleTags = tags.filter(t => data.tags.includes(t.标签名称)); const author = adminUser; // Admin 发布文章 articles.push({ 文章标题: data.title, URL别名: `article-${i + 1}`, 封面图: `https://picsum.photos/seed/${i + 100}/800/450`, 摘要: data.summary, 正文内容: data.content, SEO关键词: [data.category, ...data.tags], SEO描述: data.summary, 作者ID: author._id, 分类ID: cat._id, 标签ID列表: articleTags.map(t => t._id), 价格: data.isPaid ? 29.9 : 0, 支付方式: 'points', 资源属性: data.isPaid ? { 下载链接: 'https://pan.baidu.com/s/demo-link', 提取码: '8888', 解压密码: 'aounapp.com' } : {}, 统计数据: { 阅读数: getRandomInt(100, 5000), 点赞数: getRandomInt(10, 500), 评论数: 0, 收藏数: getRandomInt(5, 200) }, 发布状态: 'published' }); } const createdArticles = await Article.insertMany(articles); // --- 4.6 评论 --- console.log('💬 创建评论...'); const comments = []; for (const article of createdArticles) { const commentCount = getRandomInt(0, 3); for (let j = 0; j < commentCount; j++) { const user = getRandomItem(demoUsers); comments.push({ 文章ID: article._id, 用户ID: user._id, 评论内容: `这篇文章写得很好,对我帮助很大!期待更多关于 ${article.SEO关键词[0]} 的内容。`, 点赞数: getRandomInt(0, 10), 状态: 'visible' }); } } await Comment.insertMany(comments); // 更新文章评论数 for (const article of createdArticles) { const count = comments.filter(c => c.文章ID === article._id).length; await Article.findByIdAndUpdate(article._id, { '统计数据.评论数': count }); } // --- 4.7 订单 --- console.log('🧾 创建订单...'); const orders = []; for (let k = 0; k < 3; k++) { const user = getRandomItem(demoUsers); const plan = getRandomItem(plans); orders.push({ 订单号: `ORDER${Date.now()}${k}`, 用户ID: user._id, 订单类型: 'buy_membership', 商品ID: plan._id, 商品快照: { 标题: plan.套餐名称, 封面: '/images/membership.png' }, 支付金额: plan.价格, 支付方式: 'alipay', 订单状态: 'paid', 支付时间: new Date() }); } await Order.insertMany(orders); console.log('✨ 数据库填充完成!'); console.log(` 📊 数据统计: - 用户: ${allUsers.length} (管理员: admin@aoun.ltd / admin@aoun.ltd) - 文章: ${createdArticles.length} - 评论: ${comments.length} - 订单: ${orders.length} - 分类: ${categories.length} - 标签: ${tags.length} - 会员套餐: ${plans.length} `); } catch (error) { console.error('❌ 数据库填充失败:', error); process.exit(1); } finally { await mongoose.disconnect(); console.log('👋 数据库连接已断开'); process.exit(0); } }; seed();