import { NextRequest } from "next/server"; import { db } from "@/lib/db"; import { generatedImage, user, aiModel } from "@/lib/db/schema"; import { eq, desc, sql } from "drizzle-orm"; import { cachedPublicJson } from "@/lib/public-api/performance"; const FEATURED_TIMEOUT_MS = 1800; const FEATURED_DEFAULT_PAGE_SIZE = 24; const FEATURED_MAX_PAGE_SIZE = 48; function clampPositiveInt(value: string | null, fallback: number, max: number) { const parsed = Number.parseInt(value || "", 10); if (!Number.isFinite(parsed) || parsed < 1) { return fallback; } return Math.min(parsed, max); } // GET: 获取精选图片列表 export async function GET(request: NextRequest) { const searchParams = request.nextUrl.searchParams; const page = clampPositiveInt(searchParams.get("page"), 1, 500); const pageSize = clampPositiveInt( searchParams.get("pageSize"), FEATURED_DEFAULT_PAGE_SIZE, FEATURED_MAX_PAGE_SIZE ); const offset = (page - 1) * pageSize; const fallbackData = { images: [], pagination: { page, pageSize, total: 0, totalPages: 0, }, }; return cachedPublicJson({ cacheKey: `featured:${page}:${pageSize}`, fallbackData, timeoutMs: FEATURED_TIMEOUT_MS, load: async () => { // 查询精选图片,关联用户和模型信息 const featuredImages = await db .select({ id: generatedImage.id, thumbnailUrl: generatedImage.thumbnailUrl, originalUrl: generatedImage.originalUrl, imageSize: generatedImage.imageSize, prompt: generatedImage.prompt, modelId: generatedImage.modelId, featuredCategory: generatedImage.featuredCategory, createdAt: generatedImage.createdAt, featuredAt: generatedImage.featuredAt, userId: generatedImage.userId, userName: user.name, userEmail: user.email, userDepartment: user.department, modelName: aiModel.displayName, totalCount: sql`count(*) over()::int`, }) .from(generatedImage) .leftJoin(user, eq(generatedImage.userId, user.id)) .leftJoin(aiModel, eq(generatedImage.modelId, aiModel.modelId)) .where(eq(generatedImage.isFeatured, true)) .orderBy(desc(generatedImage.featuredAt)) .limit(pageSize) .offset(offset); const total = featuredImages[0]?.totalCount || 0; const totalPages = Math.ceil(total / pageSize); return { images: featuredImages.map((img) => ({ id: img.id, thumbnailUrl: img.thumbnailUrl, originalUrl: img.originalUrl, imageSize: img.imageSize, prompt: img.prompt, modelId: img.modelId, modelName: img.modelName || img.modelId || "未知模型", featuredCategory: img.featuredCategory, createdAt: img.createdAt, featuredAt: img.featuredAt, user: { id: img.userId, name: img.userName || img.userEmail?.split("@")[0] || "未知用户", department: img.userDepartment || "未设置部门", }, })), pagination: { page, pageSize, total, totalPages, }, }; }, }); }