🎉 feat: initialize 宇之然AI维度 project

- uni-app Vue3 frontend with dark glassmorphism theme
- 5 AI dimensions: Origin / Development / Current / Learning / Trend
- AI Chat system prompt updated from geometry to AI
- 23 AI knowledge articles initialized in DB
- Trend API + cron job for daily news
- Product pricing (Pro ¥19.9, VIP ¥39.9)
- Pinia stores + API utilities
- AGENTS.md documentation
This commit is contained in:
Yuzhiran Dev
2026-07-10 13:26:04 +08:00
commit 24cd9cef53
107 changed files with 20665 additions and 0 deletions
+37
View File
@@ -0,0 +1,37 @@
@echo off
echo ========================================
echo MongoDB 用户创建工具
echo ========================================
echo.
echo 请在 MongoDB 服务器上执行以下命令:
echo.
echo 1. 连接到 MongoDB:
echo mongo --host 192.168.136.130 --port 27017
echo.
echo 2. 切换到 admin 数据库:
echo use admin
echo.
echo 3. 创建管理员用户(如果还没有):
echo db.createUser({
echo user: "admin",
echo pwd: "admin123",
echo roles: ["root"]
echo })
echo.
echo 4. 切换到 wdkj 数据库:
echo use wdkj
echo.
echo 5. 创建 wdkj 用户:
echo db.createUser({
echo user: "wdkj123",
echo pwd: "wdkj123",
echo roles: [
echo { role: "readWrite", db: "wdkj" }
echo ]
echo })
echo.
echo 6. 验证用户:
echo db.auth("wdkj123", "wdkj123")
echo.
echo ========================================
pause
+36
View File
@@ -0,0 +1,36 @@
/**
* 快速修复BGM URL - 使用测试音频
*/
const mongoose = require('mongoose')
require('dotenv').config()
const { BGM } = require('../src/models')
// 免费的测试音频URL (SoundHelix - 可商用)
const TEST_AUDIO_URL = 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3'
async function fixBGMUrls() {
try {
await mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/yuzhiran')
console.log('✅ 数据库连接成功')
// 更新所有BGM的URL为测试音频
const result = await BGM.updateMany(
{},
{ url: TEST_AUDIO_URL }
)
console.log(`✅ 已更新 ${result.modifiedCount} 条BGM记录`)
console.log(` 新URL: ${TEST_AUDIO_URL}`)
console.log('\n💡 现在可以测试BGM播放功能了')
console.log('⚠️ 注意: 这是测试音频,生产环境请替换为正式音乐')
} catch (error) {
console.error('❌ 更新失败:', error)
} finally {
await mongoose.disconnect()
}
}
fixBGMUrls()
+171
View File
@@ -0,0 +1,171 @@
/**
* BGM音效初始化脚本
* 为每个维度添加示例背景音乐
*/
const mongoose = require('mongoose')
require('dotenv').config()
// 导入模型
const { BGM } = require('../src/models')
// 示例BGM数据(实际使用时需要替换为真实的音频URL)
const sampleBGMs = [
// 一维 - 线性旋律
{
name: '星光轨迹',
dimension: 1,
url: 'https://example.com/bgm/dim1_starlight.mp3',
duration: 120,
loop: true,
volume: 0.4,
sortOrder: 1,
description: '轻柔的线性旋律,适合一维空间的探索',
isActive: true
},
{
name: '弦之舞',
dimension: 1,
url: 'https://example.com/bgm/dim1_string_dance.mp3',
duration: 95,
loop: true,
volume: 0.45,
sortOrder: 2,
description: '活泼的弦乐节奏',
isActive: true
},
// 二维 - 平面和声
{
name: '星座幻想',
dimension: 2,
url: 'https://example.com/bgm/dim2_constellation.mp3',
duration: 150,
loop: true,
volume: 0.5,
sortOrder: 1,
description: '梦幻的星空音乐,配合二维星座绘制',
isActive: true
},
{
name: '几何韵律',
dimension: 2,
url: 'https://example.com/bgm/dim2_geometry.mp3',
duration: 110,
loop: true,
volume: 0.48,
sortOrder: 2,
description: '轻快的几何节奏',
isActive: true
},
// 三维 - 立体环绕
{
name: '立方体回响',
dimension: 3,
url: 'https://example.com/bgm/dim3_cube_echo.mp3',
duration: 180,
loop: true,
volume: 0.5,
sortOrder: 1,
description: '空间感强烈的立体音效',
isActive: true
},
{
name: '多维共振',
dimension: 3,
url: 'https://example.com/bgm/dim3_resonance.mp3',
duration: 140,
loop: true,
volume: 0.52,
sortOrder: 2,
description: '富有层次感的三维音效',
isActive: true
},
// 四维 - 时间流动
{
name: '时空涟漪',
dimension: 4,
url: 'https://example.com/bgm/dim4_spacetime.mp3',
duration: 200,
loop: true,
volume: 0.45,
sortOrder: 1,
description: '神秘的四维时空音乐',
isActive: true
},
{
name: '超立方漫游',
dimension: 4,
url: 'https://example.com/bgm/dim4_tesseract.mp3',
duration: 165,
loop: true,
volume: 0.47,
sortOrder: 2,
description: '穿越维度的奇妙旅程',
isActive: true
},
// 五维 - 高维混沌
{
name: '思维风暴',
dimension: 5,
url: 'https://example.com/bgm/dim5_thought_storm.mp3',
duration: 220,
loop: true,
volume: 0.5,
sortOrder: 1,
description: '复杂而迷人的高维音效',
isActive: true
},
{
name: '量子纠缠',
dimension: 5,
url: 'https://example.com/bgm/dim5_quantum.mp3',
duration: 190,
loop: true,
volume: 0.48,
sortOrder: 2,
description: '微观世界的奇妙声音',
isActive: true
}
]
async function initBGM() {
try {
// 连接数据库
await mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/yuzhiran')
console.log('✅ 数据库连接成功')
// 检查是否已有BGM数据
const existingCount = await BGM.countDocuments()
if (existingCount > 0) {
console.log(`⚠️ 数据库中已有 ${existingCount} 条BGM记录,跳过初始化`)
await mongoose.disconnect()
return
}
// 插入示例数据
const result = await BGM.insertMany(sampleBGMs)
console.log(`✅ 成功初始化 ${result.length} 条BGM记录`)
// 按维度统计
for (let dim = 1; dim <= 5; dim++) {
const count = await BGM.countDocuments({ dimension: dim })
console.log(` 维度${dim}: ${count}`)
}
console.log('\n🎵 BGM初始化完成!')
console.log('💡 提示:请在后台管理系统中上传真实的音频文件并更新URL')
} catch (error) {
console.error('❌ BGM初始化失败:', error)
} finally {
await mongoose.disconnect()
console.log('数据库连接已关闭')
}
}
// 执行初始化
initBGM()
+218
View File
@@ -0,0 +1,218 @@
require('dotenv').config({ path: require('path').resolve(__dirname, '../.env') })
const mongoose = require('mongoose')
const { Admin, ShopItem, Knowledge, Gallery } = require('../src/models')
/**
* 数据库初始化脚本
*/
async function initDatabase() {
try {
// 连接数据库
const mongoURI = process.env.MONGODB_URI
await mongoose.connect(mongoURI)
console.log('✅ 数据库连接成功')
// 1. 创建超级管理员
console.log('\n📋 创建超级管理员...')
const existingAdmin = await Admin.findOne({ username: 'admin' })
if (!existingAdmin) {
const admin = new Admin({
username: process.env.ADMIN_USERNAME || 'admin',
password: process.env.ADMIN_PASSWORD || 'admin123456',
email: process.env.ADMIN_EMAIL || 'admin@wdkj.com',
realName: '超级管理员',
role: 'super_admin',
permissions: ['*'],
status: 'active'
})
await admin.save()
console.log('✅ 超级管理员创建成功')
console.log(` 用户名: ${admin.username}`)
console.log(` 密码: ${process.env.ADMIN_PASSWORD || 'admin123456'}`)
} else {
console.log('⚠️ 管理员账户已存在')
}
// 2. 初始化商品
console.log('\n📋 初始化商品...')
const products = [
{
itemId: 'skin_quantum',
name: '量子线皮肤',
description: '一维世界专属皮肤,让你的线条闪耀量子光芒',
price: 6,
type: 'skin',
status: 'active',
sortOrder: 1
},
{
itemId: 'skin_hologram',
name: '全息面皮肤',
description: '二维世界专属皮肤,让你的图形绽放全息色彩',
price: 18,
type: 'skin',
status: 'active',
sortOrder: 2
},
{
itemId: 'skin_nebula',
name: '星云体皮肤',
description: '三维世界专属皮肤,让你的立方体流光溢彩',
price: 30,
type: 'skin',
status: 'active',
sortOrder: 3
},
{
itemId: 'no_ads',
name: '永久去广告',
description: '一键去除所有广告,享受纯净的维度探索之旅。',
price: 1200,
type: 'ad_free',
status: 'active',
sortOrder: 4
},
{
itemId: 'monthly_sub',
name: '星云月卡',
description: '尊享月度特权:全皮肤免费用、得分翻倍、专属身份标识。',
price: 1800,
type: 'vip',
duration: 30,
status: 'active',
sortOrder: 5
}
]
for (const product of products) {
const existing = await ShopItem.findOne({ itemId: product.itemId })
if (!existing) {
await ShopItem.create(product)
console.log(`${product.name} 创建成功`)
} else {
console.log(` ⚠️ ${product.name} 已存在`)
}
}
// 3. 初始化知识库示例
console.log('\n📋 初始化知识库示例...')
const knowledgeList = [
{
title: '什么是维度?',
content: '维度是描述空间独立方向的参数。零维是点,一维是线,二维是面,三维是体。',
dim: 1,
category: 'concept',
tags: ['基础概念', '维度'],
status: 'approved',
sortOrder: 1
},
{
title: '一维世界:线的宇宙',
content: '一维世界只有长度这一个维度,就像一条无限延伸的直线。生活在一维世界的生物只能前进或后退。',
dim: 1,
category: 'concept',
tags: ['一维', '线'],
status: 'approved',
sortOrder: 2
},
{
title: '二维世界:平面王国',
content: '二维世界有长度和宽度两个维度,就像一张无限大的纸。平面国中的生物可以在平面上自由移动。',
dim: 2,
category: 'concept',
tags: ['二维', '平面'],
status: 'approved',
sortOrder: 1
},
{
title: '三维世界:立体空间',
content: '三维世界有长度、宽度和高度三个维度,这正是我们生活的世界。我们可以前后、左右、上下移动。',
dim: 3,
category: 'concept',
tags: ['三维', '立体'],
status: 'approved',
sortOrder: 1
}
]
for (const knowledge of knowledgeList) {
const existing = await Knowledge.findOne({ title: knowledge.title })
if (!existing) {
await Knowledge.create(knowledge)
console.log(`${knowledge.title} 创建成功`)
} else {
console.log(` ⚠️ ${knowledge.title} 已存在`)
}
}
// 4. 初始化画廊示例作品
console.log('\n📋 初始化画廊示例作品...')
const galleryWorks = [
{
title: '一维·星点轨迹',
description: '在一维世界中,点动成线。这是我探索宇宙起点的轨迹记录。',
imageData: '/static/images/gallery_demo_dim1.png',
dim: 1,
openid: 'system_admin',
authorName: '系统管理员',
authorAvatar: '/static/images/default_avatar.png',
likeCount: 128,
tags: ['一维', '轨迹', '起点'],
status: 'approved',
createdAt: new Date()
},
{
title: '二维·星座图腾',
description: '连接星体,绘制出属于我的平面王国图腾。',
imageData: '/static/images/gallery_demo_dim2.png',
dim: 2,
openid: 'system_admin',
authorName: '星辰旅者',
authorAvatar: '/static/images/default_avatar.png',
likeCount: 96,
tags: ['二维', '星座', '图腾'],
status: 'approved',
createdAt: new Date()
},
{
title: '三维·流光立方',
description: '旋转的立方体展现了立体几何的魅力,色彩在空间中流动。',
imageData: '/static/images/gallery_demo_dim3.png',
dim: 3,
openid: 'system_admin',
authorName: '维度探索者',
authorAvatar: '/static/images/default_avatar.png',
likeCount: 215,
tags: ['三维', '立方体', '流光'],
status: 'approved',
createdAt: new Date()
}
]
for (const work of galleryWorks) {
const existing = await Gallery.findOne({ title: work.title })
if (!existing) {
await Gallery.create(work)
console.log(`${work.title} 创建成功`)
} else {
console.log(` ⚠️ ${work.title} 已存在`)
}
}
console.log('\n✅ 数据库初始化完成!')
console.log('\n📝 管理员登录信息:')
console.log(` 用户名: ${process.env.ADMIN_USERNAME || 'admin'}`)
console.log(` 密码: ${process.env.ADMIN_PASSWORD || 'admin123456'}`)
} catch (error) {
console.error('❌ 初始化失败:', error)
} finally {
await mongoose.connection.close()
console.log('\n🔌 数据库连接已关闭')
process.exit(0)
}
}
// 执行初始化
initDatabase()
+182
View File
@@ -0,0 +1,182 @@
/**
* 初始化知识数据脚本
* 用于填充测试知识内容
*/
const mongoose = require('mongoose')
require('dotenv').config()
// 导入知识模型
const Knowledge = require('../src/models/Knowledge')
// 测试知识数据
const testKnowledgeData = [
// 一维知识
{
title: '一维空间的基本概念',
content: '一维空间是数学中最简单的空间形式,它只有长度这一个维度。在物理学中,一维空间可以用来描述直线运动,比如物体在直线上来回运动。一维空间的特点是所有点都在同一条直线上,没有宽度和高度。',
dim: 1,
category: 'concept',
tags: ['一维', '空间', '数学'],
status: 'approved',
isPremium: false,
requiredPoints: 0,
price: 0
},
{
title: '一维空间的数学表示',
content: '在数学中,一维空间通常用实数轴来表示。实数轴上的每个点对应一个实数,点与点之间的距离就是它们对应实数的差的绝对值。一维空间中的向量只有一个分量,可以用一个实数来表示。',
dim: 1,
category: 'application',
tags: ['一维', '数学', '实数轴'],
status: 'approved',
isPremium: false,
requiredPoints: 0,
price: 0
},
// 二维知识
{
title: '二维空间的几何特性',
content: '二维空间具有两个维度:长度和宽度。在二维空间中,我们可以定义平面几何图形,如点、线、三角形、圆形等。二维空间中的每个点可以用两个坐标(x, y)来表示。',
dim: 2,
category: 'concept',
tags: ['二维', '几何', '平面'],
status: 'approved',
isPremium: false,
requiredPoints: 10,
price: 0
},
{
title: '二维坐标系的应用',
content: '二维坐标系在计算机图形学、地理信息系统、工程设计等领域有广泛应用。笛卡尔坐标系是最常见的二维坐标系,它使用相互垂直的x轴和y轴来定位平面上的点。',
dim: 2,
category: 'application',
tags: ['二维', '坐标系', '应用'],
status: 'approved',
isPremium: false,
requiredPoints: 20,
price: 0
},
// 三维知识
{
title: '理解三维空间',
content: '三维空间是我们最熟悉的空间形式,它具有长度、宽度和高度三个维度。在三维空间中,物体具有体积,可以定义立体几何图形如立方体、球体、圆柱体等。',
dim: 3,
category: 'concept',
tags: ['三维', '空间', '立体'],
status: 'approved',
isPremium: false,
requiredPoints: 30,
price: 0
},
{
title: '三维建模技术',
content: '三维建模技术在动画制作、游戏开发、建筑设计等领域有重要应用。常见的三维建模方法包括多边形建模、NURBS建模、体素建模等。',
dim: 3,
category: 'application',
tags: ['三维', '建模', '技术'],
status: 'approved',
isPremium: false,
requiredPoints: 40,
price: 0
},
// 四维知识(付费内容)
{
title: '四维时空的概念',
content: '四维时空是爱因斯坦相对论中的核心概念,它将三维空间与时间维度结合。在四维时空中,事件的发生不仅取决于空间位置,还取决于时间点。四维时空的几何特性由闵可夫斯基度规描述。',
dim: 4,
category: 'concept',
tags: ['四维', '时空', '相对论'],
status: 'approved',
isPremium: true,
requiredPoints: 0,
price: 9.9
},
{
title: '四维空间的可视化',
content: '由于人类无法直接感知四维空间,科学家们开发了多种可视化技术。常见的方法包括投影法、切片法、颜色编码法等,帮助我们理解高维空间的特性。',
dim: 4,
category: 'application',
tags: ['四维', '可视化', '高维'],
status: 'approved',
isPremium: true,
requiredPoints: 0,
price: 12.9
},
// 五维知识(高级付费内容)
{
title: '五维空间的物理意义',
content: '五维空间理论在弦理论和卡鲁扎-克莱因理论中有重要应用。在五维空间中,除了三维空间和一维时间外,还可能存在额外的紧致维度。这些理论试图统一引力与其他基本力。',
dim: 5,
category: 'concept',
tags: ['五维', '弦理论', '统一理论'],
status: 'approved',
isPremium: true,
requiredPoints: 0,
price: 19.9
},
{
title: '高维空间的数学基础',
content: '高维空间的数学研究涉及线性代数、微分几何、拓扑学等多个领域。n维空间中的点可以用n个坐标表示,距离和角度等概念可以通过推广低维空间的定义来建立。',
dim: 5,
category: 'application',
tags: ['高维', '数学', '几何'],
status: 'approved',
isPremium: true,
requiredPoints: 0,
price: 24.9
}
]
async function initKnowledgeData() {
try {
// 连接数据库
await mongoose.connect(process.env.MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true
})
console.log('✅ 数据库连接成功')
// 清空现有知识数据(可选)
await Knowledge.deleteMany({})
console.log('✅ 已清空现有知识数据')
// 插入测试数据
const inserted = await Knowledge.insertMany(testKnowledgeData)
console.log(`✅ 成功插入 ${inserted.length} 条知识数据`)
// 显示插入的数据统计
const dimCounts = {}
inserted.forEach(item => {
dimCounts[item.dim] = (dimCounts[item.dim] || 0) + 1
})
console.log('📊 知识数据统计:')
Object.keys(dimCounts).sort().forEach(dim => {
console.log(` ${dim}维知识: ${dimCounts[dim]}`)
})
const premiumCount = inserted.filter(item => item.isPremium).length
const freeCount = inserted.length - premiumCount
console.log(` 💰 付费内容: ${premiumCount}`)
console.log(` 🆓 免费内容: ${freeCount}`)
} catch (error) {
console.error('❌ 初始化知识数据失败:', error)
} finally {
await mongoose.connection.close()
console.log('🔚 数据库连接已关闭')
}
}
// 执行初始化
if (require.main === module) {
initKnowledgeData()
}
module.exports = { initKnowledgeData }
+241
View File
@@ -0,0 +1,241 @@
/**
* 拼音探索模块初始化数据脚本
* 创建拼音内容、成就等基础数据
*/
require('dotenv').config({ path: './.env' });
const mongoose = require('mongoose');
const { PinyinContent, PinyinAchievement } = require('../src/models/pinyin');
const connectDB = require('../src/config/database');
// 声母数据
const initials = [
{ symbol: 'b', name: '玻', order: 1 },
{ symbol: 'p', name: '坡', order: 2 },
{ symbol: 'm', name: '摸', order: 3 },
{ symbol: 'f', name: '佛', order: 4 },
{ symbol: 'd', name: '得', order: 5 },
{ symbol: 't', name: '特', order: 6 },
{ symbol: 'n', name: '讷', order: 7 },
{ symbol: 'l', name: '勒', order: 8 },
{ symbol: 'g', name: '哥', order: 9 },
{ symbol: 'k', name: '科', order: 10 },
{ symbol: 'h', name: '喝', order: 11 },
{ symbol: 'j', name: '基', order: 12 },
{ symbol: 'q', name: '期', order: 13 },
{ symbol: 'x', name: '希', order: 14 },
{ symbol: 'zh', name: '知', order: 15 },
{ symbol: 'ch', name: '蚩', order: 16 },
{ symbol: 'sh', name: '诗', order: 17 },
{ symbol: 'r', name: '日', order: 18 },
{ symbol: 'z', name: '资', order: 19 },
{ symbol: 'c', name: '雌', order: 20 },
{ symbol: 's', name: '思', order: 21 },
{ symbol: 'y', name: '医', order: 22 },
{ symbol: 'w', name: '巫', order: 23 }
];
// 韵母数据
const finals = [
// 单韵母
{ symbol: 'a', name: '啊', order: 1 },
{ symbol: 'o', name: '喔', order: 2 },
{ symbol: 'e', name: '鹅', order: 3 },
{ symbol: 'i', name: '衣', order: 4 },
{ symbol: 'u', name: '乌', order: 5 },
{ symbol: 'ü', name: '迂', order: 6 },
// 复韵母
{ symbol: 'ai', name: '哀', order: 7 },
{ symbol: 'ei', name: '诶', order: 8 },
{ symbol: 'ui', name: '威', order: 9 },
{ symbol: 'ao', name: '熬', order: 10 },
{ symbol: 'ou', name: '欧', order: 11 },
{ symbol: 'iu', name: '优', order: 12 },
{ symbol: 'ie', name: '耶', order: 13 },
{ symbol: 'üe', name: '约', order: 14 },
{ symbol: 'er', name: '儿', order: 15 },
// 前鼻韵母
{ symbol: 'an', name: '安', order: 16 },
{ symbol: 'en', name: '恩', order: 17 },
{ symbol: 'in', name: '因', order: 18 },
{ symbol: 'un', name: '温', order: 19 },
{ symbol: 'ün', name: '晕', order: 20 },
// 后鼻韵母
{ symbol: 'ang', name: '昂', order: 21 },
{ symbol: 'eng', name: '亨', order: 22 },
{ symbol: 'ing', name: '英', order: 23 },
{ symbol: 'ong', name: '雍', order: 24 }
];
// 整体认读音节
const overalls = [
{ symbol: 'zhi', name: '织', order: 1 },
{ symbol: 'chi', name: '吃', order: 2 },
{ symbol: 'shi', name: '诗', order: 3 },
{ symbol: 'ri', name: '日', order: 4 },
{ symbol: 'zi', name: '资', order: 5 },
{ symbol: 'ci', name: '雌', order: 6 },
{ symbol: 'si', name: '思', order: 7 },
{ symbol: 'yi', name: '衣', order: 8 },
{ symbol: 'wu', name: '乌', order: 9 },
{ symbol: 'yu', name: '迂', order: 10 },
{ symbol: 'ye', name: '耶', order: 11 },
{ symbol: 'yue', name: '约', order: 12 },
{ symbol: 'yuan', name: '冤', order: 13 },
{ symbol: 'yin', name: '因', order: 14 },
{ symbol: 'yun', name: '晕', order: 15 },
{ symbol: 'ying', name: '英', order: 16 }
];
// 成就数据
const achievements = [
// 探索类成就
{ code: 'first_explore', name: '初次探索', description: '完成首次拼音探索', type: 'explore', condition: { type: 'explore_count', value: 1 } },
{ code: 'explorer_5', name: '初级探索者', description: '探索5个拼音', type: 'explore', condition: { type: 'explore_count', value: 5 } },
{ code: 'explorer_10', name: '中级探索者', description: '探索10个拼音', type: 'explore', condition: { type: 'explore_count', value: 10 } },
{ code: 'explorer_23', name: '声母专家', description: '探索所有声母', type: 'explore', condition: { type: 'explore_count', value: 23 } },
{ code: 'explorer_47', name: '拼音达人', description: '探索47个拼音', type: 'explore', condition: { type: 'explore_count', value: 47 } },
{ code: 'explorer_all', name: '拼音大师', description: '探索所有拼音', type: 'explore', condition: { type: 'explore_count', value: 63 } },
// 收集类成就
{ code: 'first_stone', name: '第一颗能量石', description: '收集第一颗能量石', type: 'collection', condition: { type: 'collect_count', value: 1 } },
{ code: 'collector_5', name: '能量收集者', description: '收集5颗能量石', type: 'collection', condition: { type: 'collect_count', value: 5 } },
{ code: 'collector_10', name: '能量守护者', description: '收集10颗能量石', type: 'collection', condition: { type: 'collect_count', value: 10 } },
{ code: 'collector_23', name: '声母守护者', description: '收集所有声母能量石', type: 'collection', condition: { type: 'collect_count', value: 23 } },
{ code: 'collector_all', name: '能量大师', description: '收集所有能量石', type: 'collection', condition: { type: 'collect_count', value: 63 } },
// 连续探索成就
{ code: 'streak_3', name: '坚持3天', description: '连续探索3天', type: 'streak', condition: { type: 'streak_days', value: 3 } },
{ code: 'streak_7', name: '坚持一周', description: '连续探索7天', type: 'streak', condition: { type: 'streak_days', value: 7 } },
{ code: 'streak_30', name: '坚持一个月', description: '连续探索30天', type: 'streak', condition: { type: 'streak_days', value: 30 } }
];
async function initPinyinContents() {
console.log('开始初始化拼音内容...');
// 初始化声母
for (const item of initials) {
await PinyinContent.findOneAndUpdate(
{ symbol: item.symbol },
{
symbol: item.symbol,
type: 'initial',
name: item.name,
order: item.order,
isFree: item.order <= 5, // 前5个免费
status: 'active',
pronunciation: `${item.name}的音`,
exploreAreas: {
audio: true,
mouth: true,
speak: true,
write: true,
game: true,
words: true
}
},
{ upsert: true, new: true }
);
}
console.log(`✅ 已初始化 ${initials.length} 个声母`);
// 初始化韵母
for (const item of finals) {
await PinyinContent.findOneAndUpdate(
{ symbol: item.symbol },
{
symbol: item.symbol,
type: 'final',
name: item.name,
order: item.order,
isFree: false, // 韵母需要解锁
status: 'active',
pronunciation: `${item.name}的音`,
exploreAreas: {
audio: true,
mouth: true,
speak: true,
write: true,
game: true,
words: true
}
},
{ upsert: true, new: true }
);
}
console.log(`✅ 已初始化 ${finals.length} 个韵母`);
// 初始化整体认读音节
for (const item of overalls) {
await PinyinContent.findOneAndUpdate(
{ symbol: item.symbol },
{
symbol: item.symbol,
type: 'overall',
name: item.name,
order: item.order,
isFree: false, // 整体认读需要解锁
status: 'active',
pronunciation: `${item.name}的音`,
exploreAreas: {
audio: true,
mouth: true,
speak: true,
write: true,
game: true,
words: true
}
},
{ upsert: true, new: true }
);
}
console.log(`✅ 已初始化 ${overalls.length} 个整体认读音节`);
}
async function initAchievements() {
console.log('开始初始化成就...');
for (let i = 0; i < achievements.length; i++) {
const item = achievements[i];
await PinyinAchievement.findOneAndUpdate(
{ code: item.code },
{
code: item.code,
name: item.name,
description: item.description,
type: item.type,
condition: item.condition,
order: i + 1,
isActive: true,
reward: {
type: 'stone',
value: 1
}
},
{ upsert: true, new: true }
);
}
console.log(`✅ 已初始化 ${achievements.length} 个成就`);
}
async function main() {
try {
// 连接数据库
await connectDB();
console.log('数据库连接成功');
// 初始化拼音内容
await initPinyinContents();
// 初始化成就
await initAchievements();
console.log('\n✨ 数据初始化完成!');
process.exit(0);
} catch (error) {
console.error('初始化失败:', error);
process.exit(1);
}
}
main();