import { NextRequest, NextResponse } from 'next/server'; import { getServerSession } from 'next-auth'; import { authOptions } from '@/lib/auth'; import { prisma } from '@/lib/prisma'; import { normalizeMaterial } from '@/lib/utils'; import { rewriteMaterialStorageUrlsForClient } from '@/lib/url-rewrite'; // 解析 displayImages 字段 function parseDisplayImages(displayImages: any): string[] { if (!displayImages) return []; if (Array.isArray(displayImages)) { return displayImages.filter(img => typeof img === 'string' && img.trim().length > 0); } if (typeof displayImages === 'string') { try { const parsed = JSON.parse(displayImages); if (Array.isArray(parsed)) { return parsed.filter(img => typeof img === 'string' && img.trim().length > 0); } } catch (e) { console.warn('[user/materials] Failed to parse displayImages JSON, fallback to single value:', e); } return displayImages.trim() ? [displayImages] : []; } return []; } // GET - 获取当前用户上传的素材 export async function GET(request: NextRequest) { try { const session = await getServerSession(authOptions); if (!session?.user || !(session.user as any).id) { return NextResponse.json({ error: '未登录' }, { status: 401 }); } const userId = (session.user as any).id; const materials = await prisma.material.findMany({ where: { userId, deletedAt: null, }, select: { id: true, userId: true, productModel: true, previewUrl: true, displayImages: true, materialType: true, categoryName: true, sceneName: true, platformName: true, productCategoryName: true, renderer: true, downloadCount: true, likeCount: true, createdAt: true, videoSourceUrl: true, videoPreview540Url: true, videoPreviewClipUrl: true, videoCoverUrl: true, videoProcessingStatus: true, videoDuration: true, zipFileName: true, zipFileSize: true, } as any, orderBy: { createdAt: 'desc', }, }); const formattedMaterials = materials.map((material: any) => { const zipFileSize = typeof material.zipFileSize === 'bigint' ? material.zipFileSize.toString() : material.zipFileSize ?? null; const formatted = { ...material, zipFileSize, displayImages: parseDisplayImages(material.displayImages || null), }; // 使用智能 URL 重写(根据用户网络位置选择内网或外网地址) return rewriteMaterialStorageUrlsForClient(normalizeMaterial(formatted), request); }); return NextResponse.json(formattedMaterials); } catch (error: any) { // 兼容旧版本数据库(没有deletedAt字段) if (error?.meta?.column?.includes('deletedAt')) { try { const session = await getServerSession(authOptions); if (!session?.user || !(session.user as any).id) { return NextResponse.json({ error: '未登录' }, { status: 401 }); } const userId = (session.user as any).id; const materials = await prisma.material.findMany({ where: { userId, }, select: { id: true, userId: true, productModel: true, previewUrl: true, displayImages: true, materialType: true, categoryName: true, sceneName: true, platformName: true, productCategoryName: true, renderer: true, downloadCount: true, likeCount: true, createdAt: true, videoSourceUrl: true, videoPreview540Url: true, videoPreviewClipUrl: true, videoCoverUrl: true, videoProcessingStatus: true, videoDuration: true, zipFileName: true, zipFileSize: true, } as any, orderBy: { createdAt: 'desc', }, }); const formattedMaterials = materials.map((material: any) => { const zipFileSize = typeof material.zipFileSize === 'bigint' ? material.zipFileSize.toString() : material.zipFileSize ?? null; const formatted = { ...material, zipFileSize, displayImages: Array.isArray(material.displayImages) ? material.displayImages : material.displayImages ? [material.displayImages] : [], }; return normalizeMaterial(formatted); }); return NextResponse.json(formattedMaterials); } catch (fallbackError) { console.error('[UserMaterials] Fallback query error:', fallbackError); return NextResponse.json( { error: '获取素材列表失败' }, { status: 500 } ); } } console.error('[UserMaterials] GET error:', error); return NextResponse.json( { error: '获取素材列表失败' }, { status: 500 } ); } }