/* eslint-disable @typescript-eslint/no-require-imports */ /** * 更新演示数据脚本 - 添加真实的3D模型 * 使用更好的3D模型替换默认的演示模型 */ const DEMO_CONFIRM_TOKEN = 'reset-local-demo' let prisma = null let pool = null function assertLocalDestructiveDemoAllowed() { if (!process.argv.includes(`--confirm=${DEMO_CONFIRM_TOKEN}`)) { throw new Error(`拒绝执行 destructive demo seed:必须显式追加 --confirm=${DEMO_CONFIRM_TOKEN}。`) } if (process.env.NODE_ENV === 'production') { throw new Error('拒绝执行 destructive demo seed:NODE_ENV=production。') } const databaseUrl = process.env.DATABASE_URL || '' if (!databaseUrl) { throw new Error('拒绝执行 destructive demo seed:DATABASE_URL 未配置。') } const parsed = new URL(databaseUrl) if (!['localhost', '127.0.0.1', '::1'].includes(parsed.hostname)) { throw new Error('拒绝执行 destructive demo seed:只允许本机数据库。') } } function createPrismaClient() { const { PrismaClient } = require('@prisma/client') const { PrismaPg } = require('@prisma/adapter-pg') const { Pool } = require('pg') pool = new Pool({ connectionString: process.env.DATABASE_URL }) prisma = new PrismaClient({ adapter: new PrismaPg(pool) }) } // 优质的免费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() { assertLocalDestructiveDemoAllowed() createPrismaClient() 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})`) } 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() await pool?.end() } } // 生成装配步骤 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 }