🎉 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:
+37
@@ -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
|
||||
Executable
+36
@@ -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()
|
||||
Executable
+171
@@ -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()
|
||||
Executable
+218
@@ -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
@@ -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
@@ -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();
|
||||
Reference in New Issue
Block a user