2026-05-26 20:10:36 +08:00

102 lines
3.2 KiB
TypeScript

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<number>`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,
},
};
},
});
}