/** * 在服务器上清理孤立的任务-详情素材残留文件 */ import { PrismaClient } from '@prisma/client'; import { deleteFromS3, isS3Url } from '../lib/s3'; import { promises as fs } from 'fs'; import path from 'path'; const MATERIAL_ID = 'cc7e5cdd-d408-4bcd-a73f-4ddcaa8054b9'; const prisma = new PrismaClient(); async function main() { console.log('开始检查服务器上的残留文件...\n'); try { // 1. 检查数据库中的素材记录 console.log('1. 检查数据库中的素材记录...'); const material = await prisma.material.findUnique({ where: { id: MATERIAL_ID }, select: { id: true, materialType: true }, }); if (material) { console.log(` ❌ 发现素材记录: id=${material.id}, type=${material.materialType}`); } else { console.log(` ✅ 素材记录已删除`); } // 2. 检查数据库中的资产记录 console.log('\n2. 检查数据库中的资产记录...'); const assets = await prisma.materialAsset.findMany({ where: { materialId: MATERIAL_ID }, select: { id: true, assetType: true, fileUrl: true, fileName: true, }, }); if (assets.length > 0) { console.log(` ❌ 发现 ${assets.length} 条资产记录:`); assets.forEach((asset, index) => { console.log(` [${index + 1}] type=${asset.assetType}, url=${asset.fileUrl}`); }); } else { console.log(` ✅ 资产记录已删除`); } // 3. 检查本地文件系统 console.log('\n3. 检查本地文件系统...'); const uploadsDir = path.join(process.cwd(), 'uploads', 'product-package', MATERIAL_ID); let localFiles: string[] = []; try { const files = await fs.readdir(uploadsDir, { recursive: true }); localFiles = files.map(f => path.join(uploadsDir, f)); console.log(` ❌ 发现本地目录: ${uploadsDir}`); console.log(` 包含 ${files.length} 个文件/目录`); files.forEach((file, index) => { console.log(` [${index + 1}] ${file}`); }); } catch (err: any) { if (err.code === 'ENOENT') { console.log(` ✅ 本地目录已删除`); } else { console.log(` ⚠️ 检查本地目录时出错: ${err.message}`); } } // 4. 总结 console.log('\n' + '='.repeat(60)); console.log('检查总结:'); console.log(` - 素材记录: ${material ? '存在' : '已删除'}`); console.log(` - 资产记录: ${assets.length} 条`); console.log(` - 本地文件: ${localFiles.length} 个`); console.log('='.repeat(60)); // 5. 自动清理 if (material || assets.length > 0 || localFiles.length > 0) { console.log('\n开始自动清理...\n'); // 删除资产记录 if (assets.length > 0) { console.log('删除资产记录...'); const result = await prisma.materialAsset.deleteMany({ where: { materialId: MATERIAL_ID }, }); console.log(` ✅ 已删除 ${result.count} 条资产记录`); } // 删除素材记录 if (material) { console.log('删除素材记录...'); try { await prisma.material.delete({ where: { id: MATERIAL_ID }, }); console.log(` ✅ 已删除素材记录`); } catch (err: any) { console.log(` ⚠️ 删除素材记录失败: ${err.message}`); } } // 删除本地文件 if (localFiles.length > 0) { console.log('删除本地文件...'); try { await fs.rm(uploadsDir, { recursive: true, force: true }); console.log(` ✅ 已删除本地目录: ${uploadsDir}`); } catch (err: any) { console.log(` ⚠️ 删除本地文件失败: ${err.message}`); } } // 删除 S3 文件 const s3Files = assets.filter(a => isS3Url(a.fileUrl)); if (s3Files.length > 0) { console.log(`删除 S3 文件 (${s3Files.length} 个)...`); let deletedCount = 0; let failedCount = 0; for (const asset of s3Files) { try { await deleteFromS3(asset.fileUrl); deletedCount++; console.log(` ✅ 已删除: ${asset.fileUrl}`); } catch (err: any) { failedCount++; console.log(` ❌ 删除失败: ${asset.fileUrl} - ${err.message}`); } } console.log(` S3 文件清理完成: ${deletedCount} 成功, ${failedCount} 失败`); } console.log('\n✅ 清理完成!'); } else { console.log('\n✅ 没有发现残留文件,无需清理。'); } } catch (error: any) { console.error('检查失败:', error.message); throw error; } finally { await prisma.$disconnect(); } } main().catch(console.error);