gongxiangp2/scripts/rollback-migrated-material.ts
chenpei 4dba93c4e5 feat: 完成任务-详情素材迁移到新逻辑
- 迁移57个老素材到新逻辑(本地zip + S3预览图)
- 优化文件命名:SPU_作者_店铺_类型.zip
- PSD文件单独存储,不打包进all.zip
- 修复下载接口,优先使用本地zip文件
- 修复预览图重复问题
- 添加迁移脚本和验证脚本
- 更新文档和迁移计划

迁移统计:
- 总素材数:77个
- 成功迁移:57个
- 新逻辑上传:18个
- 空记录:2个
2026-01-21 10:16:28 +08:00

117 lines
3.1 KiB
TypeScript
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.

/**
* 回滚已迁移的素材
*
* 用于回滚迁移失败或路径错误的素材
*/
import { PrismaClient } from '@prisma/client';
import { unlink } from 'fs/promises';
import path from 'path';
const prisma = new PrismaClient();
// 要回滚的素材型号
const PRODUCT_MODEL = 'XG0176';
async function main() {
console.log('='.repeat(60));
console.log('回滚已迁移的素材');
console.log('='.repeat(60));
console.log(`素材型号: ${PRODUCT_MODEL}`);
console.log('');
try {
// 1. 查询素材
const material = await prisma.material.findFirst({
where: {
productModel: PRODUCT_MODEL,
materialType: '任务-详情',
deletedAt: null,
},
include: {
assets: true,
},
});
if (!material) {
console.log('未找到素材');
return;
}
console.log(`找到素材: ${material.id}`);
console.log(`当前 zipFilePath: ${material.zipFilePath}`);
console.log(`当前 zipFileSize: ${material.zipFileSize}`);
console.log('');
// 2. 删除新增的资产记录(只删除新增的,保留老的 main/sku/detail)
const assetsToDelete = material.assets.filter(
(a) =>
a.assetType === 'main-preview' ||
a.assetType === 'sku-preview' ||
a.assetType === 'detail-preview' ||
(a.assetType === 'main' && a.fileUrl.startsWith('uploads/')) ||
(a.assetType === 'sku' && a.fileUrl.startsWith('uploads/')) ||
(a.assetType === 'detail' && a.fileUrl.startsWith('uploads/')) ||
a.assetType === 'all'
);
console.log(`将删除 ${assetsToDelete.length} 条资产记录:`);
assetsToDelete.forEach((a) => {
console.log(` - ${a.assetType}: ${a.fileName}`);
});
console.log('');
// 3. 删除生成的zip文件(只删除本地zip文件)
const zipFiles = material.assets.filter(
(a) =>
((a.assetType === 'main' || a.assetType === 'sku' || a.assetType === 'detail') &&
a.fileUrl.startsWith('uploads/')) ||
a.assetType === 'all'
);
console.log(`将删除 ${zipFiles.length} 个zip文件:`);
for (const asset of zipFiles) {
const filePath = path.join(process.cwd(), asset.fileUrl);
console.log(` - ${filePath}`);
try {
await unlink(filePath);
console.log(` ✅ 删除成功`);
} catch (err) {
console.log(` ⚠️ 删除失败: ${err}`);
}
}
console.log('');
// 4. 执行数据库回滚
await prisma.$transaction(async (tx) => {
// 删除新增的资产记录(逐个删除,避免误删老记录)
for (const asset of assetsToDelete) {
await tx.materialAsset.delete({
where: { id: asset.id },
});
}
// 恢复素材记录
await tx.material.update({
where: { id: material.id },
data: {
zipFilePath: '',
zipFileName: '',
zipFileSize: BigInt(0),
},
});
});
console.log('✅ 回滚完成');
} catch (error) {
console.error('回滚失败:', error);
process.exit(1);
} finally {
await prisma.$disconnect();
}
}
main();