Files
ContentGen_BE/src/modules/dashboard/dashboard.service.ts
Harun CAN acb103657b
Some checks failed
Backend Deploy 🚀 / build-and-deploy (push) Has been cancelled
main
2026-03-30 00:21:32 +03:00

191 lines
5.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Injectable, Logger } from '@nestjs/common';
import { PrismaService } from '../../database/prisma.service';
import { VideoGenerationProducer } from '../video-queue/video-generation.producer';
import { EventsGateway } from '../events/events.gateway';
@Injectable()
export class DashboardService {
private readonly logger = new Logger(DashboardService.name);
constructor(
private readonly db: PrismaService,
private readonly videoGenerationProducer: VideoGenerationProducer,
private readonly eventsGateway: EventsGateway,
) {}
/**
* Dashboard istatistiklerini hesaplar.
* UserID bazlı filtreleme — her kullanıcı kendi verilerini görür.
* Gerçek plan bilgisini Subscription → Plan tablosundan çeker.
*/
async getStats(userId: string) {
const [
totalProjects,
completedVideos,
activeRenderJobs,
failedProjects,
draftProjects,
totalCreditsUsed,
recentProjects,
activeSubscription,
creditBalance,
] = await Promise.all([
// Toplam proje sayısı
this.db.project.count({
where: { userId, deletedAt: null },
}),
// Tamamlanan video sayısı
this.db.project.count({
where: { userId, status: 'COMPLETED', deletedAt: null },
}),
// Aktif render job sayısı
this.db.project.count({
where: {
userId,
deletedAt: null,
status: { in: ['PENDING', 'GENERATING_MEDIA', 'RENDERING', 'GENERATING_SCRIPT'] },
},
}),
// Başarısız projeler
this.db.project.count({
where: { userId, status: 'FAILED', deletedAt: null },
}),
// Draft projeler
this.db.project.count({
where: { userId, status: 'DRAFT', deletedAt: null },
}),
// Toplam harcanan kredi
this.db.project.aggregate({
where: { userId, deletedAt: null },
_sum: { creditsUsed: true },
}),
// Son 5 proje
this.db.project.findMany({
where: { userId, deletedAt: null },
orderBy: { createdAt: 'desc' },
take: 5,
select: {
id: true,
title: true,
status: true,
progress: true,
thumbnailUrl: true,
finalVideoUrl: true,
videoStyle: true,
aspectRatio: true,
language: true,
sourceType: true,
createdAt: true,
updatedAt: true,
completedAt: true,
},
}),
// Kullanıcının aktif aboneliği (plan dahil)
this.db.subscription.findFirst({
where: {
userId,
status: { in: ['active', 'trialing'] },
},
include: { plan: true },
orderBy: { createdAt: 'desc' },
}),
// Bu ayki net kredi bakiyesi (CreditTransaction tablosundan)
this.db.creditTransaction.aggregate({
where: {
userId,
createdAt: {
gte: new Date(new Date().getFullYear(), new Date().getMonth(), 1),
},
},
_sum: { amount: true },
}),
]);
// Plan bilgisini aktif abonelikten çek, yoksa free plan varsayılanları
const plan = activeSubscription?.plan;
const currentPlan = plan?.name || 'free';
const monthlyLimit = plan?.monthlyCredits ?? 3;
const maxDuration = plan?.maxDuration ?? 30;
const maxResolution = plan?.maxResolution ?? '720p';
// Kredi hesaplama: CreditTransaction tablosundan kalan bakiye
const creditsUsed = totalCreditsUsed._sum.creditsUsed || 0;
const netCreditBalance = creditBalance._sum.amount || 0;
const creditsRemaining = Math.max(0, netCreditBalance);
return {
totalProjects,
completedVideos,
activeRenderJobs,
failedProjects,
draftProjects,
totalCreditsUsed: creditsUsed,
creditsRemaining,
monthlyLimit,
currentPlan,
maxDuration,
maxResolution,
recentProjects,
};
}
/**
* Kuyruk durumunu BullMQ ve Worker kuyruğundan alır.
*/
async getQueueStatus() {
const queueStats = await this.videoGenerationProducer.getQueueStats();
const wsClients = this.eventsGateway.getConnectedClientsCount();
return {
queue: queueStats,
websocket: {
connectedClients: wsClients,
},
};
}
/**
* Bu ay üretilen videoların gün bazlı dağılımı (chart verisi).
*/
async getMonthlyChart(userId: string) {
const startOfMonth = new Date();
startOfMonth.setDate(1);
startOfMonth.setHours(0, 0, 0, 0);
const projects = await this.db.project.findMany({
where: {
userId,
deletedAt: null,
createdAt: { gte: startOfMonth },
},
select: {
createdAt: true,
status: true,
},
orderBy: { createdAt: 'asc' },
});
// Gün bazlı gruplama
const dailyMap: Record<string, { created: number; completed: number }> = {};
projects.forEach((p) => {
const day = p.createdAt.toISOString().split('T')[0];
if (!dailyMap[day]) dailyMap[day] = { created: 0, completed: 0 };
dailyMap[day].created++;
if (p.status === 'COMPLETED') dailyMap[day].completed++;
});
return Object.entries(dailyMap).map(([date, counts]) => ({
date,
...counts,
}));
}
}