/** * 数据迁移脚本:迁移旧素材的templateId * * 功能: * 1. 为没有templateId的素材分配默认模板 * 2. 验证所有模板的version字段 * 3. 验证数据完整性 */ import { PrismaClient } from '@prisma/client' const prisma = new PrismaClient() // 内置模板映射 const BUILTIN_TEMPLATES: Record = { '3DMAX': 'builtin-3dmax-material', 'PSD': 'builtin-psd-material', '音频': 'builtin-audio-material', '视频': 'builtin-video-material', '模型': 'builtin-model-material', '其他': 'builtin-other-material', } async function migrateTemplateData() { console.log('开始数据迁移...\n') try { // 1. 检查并更新模板version字段 console.log('步骤 1: 检查模板version字段...') const templatesWithoutVersion = await prisma.uploadTemplate.findMany({ where: { OR: [ { version: { equals: 0 } }, { version: { lt: 1 } } ] }, select: { id: true } }) if (templatesWithoutVersion.length > 0) { console.log(`发现 ${templatesWithoutVersion.length} 个模板version字段异常,正在更新...`) for (const template of templatesWithoutVersion) { await prisma.uploadTemplate.update({ where: { id: template.id }, data: { version: 1 } }) } console.log('✓ 模板version字段更新完成\n') } else { console.log('✓ 所有模板都有version字段\n') } // 2. 迁移没有templateId的素材 console.log('步骤 2: 检查没有templateId的素材...') const materialsWithoutTemplate = await prisma.material.findMany({ where: { templateId: null, deletedAt: null // 只处理未删除的素材 }, select: { id: true, materialType: true, materialKind: true } }) if (materialsWithoutTemplate.length > 0) { console.log(`发现 ${materialsWithoutTemplate.length} 个素材没有templateId,正在分配默认模板...`) let successCount = 0 let errorCount = 0 for (const material of materialsWithoutTemplate) { try { // 根据materialType分配内置模板 const templateId = BUILTIN_TEMPLATES[material.materialType] || 'builtin-other-material' await prisma.material.update({ where: { id: material.id }, data: { templateId } }) successCount++ if (successCount % 100 === 0) { console.log(` 已处理 ${successCount}/${materialsWithoutTemplate.length} 个素材...`) } } catch (error) { console.error(` ✗ 处理素材失败: ${material.id}`, error) errorCount++ } } console.log(`✓ 素材templateId迁移完成: 成功 ${successCount}, 失败 ${errorCount}\n`) } else { console.log('✓ 所有素材都有templateId\n') } // 3. 验证数据完整性 console.log('步骤 3: 验证数据完整性...') // 3.1 检查模板 const totalTemplates = await prisma.uploadTemplate.count() const templatesWithVersion = await prisma.uploadTemplate.count({ where: { version: { gte: 1 } } }) console.log(` 模板总数: ${totalTemplates}`) console.log(` 有version的模板: ${templatesWithVersion}`) if (totalTemplates !== templatesWithVersion) { console.warn(` ⚠ 警告: ${totalTemplates - templatesWithVersion} 个模板没有version字段`) } // 3.2 检查素材 const totalMaterials = await prisma.material.count({ where: { deletedAt: null } }) const materialsWithTemplate = await prisma.material.count({ where: { NOT: { templateId: null }, deletedAt: null } }) console.log(` 素材总数(未删除): ${totalMaterials}`) console.log(` 有templateId的素材: ${materialsWithTemplate}`) if (totalMaterials !== materialsWithTemplate) { console.warn(` ⚠ 警告: ${totalMaterials - materialsWithTemplate} 个素材没有templateId`) } // 3.3 检查字段 const totalFields = await prisma.uploadField.count() const fieldsWithTemplate = await prisma.uploadField.count({ where: { templateId: { not: '' } } }) console.log(` 字段总数: ${totalFields}`) console.log(` 有templateId的字段: ${fieldsWithTemplate}`) // 3.4 检查字段池 const totalFieldDefs = await prisma.fieldDefinition.count() console.log(` 字段池定义总数: ${totalFieldDefs}`) console.log('\n✓ 数据完整性验证完成') // 4. 生成迁移报告 console.log('\n' + '='.repeat(60)) console.log('迁移报告') console.log('='.repeat(60)) console.log(`模板: ${templatesWithVersion}/${totalTemplates} 有version字段`) console.log(`素材: ${materialsWithTemplate}/${totalMaterials} 有templateId`) console.log(`字段: ${fieldsWithTemplate}/${totalFields} 有templateId`) console.log(`字段池: ${totalFieldDefs} 个定义`) console.log('='.repeat(60)) if (totalTemplates === templatesWithVersion && totalMaterials === materialsWithTemplate) { console.log('\n✓ 数据迁移成功完成!') return true } else { console.warn('\n⚠ 数据迁移完成,但存在警告,请检查上述信息') return false } } catch (error) { console.error('\n✗ 数据迁移失败:', error) throw error } finally { await prisma.$disconnect() } } // 执行迁移 if (require.main === module) { migrateTemplateData() .then((success) => { process.exit(success ? 0 : 1) }) .catch((error) => { console.error('迁移脚本执行失败:', error) process.exit(1) }) } export { migrateTemplateData }