- 迁移57个老素材到新逻辑(本地zip + S3预览图) - 优化文件命名:SPU_作者_店铺_类型.zip - PSD文件单独存储,不打包进all.zip - 修复下载接口,优先使用本地zip文件 - 修复预览图重复问题 - 添加迁移脚本和验证脚本 - 更新文档和迁移计划 迁移统计: - 总素材数:77个 - 成功迁移:57个 - 新逻辑上传:18个 - 空记录:2个
149 lines
4.9 KiB
TypeScript
149 lines
4.9 KiB
TypeScript
/**
|
|
* 验证已迁移素材的文件是否真实存在
|
|
*/
|
|
|
|
import { PrismaClient } from '@prisma/client';
|
|
import { exec } from 'child_process';
|
|
import { promisify } from 'util';
|
|
|
|
const execAsync = promisify(exec);
|
|
const prisma = new PrismaClient();
|
|
|
|
async function checkFileExists(filePath: string): Promise<boolean> {
|
|
try {
|
|
const remotePath = `/mnt/PoolOne/apps/youyidian-3d/${filePath}`;
|
|
const { stdout } = await execAsync(`ssh root@192.168.58.20 "test -f ${remotePath} && echo 'exists' || echo 'not_found'"`);
|
|
return stdout.trim() === 'exists';
|
|
} catch (error) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
async function main() {
|
|
console.log('开始验证所有"任务-详情"素材的文件...\n');
|
|
|
|
// 查询所有素材(不管 zipFilePath 是否为空)
|
|
const materials = await prisma.material.findMany({
|
|
where: {
|
|
materialType: '任务-详情',
|
|
deletedAt: null,
|
|
},
|
|
select: {
|
|
id: true,
|
|
productModel: true,
|
|
zipFilePath: true,
|
|
zipFileName: true,
|
|
taskNo: true,
|
|
user: {
|
|
select: {
|
|
realName: true,
|
|
username: true,
|
|
},
|
|
},
|
|
assets: {
|
|
where: {
|
|
assetType: { in: ['main', 'sku', 'detail', 'all'] },
|
|
},
|
|
select: {
|
|
assetType: true,
|
|
fileUrl: true,
|
|
fileName: true,
|
|
},
|
|
},
|
|
},
|
|
orderBy: { createdAt: 'desc' },
|
|
});
|
|
|
|
console.log(`找到 ${materials.length} 个"任务-详情"素材\n`);
|
|
|
|
const results = {
|
|
total: materials.length,
|
|
allFilesExist: 0,
|
|
someFilesMissing: 0,
|
|
allFilesMissing: 0,
|
|
noZipRecords: 0,
|
|
details: [] as any[],
|
|
};
|
|
|
|
for (const material of materials) {
|
|
const authorName = material.user?.realName || material.user?.username || '未知';
|
|
const zipAssets = material.assets.filter(a => a.fileUrl.startsWith('uploads/'));
|
|
|
|
if (zipAssets.length === 0) {
|
|
console.log(`❌ ${material.productModel.padEnd(20)} (${authorName.padEnd(10)}): 没有本地 zip 文件记录 | 任务号: ${material.taskNo || '无'}`);
|
|
results.noZipRecords++;
|
|
results.details.push({
|
|
productModel: material.productModel,
|
|
authorName,
|
|
taskNo: material.taskNo,
|
|
status: 'no_zip_records',
|
|
zipFilePath: material.zipFilePath,
|
|
});
|
|
continue;
|
|
}
|
|
|
|
const fileChecks = await Promise.all(
|
|
zipAssets.map(async (asset) => {
|
|
const exists = await checkFileExists(asset.fileUrl);
|
|
return { assetType: asset.assetType, fileUrl: asset.fileUrl, exists };
|
|
})
|
|
);
|
|
|
|
const existsCount = fileChecks.filter(c => c.exists).length;
|
|
const missingFiles = fileChecks.filter(c => !c.exists);
|
|
|
|
if (existsCount === zipAssets.length) {
|
|
console.log(`✅ ${material.productModel.padEnd(20)} (${authorName.padEnd(10)}): 所有文件存在 (${existsCount}/${zipAssets.length})`);
|
|
results.allFilesExist++;
|
|
} else if (existsCount > 0) {
|
|
console.log(`⚠️ ${material.productModel.padEnd(20)} (${authorName.padEnd(10)}): 部分文件缺失 (${existsCount}/${zipAssets.length})`);
|
|
missingFiles.forEach(f => console.log(` - 缺失: ${f.assetType} (${f.fileUrl})`));
|
|
results.someFilesMissing++;
|
|
results.details.push({
|
|
productModel: material.productModel,
|
|
authorName,
|
|
taskNo: material.taskNo,
|
|
status: 'partial',
|
|
missingFiles: missingFiles.map(f => f.assetType),
|
|
});
|
|
} else {
|
|
console.log(`❌ ${material.productModel.padEnd(20)} (${authorName.padEnd(10)}): 所有文件缺失 (0/${zipAssets.length}) | 任务号: ${material.taskNo || '无'}`);
|
|
missingFiles.forEach(f => console.log(` - 缺失: ${f.assetType} (${f.fileUrl})`));
|
|
results.allFilesMissing++;
|
|
results.details.push({
|
|
productModel: material.productModel,
|
|
authorName,
|
|
taskNo: material.taskNo,
|
|
status: 'all_missing',
|
|
zipFilePath: material.zipFilePath,
|
|
});
|
|
}
|
|
}
|
|
|
|
console.log('\n' + '='.repeat(80));
|
|
console.log('验证结果统计');
|
|
console.log('='.repeat(80));
|
|
console.log(`总计: ${results.total} 个素材`);
|
|
console.log(`✅ 所有文件存在: ${results.allFilesExist} 个`);
|
|
console.log(`⚠️ 部分文件缺失: ${results.someFilesMissing} 个`);
|
|
console.log(`❌ 所有文件缺失: ${results.allFilesMissing} 个`);
|
|
console.log(`❌ 没有 zip 记录: ${results.noZipRecords} 个`);
|
|
console.log(`\n需要迁移的素材总数: ${results.allFilesMissing + results.noZipRecords} 个`);
|
|
|
|
if (results.details.length > 0) {
|
|
console.log('\n' + '='.repeat(80));
|
|
console.log('需要迁移的素材详情:');
|
|
console.log('='.repeat(80));
|
|
results.details.forEach(d => {
|
|
console.log(` - ${d.productModel.padEnd(20)} (${d.authorName.padEnd(10)}): ${d.status.padEnd(20)} | 任务号: ${d.taskNo || '无'}`);
|
|
if (d.missingFiles) {
|
|
console.log(` 缺失类型: ${d.missingFiles.join(', ')}`);
|
|
}
|
|
});
|
|
}
|
|
|
|
await prisma.$disconnect();
|
|
}
|
|
|
|
main();
|