116 lines
4.3 KiB
TypeScript
116 lines
4.3 KiB
TypeScript
import { NextResponse } from 'next/server';
|
|
import { auth } from '@/auth';
|
|
import { PrismaClient } from '@/app/generated/prisma';
|
|
|
|
const prisma = new PrismaClient();
|
|
|
|
function parseDateOr(value: string | null, fallback: Date): Date {
|
|
if (!value) return fallback;
|
|
const d = new Date(value);
|
|
return isNaN(d.getTime()) ? fallback : d;
|
|
}
|
|
|
|
export async function GET(request: Request) {
|
|
try {
|
|
const session = await auth();
|
|
if (!session) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|
const email = session.user?.email as string | undefined;
|
|
if (!email) return NextResponse.json({ error: '세션 이메일을 찾을 수 없습니다' }, { status: 400 });
|
|
|
|
const { searchParams } = new URL(request.url);
|
|
const endDefault = new Date();
|
|
const startDefault = new Date(endDefault.getTime() - 30 * 24 * 60 * 60 * 1000);
|
|
const startParam = searchParams.get('start');
|
|
const endParam = searchParams.get('end');
|
|
const startDate = startParam ? new Date(startParam) : startDefault;
|
|
const endDate = endParam ? new Date(endParam) : endDefault;
|
|
// 날짜 경계 보정: 하루 전체 포함 (UTC 기준)
|
|
const startInclusive = new Date(startDate);
|
|
startInclusive.setUTCHours(0, 0, 0, 0);
|
|
const endInclusive = new Date(endDate);
|
|
endInclusive.setUTCHours(23, 59, 59, 999);
|
|
|
|
// 안전가드
|
|
if (endInclusive < startInclusive) endInclusive.setTime(startInclusive.getTime());
|
|
console.log('startDate', startDate.toISOString());
|
|
console.log('endDate', endDate.toISOString());
|
|
|
|
// 1) 내 핸들
|
|
const handles = await prisma.userHandle.findMany({
|
|
where: { email },
|
|
select: { id: true, handle: true, icon: true }
|
|
});
|
|
if (handles.length === 0) return NextResponse.json({ items: [] });
|
|
const handleStrs = handles.map(h => h.handle);
|
|
const handleByStr = new Map(handles.map(h => [h.handle, h] as const));
|
|
|
|
// 2) 매핑된 콘텐츠
|
|
const links = await prisma.contentHandle.findMany({
|
|
where: { handle: { in: handleStrs } },
|
|
select: { contentId: true, handle: true }
|
|
});
|
|
if (links.length === 0) return NextResponse.json({ items: [] });
|
|
const contentIds = links.map(l => l.contentId);
|
|
const normalizeId = (s: string) => (s ?? '').trim();
|
|
const contentIdsNormalized = Array.from(new Set(contentIds.map(normalizeId)));
|
|
const idsForQuery = Array.from(new Set([...contentIds, ...contentIdsNormalized]));
|
|
const handleByContentId = new Map(links.map(l => [l.contentId, l.handle] as const));
|
|
|
|
// 3) 콘텐츠 본문
|
|
const contents = await prisma.content.findMany({
|
|
where: { id: { in: contentIds } },
|
|
select: {
|
|
id: true, subject: true, pubDate: true,
|
|
views: true, premiumViews: true, watchTime: true
|
|
}
|
|
});
|
|
|
|
// 4) 기간 합계 유효조회수 (findMany로 가져와 JS에서 합산 - groupBy 일부 환경 이슈 회피)
|
|
const viewRows = await prisma.viewPerDay.findMany({
|
|
where: {
|
|
contented: { in: idsForQuery },
|
|
date: { gte: startInclusive, lte: endInclusive }
|
|
},
|
|
select: { contented: true, validViewDay: true }
|
|
});
|
|
const validSumById = new Map<string, number>();
|
|
for (const r of viewRows) {
|
|
const key = normalizeId(r.contented);
|
|
validSumById.set(key, (validSumById.get(key) ?? 0) + (r.validViewDay ?? 0));
|
|
}
|
|
|
|
// 5) 비용 단가
|
|
const cpvRow = await prisma.costPerView.findUnique({ where: { id: 1 } });
|
|
const cpv = cpvRow?.costPerView ?? 0;
|
|
|
|
const items = contents.map(c => {
|
|
const handle = handleByContentId.get(c.id) ?? '';
|
|
const handleObj = handleByStr.get(handle);
|
|
const validViews = validSumById.get(normalizeId(c.id)) ?? 0;
|
|
const expectedRevenue = validViews * cpv;
|
|
return {
|
|
id: c.id,
|
|
subject: c.subject,
|
|
pubDate: c.pubDate,
|
|
views: c.views,
|
|
premiumViews: c.premiumViews,
|
|
watchTime: c.watchTime,
|
|
handle,
|
|
handleId: handleObj?.id ?? null,
|
|
icon: handleObj?.icon ?? '',
|
|
validViews,
|
|
expectedRevenue,
|
|
};
|
|
});
|
|
|
|
return NextResponse.json({ items, cpv, start: startInclusive.toISOString(), end: endInclusive.toISOString() });
|
|
} catch (e) {
|
|
console.error('my_contents 오류:', e);
|
|
return NextResponse.json({ error: '조회 실패' }, { status: 500 });
|
|
} finally {
|
|
await prisma.$disconnect();
|
|
}
|
|
}
|
|
|
|
|