import { prisma } from "../database"; import type { EventFeedback, Prisma } from "@/prisma/generated/prisma/client"; import { ValidationError, NotFoundError } from "../errors"; import { eventService } from "./event.service"; import { sitePreferencesService } from "../preferences/site-preferences.service"; export interface CreateOrUpdateFeedbackInput { rating: number; comment?: string | null; } export interface FeedbackStatistics { eventId: string; eventName: string; eventDate: Date | null; eventType: string | null; averageRating: number; feedbackCount: number; } /** * Service de gestion des feedbacks sur les événements */ export class EventFeedbackService { /** * Crée ou met à jour un feedback */ async createOrUpdateFeedback( userId: string, eventId: string, data: CreateOrUpdateFeedbackInput ): Promise { return prisma.eventFeedback.upsert({ where: { userId_eventId: { userId, eventId, }, }, update: { rating: data.rating, comment: data.comment || null, }, create: { userId, eventId, rating: data.rating, comment: data.comment || null, }, }); } /** * Récupère le feedback d'un utilisateur pour un événement */ async getUserFeedback( userId: string, eventId: string ): Promise { return prisma.eventFeedback.findUnique({ where: { userId_eventId: { userId, eventId, }, }, include: { event: { select: { id: true, name: true, date: true, }, }, }, }); } /** * Récupère tous les feedbacks (pour admin) */ async getAllFeedbacks(options?: { include?: Prisma.EventFeedbackInclude; orderBy?: Prisma.EventFeedbackOrderByWithRelationInput; }): Promise { return prisma.eventFeedback.findMany({ include: options?.include || { event: { select: { id: true, name: true, date: true, type: true, }, }, user: { select: { id: true, username: true, email: true, }, }, }, orderBy: options?.orderBy || { createdAt: "desc" }, }); } /** * Récupère les statistiques de feedback par événement */ async getFeedbackStatistics(): Promise { // Calculer les statistiques par événement const eventStats = await prisma.eventFeedback.groupBy({ by: ["eventId"], _avg: { rating: true, }, _count: { id: true, }, }); // Récupérer les détails des événements pour les stats const eventIds = eventStats.map((stat) => stat.eventId); const events = await prisma.event.findMany({ where: { id: { in: eventIds, }, }, select: { id: true, name: true, date: true, type: true, }, }); // Combiner les stats avec les détails des événements return eventStats.map((stat) => { const event = events.find((e) => e.id === stat.eventId); return { eventId: stat.eventId, eventName: event?.name || "Événement supprimé", eventDate: event?.date || null, eventType: event?.type || null, averageRating: stat._avg.rating || 0, feedbackCount: stat._count.id, }; }); } /** * Valide et crée/met à jour un feedback avec toutes les règles métier */ async validateAndCreateFeedback( userId: string, eventId: string, data: { rating: number; comment?: string | null } ): Promise { // Valider la note (1-5) if (!data.rating || data.rating < 1 || data.rating > 5) { throw new ValidationError("La note doit être entre 1 et 5", "rating"); } // Vérifier que l'événement existe const event = await eventService.getEventById(eventId); if (!event) { throw new NotFoundError("Événement"); } // Vérifier si c'est un nouveau feedback ou une mise à jour const existingFeedback = await this.getUserFeedback(userId, eventId); const isNewFeedback = !existingFeedback; // Récupérer les points à attribuer depuis les préférences du site const sitePreferences = await sitePreferencesService.getOrCreateSitePreferences(); const pointsToAward = sitePreferences.eventFeedbackPoints || 100; // Créer ou mettre à jour le feedback et attribuer les points (seulement pour nouveau feedback) const [feedback] = await Promise.all([ this.createOrUpdateFeedback(userId, eventId, { rating: data.rating, comment: data.comment || null, }), // Attribuer les points seulement si c'est un nouveau feedback isNewFeedback ? prisma.user.update({ where: { id: userId }, data: { score: { increment: pointsToAward, }, }, }) : Promise.resolve(null), ]); return feedback; } } export const eventFeedbackService = new EventFeedbackService();