192 lines
5.5 KiB
TypeScript
192 lines
5.5 KiB
TypeScript
/**
|
||
* 修复已迁移素材的错误URL格式
|
||
* 移除URL中的jsxiangche路径段
|
||
*/
|
||
|
||
import { PrismaClient } from '@prisma/client';
|
||
|
||
const DATABASE_URL = process.env.DATABASE_URL || 'postgresql://gongxiangpan:gongxiangpan123@192.168.58.20:5432/lobechat';
|
||
const prisma = new PrismaClient({
|
||
datasources: {
|
||
db: {
|
||
url: DATABASE_URL,
|
||
},
|
||
},
|
||
});
|
||
|
||
// 视频/音频素材类型
|
||
const VIDEO_TYPES = ['主图视频', '安装视频', '种草视频'];
|
||
const AUDIO_TYPES = ['音频素材', '音乐'];
|
||
|
||
/**
|
||
* 修复单个URL,移除jsxiangche路径段
|
||
*/
|
||
function fixUrl(url: string): string {
|
||
// 如果URL包含jsxiangche,移除它
|
||
// 例如: https://s3.yydd.ai/gongxiangpan/jsxiangche/由壹点AI/...
|
||
// 应该变成: https://s3.yydd.ai/gongxiangpan/由壹点AI/...
|
||
return url.replace(/\/jsxiangche\//g, '/').replace(/jsxiangche\//g, '');
|
||
}
|
||
|
||
async function main() {
|
||
console.log('='.repeat(60));
|
||
console.log('修复已迁移素材的错误URL格式');
|
||
console.log('='.repeat(60));
|
||
console.log();
|
||
|
||
const args = process.argv.slice(2);
|
||
const isDryRun = args.includes('--dry-run');
|
||
|
||
if (isDryRun) {
|
||
console.log('⚠️ 试运行模式:不会实际修改数据库,只会显示将要修复的URL');
|
||
console.log();
|
||
}
|
||
|
||
try {
|
||
// 查询所有视频/音频素材
|
||
const allMaterials = await prisma.material.findMany({
|
||
where: {
|
||
materialType: {
|
||
in: [...VIDEO_TYPES, ...AUDIO_TYPES],
|
||
},
|
||
deletedAt: null,
|
||
},
|
||
select: {
|
||
id: true,
|
||
productModel: true,
|
||
materialType: true,
|
||
previewUrl: true,
|
||
videoCoverUrl: true,
|
||
videoPreview540Url: true,
|
||
videoPreviewClipUrl: true,
|
||
} as any,
|
||
});
|
||
|
||
console.log(`查询到 ${allMaterials.length} 个视频/音频素材`);
|
||
console.log();
|
||
|
||
// 查找需要修复的素材
|
||
const materialsToFix: Array<{
|
||
id: string;
|
||
productModel: string;
|
||
materialType: string;
|
||
updates: Record<string, string>;
|
||
}> = [];
|
||
|
||
for (const material of allMaterials) {
|
||
const m = material as any;
|
||
const updates: Record<string, string> = {};
|
||
|
||
// 检查各个URL字段
|
||
const urlFields = [
|
||
{ name: 'previewUrl', value: m.previewUrl },
|
||
{ name: 'videoCoverUrl', value: m.videoCoverUrl },
|
||
{ name: 'videoPreview540Url', value: m.videoPreview540Url },
|
||
{ name: 'videoPreviewClipUrl', value: m.videoPreviewClipUrl },
|
||
];
|
||
|
||
for (const field of urlFields) {
|
||
if (field.value) {
|
||
// 如果URL指向新存储桶(s3.yydd.ai),但包含jsxiangche,需要修复
|
||
if (field.value.includes('s3.yydd.ai') && field.value.includes('jsxiangche')) {
|
||
const fixedUrl = fixUrl(field.value);
|
||
updates[field.name] = fixedUrl;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (Object.keys(updates).length > 0) {
|
||
materialsToFix.push({
|
||
id: m.id,
|
||
productModel: m.productModel,
|
||
materialType: m.materialType,
|
||
updates,
|
||
});
|
||
}
|
||
}
|
||
|
||
console.log(`找到 ${materialsToFix.length} 个需要修复的素材`);
|
||
console.log();
|
||
|
||
if (materialsToFix.length === 0) {
|
||
console.log('✅ 没有需要修复的素材!');
|
||
return;
|
||
}
|
||
|
||
// 显示将要修复的内容
|
||
console.log('将要修复的素材:');
|
||
console.log();
|
||
|
||
let totalFieldsFixed = 0;
|
||
for (let i = 0; i < materialsToFix.length; i++) {
|
||
const m = materialsToFix[i];
|
||
console.log(`${i + 1}. 素材ID: ${m.id}`);
|
||
console.log(` 产品型号: ${m.productModel}`);
|
||
console.log(` 素材类型: ${m.materialType}`);
|
||
console.log(` 需要修复的字段:`);
|
||
Object.entries(m.updates).forEach(([fieldName, newUrl]) => {
|
||
console.log(` - ${fieldName}:`);
|
||
// 找到原始URL
|
||
const originalUrl = (allMaterials.find(mat => (mat as any).id === m.id) as any)?.[fieldName];
|
||
if (originalUrl) {
|
||
console.log(` 旧: ${originalUrl}`);
|
||
}
|
||
console.log(` 新: ${newUrl}`);
|
||
totalFieldsFixed++;
|
||
});
|
||
console.log();
|
||
}
|
||
|
||
console.log('='.repeat(60));
|
||
console.log('总结');
|
||
console.log('='.repeat(60));
|
||
console.log(`需要修复的素材数: ${materialsToFix.length} 个`);
|
||
console.log(`需要修复的URL字段总数: ${totalFieldsFixed} 个`);
|
||
console.log();
|
||
|
||
if (isDryRun) {
|
||
console.log('⚠️ 试运行模式:未实际修改数据库');
|
||
return;
|
||
}
|
||
|
||
// 执行修复
|
||
console.log('开始修复...');
|
||
console.log();
|
||
|
||
let successCount = 0;
|
||
let failCount = 0;
|
||
|
||
for (let i = 0; i < materialsToFix.length; i++) {
|
||
const m = materialsToFix[i];
|
||
try {
|
||
await prisma.material.update({
|
||
where: { id: m.id },
|
||
data: m.updates as any,
|
||
});
|
||
successCount++;
|
||
console.log(`[${i + 1}/${materialsToFix.length}] ✅ 已修复: ${m.productModel} (${Object.keys(m.updates).length} 个字段)`);
|
||
} catch (error: any) {
|
||
failCount++;
|
||
console.error(`[${i + 1}/${materialsToFix.length}] ❌ 修复失败: ${m.productModel}`);
|
||
console.error(` 错误: ${error.message}`);
|
||
}
|
||
}
|
||
|
||
console.log();
|
||
console.log('='.repeat(60));
|
||
console.log('修复完成');
|
||
console.log('='.repeat(60));
|
||
console.log(`成功: ${successCount} 个素材`);
|
||
console.log(`失败: ${failCount} 个素材`);
|
||
console.log();
|
||
|
||
} catch (error) {
|
||
console.error('执行失败:', error);
|
||
process.exit(1);
|
||
} finally {
|
||
await prisma.$disconnect();
|
||
}
|
||
}
|
||
|
||
main();
|