2025.11.27.17.50

This commit is contained in:
RUI
2025-11-27 17:50:44 +08:00
commit 5dbb30b32c
111 changed files with 18320 additions and 0 deletions

482
scripts/seed.ts Normal file
View File

@@ -0,0 +1,482 @@
/**
* 作者: 阿瑞
* 功能: 数据库填充脚本 (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 = <T>(arr: T[]): T => arr[Math.floor(Math.random() * arr.length)];
const getRandomItems = <T>(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 <main>{data.title}</main>;
}
\`\`\`
## 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 <p>{message}</p>;
}
\`\`\`
## 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();