- 修复内置模板验证问题(builtin-video-material等) - 添加模板验证工具 lib/template-validator.ts - 创建模板系统优化需求文档(30个详细需求) - 优化上传API的模板验证逻辑 - 改进错误提示信息的清晰度 - 添加字段池和分类管理相关需求 - 完善自定义字段的创建、验证、存储逻辑 - 清理过期的 .specstory 和 .trae 文件
107 lines
3.1 KiB
TypeScript
107 lines
3.1 KiB
TypeScript
/**
|
||
* 用户素材列表API
|
||
*
|
||
* GET: 获取当前登录用户上传的所有素材
|
||
*
|
||
* @created 2025-11-25
|
||
* @api
|
||
*/
|
||
|
||
import { NextRequest, NextResponse } from 'next/server'
|
||
import { getServerSession } from 'next-auth'
|
||
import { authOptions } from '@/lib/auth'
|
||
import { prisma } from '@/lib/prisma'
|
||
import { rewriteMaterialStorageUrlsForClient } from '@/lib/url-rewrite'
|
||
|
||
function parseDisplayImages(value: string | null): string[] {
|
||
if (!value) return []
|
||
try {
|
||
const parsed = JSON.parse(value)
|
||
if (Array.isArray(parsed)) {
|
||
return parsed.filter((item): item is string => typeof item === 'string' && item.trim().length > 0)
|
||
}
|
||
} catch (error) {
|
||
console.warn('[UserMaterials] Failed to parse displayImages JSON:', error)
|
||
}
|
||
return value ? [value] : []
|
||
}
|
||
|
||
export async function GET(req: NextRequest) {
|
||
try {
|
||
const session = await getServerSession(authOptions)
|
||
if (!session?.user?.id) {
|
||
return NextResponse.json(
|
||
{ error: '未登录' },
|
||
{ status: 401 }
|
||
)
|
||
}
|
||
|
||
const currentUserId = session.user.id
|
||
|
||
const materials = await (prisma as any).material.findMany({
|
||
where: {
|
||
// 个人中心「我的素材」:
|
||
// 1)我上传的:createdByUserId = 当前用户(新字段);
|
||
// 2)当前由我负责的:ownerType = 'USER' 且 ownerId = 当前用户;
|
||
// 3)兜底兼容:userId = 当前用户(历史/遗留字段,且新上传目前也会写入)。
|
||
OR: [
|
||
{ createdByUserId: currentUserId },
|
||
{ ownerType: 'USER', ownerId: currentUserId },
|
||
{ userId: currentUserId },
|
||
],
|
||
deletedAt: null,
|
||
} as any,
|
||
select: {
|
||
id: true,
|
||
userId: true,
|
||
productModel: true,
|
||
previewUrl: true,
|
||
displayImages: true,
|
||
materialType: true,
|
||
materialKind: true,
|
||
categoryName: true,
|
||
sceneName: true,
|
||
renderer: true,
|
||
downloadCount: true,
|
||
likeCount: true,
|
||
createdAt: true,
|
||
isPremium: true,
|
||
videoCoverUrl: true,
|
||
videoPreview540Url: true,
|
||
videoPreviewClipUrl: true,
|
||
videoProcessingStatus: true,
|
||
videoDuration: true,
|
||
zipFileName: true,
|
||
zipFileSize: true,
|
||
},
|
||
orderBy: { createdAt: 'desc' },
|
||
})
|
||
|
||
const formatted = materials.map((material: any) => {
|
||
// 统一处理 BigInt 等前端无法直接消费的类型
|
||
const safeZipFileSize =
|
||
typeof material.zipFileSize === 'bigint'
|
||
? material.zipFileSize.toString()
|
||
: material.zipFileSize ?? null
|
||
|
||
const base = {
|
||
...material,
|
||
zipFileSize: safeZipFileSize,
|
||
displayImages: parseDisplayImages(material.displayImages as unknown as string | null),
|
||
}
|
||
|
||
// 统一修正存储相关 URL,避免 localhost/127.0.0.1 导致客户端 404
|
||
return rewriteMaterialStorageUrlsForClient(base)
|
||
})
|
||
|
||
return NextResponse.json(formatted)
|
||
} catch (error) {
|
||
console.error('[UserMaterials] GET error:', error)
|
||
return NextResponse.json(
|
||
{ error: '获取素材列表失败' },
|
||
{ status: 500 }
|
||
)
|
||
}
|
||
}
|
||
|