102 lines
3.2 KiB
TypeScript
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,
|
|
},
|
|
};
|
|
},
|
|
});
|
|
}
|