furniture-3d-system-Wu/scripts/update-demo-models.js
WuYongJie 0db3c07dfb 添加多样化演示数据和模型更新脚本
✨ 新功能:
- 创建多样化的演示产品(办公椅、餐桌、书架)
- 添加 update-demo-models.js 脚本,支持更丰富的演示数据
- 每个产品类别都有专门的装配步骤和板件信息
- 随机生成访问量,模拟真实使用场景

📦 演示数据:
- 现代办公椅:6个装配步骤,4个板件
- 实木餐桌:4个装配步骤,5个板件
- 北欧书架:4个装配步骤,6个板件
- 保持向后兼容的 demo123 链接

🔧 脚本命令:
- npm run demo:update - 更新演示模型
- npm run demo:refresh - 推送数据库并更新模型
- 支持多种家具类别的自动化数据生成

🎯 用户体验:
- 产品管理页面显示多样化的产品列表
- 每个产品都有独特的分享链接
- 真实的统计数据和访问量显示
2025-12-11 17:14:55 +08:00

343 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 更新演示数据脚本 - 添加真实的3D模型
* 使用更好的3D模型替换默认的演示模型
*/
const { PrismaClient } = require('@prisma/client')
const prisma = new PrismaClient()
// 优质的免费3D模型资源
const QUALITY_MODELS = [
{
name: '现代办公椅',
sku: 'CHAIR-MOD-001',
category: '办公家具',
modelUrl: 'https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf',
thumbnailUrl: 'https://threejs.org/examples/screenshots/misc_controls_orbit.jpg',
description: '现代简约设计的办公椅,符合人体工程学'
},
{
name: '实木餐桌',
sku: 'TABLE-WOOD-001',
category: '餐厅家具',
modelUrl: 'https://threejs.org/examples/models/gltf/Flamingo.glb',
thumbnailUrl: 'https://threejs.org/examples/screenshots/webgl_animation_skinning_blending.jpg',
description: '天然实木制作的餐桌,环保耐用'
},
{
name: '北欧书架',
sku: 'SHELF-NORDIC-001',
category: '书房家具',
modelUrl: 'https://threejs.org/examples/models/gltf/Parrot.glb',
thumbnailUrl: 'https://threejs.org/examples/screenshots/webgl_animation_skinning_morph.jpg',
description: '北欧风格设计的多层书架,简洁实用'
}
]
async function updateDemoModels() {
console.log('🔄 开始更新演示模型...')
try {
// 清理现有数据
console.log('🧹 清理现有数据...')
await prisma.shareLink.deleteMany()
await prisma.assemblyStep.deleteMany()
await prisma.panel.deleteMany()
await prisma.product.deleteMany()
// 创建新的产品和数据
for (let i = 0; i < QUALITY_MODELS.length; i++) {
const modelData = QUALITY_MODELS[i]
console.log(`📦 创建产品: ${modelData.name}`)
// 创建产品
const product = await prisma.product.create({
data: {
name: modelData.name,
sku: modelData.sku,
category: modelData.category,
modelUrl: modelData.modelUrl,
thumbnailUrl: modelData.thumbnailUrl
}
})
// 为每个产品创建装配步骤
const steps = generateAssemblySteps(modelData.name, i)
for (const stepData of steps) {
await prisma.assemblyStep.create({
data: {
...stepData,
productId: product.id
}
})
}
// 创建板件信息
const panels = generatePanels(modelData.name, modelData.category)
for (const panelData of panels) {
await prisma.panel.create({
data: {
...panelData,
productId: product.id
}
})
}
// 创建分享链接
const shareToken = `demo${i + 1}${Math.random().toString(36).substr(2, 3)}`
await prisma.shareLink.create({
data: {
productId: product.id,
token: shareToken,
expiresAt: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000), // 90天后过期
viewCount: Math.floor(Math.random() * 50) + 10 // 随机访问量
}
})
console.log(`✅ ${modelData.name} 创建完成 (分享链接: /share/${shareToken})`)
}
// 创建一个特殊的演示链接,保持向后兼容
const firstProduct = await prisma.product.findFirst()
if (firstProduct) {
await prisma.shareLink.create({
data: {
productId: firstProduct.id,
token: 'demo123',
expiresAt: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000),
viewCount: 25
}
})
console.log('✅ 保持向后兼容的 demo123 链接已创建')
}
console.log('\n🎉 演示模型更新完成!')
console.log('\n📋 可用的分享链接:')
const allShareLinks = await prisma.shareLink.findMany({
include: {
product: true
}
})
allShareLinks.forEach(link => {
console.log(` ${link.product.name}: http://localhost:3000/share/${link.token}`)
})
console.log('\n📊 更新统计:')
const stats = await getStats()
console.log(` 产品: ${stats.products}个`)
console.log(` 装配步骤: ${stats.assemblySteps}个`)
console.log(` 板件: ${stats.panels}个`)
console.log(` 分享链接: ${stats.shareLinks}个`)
} catch (error) {
console.error('❌ 更新演示模型失败:', error)
throw error
} finally {
await prisma.$disconnect()
}
}
// 生成装配步骤
function generateAssemblySteps(productName, index) {
const stepTemplates = [
// 办公椅步骤
[
{
order: 1,
title: '准备工作',
description: '检查包装内容,确认所有配件齐全',
partIds: ['base', 'wheels', 'screws'],
transform: { position: { x: 0, y: 0, z: 0 }, rotation: { x: 0, y: 0, z: 0 }, scale: { x: 1, y: 1, z: 1 } },
duration: 2000,
easing: 'power2.inOut'
},
{
order: 2,
title: '安装轮子',
description: '将5个轮子插入椅子底座的孔中',
partIds: ['base', 'wheel1', 'wheel2', 'wheel3', 'wheel4', 'wheel5'],
transform: { position: { x: 0, y: -0.3, z: 0 }, rotation: { x: 0, y: 0, z: 0 }, scale: { x: 1, y: 1, z: 1 } },
duration: 3000,
easing: 'power2.inOut'
},
{
order: 3,
title: '安装气压杆',
description: '将气压杆插入底座中心孔',
partIds: ['base', 'cylinder'],
transform: { position: { x: 0, y: 0.2, z: 0 }, rotation: { x: 0, y: 0, z: 0 }, scale: { x: 1, y: 1, z: 1 } },
duration: 2500,
easing: 'power2.inOut'
},
{
order: 4,
title: '安装座椅',
description: '将座椅放置在气压杆上,确保稳固',
partIds: ['seat', 'cylinder'],
transform: { position: { x: 0, y: 0.5, z: 0 }, rotation: { x: 0, y: 0, z: 0 }, scale: { x: 1, y: 1, z: 1 } },
duration: 2500,
easing: 'power2.inOut'
},
{
order: 5,
title: '安装靠背',
description: '将靠背与座椅连接,调整到合适角度',
partIds: ['seat', 'backrest'],
transform: { position: { x: 0, y: 0.3, z: -0.2 }, rotation: { x: -0.1, y: 0, z: 0 }, scale: { x: 1, y: 1, z: 1 } },
duration: 3000,
easing: 'power2.inOut'
},
{
order: 6,
title: '最终检查',
description: '检查所有连接点,测试椅子功能',
partIds: ['complete'],
transform: { position: { x: 0, y: 0, z: 0 }, rotation: { x: 0, y: Math.PI * 2, z: 0 }, scale: { x: 1, y: 1, z: 1 } },
duration: 4000,
easing: 'power2.inOut'
}
],
// 餐桌步骤
[
{
order: 1,
title: '准备桌腿',
description: '取出四条桌腿,检查表面是否完好',
partIds: ['leg1', 'leg2', 'leg3', 'leg4'],
transform: { position: { x: 0, y: 0, z: 0 }, rotation: { x: 0, y: 0, z: 0 }, scale: { x: 1, y: 1, z: 1 } },
duration: 2000,
easing: 'power2.inOut'
},
{
order: 2,
title: '安装桌腿',
description: '将桌腿固定到桌面底部的预留孔位',
partIds: ['tabletop', 'leg1', 'leg2', 'leg3', 'leg4'],
transform: { position: { x: 0, y: -0.4, z: 0 }, rotation: { x: 0, y: 0, z: 0 }, scale: { x: 1, y: 1, z: 1 } },
duration: 4000,
easing: 'power2.inOut'
},
{
order: 3,
title: '加固连接',
description: '使用螺丝加固桌腿与桌面的连接',
partIds: ['tabletop', 'legs', 'screws'],
transform: { position: { x: 0, y: -0.2, z: 0 }, rotation: { x: 0, y: 0, z: 0 }, scale: { x: 1, y: 1, z: 1 } },
duration: 3000,
easing: 'power2.inOut'
},
{
order: 4,
title: '翻转桌子',
description: '小心翻转桌子,使其正面朝上',
partIds: ['complete'],
transform: { position: { x: 0, y: 0, z: 0 }, rotation: { x: Math.PI, y: 0, z: 0 }, scale: { x: 1, y: 1, z: 1 } },
duration: 3500,
easing: 'power2.inOut'
}
],
// 书架步骤
[
{
order: 1,
title: '组装侧板',
description: '将左右两块侧板准备就位',
partIds: ['side_left', 'side_right'],
transform: { position: { x: 0, y: 0, z: 0 }, rotation: { x: 0, y: 0, z: 0 }, scale: { x: 1, y: 1, z: 1 } },
duration: 2000,
easing: 'power2.inOut'
},
{
order: 2,
title: '安装层板',
description: '依次安装各层隔板,从下往上',
partIds: ['side_left', 'side_right', 'shelf1', 'shelf2', 'shelf3'],
transform: { position: { x: 0, y: 0.1, z: 0 }, rotation: { x: 0, y: 0, z: 0 }, scale: { x: 1, y: 1, z: 1 } },
duration: 4000,
easing: 'power2.inOut'
},
{
order: 3,
title: '安装背板',
description: '将背板固定到书架后方',
partIds: ['frame', 'backpanel'],
transform: { position: { x: 0, y: 0, z: -0.1 }, rotation: { x: 0, y: 0, z: 0 }, scale: { x: 1, y: 1, z: 1 } },
duration: 3000,
easing: 'power2.inOut'
},
{
order: 4,
title: '最终调整',
description: '检查书架稳定性,调整水平',
partIds: ['complete'],
transform: { position: { x: 0, y: 0, z: 0 }, rotation: { x: 0, y: Math.PI / 4, z: 0 }, scale: { x: 1, y: 1, z: 1 } },
duration: 2500,
easing: 'power2.inOut'
}
]
]
return stepTemplates[index] || stepTemplates[0]
}
// 生成板件信息
function generatePanels(productName, category) {
const panelTemplates = {
'办公家具': [
{ code: 'A01', name: '座椅面板', gltfNodeName: 'seat', length: 45.0, width: 40.0, thickness: 3.0 },
{ code: 'A02', name: '靠背面板', gltfNodeName: 'backrest', length: 40.0, width: 50.0, thickness: 2.5 },
{ code: 'B01', name: '椅腿组件', gltfNodeName: 'legs', length: 60.0, width: 60.0, thickness: 45.0 },
{ code: 'C01', name: '气压杆', gltfNodeName: 'cylinder', length: 5.0, width: 5.0, thickness: 20.0 }
],
'餐厅家具': [
{ code: 'T01', name: '桌面板', gltfNodeName: 'tabletop', length: 120.0, width: 80.0, thickness: 3.0 },
{ code: 'T02', name: '桌腿(前左)', gltfNodeName: 'leg1', length: 5.0, width: 5.0, thickness: 72.0 },
{ code: 'T03', name: '桌腿(前右)', gltfNodeName: 'leg2', length: 5.0, width: 5.0, thickness: 72.0 },
{ code: 'T04', name: '桌腿(后左)', gltfNodeName: 'leg3', length: 5.0, width: 5.0, thickness: 72.0 },
{ code: 'T05', name: '桌腿(后右)', gltfNodeName: 'leg4', length: 5.0, width: 5.0, thickness: 72.0 }
],
'书房家具': [
{ code: 'S01', name: '左侧板', gltfNodeName: 'side_left', length: 30.0, width: 180.0, thickness: 2.0 },
{ code: 'S02', name: '右侧板', gltfNodeName: 'side_right', length: 30.0, width: 180.0, thickness: 2.0 },
{ code: 'S03', name: '层板1', gltfNodeName: 'shelf1', length: 80.0, width: 28.0, thickness: 2.0 },
{ code: 'S04', name: '层板2', gltfNodeName: 'shelf2', length: 80.0, width: 28.0, thickness: 2.0 },
{ code: 'S05', name: '层板3', gltfNodeName: 'shelf3', length: 80.0, width: 28.0, thickness: 2.0 },
{ code: 'S06', name: '背板', gltfNodeName: 'backpanel', length: 80.0, width: 180.0, thickness: 0.5 }
]
}
return panelTemplates[category] || panelTemplates['办公家具']
}
// 获取统计信息
async function getStats() {
const [products, assemblySteps, panels, shareLinks] = await Promise.all([
prisma.product.count(),
prisma.assemblyStep.count(),
prisma.panel.count(),
prisma.shareLink.count()
])
return { products, assemblySteps, panels, shareLinks }
}
// 运行脚本
if (require.main === module) {
updateDemoModels()
.then(() => {
console.log('\n✨ 演示模型更新完成!现在可以体验更丰富的3D展示效果了!')
process.exit(0)
})
.catch((error) => {
console.error('脚本执行失败:', error)
process.exit(1)
})
}
module.exports = { updateDemoModels }