Refactoring
This commit is contained in:
171
src/main/java/com/lions/dev/config/ScheduledJobs.java
Normal file
171
src/main/java/com/lions/dev/config/ScheduledJobs.java
Normal file
@@ -0,0 +1,171 @@
|
||||
package com.lions.dev.config;
|
||||
|
||||
import com.lions.dev.entity.events.Events;
|
||||
import com.lions.dev.entity.establishment.Establishment;
|
||||
import com.lions.dev.entity.establishment.EstablishmentSubscription;
|
||||
import com.lions.dev.entity.users.Users;
|
||||
import com.lions.dev.repository.EstablishmentRepository;
|
||||
import com.lions.dev.repository.EstablishmentSubscriptionRepository;
|
||||
import com.lions.dev.repository.EventsRepository;
|
||||
import com.lions.dev.repository.PasswordResetTokenRepository;
|
||||
import com.lions.dev.repository.StoryRepository;
|
||||
import com.lions.dev.service.EmailService;
|
||||
import com.lions.dev.service.NotificationService;
|
||||
import io.quarkus.scheduler.Scheduled;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.transaction.Transactional;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Jobs planifiés (Quarkus Scheduler) pour :
|
||||
* - Nettoyage des stories expirées (24h)
|
||||
* - Nettoyage des tokens de reset password expirés
|
||||
* - Expiration des abonnements établissements
|
||||
* - Désactivation des établissements non payés
|
||||
* - Rappels d'événements (J-1, H-1)
|
||||
*/
|
||||
@ApplicationScoped
|
||||
public class ScheduledJobs {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(ScheduledJobs.class);
|
||||
|
||||
@Inject
|
||||
StoryRepository storyRepository;
|
||||
|
||||
@Inject
|
||||
PasswordResetTokenRepository passwordResetTokenRepository;
|
||||
|
||||
@Inject
|
||||
EstablishmentSubscriptionRepository subscriptionRepository;
|
||||
|
||||
@Inject
|
||||
EstablishmentRepository establishmentRepository;
|
||||
|
||||
@Inject
|
||||
EventsRepository eventsRepository;
|
||||
|
||||
@Inject
|
||||
NotificationService notificationService;
|
||||
|
||||
@Inject
|
||||
EmailService emailService;
|
||||
|
||||
/** Nettoyage des stories expirées : toutes les heures. */
|
||||
@Scheduled(cron = "0 0 * * * ?")
|
||||
@Transactional
|
||||
public void deactivateExpiredStories() {
|
||||
int count = storyRepository.deactivateExpiredStories();
|
||||
if (count > 0) {
|
||||
LOG.info("[ScheduledJobs] Stories expirées désactivées : " + count);
|
||||
}
|
||||
}
|
||||
|
||||
/** Nettoyage des tokens de reset password expirés : tous les jours à 3h. */
|
||||
@Scheduled(cron = "0 0 3 * * ?")
|
||||
@Transactional
|
||||
public void deleteExpiredPasswordResetTokens() {
|
||||
long count = passwordResetTokenRepository.deleteExpiredTokens();
|
||||
if (count > 0) {
|
||||
LOG.info("[ScheduledJobs] Tokens de reset password supprimés : " + count);
|
||||
}
|
||||
}
|
||||
|
||||
/** Expiration des abonnements et désactivation des établissements non payés : toutes les heures. */
|
||||
@Scheduled(cron = "0 5 * * * ?")
|
||||
@Transactional
|
||||
public void expireSubscriptionsAndDisableEstablishments() {
|
||||
List<EstablishmentSubscription> expired = subscriptionRepository.findExpiredActiveSubscriptions();
|
||||
for (EstablishmentSubscription sub : expired) {
|
||||
sub.setStatus(EstablishmentSubscription.STATUS_EXPIRED);
|
||||
subscriptionRepository.persist(sub);
|
||||
Establishment est = establishmentRepository.findById(sub.getEstablishmentId());
|
||||
if (est != null && Boolean.TRUE.equals(est.getIsActive())) {
|
||||
est.setIsActive(false);
|
||||
establishmentRepository.persist(est);
|
||||
LOG.info("[ScheduledJobs] Établissement désactivé (abonnement expiré) : " + est.getId());
|
||||
}
|
||||
}
|
||||
if (!expired.isEmpty()) {
|
||||
LOG.info("[ScheduledJobs] Abonnements expirés traités : " + expired.size());
|
||||
}
|
||||
}
|
||||
|
||||
/** Rappels d'événements J-1 (dans ~24h) et H-1 (dans ~1h) : toutes les 15 minutes. */
|
||||
@Scheduled(cron = "0 */15 * * * ?")
|
||||
@Transactional
|
||||
public void sendEventReminders() {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
// Fenêtre J-1 : début entre 23h30 et 24h30
|
||||
LocalDateTime j1From = now.plus(23, ChronoUnit.HOURS).plus(30, ChronoUnit.MINUTES);
|
||||
LocalDateTime j1To = now.plus(24, ChronoUnit.HOURS).plus(30, ChronoUnit.MINUTES);
|
||||
List<Events> eventsJ1 = eventsRepository.findEventsStartingBetween(j1From, j1To);
|
||||
for (Events event : eventsJ1) {
|
||||
sendReminderToParticipants(event, "J-1", "demain");
|
||||
}
|
||||
// Fenêtre H-1 : début entre 50 min et 1h10
|
||||
LocalDateTime h1From = now.plus(50, ChronoUnit.MINUTES);
|
||||
LocalDateTime h1To = now.plus(70, ChronoUnit.MINUTES);
|
||||
List<Events> eventsH1 = eventsRepository.findEventsStartingBetween(h1From, h1To);
|
||||
for (Events event : eventsH1) {
|
||||
sendReminderToParticipants(event, "H-1", "dans 1 heure");
|
||||
}
|
||||
}
|
||||
|
||||
/** Avertissement expiration abonnement (J-3) : email au manager. */
|
||||
@Scheduled(cron = "0 0 9 * * ?")
|
||||
@Transactional
|
||||
public void sendSubscriptionExpirationWarningEmails() {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
LocalDateTime in3DaysStart = now.plusDays(3);
|
||||
LocalDateTime in3DaysEnd = now.plusDays(3).plusHours(23).plusMinutes(59);
|
||||
List<EstablishmentSubscription> expiring = subscriptionRepository.findActiveSubscriptionsExpiringBetween(in3DaysStart, in3DaysEnd);
|
||||
for (EstablishmentSubscription sub : expiring) {
|
||||
Establishment est = establishmentRepository.findById(sub.getEstablishmentId());
|
||||
if (est == null) continue;
|
||||
Users manager = est.getManager();
|
||||
if (manager == null || manager.getEmail() == null) continue;
|
||||
try {
|
||||
emailService.sendSubscriptionExpirationWarningEmail(
|
||||
manager.getEmail(),
|
||||
manager.getFirstName(),
|
||||
est.getName(),
|
||||
sub.getExpiresAt()
|
||||
);
|
||||
} catch (Exception e) {
|
||||
LOG.warn("[ScheduledJobs] Email expiration abonnement échoué pour " + est.getId() + ": " + e.getMessage());
|
||||
}
|
||||
}
|
||||
if (!expiring.isEmpty()) {
|
||||
LOG.info("[ScheduledJobs] Emails avertissement expiration envoyés : " + expiring.size());
|
||||
}
|
||||
}
|
||||
|
||||
private void sendReminderToParticipants(Events event, String reminderType, String whenText) {
|
||||
Set<Users> participants = event.getParticipants();
|
||||
if (participants == null) return;
|
||||
Users creator = event.getCreator();
|
||||
String title = "Rappel événement " + reminderType + " : " + event.getTitle();
|
||||
String message = "L'événement « " + event.getTitle() + " » commence " + whenText + ".";
|
||||
for (Users participant : participants) {
|
||||
if (participant == null || participant.getId() == null) continue;
|
||||
try {
|
||||
notificationService.createNotification(title, message, "reminder", participant.getId(), event.getId());
|
||||
} catch (Exception e) {
|
||||
LOG.warn("[ScheduledJobs] Impossible de créer rappel pour participant " + participant.getId() + ": " + e.getMessage());
|
||||
}
|
||||
}
|
||||
if (creator != null && creator.getId() != null && (participants.isEmpty() || !participants.stream().anyMatch(p -> p.getId().equals(creator.getId())))) {
|
||||
try {
|
||||
notificationService.createNotification(title, message, "reminder", creator.getId(), event.getId());
|
||||
} catch (Exception e) {
|
||||
LOG.warn("[ScheduledJobs] Impossible de créer rappel pour créateur: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,5 @@ public abstract class Exceptions extends Exception {
|
||||
*/
|
||||
public Exceptions(String message) {
|
||||
super(message);
|
||||
System.out.println("[ERROR] Exception déclenchée : " + message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ public class Failures {
|
||||
*/
|
||||
public Failures(String failureMessage) {
|
||||
this.failureMessage = failureMessage;
|
||||
System.out.println("[FAILURE] Échec détecté : " + failureMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,7 +16,6 @@ public class BadRequestException extends WebApplicationException {
|
||||
*/
|
||||
public BadRequestException(String message) {
|
||||
super(message, Response.Status.BAD_REQUEST);
|
||||
System.out.println("[ERROR] Requête invalide : " + message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,5 @@ public class NotFoundException extends WebApplicationException {
|
||||
*/
|
||||
public NotFoundException(String message) {
|
||||
super(message, Response.Status.NOT_FOUND);
|
||||
System.out.println("[ERROR] Ressource non trouvée : " + message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,5 @@ public class ServerException extends RuntimeException {
|
||||
*/
|
||||
public ServerException(String message) {
|
||||
super(message);
|
||||
System.out.println("[ERROR] Erreur serveur : " + message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,5 @@ public class UnauthorizedException extends WebApplicationException {
|
||||
*/
|
||||
public UnauthorizedException(String message) {
|
||||
super(message, Response.Status.UNAUTHORIZED);
|
||||
System.out.println("[ERROR] Accès non autorisé : " + message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.lions.dev.dto.request.establishment;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
@@ -21,5 +22,6 @@ public class InitiateSubscriptionRequestDTO {
|
||||
|
||||
/** Numéro de téléphone client au format international (ex. 221771234567). */
|
||||
@NotBlank(message = "Le numéro de téléphone client est obligatoire pour Wave")
|
||||
@Size(max = 25, message = "Le numéro de téléphone ne peut pas dépasser 25 caractères")
|
||||
private String clientPhone;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import lombok.Setter;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import java.time.LocalDateTime;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* DTO pour la création d'un événement.
|
||||
@@ -61,7 +62,6 @@ public class EventCreateRequestDTO {
|
||||
private String location;
|
||||
|
||||
public EventCreateRequestDTO() {
|
||||
System.out.println("[LOG] DTO de requête de création d'événement initialisé.");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.lions.dev.dto.request.events;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.util.UUID;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* DTO pour la suppression d'un événement.
|
||||
@@ -15,6 +16,5 @@ public class EventDeleteRequestDTO {
|
||||
private UUID eventId; // ID de l'événement à supprimer
|
||||
|
||||
public EventDeleteRequestDTO() {
|
||||
System.out.println("[LOG] DTO de requête de suppression d'événement initialisé.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.lions.dev.dto.request.events;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.util.UUID;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* DTO pour lire un événement par son ID.
|
||||
@@ -15,6 +16,5 @@ public class EventReadOneByIdRequestDTO {
|
||||
private UUID eventId; // ID de l'événement à lire
|
||||
|
||||
public EventReadOneByIdRequestDTO() {
|
||||
System.out.println("[LOG] DTO de requête de lecture d'événement initialisé.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import jakarta.validation.constraints.Size;
|
||||
import java.util.UUID;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* DTO pour la création d'un post social.
|
||||
@@ -29,7 +30,6 @@ public class SocialPostCreateRequestDTO {
|
||||
private String imageUrl; // URL de l'image (optionnel)
|
||||
|
||||
public SocialPostCreateRequestDTO() {
|
||||
System.out.println("[LOG] DTO de requête de création de post social initialisé.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import jakarta.validation.constraints.Size;
|
||||
import java.util.UUID;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* DTO pour la création d'une story.
|
||||
@@ -34,6 +35,5 @@ public class StoryCreateRequestDTO {
|
||||
private Integer durationSeconds; // Durée en secondes (optionnel, pour les vidéos)
|
||||
|
||||
public StoryCreateRequestDTO() {
|
||||
System.out.println("[LOG] DTO de requête de création de story initialisé.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.lions.dev.dto.request.users;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* DTO pour forcer l'activation ou la suspension d'un utilisateur (opération réservée au super admin).
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class SetUserActiveRequestDTO {
|
||||
|
||||
@NotNull(message = "Le champ active est obligatoire")
|
||||
private Boolean active;
|
||||
|
||||
public SetUserActiveRequestDTO(Boolean active) {
|
||||
this.active = active;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.lions.dev.dto.response.establishment;
|
||||
|
||||
import com.lions.dev.entity.establishment.BusinessHours;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* DTO pour renvoyer les horaires d'ouverture d'un établissement.
|
||||
* Conforme à l'architecture AfterWork v2.0.
|
||||
*/
|
||||
@Getter
|
||||
public class BusinessHoursResponseDTO {
|
||||
|
||||
private String id;
|
||||
private String establishmentId;
|
||||
private String dayOfWeek;
|
||||
private String openTime;
|
||||
private String closeTime;
|
||||
private Boolean isClosed;
|
||||
private Boolean isException;
|
||||
private LocalDateTime exceptionDate;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
/**
|
||||
* Constructeur qui transforme une entité BusinessHours en DTO.
|
||||
* Utilise establishmentId fourni pour éviter LazyInitializationException.
|
||||
*
|
||||
* @param businessHours L'entité à convertir.
|
||||
* @param establishmentId ID de l'établissement (déjà connu par l'appelant).
|
||||
*/
|
||||
public BusinessHoursResponseDTO(BusinessHours businessHours, UUID establishmentId) {
|
||||
this.id = businessHours.getId() != null ? businessHours.getId().toString() : null;
|
||||
this.establishmentId = establishmentId != null ? establishmentId.toString() : null;
|
||||
this.dayOfWeek = businessHours.getDayOfWeek();
|
||||
this.openTime = businessHours.getOpenTime();
|
||||
this.closeTime = businessHours.getCloseTime();
|
||||
this.isClosed = businessHours.getIsClosed();
|
||||
this.isException = businessHours.getIsException();
|
||||
this.exceptionDate = businessHours.getExceptionDate();
|
||||
this.createdAt = businessHours.getCreatedAt();
|
||||
this.updatedAt = businessHours.getUpdatedAt();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.lions.dev.dto.response.establishment;
|
||||
|
||||
import com.lions.dev.entity.establishment.EstablishmentAmenity;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* DTO pour renvoyer un équipement d'établissement (avec nom du type).
|
||||
* Conforme à l'architecture AfterWork v2.0.
|
||||
* Utilise des paramètres explicites pour éviter LazyInitializationException.
|
||||
*/
|
||||
@Getter
|
||||
public class EstablishmentAmenityResponseDTO {
|
||||
|
||||
private String establishmentId;
|
||||
private String amenityId;
|
||||
private String amenityName;
|
||||
private String category;
|
||||
private String icon;
|
||||
private String details;
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* Constructeur qui transforme une entité EstablishmentAmenity en DTO.
|
||||
* Les champs du type (name, category, icon) sont passés en paramètres car ils peuvent
|
||||
* provenir d'un JOIN FETCH déjà résolu ou être null si le type n'est pas chargé.
|
||||
*
|
||||
* @param ea L'entité à convertir.
|
||||
* @param amenityName Nom du type d'équipement (ex: "WiFi", "Parking").
|
||||
* @param category Catégorie du type (ex: "Comfort", "Accessibility").
|
||||
* @param icon Nom de l'icône (ex: "wifi", "parking").
|
||||
*/
|
||||
public EstablishmentAmenityResponseDTO(EstablishmentAmenity ea, String amenityName, String category, String icon) {
|
||||
this.establishmentId = ea.getEstablishmentId() != null ? ea.getEstablishmentId().toString() : null;
|
||||
this.amenityId = ea.getAmenityId() != null ? ea.getAmenityId().toString() : null;
|
||||
this.amenityName = amenityName;
|
||||
this.category = category;
|
||||
this.icon = icon;
|
||||
this.details = ea.getDetails();
|
||||
this.createdAt = ea.getCreatedAt();
|
||||
}
|
||||
}
|
||||
@@ -27,8 +27,24 @@ public class FriendshipCreateOneResponseDTO {
|
||||
|
||||
/**
|
||||
* Constructeur pour mapper l'entité `Friendship` à ce DTO.
|
||||
* Utilise les IDs fournis pour éviter LazyInitializationException sur user/friend.
|
||||
*
|
||||
* @param friendship L'entité `Friendship` à convertir en DTO.
|
||||
* @param userId ID de l'utilisateur qui envoie la demande (déjà chargé).
|
||||
* @param friendId ID de l'utilisateur qui reçoit la demande (déjà chargé).
|
||||
*/
|
||||
public FriendshipCreateOneResponseDTO(Friendship friendship, UUID userId, UUID friendId) {
|
||||
this.id = friendship.getId();
|
||||
this.userId = userId;
|
||||
this.friendId = friendId;
|
||||
this.status = friendship.getStatus();
|
||||
this.createdAt = friendship.getCreatedAt();
|
||||
this.updatedAt = friendship.getUpdatedAt();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructeur pour mapper l'entité `Friendship` à ce DTO (charge les associations lazy).
|
||||
* Préférer {@link #FriendshipCreateOneResponseDTO(Friendship, UUID, UUID)} en fin de transaction.
|
||||
*/
|
||||
public FriendshipCreateOneResponseDTO(Friendship friendship) {
|
||||
this.id = friendship.getId();
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.lions.dev.dto.response.users;
|
||||
import com.lions.dev.entity.users.Users;
|
||||
import java.util.UUID;
|
||||
import lombok.Getter;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* DTO pour renvoyer les informations d'un utilisateur.
|
||||
@@ -68,6 +69,5 @@ public class UserCreateResponseDTO {
|
||||
this.nom = this.lastName;
|
||||
this.prenoms = this.firstName;
|
||||
|
||||
System.out.println("[LOG] DTO créé pour l'utilisateur : " + this.email);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.lions.dev.dto;
|
||||
package com.lions.dev.dto.response.users;
|
||||
|
||||
import com.lions.dev.entity.users.Users; // Import de l'entité Users
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -37,7 +37,6 @@ public abstract class BaseEntity {
|
||||
protected void onCreate() {
|
||||
this.createdAt = LocalDateTime.now();
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
System.out.println("[LOG] Nouvelle entité créée avec ID : " + this.id + " à " + this.createdAt);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -47,6 +46,5 @@ public abstract class BaseEntity {
|
||||
@PreUpdate
|
||||
protected void onUpdate() {
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
System.out.println("[LOG] Entité mise à jour avec ID : " + this.id + " à " + this.updatedAt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.lions.dev.entity.auth;
|
||||
|
||||
import com.lions.dev.entity.users.Users;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Entité représentant un token de réinitialisation de mot de passe.
|
||||
*
|
||||
* Le token est valide pendant 1 heure après sa création.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "password_reset_tokens")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class PasswordResetToken {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
private UUID id;
|
||||
|
||||
@Column(name = "token", nullable = false, unique = true, length = 64)
|
||||
private String token;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "user_id", nullable = false)
|
||||
private Users user;
|
||||
|
||||
@Column(name = "expires_at", nullable = false)
|
||||
private LocalDateTime expiresAt;
|
||||
|
||||
@Column(name = "used", nullable = false)
|
||||
private boolean used = false;
|
||||
|
||||
@Column(name = "created_at", nullable = false, updatable = false)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* Crée un nouveau token de réinitialisation pour un utilisateur.
|
||||
* Le token expire après 1 heure.
|
||||
*
|
||||
* @param user L'utilisateur pour lequel créer le token
|
||||
*/
|
||||
public PasswordResetToken(Users user) {
|
||||
this.user = user;
|
||||
this.token = generateToken();
|
||||
this.createdAt = LocalDateTime.now();
|
||||
this.expiresAt = this.createdAt.plusHours(1);
|
||||
this.used = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Génère un token aléatoire sécurisé.
|
||||
*/
|
||||
private String generateToken() {
|
||||
return UUID.randomUUID().toString().replace("-", "") +
|
||||
UUID.randomUUID().toString().replace("-", "").substring(0, 32);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si le token est valide (non expiré et non utilisé).
|
||||
*/
|
||||
public boolean isValid() {
|
||||
return !used && LocalDateTime.now().isBefore(expiresAt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Marque le token comme utilisé.
|
||||
*/
|
||||
public void markAsUsed() {
|
||||
this.used = true;
|
||||
}
|
||||
}
|
||||
@@ -62,7 +62,6 @@ public class Conversation extends BaseEntity {
|
||||
this.user1 = user1;
|
||||
this.user2 = user2;
|
||||
this.lastMessageTimestamp = LocalDateTime.now();
|
||||
System.out.println("[LOG] Conversation créée entre " + user1.getEmail() + " et " + user2.getEmail());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -80,7 +79,6 @@ public class Conversation extends BaseEntity {
|
||||
unreadCountUser1++;
|
||||
}
|
||||
|
||||
System.out.println("[LOG] Dernier message mis à jour pour la conversation " + this.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,10 +87,8 @@ public class Conversation extends BaseEntity {
|
||||
public void markAllAsReadForUser(Users user) {
|
||||
if (user != null && user1 != null && user.getId().equals(user1.getId())) {
|
||||
unreadCountUser1 = 0;
|
||||
System.out.println("[LOG] Messages marqués comme lus pour user1 dans la conversation " + this.getId());
|
||||
} else if (user != null && user2 != null && user.getId().equals(user2.getId())) {
|
||||
unreadCountUser2 = 0;
|
||||
System.out.println("[LOG] Messages marqués comme lus pour user2 dans la conversation " + this.getId());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -57,7 +57,6 @@ public class Message extends BaseEntity {
|
||||
this.messageType = "text";
|
||||
this.isRead = false;
|
||||
this.isDelivered = false;
|
||||
System.out.println("[LOG] Message créé par " + sender.getEmail() + " dans la conversation " + conversation.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,7 +65,6 @@ public class Message extends BaseEntity {
|
||||
public void markAsRead() {
|
||||
if (!this.isRead) {
|
||||
this.isRead = true;
|
||||
System.out.println("[LOG] Message " + this.getId() + " marqué comme lu");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,7 +74,6 @@ public class Message extends BaseEntity {
|
||||
public void markAsDelivered() {
|
||||
if (!this.isDelivered) {
|
||||
this.isDelivered = true;
|
||||
System.out.println("[LOG] Message " + this.getId() + " marqué comme délivré");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -53,15 +53,7 @@ public class Comment extends BaseEntity {
|
||||
this.user = user;
|
||||
this.event = event;
|
||||
this.text = text;
|
||||
this.commentDate =
|
||||
LocalDateTime.now(); // La date est définie automatiquement lors de la création
|
||||
System.out.println(
|
||||
"[LOG] Nouveau commentaire ajouté par "
|
||||
+ user.getEmail()
|
||||
+ " sur l'événement : "
|
||||
+ event.getTitle()
|
||||
+ " - Texte : "
|
||||
+ text);
|
||||
this.commentDate = LocalDateTime.now();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -73,14 +65,7 @@ public class Comment extends BaseEntity {
|
||||
* @param newText Le nouveau texte du commentaire.
|
||||
*/
|
||||
public void updateComment(String newText) {
|
||||
System.out.println(
|
||||
"[LOG] Modification du commentaire de "
|
||||
+ user.getEmail()
|
||||
+ " sur l'événement : "
|
||||
+ event.getTitle()
|
||||
+ " - Nouveau texte : "
|
||||
+ newText);
|
||||
this.text = newText;
|
||||
this.commentDate = LocalDateTime.now(); // Mise à jour de la date de modification
|
||||
this.commentDate = LocalDateTime.now();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.lions.dev.entity.establishment;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Entité représentant un type d'équipement (référence amenity_types).
|
||||
* Utilisée pour afficher le nom/catégorie des équipements d'un établissement.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "amenity_types")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class AmenityType {
|
||||
|
||||
@Id
|
||||
@Column(name = "id", nullable = false)
|
||||
private UUID id;
|
||||
|
||||
@Column(name = "name", nullable = false, unique = true, length = 100)
|
||||
private String name;
|
||||
|
||||
@Column(name = "category", length = 50)
|
||||
private String category;
|
||||
|
||||
@Column(name = "icon", length = 50)
|
||||
private String icon;
|
||||
|
||||
@Column(name = "created_at", nullable = false, updatable = false)
|
||||
private LocalDateTime createdAt = LocalDateTime.now();
|
||||
}
|
||||
@@ -45,6 +45,10 @@ public class EstablishmentAmenity {
|
||||
@JoinColumn(name = "establishment_id", insertable = false, updatable = false)
|
||||
private Establishment establishment; // L'établissement concerné
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "amenity_id", insertable = false, updatable = false)
|
||||
private AmenityType amenityType; // Type d'équipement (nom, catégorie, icône)
|
||||
|
||||
/**
|
||||
* Constructeur pour créer une liaison établissement-équipement.
|
||||
*
|
||||
@@ -67,32 +71,3 @@ public class EstablishmentAmenity {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Classe composite pour la clé primaire de EstablishmentAmenity.
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
class EstablishmentAmenityId implements java.io.Serializable {
|
||||
private UUID establishmentId;
|
||||
private UUID amenityId;
|
||||
|
||||
public EstablishmentAmenityId(UUID establishmentId, UUID amenityId) {
|
||||
this.establishmentId = establishmentId;
|
||||
this.amenityId = amenityId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
EstablishmentAmenityId that = (EstablishmentAmenityId) o;
|
||||
return establishmentId.equals(that.establishmentId) && amenityId.equals(that.amenityId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return establishmentId.hashCode() + amenityId.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.lions.dev.entity.establishment;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Classe composite pour la clé primaire de EstablishmentAmenity.
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class EstablishmentAmenityId implements Serializable {
|
||||
|
||||
private UUID establishmentId;
|
||||
private UUID amenityId;
|
||||
|
||||
public EstablishmentAmenityId(UUID establishmentId, UUID amenityId) {
|
||||
this.establishmentId = establishmentId;
|
||||
this.amenityId = amenityId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
EstablishmentAmenityId that = (EstablishmentAmenityId) o;
|
||||
return establishmentId.equals(that.establishmentId) && amenityId.equals(that.amenityId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return establishmentId.hashCode() + amenityId.hashCode();
|
||||
}
|
||||
}
|
||||
@@ -117,7 +117,6 @@ public class Events extends BaseEntity {
|
||||
*/
|
||||
public void addParticipant(Users user) {
|
||||
participants.add(user);
|
||||
System.out.println("[LOG] Participant ajouté : " + user.getEmail() + " à l'événement : " + this.title);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -127,7 +126,6 @@ public class Events extends BaseEntity {
|
||||
*/
|
||||
public void removeParticipant(Users user) {
|
||||
participants.remove(user);
|
||||
System.out.println("[LOG] Participant supprimé : " + user.getEmail() + " de l'événement : " + this.title);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -137,7 +135,6 @@ public class Events extends BaseEntity {
|
||||
*/
|
||||
public int getNumberOfParticipants() {
|
||||
int count = participants.size();
|
||||
System.out.println("[LOG] Nombre de participants à l'événement : " + this.title + " - " + count);
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -146,7 +143,6 @@ public class Events extends BaseEntity {
|
||||
*/
|
||||
public void setClosed(boolean closed) {
|
||||
this.status = closed ? "CLOSED" : "OPEN";
|
||||
System.out.println("[LOG] Statut de l'événement mis à jour : " + this.title + " - " + this.status);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -189,7 +185,6 @@ public class Events extends BaseEntity {
|
||||
* @return Une liste de commentaires.
|
||||
*/
|
||||
public List<Comment> getComments() {
|
||||
System.out.println("[LOG] Récupération des commentaires pour l'événement : " + this.title);
|
||||
return comments;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,12 +38,5 @@ public class Friendship extends BaseEntity {
|
||||
*/
|
||||
public void setStatus(FriendshipStatus newStatus) {
|
||||
this.status = newStatus;
|
||||
System.out.println(
|
||||
"[LOG] Statut changé pour l'amitié entre "
|
||||
+ this.user.getEmail()
|
||||
+ " et "
|
||||
+ this.friend.getEmail()
|
||||
+ " - Nouveau statut : "
|
||||
+ this.status);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,6 @@ public class Notification extends BaseEntity {
|
||||
this.type = type;
|
||||
this.user = user;
|
||||
this.isRead = false;
|
||||
System.out.println("[LOG] Notification créée : " + title + " pour l'utilisateur " + user.getEmail());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,7 +68,6 @@ public class Notification extends BaseEntity {
|
||||
public void markAsRead() {
|
||||
if (!this.isRead) {
|
||||
this.isRead = true;
|
||||
System.out.println("[LOG] Notification marquée comme lue : " + this.title);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,6 @@ public class SocialPost extends BaseEntity {
|
||||
this.likesCount = 0;
|
||||
this.commentsCount = 0;
|
||||
this.sharesCount = 0;
|
||||
System.out.println("[LOG] Post social créé par l'utilisateur : " + user.getEmail());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,7 +59,6 @@ public class SocialPost extends BaseEntity {
|
||||
*/
|
||||
public void incrementLikes() {
|
||||
this.likesCount++;
|
||||
System.out.println("[LOG] Like ajouté au post ID : " + this.getId() + " (total: " + this.likesCount + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,7 +67,6 @@ public class SocialPost extends BaseEntity {
|
||||
public void decrementLikes() {
|
||||
if (this.likesCount > 0) {
|
||||
this.likesCount--;
|
||||
System.out.println("[LOG] Like retiré du post ID : " + this.getId() + " (total: " + this.likesCount + ")");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +75,6 @@ public class SocialPost extends BaseEntity {
|
||||
*/
|
||||
public void incrementComments() {
|
||||
this.commentsCount++;
|
||||
System.out.println("[LOG] Commentaire ajouté au post ID : " + this.getId() + " (total: " + this.commentsCount + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,7 +82,6 @@ public class SocialPost extends BaseEntity {
|
||||
*/
|
||||
public void incrementShares() {
|
||||
this.sharesCount++;
|
||||
System.out.println("[LOG] Partage ajouté au post ID : " + this.getId() + " (total: " + this.sharesCount + ")");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,6 @@ public class Story extends BaseEntity {
|
||||
this.expiresAt = LocalDateTime.now().plusHours(24); // Expire après 24h
|
||||
this.isActive = true;
|
||||
this.viewsCount = 0;
|
||||
System.out.println("[LOG] Story créée par l'utilisateur : " + user.getEmail());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,7 +81,6 @@ public class Story extends BaseEntity {
|
||||
public boolean markAsViewed(UUID viewerId) {
|
||||
if (viewerIds.add(viewerId)) {
|
||||
this.viewsCount++;
|
||||
System.out.println("[LOG] Story ID : " + this.getId() + " vue par l'utilisateur ID : " + viewerId + " (total: " + this.viewsCount + ")");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -102,7 +100,6 @@ public class Story extends BaseEntity {
|
||||
*/
|
||||
public void deactivate() {
|
||||
this.isActive = false;
|
||||
System.out.println("[LOG] Story ID : " + this.getId() + " désactivée");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -84,7 +84,6 @@ public class Users extends BaseEntity {
|
||||
*/
|
||||
public void setPassword(String password) {
|
||||
this.passwordHash = BCrypt.withDefaults().hashToString(12, password.toCharArray());
|
||||
System.out.println("[LOG] Mot de passe haché pour l'utilisateur : " + this.email);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,9 +104,7 @@ public class Users extends BaseEntity {
|
||||
*/
|
||||
public boolean verifyPassword(String password) {
|
||||
BCrypt.Result result = BCrypt.verifyer().verify(password.toCharArray(), this.passwordHash);
|
||||
boolean isValid = result.verified;
|
||||
System.out.println("[LOG] Vérification du mot de passe pour l'utilisateur : " + this.email + " - Résultat : " + isValid);
|
||||
return isValid;
|
||||
return result.verified;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -134,9 +131,7 @@ public class Users extends BaseEntity {
|
||||
* @return true si l'utilisateur est un administrateur, false sinon.
|
||||
*/
|
||||
public boolean isAdmin() {
|
||||
boolean isAdmin = "ADMIN".equalsIgnoreCase(this.role);
|
||||
System.out.println("[LOG] Vérification du rôle ADMIN pour l'utilisateur : " + this.email + " - Résultat : " + isAdmin);
|
||||
return isAdmin;
|
||||
return "ADMIN".equalsIgnoreCase(this.role);
|
||||
}
|
||||
|
||||
@ManyToMany(fetch = FetchType.LAZY)
|
||||
@@ -154,7 +149,6 @@ public class Users extends BaseEntity {
|
||||
*/
|
||||
public void addFavoriteEvent(Events event) {
|
||||
favoriteEvents.add(event);
|
||||
System.out.println("[LOG] Événement ajouté aux favoris pour l'utilisateur : " + this.email);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -164,7 +158,6 @@ public class Users extends BaseEntity {
|
||||
*/
|
||||
public void removeFavoriteEvent(Events event) {
|
||||
favoriteEvents.remove(event);
|
||||
System.out.println("[LOG] Événement retiré des favoris pour l'utilisateur : " + this.email);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -183,7 +176,6 @@ public class Users extends BaseEntity {
|
||||
* @return Une liste d'événements favoris.
|
||||
*/
|
||||
public Set<Events> getFavoriteEvents() {
|
||||
System.out.println("[LOG] Récupération des événements favoris pour l'utilisateur : " + this.email);
|
||||
return favoriteEvents;
|
||||
}
|
||||
|
||||
@@ -214,7 +206,6 @@ public class Users extends BaseEntity {
|
||||
} else {
|
||||
preferences.remove("preferred_category");
|
||||
}
|
||||
System.out.println("[LOG] Catégorie préférée définie pour l'utilisateur : " + this.email + " - Catégorie : " + category);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -232,7 +223,6 @@ public class Users extends BaseEntity {
|
||||
public void updatePresence() {
|
||||
this.isOnline = true;
|
||||
this.lastSeen = java.time.LocalDateTime.now();
|
||||
System.out.println("[LOG] Présence mise à jour pour l'utilisateur : " + this.email + " - Online: true");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -241,7 +231,6 @@ public class Users extends BaseEntity {
|
||||
public void setOffline() {
|
||||
this.isOnline = false;
|
||||
this.lastSeen = java.time.LocalDateTime.now();
|
||||
System.out.println("[LOG] Utilisateur marqué hors ligne : " + this.email);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,6 +17,5 @@ public class EventNotFoundException extends WebApplicationException {
|
||||
*/
|
||||
public EventNotFoundException(UUID eventId) {
|
||||
super("Événement non trouvé avec l'ID : " + eventId.toString(), Response.Status.NOT_FOUND);
|
||||
System.out.println("[ERROR] Événement non trouvé avec l'ID : " + eventId.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,5 @@ public class UserNotFoundException extends WebApplicationException {
|
||||
*/
|
||||
public UserNotFoundException(String message) {
|
||||
super(message, Response.Status.NOT_FOUND);
|
||||
System.out.println("[ERROR] Utilisateur non trouvé : " + message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.lions.dev.repository;
|
||||
|
||||
import com.lions.dev.entity.establishment.BusinessHours;
|
||||
import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Repository pour l'entité BusinessHours.
|
||||
* Gère les opérations de lecture sur les horaires d'ouverture des établissements.
|
||||
*/
|
||||
@ApplicationScoped
|
||||
public class BusinessHoursRepository implements PanacheRepositoryBase<BusinessHours, UUID> {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(BusinessHoursRepository.class);
|
||||
|
||||
/**
|
||||
* Récupère tous les horaires d'ouverture d'un établissement.
|
||||
*
|
||||
* @param establishmentId L'ID de l'établissement.
|
||||
* @return La liste des horaires (triés par jour de la semaine).
|
||||
*/
|
||||
public List<BusinessHours> findByEstablishmentId(UUID establishmentId) {
|
||||
LOG.infof("[LOG] Récupération des horaires pour l'établissement : %s", establishmentId);
|
||||
List<BusinessHours> list = list("establishment.id = ?1 ORDER BY dayOfWeek", establishmentId);
|
||||
LOG.infof("[LOG] Nombre d'horaires trouvés pour l'établissement %s : %d", establishmentId, list.size());
|
||||
return list;
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import com.lions.dev.entity.users.Users;
|
||||
import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
|
||||
import io.quarkus.panache.common.Sort;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@@ -27,7 +28,6 @@ public class ConversationRepository implements PanacheRepositoryBase<Conversatio
|
||||
* @return La conversation si elle existe, null sinon
|
||||
*/
|
||||
public Conversation findBetweenUsers(Users user1, Users user2) {
|
||||
System.out.println("[LOG] Recherche de conversation entre " + user1.getEmail() + " et " + user2.getEmail());
|
||||
|
||||
// Une conversation peut avoir user1 et user2 dans n'importe quel ordre
|
||||
return find("(user1 = ?1 and user2 = ?2) or (user1 = ?2 and user2 = ?1)", user1, user2)
|
||||
@@ -47,9 +47,7 @@ public class ConversationRepository implements PanacheRepositoryBase<Conversatio
|
||||
if (conversation == null) {
|
||||
conversation = new Conversation(user1, user2);
|
||||
persist(conversation);
|
||||
System.out.println("[LOG] Nouvelle conversation créée avec l'ID : " + conversation.getId());
|
||||
} else {
|
||||
System.out.println("[LOG] Conversation existante trouvée avec l'ID : " + conversation.getId());
|
||||
}
|
||||
|
||||
return conversation;
|
||||
@@ -62,7 +60,6 @@ public class ConversationRepository implements PanacheRepositoryBase<Conversatio
|
||||
* @return Liste des conversations triées par date du dernier message
|
||||
*/
|
||||
public List<Conversation> findByUser(Users user) {
|
||||
System.out.println("[LOG] Récupération des conversations pour l'utilisateur : " + user.getEmail());
|
||||
return find("user1 = ?1 or user2 = ?1", Sort.by("lastMessageTimestamp", Sort.Direction.Descending), user)
|
||||
.list();
|
||||
}
|
||||
@@ -74,7 +71,6 @@ public class ConversationRepository implements PanacheRepositoryBase<Conversatio
|
||||
* @return Liste des conversations avec des messages non lus
|
||||
*/
|
||||
public List<Conversation> findConversationsWithUnreadMessages(UUID userId) {
|
||||
System.out.println("[LOG] Récupération des conversations avec messages non lus pour l'utilisateur ID : " + userId);
|
||||
|
||||
return find(
|
||||
"(user1.id = ?1 and unreadCountUser1 > 0) or (user2.id = ?1 and unreadCountUser2 > 0)",
|
||||
@@ -101,7 +97,6 @@ public class ConversationRepository implements PanacheRepositoryBase<Conversatio
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("[LOG] Total de messages non lus pour l'utilisateur " + userId + " : " + total);
|
||||
return total;
|
||||
}
|
||||
|
||||
@@ -112,7 +107,6 @@ public class ConversationRepository implements PanacheRepositoryBase<Conversatio
|
||||
* @return true si la conversation a été supprimée, false sinon
|
||||
*/
|
||||
public boolean deleteConversation(UUID conversationId) {
|
||||
System.out.println("[LOG] Suppression de la conversation ID : " + conversationId);
|
||||
return deleteById(conversationId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.lions.dev.repository;
|
||||
|
||||
import com.lions.dev.entity.establishment.EstablishmentAmenity;
|
||||
import com.lions.dev.entity.establishment.EstablishmentAmenityId;
|
||||
import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.PersistenceContext;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Repository pour l'entité EstablishmentAmenity.
|
||||
* Gère les opérations de lecture sur les équipements des établissements.
|
||||
*/
|
||||
@ApplicationScoped
|
||||
public class EstablishmentAmenityRepository implements PanacheRepositoryBase<EstablishmentAmenity, EstablishmentAmenityId> {
|
||||
|
||||
@PersistenceContext
|
||||
EntityManager entityManager;
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(EstablishmentAmenityRepository.class);
|
||||
|
||||
/**
|
||||
* Récupère tous les équipements d'un établissement avec le type chargé (nom, catégorie, icône).
|
||||
* Utilise JOIN FETCH pour éviter LazyInitializationException.
|
||||
*
|
||||
* @param establishmentId L'ID de l'établissement.
|
||||
* @return La liste des équipements (avec amenityType chargé).
|
||||
*/
|
||||
public List<EstablishmentAmenity> findByEstablishmentIdWithType(UUID establishmentId) {
|
||||
LOG.infof("[LOG] Récupération des équipements pour l'établissement : %s", establishmentId);
|
||||
List<EstablishmentAmenity> list = entityManager
|
||||
.createQuery(
|
||||
"SELECT ea FROM EstablishmentAmenity ea LEFT JOIN FETCH ea.amenityType WHERE ea.establishmentId = :eid ORDER BY ea.amenityId",
|
||||
EstablishmentAmenity.class)
|
||||
.setParameter("eid", establishmentId)
|
||||
.getResultList();
|
||||
LOG.infof("[LOG] Nombre d'équipements trouvés pour l'établissement %s : %d", establishmentId, list.size());
|
||||
return list;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import com.lions.dev.entity.establishment.EstablishmentSubscription;
|
||||
import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
@@ -23,4 +24,16 @@ public class EstablishmentSubscriptionRepository implements PanacheRepositoryBas
|
||||
return find("establishmentId = ?1 and status = ?2", establishmentId, EstablishmentSubscription.STATUS_ACTIVE)
|
||||
.firstResultOptional();
|
||||
}
|
||||
|
||||
/** Abonnements actifs dont la date d'expiration est dépassée (pour job de nettoyage). */
|
||||
public List<EstablishmentSubscription> findExpiredActiveSubscriptions() {
|
||||
return list("status = ?1 AND expiresAt IS NOT NULL AND expiresAt <= ?2",
|
||||
EstablishmentSubscription.STATUS_ACTIVE, LocalDateTime.now());
|
||||
}
|
||||
|
||||
/** Abonnements actifs dont l'expiration est dans la fenêtre [from, to] (pour avertissement J-3). */
|
||||
public List<EstablishmentSubscription> findActiveSubscriptionsExpiringBetween(LocalDateTime from, LocalDateTime to) {
|
||||
return list("status = ?1 AND expiresAt IS NOT NULL AND expiresAt >= ?2 AND expiresAt <= ?3",
|
||||
EstablishmentSubscription.STATUS_ACTIVE, from, to);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,4 +100,12 @@ public class EventsRepository implements PanacheRepositoryBase<Events, UUID> {
|
||||
.setParameter("loc", pattern)
|
||||
.getResultList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère les événements dont la date de début est dans la fenêtre [from, to].
|
||||
* Utilisé pour les rappels (J-1, H-1).
|
||||
*/
|
||||
public List<Events> findEventsStartingBetween(LocalDateTime from, LocalDateTime to) {
|
||||
return list("startDate >= ?1 AND startDate <= ?2", from, to);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,4 +136,16 @@ public class FriendshipRepository implements PanacheRepositoryBase<Friendship, U
|
||||
logger.infof("Nombre de demandes reçues récupérées pour l'utilisateur %s : %d", user.getId(), friendships.size());
|
||||
return friendships;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupérer toutes les amitiés acceptées d'un utilisateur (par UUID).
|
||||
* Méthode simplifiée pour les notifications temps réel.
|
||||
*
|
||||
* @param userId L'UUID de l'utilisateur
|
||||
* @return Liste de toutes les amitiés acceptées
|
||||
*/
|
||||
public List<Friendship> findAcceptedFriendships(UUID userId) {
|
||||
return find("(user.id = ?1 OR friend.id = ?1) AND status = ?2", userId, FriendshipStatus.ACCEPTED)
|
||||
.list();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
|
||||
import io.quarkus.panache.common.Page;
|
||||
import io.quarkus.panache.common.Sort;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
@@ -28,7 +29,6 @@ public class MessageRepository implements PanacheRepositoryBase<Message, UUID> {
|
||||
* @return Liste paginée des messages triés par date (du plus récent au plus ancien)
|
||||
*/
|
||||
public List<Message> findByConversationId(UUID conversationId, int page, int size) {
|
||||
System.out.println("[LOG] Récupération des messages pour la conversation ID : " + conversationId);
|
||||
return find("conversation.id = ?1", Sort.by("createdAt", Sort.Direction.Descending), conversationId)
|
||||
.page(Page.of(page, size))
|
||||
.list();
|
||||
@@ -41,7 +41,6 @@ public class MessageRepository implements PanacheRepositoryBase<Message, UUID> {
|
||||
* @return Liste de tous les messages triés par date
|
||||
*/
|
||||
public List<Message> findByConversation(Conversation conversation) {
|
||||
System.out.println("[LOG] Récupération de tous les messages pour la conversation ID : " + conversation.getId());
|
||||
return find("conversation = ?1", Sort.by("createdAt", Sort.Direction.Ascending), conversation).list();
|
||||
}
|
||||
|
||||
@@ -64,7 +63,6 @@ public class MessageRepository implements PanacheRepositoryBase<Message, UUID> {
|
||||
* @return Le nombre de messages mis à jour
|
||||
*/
|
||||
public int markAllAsRead(UUID conversationId, UUID recipientId) {
|
||||
System.out.println("[LOG] Marquage de tous les messages comme lus pour la conversation " + conversationId);
|
||||
return update("isRead = true where conversation.id = ?1 and sender.id != ?2 and isRead = false", conversationId, recipientId);
|
||||
}
|
||||
|
||||
@@ -86,7 +84,6 @@ public class MessageRepository implements PanacheRepositoryBase<Message, UUID> {
|
||||
* @return Le nombre de messages supprimés
|
||||
*/
|
||||
public long deleteByConversationId(UUID conversationId) {
|
||||
System.out.println("[LOG] Suppression de tous les messages pour la conversation " + conversationId);
|
||||
return delete("conversation.id = ?1", conversationId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import io.quarkus.panache.common.Sort;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Repository pour l'entité Notification.
|
||||
@@ -27,9 +28,7 @@ public class NotificationRepository implements PanacheRepositoryBase<Notificatio
|
||||
* @return Liste des notifications de l'utilisateur, triées par date de création décroissante
|
||||
*/
|
||||
public List<Notification> findByUserId(UUID userId) {
|
||||
System.out.println("[LOG] Récupération des notifications pour l'utilisateur ID : " + userId);
|
||||
List<Notification> notifications = find("user.id", Sort.by("createdAt", Sort.Direction.Descending), userId).list();
|
||||
System.out.println("[LOG] " + notifications.size() + " notification(s) trouvée(s) pour l'utilisateur ID : " + userId);
|
||||
return notifications;
|
||||
}
|
||||
|
||||
@@ -40,9 +39,7 @@ public class NotificationRepository implements PanacheRepositoryBase<Notificatio
|
||||
* @return Liste des notifications non lues de l'utilisateur
|
||||
*/
|
||||
public List<Notification> findUnreadByUserId(UUID userId) {
|
||||
System.out.println("[LOG] Récupération des notifications non lues pour l'utilisateur ID : " + userId);
|
||||
List<Notification> notifications = find("user.id = ?1 AND isRead = false", Sort.by("createdAt", Sort.Direction.Descending), userId).list();
|
||||
System.out.println("[LOG] " + notifications.size() + " notification(s) non lue(s) trouvée(s) pour l'utilisateur ID : " + userId);
|
||||
return notifications;
|
||||
}
|
||||
|
||||
@@ -55,11 +52,9 @@ public class NotificationRepository implements PanacheRepositoryBase<Notificatio
|
||||
* @return Liste paginée des notifications
|
||||
*/
|
||||
public List<Notification> findByUserIdWithPagination(UUID userId, int page, int size) {
|
||||
System.out.println("[LOG] Récupération paginée des notifications pour l'utilisateur ID : " + userId + " (page: " + page + ", size: " + size + ")");
|
||||
List<Notification> notifications = find("user.id", Sort.by("createdAt", Sort.Direction.Descending), userId)
|
||||
.page(Page.of(page, size))
|
||||
.list();
|
||||
System.out.println("[LOG] " + notifications.size() + " notification(s) récupérée(s) pour la page " + page);
|
||||
return notifications;
|
||||
}
|
||||
|
||||
@@ -71,7 +66,6 @@ public class NotificationRepository implements PanacheRepositoryBase<Notificatio
|
||||
*/
|
||||
public long countUnreadByUserId(UUID userId) {
|
||||
long count = count("user.id = ?1 AND isRead = false", userId);
|
||||
System.out.println("[LOG] " + count + " notification(s) non lue(s) pour l'utilisateur ID : " + userId);
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -82,9 +76,7 @@ public class NotificationRepository implements PanacheRepositoryBase<Notificatio
|
||||
* @return Le nombre de notifications mises à jour
|
||||
*/
|
||||
public int markAllAsReadByUserId(UUID userId) {
|
||||
System.out.println("[LOG] Marquage de toutes les notifications comme lues pour l'utilisateur ID : " + userId);
|
||||
int updated = update("isRead = true WHERE user.id = ?1", userId);
|
||||
System.out.println("[LOG] " + updated + " notification(s) marquée(s) comme lue(s)");
|
||||
return updated;
|
||||
}
|
||||
|
||||
@@ -95,9 +87,7 @@ public class NotificationRepository implements PanacheRepositoryBase<Notificatio
|
||||
* @return Le nombre de notifications supprimées
|
||||
*/
|
||||
public long deleteReadByUserId(UUID userId) {
|
||||
System.out.println("[LOG] Suppression des notifications lues pour l'utilisateur ID : " + userId);
|
||||
long deleted = delete("user.id = ?1 AND isRead = true", userId);
|
||||
System.out.println("[LOG] " + deleted + " notification(s) supprimée(s)");
|
||||
return deleted;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.lions.dev.repository;
|
||||
|
||||
import com.lions.dev.entity.auth.PasswordResetToken;
|
||||
import com.lions.dev.entity.users.Users;
|
||||
import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Repository pour la gestion des tokens de réinitialisation de mot de passe.
|
||||
*/
|
||||
@ApplicationScoped
|
||||
public class PasswordResetTokenRepository implements PanacheRepositoryBase<PasswordResetToken, UUID> {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(PasswordResetTokenRepository.class);
|
||||
|
||||
/**
|
||||
* Trouve un token par sa valeur.
|
||||
*
|
||||
* @param token La valeur du token
|
||||
* @return Le token s'il existe
|
||||
*/
|
||||
public Optional<PasswordResetToken> findByToken(String token) {
|
||||
return find("token", token).firstResultOptional();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve un token valide par sa valeur.
|
||||
*
|
||||
* @param token La valeur du token
|
||||
* @return Le token s'il existe, n'est pas expiré et n'a pas été utilisé
|
||||
*/
|
||||
public Optional<PasswordResetToken> findValidToken(String token) {
|
||||
return find("token = ?1 AND used = false AND expiresAt > ?2", token, LocalDateTime.now())
|
||||
.firstResultOptional();
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalide tous les tokens existants pour un utilisateur.
|
||||
*
|
||||
* @param user L'utilisateur dont les tokens doivent être invalidés
|
||||
* @return Le nombre de tokens invalidés
|
||||
*/
|
||||
public long invalidateAllForUser(Users user) {
|
||||
long count = update("used = true WHERE user = ?1 AND used = false", user);
|
||||
if (count > 0) {
|
||||
logger.info("Invalidé " + count + " token(s) existant(s) pour l'utilisateur: " + user.getEmail());
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Supprime les tokens expirés (nettoyage).
|
||||
*
|
||||
* @return Le nombre de tokens supprimés
|
||||
*/
|
||||
public long deleteExpiredTokens() {
|
||||
long count = delete("expiresAt < ?1 OR used = true", LocalDateTime.now().minusDays(7));
|
||||
if (count > 0) {
|
||||
logger.info("Supprimé " + count + " token(s) expiré(s)");
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import io.quarkus.panache.common.Sort;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Repository pour l'entité SocialPost.
|
||||
@@ -27,11 +28,9 @@ public class SocialPostRepository implements PanacheRepositoryBase<SocialPost, U
|
||||
* @return Liste paginée des posts
|
||||
*/
|
||||
public List<SocialPost> findAllWithPagination(int page, int size) {
|
||||
System.out.println("[LOG] Récupération paginée des posts (page: " + page + ", size: " + size + ")");
|
||||
List<SocialPost> posts = findAll(Sort.by("createdAt", Sort.Direction.Descending))
|
||||
.page(Page.of(page, size))
|
||||
.list();
|
||||
System.out.println("[LOG] " + posts.size() + " post(s) récupéré(s)");
|
||||
return posts;
|
||||
}
|
||||
|
||||
@@ -42,9 +41,7 @@ public class SocialPostRepository implements PanacheRepositoryBase<SocialPost, U
|
||||
* @return Liste des posts de l'utilisateur
|
||||
*/
|
||||
public List<SocialPost> findByUserId(UUID userId) {
|
||||
System.out.println("[LOG] Récupération des posts pour l'utilisateur ID : " + userId);
|
||||
List<SocialPost> posts = find("user.id", Sort.by("createdAt", Sort.Direction.Descending), userId).list();
|
||||
System.out.println("[LOG] " + posts.size() + " post(s) trouvé(s) pour l'utilisateur ID : " + userId);
|
||||
return posts;
|
||||
}
|
||||
|
||||
@@ -55,10 +52,8 @@ public class SocialPostRepository implements PanacheRepositoryBase<SocialPost, U
|
||||
* @return Liste des posts correspondant à la recherche
|
||||
*/
|
||||
public List<SocialPost> searchByContent(String query) {
|
||||
System.out.println("[LOG] Recherche de posts avec la requête : " + query);
|
||||
String searchPattern = "%" + query.toLowerCase() + "%";
|
||||
List<SocialPost> posts = find("LOWER(content) LIKE ?1", Sort.by("createdAt", Sort.Direction.Descending), searchPattern).list();
|
||||
System.out.println("[LOG] " + posts.size() + " post(s) trouvé(s) pour la requête : " + query);
|
||||
return posts;
|
||||
}
|
||||
|
||||
@@ -69,11 +64,9 @@ public class SocialPostRepository implements PanacheRepositoryBase<SocialPost, U
|
||||
* @return Liste des posts les plus populaires
|
||||
*/
|
||||
public List<SocialPost> findPopularPosts(int limit) {
|
||||
System.out.println("[LOG] Récupération des " + limit + " posts les plus populaires");
|
||||
List<SocialPost> posts = findAll(Sort.by("likesCount", Sort.Direction.Descending))
|
||||
.page(0, limit)
|
||||
.list();
|
||||
System.out.println("[LOG] " + posts.size() + " post(s) populaire(s) récupéré(s)");
|
||||
return posts;
|
||||
}
|
||||
|
||||
@@ -87,10 +80,8 @@ public class SocialPostRepository implements PanacheRepositoryBase<SocialPost, U
|
||||
* @return Liste paginée des posts des amis
|
||||
*/
|
||||
public List<SocialPost> findPostsByFriends(UUID userId, List<UUID> friendIds, int page, int size) {
|
||||
System.out.println("[LOG] Récupération des posts des amis pour l'utilisateur ID : " + userId);
|
||||
|
||||
if (friendIds == null || friendIds.isEmpty()) {
|
||||
System.out.println("[LOG] Aucun ami trouvé pour l'utilisateur ID : " + userId);
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@@ -102,7 +93,6 @@ public class SocialPostRepository implements PanacheRepositoryBase<SocialPost, U
|
||||
.page(Page.of(page, size))
|
||||
.list();
|
||||
|
||||
System.out.println("[LOG] " + posts.size() + " post(s) récupéré(s) des amis");
|
||||
return posts;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import jakarta.enterprise.context.ApplicationScoped;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Repository pour l'entité Story.
|
||||
@@ -36,13 +37,11 @@ public class StoryRepository implements PanacheRepositoryBase<Story, UUID> {
|
||||
* @return Liste paginée des stories actives
|
||||
*/
|
||||
public List<Story> findAllActive(int page, int size) {
|
||||
System.out.println("[LOG] Récupération de toutes les stories actives (page: " + page + ", size: " + size + ")");
|
||||
List<Story> stories = find("isActive = true AND expiresAt > ?1",
|
||||
Sort.by("createdAt", Sort.Direction.Descending),
|
||||
LocalDateTime.now())
|
||||
.page(page, size)
|
||||
.list();
|
||||
System.out.println("[LOG] " + stories.size() + " story(ies) active(s) récupérée(s)");
|
||||
return stories;
|
||||
}
|
||||
|
||||
@@ -65,14 +64,12 @@ public class StoryRepository implements PanacheRepositoryBase<Story, UUID> {
|
||||
* @return Liste paginée des stories actives de l'utilisateur
|
||||
*/
|
||||
public List<Story> findActiveByUserId(UUID userId, int page, int size) {
|
||||
System.out.println("[LOG] Récupération des stories actives pour l'utilisateur ID : " + userId + " (page: " + page + ", size: " + size + ")");
|
||||
List<Story> stories = find("user.id = ?1 AND isActive = true AND expiresAt > ?2",
|
||||
Sort.by("createdAt", Sort.Direction.Descending),
|
||||
userId,
|
||||
LocalDateTime.now())
|
||||
.page(page, size)
|
||||
.list();
|
||||
System.out.println("[LOG] " + stories.size() + " story(ies) active(s) trouvée(s) pour l'utilisateur ID : " + userId);
|
||||
return stories;
|
||||
}
|
||||
|
||||
@@ -83,9 +80,7 @@ public class StoryRepository implements PanacheRepositoryBase<Story, UUID> {
|
||||
* @return Liste de toutes les stories de l'utilisateur
|
||||
*/
|
||||
public List<Story> findAllByUserId(UUID userId) {
|
||||
System.out.println("[LOG] Récupération de toutes les stories pour l'utilisateur ID : " + userId);
|
||||
List<Story> stories = find("user.id", Sort.by("createdAt", Sort.Direction.Descending), userId).list();
|
||||
System.out.println("[LOG] " + stories.size() + " story(ies) trouvée(s) pour l'utilisateur ID : " + userId);
|
||||
return stories;
|
||||
}
|
||||
|
||||
@@ -96,9 +91,7 @@ public class StoryRepository implements PanacheRepositoryBase<Story, UUID> {
|
||||
* @return Liste des stories expirées
|
||||
*/
|
||||
public List<Story> findExpiredStories() {
|
||||
System.out.println("[LOG] Récupération des stories expirées");
|
||||
List<Story> stories = find("isActive = true AND expiresAt <= ?1", LocalDateTime.now()).list();
|
||||
System.out.println("[LOG] " + stories.size() + " story(ies) expirée(s) trouvée(s)");
|
||||
return stories;
|
||||
}
|
||||
|
||||
@@ -109,9 +102,7 @@ public class StoryRepository implements PanacheRepositoryBase<Story, UUID> {
|
||||
* @return Le nombre de stories désactivées
|
||||
*/
|
||||
public int deactivateExpiredStories() {
|
||||
System.out.println("[LOG] Désactivation des stories expirées");
|
||||
int updated = update("isActive = false WHERE isActive = true AND expiresAt <= ?1", LocalDateTime.now());
|
||||
System.out.println("[LOG] " + updated + " story(ies) désactivée(s)");
|
||||
return updated;
|
||||
}
|
||||
|
||||
@@ -122,13 +113,11 @@ public class StoryRepository implements PanacheRepositoryBase<Story, UUID> {
|
||||
* @return Liste des stories les plus vues
|
||||
*/
|
||||
public List<Story> findMostViewedStories(int limit) {
|
||||
System.out.println("[LOG] Récupération des " + limit + " stories les plus vues");
|
||||
List<Story> stories = find("isActive = true AND expiresAt > ?1",
|
||||
Sort.by("viewsCount", Sort.Direction.Descending),
|
||||
LocalDateTime.now())
|
||||
.page(0, limit)
|
||||
.list();
|
||||
System.out.println("[LOG] " + stories.size() + " story(ies) populaire(s) récupérée(s)");
|
||||
return stories;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.PersistenceContext;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Repository pour l'entité Users.
|
||||
@@ -62,12 +63,9 @@ public class UsersRepository implements PanacheRepositoryBase<Users, UUID> {
|
||||
* @return Un Optional contenant l'utilisateur s'il est trouvé, sinon un Optional vide.
|
||||
*/
|
||||
public Optional<Users> findByEmail(String email) {
|
||||
System.out.println("[LOG] Recherche de l'utilisateur avec l'email : " + email);
|
||||
Users user = find("email", email).firstResult();
|
||||
if (user != null) {
|
||||
System.out.println("[LOG] Utilisateur trouvé : " + user.getEmail());
|
||||
} else {
|
||||
System.out.println("[LOG] Aucun utilisateur trouvé avec l'email : " + email);
|
||||
}
|
||||
return Optional.ofNullable(user);
|
||||
}
|
||||
@@ -79,10 +77,8 @@ public class UsersRepository implements PanacheRepositoryBase<Users, UUID> {
|
||||
* @return true si un utilisateur avec cet email existe, sinon false.
|
||||
*/
|
||||
public boolean existsByEmail(String email) {
|
||||
System.out.println("[LOG] Vérification de l'existence de l'utilisateur avec l'email : " + email);
|
||||
long count = find("email", email).count();
|
||||
boolean exists = count > 0;
|
||||
System.out.println("[LOG] Utilisateur existe : " + exists);
|
||||
return exists;
|
||||
}
|
||||
|
||||
@@ -93,13 +89,10 @@ public class UsersRepository implements PanacheRepositoryBase<Users, UUID> {
|
||||
* @return true si l'utilisateur a été supprimé, sinon false.
|
||||
*/
|
||||
public boolean deleteById(UUID id) {
|
||||
System.out.println("[LOG] Suppression de l'utilisateur avec l'ID : " + id);
|
||||
long deletedCount = delete("id", id); // Utiliser long pour récupérer le nombre d'enregistrements supprimés
|
||||
boolean deleted = deletedCount > 0; // Convertir en boolean
|
||||
if (deleted) {
|
||||
System.out.println("[LOG] Utilisateur avec l'ID " + id + " supprimé avec succès.");
|
||||
} else {
|
||||
System.out.println("[LOG] Aucune suppression, utilisateur avec l'ID " + id + " introuvable.");
|
||||
}
|
||||
return deleted;
|
||||
}
|
||||
|
||||
@@ -44,6 +44,12 @@ public class EstablishmentRatingResource {
|
||||
@PathParam("establishmentId") String establishmentId,
|
||||
@QueryParam("userId") String userIdStr,
|
||||
@Valid EstablishmentRatingRequestDTO requestDTO) {
|
||||
if (userIdStr == null || userIdStr.isBlank()) {
|
||||
LOG.warn("Soumission de note sans userId pour l'établissement " + establishmentId);
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity("Le paramètre userId est requis")
|
||||
.build();
|
||||
}
|
||||
LOG.info("Soumission d'une note pour l'établissement " + establishmentId + " par l'utilisateur " + userIdStr);
|
||||
|
||||
try {
|
||||
@@ -82,6 +88,12 @@ public class EstablishmentRatingResource {
|
||||
@PathParam("establishmentId") String establishmentId,
|
||||
@QueryParam("userId") String userIdStr,
|
||||
@Valid EstablishmentRatingRequestDTO requestDTO) {
|
||||
if (userIdStr == null || userIdStr.isBlank()) {
|
||||
LOG.warn("Mise à jour de note sans userId pour l'établissement " + establishmentId);
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity("Le paramètre userId est requis")
|
||||
.build();
|
||||
}
|
||||
LOG.info("Mise à jour de la note pour l'établissement " + establishmentId + " par l'utilisateur " + userIdStr);
|
||||
|
||||
try {
|
||||
|
||||
@@ -2,9 +2,13 @@ package com.lions.dev.resource;
|
||||
|
||||
import com.lions.dev.dto.request.establishment.EstablishmentCreateRequestDTO;
|
||||
import com.lions.dev.dto.request.establishment.EstablishmentUpdateRequestDTO;
|
||||
import com.lions.dev.dto.response.establishment.BusinessHoursResponseDTO;
|
||||
import com.lions.dev.dto.response.establishment.EstablishmentAmenityResponseDTO;
|
||||
import com.lions.dev.dto.response.establishment.EstablishmentResponseDTO;
|
||||
import com.lions.dev.entity.establishment.Establishment;
|
||||
import com.lions.dev.entity.users.Users;
|
||||
import com.lions.dev.repository.BusinessHoursRepository;
|
||||
import com.lions.dev.repository.EstablishmentAmenityRepository;
|
||||
import com.lions.dev.repository.EstablishmentRepository;
|
||||
import com.lions.dev.repository.UsersRepository;
|
||||
import com.lions.dev.service.EstablishmentService;
|
||||
@@ -43,6 +47,12 @@ public class EstablishmentResource {
|
||||
@Inject
|
||||
EstablishmentRepository establishmentRepository;
|
||||
|
||||
@Inject
|
||||
BusinessHoursRepository businessHoursRepository;
|
||||
|
||||
@Inject
|
||||
EstablishmentAmenityRepository establishmentAmenityRepository;
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(EstablishmentResource.class);
|
||||
|
||||
// *********** Création d'un établissement ***********
|
||||
@@ -145,6 +155,58 @@ public class EstablishmentResource {
|
||||
}
|
||||
}
|
||||
|
||||
// *********** Horaires d'ouverture d'un établissement ***********
|
||||
|
||||
@GET
|
||||
@Path("/{id}/business-hours")
|
||||
@Operation(summary = "Récupérer les horaires d'ouverture d'un établissement",
|
||||
description = "Retourne la liste des horaires d'ouverture pour l'établissement donné")
|
||||
public Response getBusinessHoursByEstablishmentId(@PathParam("id") UUID id) {
|
||||
LOG.info("[LOG] Récupération des horaires pour l'établissement : " + id);
|
||||
try {
|
||||
establishmentService.getEstablishmentById(id);
|
||||
var list = businessHoursRepository.findByEstablishmentId(id).stream()
|
||||
.map(bh -> new BusinessHoursResponseDTO(bh, id))
|
||||
.collect(Collectors.toList());
|
||||
return Response.ok(list).build();
|
||||
} catch (RuntimeException e) {
|
||||
LOG.warn("[WARN] " + e.getMessage());
|
||||
return Response.status(Response.Status.NOT_FOUND).entity(e.getMessage()).build();
|
||||
} catch (Exception e) {
|
||||
LOG.error("[ERROR] Erreur lors de la récupération des horaires", e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity("Erreur lors de la récupération des horaires").build();
|
||||
}
|
||||
}
|
||||
|
||||
// *********** Équipements d'un établissement ***********
|
||||
|
||||
@GET
|
||||
@Path("/{id}/amenities")
|
||||
@Operation(summary = "Récupérer les équipements d'un établissement",
|
||||
description = "Retourne la liste des équipements (amenities) pour l'établissement donné")
|
||||
public Response getAmenitiesByEstablishmentId(@PathParam("id") UUID id) {
|
||||
LOG.info("[LOG] Récupération des équipements pour l'établissement : " + id);
|
||||
try {
|
||||
establishmentService.getEstablishmentById(id);
|
||||
var list = establishmentAmenityRepository.findByEstablishmentIdWithType(id).stream()
|
||||
.map(ea -> new EstablishmentAmenityResponseDTO(
|
||||
ea,
|
||||
ea.getAmenityType() != null ? ea.getAmenityType().getName() : null,
|
||||
ea.getAmenityType() != null ? ea.getAmenityType().getCategory() : null,
|
||||
ea.getAmenityType() != null ? ea.getAmenityType().getIcon() : null))
|
||||
.collect(Collectors.toList());
|
||||
return Response.ok(list).build();
|
||||
} catch (RuntimeException e) {
|
||||
LOG.warn("[WARN] " + e.getMessage());
|
||||
return Response.status(Response.Status.NOT_FOUND).entity(e.getMessage()).build();
|
||||
} catch (Exception e) {
|
||||
LOG.error("[ERROR] Erreur lors de la récupération des équipements", e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity("Erreur lors de la récupération des équipements").build();
|
||||
}
|
||||
}
|
||||
|
||||
// *********** Récupération d'un établissement par ID ***********
|
||||
|
||||
@GET
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.lions.dev.resource;
|
||||
|
||||
import com.lions.dev.core.errors.exceptions.EventNotFoundException;
|
||||
import com.lions.dev.dto.UserResponseDTO;
|
||||
import com.lions.dev.dto.request.events.EventCreateRequestDTO;
|
||||
import com.lions.dev.dto.request.events.EventReadManyByIdRequestDTO;
|
||||
import com.lions.dev.dto.request.events.EventUpdateRequestDTO;
|
||||
@@ -10,6 +9,7 @@ import com.lions.dev.dto.response.events.EventCreateResponseDTO;
|
||||
import com.lions.dev.dto.response.events.EventReadManyByIdResponseDTO;
|
||||
import com.lions.dev.dto.response.events.EventUpdateResponseDTO;
|
||||
import com.lions.dev.dto.response.friends.FriendshipReadFriendDetailsResponseDTO;
|
||||
import com.lions.dev.dto.response.users.UserResponseDTO;
|
||||
import com.lions.dev.entity.comment.Comment;
|
||||
import com.lions.dev.entity.events.Events;
|
||||
import com.lions.dev.entity.users.Users;
|
||||
|
||||
@@ -18,6 +18,7 @@ import jakarta.inject.Inject;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@Path("/media")
|
||||
@@ -81,11 +82,16 @@ public class FileUploadResource {
|
||||
// Note: En production, vous devriez utiliser une URL publique (CDN, S3, etc.)
|
||||
String fileUrl = buildFileUrl(finalFileName, uriInfo);
|
||||
String thumbnailUrl = null;
|
||||
|
||||
// Pour les vidéos, on pourrait générer un thumbnail ici
|
||||
|
||||
if ("video".equalsIgnoreCase(mediaType)) {
|
||||
// TODO: Générer un thumbnail pour les vidéos
|
||||
thumbnailUrl = fileUrl; // Placeholder
|
||||
Optional<java.nio.file.Path> thumbPath = fileService.generateVideoThumbnail(savedFilePath);
|
||||
if (thumbPath.isPresent()) {
|
||||
thumbnailUrl = buildFileUrl(thumbPath.get().getFileName().toString(), uriInfo);
|
||||
LOG.infof("Thumbnail vidéo généré : %s", thumbnailUrl);
|
||||
} else {
|
||||
thumbnailUrl = fileUrl;
|
||||
LOG.infof("Thumbnail vidéo non généré (FFmpeg absent ou erreur), utilisation de l'URL vidéo en placeholder");
|
||||
}
|
||||
}
|
||||
|
||||
// Construire la réponse JSON
|
||||
|
||||
@@ -36,6 +36,7 @@ public class NotificationResource {
|
||||
|
||||
/**
|
||||
* Récupère toutes les notifications d'un utilisateur.
|
||||
* En production, le userId doit être dérivé du contexte d'authentification (JWT/session), pas de l'URL.
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur
|
||||
* @return Liste des notifications de l'utilisateur
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.lions.dev.resource;
|
||||
|
||||
import com.lions.dev.dto.PasswordResetRequest;
|
||||
import com.lions.dev.dto.request.users.AssignRoleRequestDTO;
|
||||
import com.lions.dev.dto.request.users.SetUserActiveRequestDTO;
|
||||
import com.lions.dev.dto.request.users.UserAuthenticateRequestDTO;
|
||||
import com.lions.dev.dto.request.users.UserCreateRequestDTO;
|
||||
import com.lions.dev.dto.response.users.UserAuthenticateResponseDTO;
|
||||
@@ -9,6 +10,7 @@ import com.lions.dev.dto.response.users.UserCreateResponseDTO;
|
||||
import com.lions.dev.dto.response.users.UserDeleteResponseDto;
|
||||
import com.lions.dev.entity.users.Users;
|
||||
import com.lions.dev.exception.UserNotFoundException;
|
||||
import com.lions.dev.service.PasswordResetService;
|
||||
import com.lions.dev.service.UsersService;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.transaction.Transactional;
|
||||
@@ -42,6 +44,9 @@ public class UsersResource {
|
||||
@Inject
|
||||
UsersService userService;
|
||||
|
||||
@Inject
|
||||
PasswordResetService passwordResetService;
|
||||
|
||||
@ConfigProperty(name = "afterwork.super-admin.api-key", defaultValue = "")
|
||||
Optional<String> superAdminApiKey;
|
||||
|
||||
@@ -300,18 +305,8 @@ public class UsersResource {
|
||||
LOG.info("Demande de reinitialisation de mot de passe pour l'email : " + request.getEmail());
|
||||
|
||||
try {
|
||||
// Rechercher l'utilisateur par email
|
||||
Users user = userService.findByEmail(request.getEmail());
|
||||
|
||||
if (user != null) {
|
||||
// En standby : pas encore de service d'envoi de mail. Quand disponible :
|
||||
// - generer un token de reset (table dédiée ou champ user avec expiration)
|
||||
// - appeler emailService.sendPasswordResetEmail(user.getEmail(), resetToken)
|
||||
// Pour l'instant, on retourne success pour ne pas reveler si l'email existe.
|
||||
LOG.info("Utilisateur trouve, email de reinitialisation (en standby - pas de mail service) : " + request.getEmail());
|
||||
} else {
|
||||
LOG.info("Aucun utilisateur trouve avec cet email (ne pas reveler) : " + request.getEmail());
|
||||
}
|
||||
// Le service gère tout : création du token et envoi de l'email
|
||||
passwordResetService.initiatePasswordReset(request.getEmail());
|
||||
|
||||
// Toujours retourner 200 pour ne pas reveler si l'email existe
|
||||
return Response.ok()
|
||||
@@ -326,6 +321,50 @@ public class UsersResource {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Endpoint pour réinitialiser le mot de passe avec un token valide.
|
||||
*
|
||||
* @param token Le token de réinitialisation reçu par email.
|
||||
* @param newPassword Le nouveau mot de passe.
|
||||
* @return Une réponse HTTP indiquant si la réinitialisation a réussi.
|
||||
*/
|
||||
@POST
|
||||
@Path("/reset-password")
|
||||
@Operation(summary = "Réinitialiser le mot de passe avec un token",
|
||||
description = "Réinitialise le mot de passe en utilisant le token reçu par email")
|
||||
@APIResponse(responseCode = "200", description = "Mot de passe réinitialisé avec succès")
|
||||
@APIResponse(responseCode = "400", description = "Token invalide ou expiré")
|
||||
public Response resetPasswordWithToken(
|
||||
@QueryParam("token") String token,
|
||||
@QueryParam("newPassword") String newPassword) {
|
||||
|
||||
if (token == null || token.isBlank()) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("message", "Le token est requis"))
|
||||
.build();
|
||||
}
|
||||
|
||||
if (newPassword == null || newPassword.length() < 8) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("message", "Le mot de passe doit contenir au moins 8 caractères"))
|
||||
.build();
|
||||
}
|
||||
|
||||
boolean success = passwordResetService.resetPassword(token, newPassword);
|
||||
|
||||
if (success) {
|
||||
LOG.info("Mot de passe réinitialisé avec succès via token");
|
||||
return Response.ok()
|
||||
.entity(Map.of("message", "Votre mot de passe a été réinitialisé avec succès"))
|
||||
.build();
|
||||
} else {
|
||||
LOG.warn("Échec de réinitialisation : token invalide ou expiré");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("message", "Le lien de réinitialisation est invalide ou a expiré"))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attribue un rôle à un utilisateur (réservé au super administrateur).
|
||||
* Requiert le header X-Super-Admin-Key correspondant à afterwork.super-admin.api-key.
|
||||
@@ -420,4 +459,51 @@ public class UsersResource {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Force l'activation ou la suspension d'un utilisateur (réservé au super administrateur).
|
||||
* Utilisé pour les managers : active = false = suspendu.
|
||||
*
|
||||
* @param id L'ID de l'utilisateur.
|
||||
* @param request Le DTO contenant active (true = forcer l'activation, false = suspendre).
|
||||
* @param apiKeyHeader Valeur du header X-Super-Admin-Key.
|
||||
* @return L'utilisateur mis à jour.
|
||||
*/
|
||||
@PATCH
|
||||
@Path("/{id}/active")
|
||||
@Transactional
|
||||
@Operation(summary = "Forcer activation ou suspendre un utilisateur (super admin)",
|
||||
description = "Modifie le statut actif (isActive) d'un utilisateur. Réservé au super administrateur (header X-Super-Admin-Key).")
|
||||
@APIResponse(responseCode = "200", description = "Statut actif mis à jour")
|
||||
@APIResponse(responseCode = "403", description = "Clé super admin invalide ou absente")
|
||||
@APIResponse(responseCode = "404", description = "Utilisateur non trouvé")
|
||||
public Response setUserActive(
|
||||
@PathParam("id") UUID id,
|
||||
@Valid SetUserActiveRequestDTO request,
|
||||
@HeaderParam(SUPER_ADMIN_KEY_HEADER) String apiKeyHeader) {
|
||||
|
||||
String key = superAdminApiKey.orElse("");
|
||||
if (key.isBlank()) {
|
||||
LOG.warn("Opération setUserActive refusée : afterwork.super-admin.api-key non configurée");
|
||||
return Response.status(Response.Status.FORBIDDEN)
|
||||
.entity("{\"message\": \"Opération non autorisée : clé super admin non configurée.\"}")
|
||||
.build();
|
||||
}
|
||||
if (apiKeyHeader == null || !apiKeyHeader.equals(key)) {
|
||||
LOG.warn("Opération setUserActive refusée : clé super admin invalide ou absente");
|
||||
return Response.status(Response.Status.FORBIDDEN)
|
||||
.entity("{\"message\": \"Clé super administrateur invalide ou absente.\"}")
|
||||
.build();
|
||||
}
|
||||
|
||||
try {
|
||||
Users user = userService.setUserActive(id, request.getActive());
|
||||
UserCreateResponseDTO responseDTO = new UserCreateResponseDTO(user);
|
||||
return Response.ok(responseDTO).build();
|
||||
} catch (UserNotFoundException e) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity("{\"message\": \"" + e.getMessage() + "\"}")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,10 +7,17 @@ import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Webhook Wave pour les notifications de paiement (payment.completed, payment.cancelled, etc.).
|
||||
* Si wave.webhook.secret est configuré, la requête doit contenir le header X-Wave-Signature (HMAC-SHA256 du body).
|
||||
*/
|
||||
@Path("/webhooks/wave")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@@ -18,15 +25,32 @@ import org.jboss.logging.Logger;
|
||||
public class WaveWebhookResource {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(WaveWebhookResource.class);
|
||||
private static final String SIGNATURE_HEADER = "X-Wave-Signature";
|
||||
|
||||
@Inject
|
||||
WavePaymentService wavePaymentService;
|
||||
|
||||
@ConfigProperty(name = "wave.webhook.secret", defaultValue = "")
|
||||
Optional<String> webhookSecret;
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
@POST
|
||||
public Response handleWebhook(String payload) {
|
||||
public Response handleWebhook(
|
||||
@HeaderParam(SIGNATURE_HEADER) String signature,
|
||||
String payload) {
|
||||
try {
|
||||
if (webhookSecret.isPresent() && !webhookSecret.get().isBlank()) {
|
||||
if (signature == null || signature.isBlank()) {
|
||||
LOG.warn("Webhook Wave rejeté : header " + SIGNATURE_HEADER + " absent");
|
||||
return Response.status(Response.Status.UNAUTHORIZED).build();
|
||||
}
|
||||
String expected = hmacSha256(webhookSecret.get(), payload);
|
||||
if (!constantTimeEquals(signature.trim(), expected)) {
|
||||
LOG.warn("Webhook Wave rejeté : signature invalide");
|
||||
return Response.status(Response.Status.UNAUTHORIZED).build();
|
||||
}
|
||||
}
|
||||
JsonNode node = objectMapper.readTree(payload);
|
||||
wavePaymentService.handleWebhook(node);
|
||||
return Response.ok().build();
|
||||
@@ -35,4 +59,28 @@ public class WaveWebhookResource {
|
||||
return Response.status(Response.Status.BAD_REQUEST).build();
|
||||
}
|
||||
}
|
||||
|
||||
private static String hmacSha256(String secret, String payload) {
|
||||
try {
|
||||
Mac mac = Mac.getInstance("HmacSHA256");
|
||||
mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
|
||||
byte[] hash = mac.doFinal(payload.getBytes(StandardCharsets.UTF_8));
|
||||
StringBuilder sb = new StringBuilder(hash.length * 2);
|
||||
for (byte b : hash) {
|
||||
sb.append(String.format("%02x", b));
|
||||
}
|
||||
return sb.toString();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("HMAC computation failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean constantTimeEquals(String a, String b) {
|
||||
if (a.length() != b.length()) return false;
|
||||
int diff = 0;
|
||||
for (int i = 0; i < a.length(); i++) {
|
||||
diff |= a.charAt(i) ^ b.charAt(i);
|
||||
}
|
||||
return diff == 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.lions.dev.service;
|
||||
|
||||
import com.lions.dev.dto.events.NotificationEvent;
|
||||
import com.lions.dev.dto.request.booking.ReservationCreateRequestDTO;
|
||||
import com.lions.dev.dto.response.booking.ReservationResponseDTO;
|
||||
import com.lions.dev.entity.booking.Booking;
|
||||
@@ -11,22 +12,35 @@ import com.lions.dev.repository.UsersRepository;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.transaction.Transactional;
|
||||
import org.eclipse.microprofile.reactive.messaging.Channel;
|
||||
import org.eclipse.microprofile.reactive.messaging.Emitter;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ApplicationScoped
|
||||
public class BookingService {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(BookingService.class);
|
||||
|
||||
@Inject
|
||||
BookingRepository bookingRepository;
|
||||
@Inject
|
||||
EstablishmentRepository establishmentRepository;
|
||||
@Inject
|
||||
UsersRepository usersRepository;
|
||||
@Inject
|
||||
EmailService emailService;
|
||||
|
||||
@Inject
|
||||
@Channel("notifications")
|
||||
Emitter<NotificationEvent> notificationEmitter;
|
||||
|
||||
private static final DateTimeFormatter ISO = DateTimeFormatter.ISO_DATE_TIME;
|
||||
|
||||
@@ -58,18 +72,121 @@ public class BookingService {
|
||||
booking.setStatus("PENDING");
|
||||
booking.setSpecialRequests(dto.getNotes());
|
||||
bookingRepository.persist(booking);
|
||||
|
||||
try {
|
||||
emailService.sendBookingConfirmationEmail(
|
||||
user.getEmail(),
|
||||
user.getFirstName(),
|
||||
establishment.getName(),
|
||||
booking.getReservationTime(),
|
||||
booking.getGuestCount() != null ? booking.getGuestCount() : 1
|
||||
);
|
||||
} catch (Exception e) {
|
||||
// Ne pas faire échouer la réservation si l'email échoue
|
||||
}
|
||||
|
||||
// Notifier le manager en temps réel
|
||||
notifyManagerOfNewBooking(booking, establishment, user);
|
||||
|
||||
return new ReservationResponseDTO(booking);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifie le manager d'un établissement d'une nouvelle réservation.
|
||||
*/
|
||||
private void notifyManagerOfNewBooking(Booking booking, Establishment establishment, Users customer) {
|
||||
try {
|
||||
Users manager = establishment.getManager();
|
||||
if (manager == null) return;
|
||||
|
||||
String customerName = customer.getFirstName() + " " + customer.getLastName();
|
||||
String dateStr = booking.getReservationTime() != null
|
||||
? booking.getReservationTime().format(DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm"))
|
||||
: "";
|
||||
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("bookingId", booking.getId().toString());
|
||||
data.put("establishmentId", establishment.getId().toString());
|
||||
data.put("establishmentName", establishment.getName());
|
||||
data.put("customerId", customer.getId().toString());
|
||||
data.put("customerName", customerName);
|
||||
data.put("customerEmail", customer.getEmail());
|
||||
data.put("reservationTime", dateStr);
|
||||
data.put("guestCount", booking.getGuestCount());
|
||||
data.put("specialRequests", booking.getSpecialRequests());
|
||||
data.put("status", booking.getStatus());
|
||||
|
||||
NotificationEvent event = new NotificationEvent(
|
||||
manager.getId().toString(),
|
||||
"booking_created",
|
||||
data
|
||||
);
|
||||
|
||||
notificationEmitter.send(event);
|
||||
LOG.debug("[BookingService] Notification nouvelle réservation envoyée au manager : " + manager.getId());
|
||||
} catch (Exception e) {
|
||||
LOG.warn("[BookingService] Échec notification Kafka réservation (non bloquant) : " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public ReservationResponseDTO cancelReservation(UUID id) {
|
||||
Booking booking = bookingRepository.findById(id);
|
||||
if (booking == null) return null;
|
||||
booking.setStatus("CANCELLED");
|
||||
bookingRepository.persist(booking);
|
||||
|
||||
// Notifier l'utilisateur et le manager
|
||||
notifyBookingCancellation(booking);
|
||||
|
||||
return new ReservationResponseDTO(booking);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifie l'utilisateur et le manager d'une annulation de réservation.
|
||||
*/
|
||||
private void notifyBookingCancellation(Booking booking) {
|
||||
try {
|
||||
Establishment establishment = booking.getEstablishment();
|
||||
Users customer = booking.getUser();
|
||||
if (establishment == null || customer == null) return;
|
||||
|
||||
String dateStr = booking.getReservationTime() != null
|
||||
? booking.getReservationTime().format(DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm"))
|
||||
: "";
|
||||
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("bookingId", booking.getId().toString());
|
||||
data.put("establishmentName", establishment.getName());
|
||||
data.put("reservationTime", dateStr);
|
||||
data.put("status", "CANCELLED");
|
||||
|
||||
// Notifier le client
|
||||
NotificationEvent customerEvent = new NotificationEvent(
|
||||
customer.getId().toString(),
|
||||
"booking_cancelled",
|
||||
data
|
||||
);
|
||||
notificationEmitter.send(customerEvent);
|
||||
|
||||
// Notifier le manager
|
||||
Users manager = establishment.getManager();
|
||||
if (manager != null) {
|
||||
data.put("customerName", customer.getFirstName() + " " + customer.getLastName());
|
||||
NotificationEvent managerEvent = new NotificationEvent(
|
||||
manager.getId().toString(),
|
||||
"booking_cancelled",
|
||||
data
|
||||
);
|
||||
notificationEmitter.send(managerEvent);
|
||||
}
|
||||
|
||||
LOG.debug("[BookingService] Notifications annulation envoyées");
|
||||
} catch (Exception e) {
|
||||
LOG.warn("[BookingService] Échec notification annulation (non bloquant) : " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void deleteReservation(UUID id) {
|
||||
bookingRepository.deleteById(id);
|
||||
|
||||
301
src/main/java/com/lions/dev/service/EmailService.java
Normal file
301
src/main/java/com/lions/dev/service/EmailService.java
Normal file
@@ -0,0 +1,301 @@
|
||||
package com.lions.dev.service;
|
||||
|
||||
import io.quarkus.mailer.Mail;
|
||||
import io.quarkus.mailer.Mailer;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Service d'envoi d'emails pour l'application AfterWork.
|
||||
*
|
||||
* Gère l'envoi des emails de réinitialisation de mot de passe,
|
||||
* notifications, et autres communications.
|
||||
*/
|
||||
@ApplicationScoped
|
||||
public class EmailService {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(EmailService.class);
|
||||
|
||||
@Inject
|
||||
Mailer mailer;
|
||||
|
||||
@ConfigProperty(name = "afterwork.app.name", defaultValue = "AfterWork")
|
||||
String appName;
|
||||
|
||||
@ConfigProperty(name = "afterwork.app.url", defaultValue = "https://afterwork.lions.dev")
|
||||
String appUrl;
|
||||
|
||||
@ConfigProperty(name = "afterwork.email.from", defaultValue = "noreply@lions.dev")
|
||||
String fromEmail;
|
||||
|
||||
/**
|
||||
* Envoie un email de réinitialisation de mot de passe.
|
||||
*
|
||||
* @param toEmail L'adresse email du destinataire
|
||||
* @param firstName Le prénom de l'utilisateur
|
||||
* @param resetToken Le token de réinitialisation
|
||||
*/
|
||||
public void sendPasswordResetEmail(String toEmail, String firstName, String resetToken) {
|
||||
String resetLink = appUrl + "/reset-password?token=" + resetToken;
|
||||
|
||||
String subject = appName + " - Réinitialisation de votre mot de passe";
|
||||
|
||||
String htmlContent = buildPasswordResetHtml(firstName, resetLink);
|
||||
String textContent = buildPasswordResetText(firstName, resetLink);
|
||||
|
||||
try {
|
||||
mailer.send(
|
||||
Mail.withHtml(toEmail, subject, htmlContent)
|
||||
.setText(textContent)
|
||||
);
|
||||
logger.info("Email de réinitialisation envoyé à: " + toEmail);
|
||||
} catch (Exception e) {
|
||||
logger.error("Erreur lors de l'envoi de l'email de réinitialisation à: " + toEmail, e);
|
||||
throw new RuntimeException("Impossible d'envoyer l'email de réinitialisation", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construit le contenu HTML de l'email de réinitialisation.
|
||||
*/
|
||||
private String buildPasswordResetHtml(String firstName, String resetLink) {
|
||||
return """
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
|
||||
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
|
||||
.header { background: linear-gradient(135deg, #667eea 0%%, #764ba2 100%%); padding: 30px; text-align: center; border-radius: 10px 10px 0 0; }
|
||||
.header h1 { color: white; margin: 0; }
|
||||
.content { background: #f9f9f9; padding: 30px; border-radius: 0 0 10px 10px; }
|
||||
.button { display: inline-block; background: #667eea; color: white; padding: 15px 30px; text-decoration: none; border-radius: 5px; margin: 20px 0; }
|
||||
.footer { text-align: center; margin-top: 20px; color: #888; font-size: 12px; }
|
||||
.warning { background: #fff3cd; border: 1px solid #ffc107; padding: 10px; border-radius: 5px; margin-top: 20px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h1>%s</h1>
|
||||
</div>
|
||||
<div class="content">
|
||||
<h2>Bonjour %s,</h2>
|
||||
<p>Vous avez demandé la réinitialisation de votre mot de passe.</p>
|
||||
<p>Cliquez sur le bouton ci-dessous pour créer un nouveau mot de passe :</p>
|
||||
<p style="text-align: center;">
|
||||
<a href="%s" class="button">Réinitialiser mon mot de passe</a>
|
||||
</p>
|
||||
<div class="warning">
|
||||
<strong>⚠️ Ce lien expire dans 1 heure.</strong>
|
||||
<p>Si vous n'avez pas demandé cette réinitialisation, ignorez cet email.</p>
|
||||
</div>
|
||||
<p>Si le bouton ne fonctionne pas, copiez ce lien dans votre navigateur :</p>
|
||||
<p style="word-break: break-all; color: #667eea;">%s</p>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<p>© 2025 %s - Tous droits réservés</p>
|
||||
<p>Cet email a été envoyé automatiquement, merci de ne pas y répondre.</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
""".formatted(appName, firstName, resetLink, resetLink, appName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construit le contenu texte de l'email de réinitialisation (fallback).
|
||||
*/
|
||||
private String buildPasswordResetText(String firstName, String resetLink) {
|
||||
return """
|
||||
Bonjour %s,
|
||||
|
||||
Vous avez demandé la réinitialisation de votre mot de passe sur %s.
|
||||
|
||||
Pour créer un nouveau mot de passe, cliquez sur ce lien :
|
||||
%s
|
||||
|
||||
⚠️ Ce lien expire dans 1 heure.
|
||||
|
||||
Si vous n'avez pas demandé cette réinitialisation, ignorez simplement cet email.
|
||||
|
||||
Cordialement,
|
||||
L'équipe %s
|
||||
""".formatted(firstName, appName, resetLink, appName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Envoie un email de bienvenue après inscription.
|
||||
*
|
||||
* @param toEmail L'adresse email du destinataire
|
||||
* @param firstName Le prénom de l'utilisateur
|
||||
*/
|
||||
public void sendWelcomeEmail(String toEmail, String firstName) {
|
||||
String subject = "Bienvenue sur " + appName + " !";
|
||||
|
||||
String htmlContent = """
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
|
||||
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
|
||||
.header { background: linear-gradient(135deg, #667eea 0%%, #764ba2 100%%); padding: 30px; text-align: center; border-radius: 10px 10px 0 0; }
|
||||
.header h1 { color: white; margin: 0; }
|
||||
.content { background: #f9f9f9; padding: 30px; border-radius: 0 0 10px 10px; }
|
||||
.button { display: inline-block; background: #667eea; color: white; padding: 15px 30px; text-decoration: none; border-radius: 5px; margin: 20px 0; }
|
||||
.footer { text-align: center; margin-top: 20px; color: #888; font-size: 12px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h1>Bienvenue sur %s !</h1>
|
||||
</div>
|
||||
<div class="content">
|
||||
<h2>Bonjour %s,</h2>
|
||||
<p>Nous sommes ravis de vous accueillir sur %s !</p>
|
||||
<p>Découvrez les meilleurs événements et établissements près de chez vous.</p>
|
||||
<p style="text-align: center;">
|
||||
<a href="%s" class="button">Découvrir l'application</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<p>© 2025 %s - Tous droits réservés</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
""".formatted(appName, firstName, appName, appUrl, appName);
|
||||
|
||||
try {
|
||||
mailer.send(Mail.withHtml(toEmail, subject, htmlContent));
|
||||
logger.info("Email de bienvenue envoyé à: " + toEmail);
|
||||
} catch (Exception e) {
|
||||
logger.error("Erreur lors de l'envoi de l'email de bienvenue à: " + toEmail, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirmation de paiement / facture (abonnement établissement Wave).
|
||||
*/
|
||||
public void sendPaymentConfirmationEmail(String toEmail, String firstName, String establishmentName, int amountXof, String plan) {
|
||||
String subject = appName + " - Confirmation de paiement - " + establishmentName;
|
||||
String amount = String.format("%d XOF", amountXof);
|
||||
String planLabel = "MONTHLY".equalsIgnoreCase(plan) ? "Mensuel" : "Annuel";
|
||||
String html = buildTransactionalHtml(
|
||||
"Paiement confirmé",
|
||||
"Bonjour " + firstName + ",",
|
||||
"Votre paiement pour l'abonnement <strong>" + planLabel + "</strong> de l'établissement <strong>" + establishmentName + "</strong> a bien été enregistré.",
|
||||
"Montant : " + amount + ".",
|
||||
"Votre établissement est actif sur " + appName + "."
|
||||
);
|
||||
sendSafe(toEmail, subject, html, "confirmation paiement");
|
||||
}
|
||||
|
||||
/**
|
||||
* Rappel d'événement (J-1 ou H-1).
|
||||
*/
|
||||
public void sendEventReminderEmail(String toEmail, String firstName, String eventTitle, java.time.LocalDateTime startDate) {
|
||||
String subject = appName + " - Rappel : " + eventTitle;
|
||||
String dateStr = startDate != null ? startDate.format(java.time.format.DateTimeFormatter.ofPattern("EEEE d MMMM à HH'h'mm")) : "";
|
||||
String html = buildTransactionalHtml(
|
||||
"Rappel événement",
|
||||
"Bonjour " + firstName + ",",
|
||||
"L'événement <strong>" + eventTitle + "</strong> commence bientôt.",
|
||||
"Date et heure : " + dateStr + ".",
|
||||
"Rendez-vous sur l'application pour plus de détails."
|
||||
);
|
||||
sendSafe(toEmail, subject, html, "rappel événement");
|
||||
}
|
||||
|
||||
/**
|
||||
* Avertissement expiration abonnement (envoi J-3 avant expiration).
|
||||
*/
|
||||
public void sendSubscriptionExpirationWarningEmail(String toEmail, String firstName, String establishmentName, java.time.LocalDateTime expiresAt) {
|
||||
String subject = appName + " - Votre abonnement expire bientôt - " + establishmentName;
|
||||
String dateStr = expiresAt != null ? expiresAt.format(java.time.format.DateTimeFormatter.ofPattern("d MMMM yyyy")) : "";
|
||||
String html = buildTransactionalHtml(
|
||||
"Abonnement bientôt expiré",
|
||||
"Bonjour " + firstName + ",",
|
||||
"L'abonnement de votre établissement <strong>" + establishmentName + "</strong> expire le <strong>" + dateStr + "</strong>.",
|
||||
"Renouvelez votre abonnement pour continuer à bénéficier des fonctionnalités.",
|
||||
"<a href=\"" + appUrl + "\" style=\"color:#667eea;\">Renouveler sur " + appName + "</a>"
|
||||
);
|
||||
sendSafe(toEmail, subject, html, "expiration abonnement");
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirmation de réservation (établissement).
|
||||
*/
|
||||
public void sendBookingConfirmationEmail(String toEmail, String firstName, String establishmentName, java.time.LocalDateTime reservationTime, int guestCount) {
|
||||
String subject = appName + " - Confirmation de réservation - " + establishmentName;
|
||||
String dateStr = reservationTime != null ? reservationTime.format(java.time.format.DateTimeFormatter.ofPattern("EEEE d MMMM à HH'h'mm")) : "";
|
||||
String html = buildTransactionalHtml(
|
||||
"Réservation confirmée",
|
||||
"Bonjour " + firstName + ",",
|
||||
"Votre réservation chez <strong>" + establishmentName + "</strong> est confirmée.",
|
||||
"Date et heure : " + dateStr + ". Nombre de convives : " + guestCount + ".",
|
||||
"À bientôt !"
|
||||
);
|
||||
sendSafe(toEmail, subject, html, "confirmation réservation");
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification échec paiement Wave (destiné au manager).
|
||||
*/
|
||||
public void sendPaymentFailureEmail(String toEmail, String firstName, String establishmentName) {
|
||||
String subject = appName + " - Échec du paiement - " + establishmentName;
|
||||
String html = buildTransactionalHtml(
|
||||
"Échec du paiement",
|
||||
"Bonjour " + firstName + ",",
|
||||
"Le paiement pour l'établissement <strong>" + establishmentName + "</strong> n'a pas abouti.",
|
||||
"Veuillez réessayer ou contacter le support si le problème persiste.",
|
||||
"<a href=\"" + appUrl + "\" style=\"color:#667eea;\">Réessayer sur " + appName + "</a>"
|
||||
);
|
||||
sendSafe(toEmail, subject, html, "échec paiement");
|
||||
}
|
||||
|
||||
private String buildTransactionalHtml(String title, String greeting, String paragraph1, String paragraph2, String paragraph3) {
|
||||
return """
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head><meta charset="UTF-8">
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
|
||||
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
|
||||
.header { background: linear-gradient(135deg, #667eea 0%%, #764ba2 100%%); padding: 20px; text-align: center; border-radius: 10px 10px 0 0; }
|
||||
.header h1 { color: white; margin: 0; font-size: 1.3em; }
|
||||
.content { background: #f9f9f9; padding: 25px; border-radius: 0 0 10px 10px; }
|
||||
.footer { text-align: center; margin-top: 15px; color: #888; font-size: 12px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header"><h1>%s</h1></div>
|
||||
<div class="content">
|
||||
<p>%s</p>
|
||||
<p>%s</p>
|
||||
<p>%s</p>
|
||||
<p>%s</p>
|
||||
</div>
|
||||
<div class="footer">© 2025 %s</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
""".formatted(title, greeting, paragraph1, paragraph2, paragraph3, appName);
|
||||
}
|
||||
|
||||
private void sendSafe(String toEmail, String subject, String htmlContent, String logLabel) {
|
||||
try {
|
||||
mailer.send(Mail.withHtml(toEmail, subject, htmlContent));
|
||||
logger.info("Email " + logLabel + " envoyé à: " + toEmail);
|
||||
} catch (Exception e) {
|
||||
logger.error("Erreur envoi email " + logLabel + " à " + toEmail, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,9 @@ public class EstablishmentRatingService {
|
||||
@Inject
|
||||
UsersRepository usersRepository;
|
||||
|
||||
@Inject
|
||||
com.lions.dev.service.NotificationService notificationService;
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(EstablishmentRatingService.class);
|
||||
|
||||
/**
|
||||
@@ -74,6 +77,24 @@ public class EstablishmentRatingService {
|
||||
// Mettre à jour les statistiques de l'établissement
|
||||
updateEstablishmentRatingStats(establishmentId);
|
||||
|
||||
// Notification au manager de l'établissement (déclencheur automatique)
|
||||
try {
|
||||
Users manager = establishment.getManager();
|
||||
if (manager != null && !manager.getId().equals(userId)) {
|
||||
String raterName = user.getFirstName() + " " + user.getLastName();
|
||||
notificationService.createNotification(
|
||||
"Nouvelle note",
|
||||
raterName + " a noté votre établissement « " + establishment.getName() + " » (" + requestDTO.getRating() + "/5)",
|
||||
"rating",
|
||||
manager.getId(),
|
||||
null
|
||||
);
|
||||
LOG.info("Notification nouvelle note envoyée au manager : " + manager.getId());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("Erreur création notification note établissement : " + e.getMessage());
|
||||
}
|
||||
|
||||
LOG.info("Note soumise avec succès : " + rating.getId());
|
||||
return rating;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
@@ -64,5 +66,63 @@ public class FileService {
|
||||
|
||||
return destinationPath;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Génère une miniature (thumbnail) à partir d'un fichier vidéo via FFmpeg.
|
||||
* Extrait une frame à 1 seconde. Si FFmpeg n'est pas disponible ou en cas d'erreur,
|
||||
* retourne Optional.empty() sans lever d'exception.
|
||||
*
|
||||
* @param videoPath Chemin absolu du fichier vidéo.
|
||||
* @return Chemin du fichier JPEG généré, ou empty si indisponible.
|
||||
*/
|
||||
public Optional<Path> generateVideoThumbnail(Path videoPath) {
|
||||
if (videoPath == null || !Files.exists(videoPath)) {
|
||||
LOG.warnf("[WARN] Génération thumbnail : fichier vidéo absent ou invalide : %s", videoPath);
|
||||
return Optional.empty();
|
||||
}
|
||||
String baseName = getBaseNameWithoutExtension(videoPath.getFileName().toString());
|
||||
Path parentDir = videoPath.getParent();
|
||||
String thumbFileName = baseName + "_thumb.jpg";
|
||||
Path thumbPath = parentDir != null ? parentDir.resolve(thumbFileName) : Paths.get(thumbFileName);
|
||||
ProcessBuilder pb = new ProcessBuilder(
|
||||
"ffmpeg",
|
||||
"-y",
|
||||
"-i", videoPath.toAbsolutePath().toString(),
|
||||
"-ss", "00:00:01",
|
||||
"-vframes", "1",
|
||||
"-q:v", "2",
|
||||
"-f", "image2",
|
||||
thumbPath.toAbsolutePath().toString()
|
||||
);
|
||||
pb.redirectErrorStream(true);
|
||||
try {
|
||||
Process process = pb.start();
|
||||
boolean finished = process.waitFor(30, TimeUnit.SECONDS);
|
||||
if (!finished) {
|
||||
process.destroyForcibly();
|
||||
LOG.warnf("[WARN] Génération thumbnail : timeout FFmpeg pour %s", videoPath);
|
||||
return Optional.empty();
|
||||
}
|
||||
if (process.exitValue() != 0) {
|
||||
LOG.warnf("[WARN] Génération thumbnail : FFmpeg a échoué (code %d) pour %s", process.exitValue(), videoPath);
|
||||
return Optional.empty();
|
||||
}
|
||||
if (Files.exists(thumbPath)) {
|
||||
LOG.infof("[LOG] Thumbnail vidéo généré : %s", thumbPath);
|
||||
return Optional.of(thumbPath);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOG.warnf("[WARN] FFmpeg non disponible ou erreur I/O pour thumbnail : %s", e.getMessage());
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
LOG.warnf("[WARN] Génération thumbnail interrompue pour %s", videoPath);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private static String getBaseNameWithoutExtension(String fileName) {
|
||||
if (fileName == null) return "video";
|
||||
int lastDot = fileName.lastIndexOf('.');
|
||||
return lastDot > 0 ? fileName.substring(0, lastDot) : fileName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,6 +90,21 @@ public class FriendshipService {
|
||||
Friendship friendship = new Friendship(user, friend, FriendshipStatus.PENDING);
|
||||
friendshipRepository.persist(friendship);
|
||||
|
||||
// Notification en base pour le destinataire (déclencheur automatique)
|
||||
try {
|
||||
String senderName = user.getFirstName() + " " + user.getLastName();
|
||||
notificationService.createNotification(
|
||||
"Demande d'amitié",
|
||||
senderName + " vous a envoyé une demande d'amitié",
|
||||
"friend",
|
||||
friend.getId(),
|
||||
null
|
||||
);
|
||||
logger.info("[LOG] Notification demande d'amitié créée pour : " + friend.getId());
|
||||
} catch (Exception e) {
|
||||
logger.error("[ERROR] Erreur création notification demande d'amitié : " + e.getMessage());
|
||||
}
|
||||
|
||||
// TEMPS RÉEL: Publier dans Kafka (v2.0)
|
||||
try {
|
||||
Map<String, Object> notificationData = new HashMap<>();
|
||||
@@ -113,7 +128,8 @@ public class FriendshipService {
|
||||
}
|
||||
|
||||
logger.info("[LOG] Demande d'amitié envoyée avec succès.");
|
||||
return new FriendshipCreateOneResponseDTO(friendship);
|
||||
// Construire le DTO avec les IDs déjà chargés pour éviter LazyInitializationException
|
||||
return new FriendshipCreateOneResponseDTO(friendship, user.getId(), friend.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -209,8 +225,10 @@ public class FriendshipService {
|
||||
logger.error("[ERROR] Erreur lors de la création des notifications d'amitié : " + e.getMessage());
|
||||
}
|
||||
|
||||
// Retourner la réponse avec les informations de la relation d'amitié
|
||||
return new FriendshipCreateOneResponseDTO(friendship);
|
||||
// Retourner la réponse avec les IDs déjà chargés (évite LazyInitializationException)
|
||||
Users user = friendship.getUser();
|
||||
Users friendUser = friendship.getFriend();
|
||||
return new FriendshipCreateOneResponseDTO(friendship, user.getId(), friendUser.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,6 +14,7 @@ import io.smallrye.reactive.messaging.kafka.api.OutgoingKafkaRecordMetadata;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.transaction.Transactional;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -25,12 +26,12 @@ import java.util.UUID;
|
||||
*
|
||||
* Ce service contient la logique métier pour l'envoi de messages,
|
||||
* la récupération de conversations, et la gestion des messages non lus.
|
||||
*
|
||||
* Tous les logs nécessaires pour la traçabilité sont intégrés.
|
||||
*/
|
||||
@ApplicationScoped
|
||||
public class MessageService {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(MessageService.class);
|
||||
|
||||
@Inject
|
||||
MessageRepository messageRepository;
|
||||
|
||||
@@ -65,7 +66,7 @@ public class MessageService {
|
||||
String content,
|
||||
String messageType,
|
||||
String mediaUrl) {
|
||||
System.out.println("[LOG] Envoi de message de " + senderId + " à " + recipientId);
|
||||
logger.info("[MessageService] Envoi de message de " + senderId + " à " + recipientId);
|
||||
|
||||
// Récupérer les utilisateurs
|
||||
Users sender = usersRepository.findById(senderId);
|
||||
@@ -91,7 +92,7 @@ public class MessageService {
|
||||
|
||||
// Persister le message
|
||||
messageRepository.persist(message);
|
||||
System.out.println("[LOG] Message créé avec l'ID : " + message.getId());
|
||||
logger.info("[MessageService] Message créé avec l'ID : " + message.getId());
|
||||
|
||||
// Mettre à jour la conversation
|
||||
conversation.updateLastMessage(message);
|
||||
@@ -112,9 +113,9 @@ public class MessageService {
|
||||
recipientId,
|
||||
null
|
||||
);
|
||||
System.out.println("[LOG] Notification créée pour le destinataire");
|
||||
logger.info("[MessageService] Notification créée pour le destinataire");
|
||||
} catch (Exception e) {
|
||||
System.out.println("[ERROR] Erreur lors de la création de la notification : " + e.getMessage());
|
||||
logger.error("[MessageService] Erreur lors de la création de la notification : " + e.getMessage());
|
||||
}
|
||||
|
||||
// TEMPS RÉEL : Publier dans Kafka (v2.0)
|
||||
@@ -149,12 +150,12 @@ public class MessageService {
|
||||
event,
|
||||
() -> java.util.concurrent.CompletableFuture.completedFuture(null), // ack
|
||||
throwable -> {
|
||||
System.out.println("[ERROR] Erreur envoi Kafka: " + throwable.getMessage());
|
||||
logger.error("[MessageService] Erreur envoi Kafka: " + throwable.getMessage());
|
||||
return java.util.concurrent.CompletableFuture.completedFuture(null); // nack
|
||||
}
|
||||
).addMetadata(kafkaMetadata));
|
||||
|
||||
System.out.println("[LOG] Message publié dans Kafka: " + message.getId());
|
||||
logger.info("[MessageService] Message publié dans Kafka: " + message.getId());
|
||||
|
||||
// Envoyer confirmation de délivrance à l'expéditeur (via Kafka aussi)
|
||||
try {
|
||||
@@ -181,9 +182,9 @@ public class MessageService {
|
||||
throwable -> java.util.concurrent.CompletableFuture.completedFuture(null)
|
||||
).addMetadata(deliveryKafkaMetadata));
|
||||
|
||||
System.out.println("[LOG] Confirmation de délivrance publiée dans Kafka pour : " + senderId);
|
||||
logger.info("[MessageService] Confirmation de délivrance publiée dans Kafka pour : " + senderId);
|
||||
} catch (Exception deliveryEx) {
|
||||
System.out.println("[ERROR] Erreur publication confirmation délivrance : " + deliveryEx.getMessage());
|
||||
logger.error("[MessageService] Erreur publication confirmation délivrance : " + deliveryEx.getMessage());
|
||||
// Ne pas bloquer si la confirmation échoue
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@@ -202,7 +203,7 @@ public class MessageService {
|
||||
* @throws UserNotFoundException Si l'utilisateur n'existe pas
|
||||
*/
|
||||
public List<Conversation> getUserConversations(UUID userId) {
|
||||
System.out.println("[LOG] Récupération des conversations pour l'utilisateur ID : " + userId);
|
||||
logger.info("[MessageService] Récupération des conversations pour l'utilisateur ID : " + userId);
|
||||
|
||||
Users user = usersRepository.findById(userId);
|
||||
if (user == null) {
|
||||
@@ -221,7 +222,7 @@ public class MessageService {
|
||||
* @return Liste paginée des messages
|
||||
*/
|
||||
public List<Message> getConversationMessages(UUID conversationId, int page, int size) {
|
||||
System.out.println("[LOG] Récupération des messages pour la conversation ID : " + conversationId);
|
||||
logger.info("[MessageService] Récupération des messages pour la conversation ID : " + conversationId);
|
||||
return messageRepository.findByConversationId(conversationId, page, size);
|
||||
}
|
||||
|
||||
@@ -232,7 +233,7 @@ public class MessageService {
|
||||
* @return La conversation
|
||||
*/
|
||||
public Conversation getConversation(UUID conversationId) {
|
||||
System.out.println("[LOG] Récupération de la conversation ID : " + conversationId);
|
||||
logger.info("[MessageService] Récupération de la conversation ID : " + conversationId);
|
||||
return conversationRepository.findById(conversationId);
|
||||
}
|
||||
|
||||
@@ -245,7 +246,7 @@ public class MessageService {
|
||||
* @throws UserNotFoundException Si l'un des utilisateurs n'existe pas
|
||||
*/
|
||||
public Conversation getConversationBetweenUsers(UUID user1Id, UUID user2Id) {
|
||||
System.out.println("[LOG] Recherche de conversation entre " + user1Id + " et " + user2Id);
|
||||
logger.info("[MessageService] Recherche de conversation entre " + user1Id + " et " + user2Id);
|
||||
|
||||
Users user1 = usersRepository.findById(user1Id);
|
||||
Users user2 = usersRepository.findById(user2Id);
|
||||
@@ -265,7 +266,7 @@ public class MessageService {
|
||||
*/
|
||||
@Transactional
|
||||
public Message markMessageAsRead(UUID messageId) {
|
||||
System.out.println("[LOG] Marquage du message comme lu : " + messageId);
|
||||
logger.info("[MessageService] Marquage du message comme lu : " + messageId);
|
||||
|
||||
Message message = messageRepository.findById(messageId);
|
||||
if (message == null) {
|
||||
@@ -309,12 +310,12 @@ public class MessageService {
|
||||
throwable -> java.util.concurrent.CompletableFuture.completedFuture(null)
|
||||
).addMetadata(readKafkaMetadata));
|
||||
|
||||
System.out.println("[LOG] Confirmation de lecture publiée dans Kafka pour : " + message.getSender().getId());
|
||||
logger.info("[MessageService] Confirmation de lecture publiée dans Kafka pour : " + message.getSender().getId());
|
||||
} catch (Exception e) {
|
||||
System.out.println("[ERROR] Erreur publication confirmation lecture : " + e.getMessage());
|
||||
logger.error("[MessageService] Erreur publication confirmation lecture : " + e.getMessage());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("[ERROR] Erreur envoi confirmation lecture : " + e.getMessage());
|
||||
logger.error("[MessageService] Erreur envoi confirmation lecture : " + e.getMessage());
|
||||
}
|
||||
|
||||
return message;
|
||||
@@ -329,7 +330,7 @@ public class MessageService {
|
||||
*/
|
||||
@Transactional
|
||||
public int markAllMessagesAsRead(UUID conversationId, UUID userId) {
|
||||
System.out.println("[LOG] Marquage de tous les messages comme lus pour la conversation " + conversationId);
|
||||
logger.info("[MessageService] Marquage de tous les messages comme lus pour la conversation " + conversationId);
|
||||
|
||||
Conversation conversation = conversationRepository.findById(conversationId);
|
||||
if (conversation == null) {
|
||||
@@ -358,7 +359,7 @@ public class MessageService {
|
||||
* @return Le nombre de messages non lus
|
||||
*/
|
||||
public long getTotalUnreadCount(UUID userId) {
|
||||
System.out.println("[LOG] Récupération du nombre total de messages non lus pour l'utilisateur " + userId);
|
||||
logger.info("[MessageService] Récupération du nombre total de messages non lus pour l'utilisateur " + userId);
|
||||
return conversationRepository.countTotalUnreadMessages(userId);
|
||||
}
|
||||
|
||||
@@ -370,7 +371,7 @@ public class MessageService {
|
||||
*/
|
||||
@Transactional
|
||||
public boolean deleteMessage(UUID messageId) {
|
||||
System.out.println("[LOG] Suppression du message ID : " + messageId);
|
||||
logger.info("[MessageService] Suppression du message ID : " + messageId);
|
||||
|
||||
Message message = messageRepository.findById(messageId);
|
||||
if (message == null) {
|
||||
@@ -389,7 +390,7 @@ public class MessageService {
|
||||
*/
|
||||
@Transactional
|
||||
public boolean deleteConversation(UUID conversationId) {
|
||||
System.out.println("[LOG] Suppression de la conversation ID : " + conversationId);
|
||||
logger.info("[MessageService] Suppression de la conversation ID : " + conversationId);
|
||||
|
||||
// Supprimer d'abord tous les messages
|
||||
messageRepository.deleteByConversationId(conversationId);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.lions.dev.service;
|
||||
|
||||
import com.lions.dev.dto.events.NotificationEvent;
|
||||
import com.lions.dev.entity.events.Events;
|
||||
import com.lions.dev.entity.notification.Notification;
|
||||
import com.lions.dev.entity.users.Users;
|
||||
@@ -10,20 +11,25 @@ import com.lions.dev.repository.UsersRepository;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.transaction.Transactional;
|
||||
import org.eclipse.microprofile.reactive.messaging.Channel;
|
||||
import org.eclipse.microprofile.reactive.messaging.Emitter;
|
||||
import org.jboss.logging.Logger;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Service de gestion des notifications.
|
||||
*
|
||||
*
|
||||
* Ce service contient la logique métier pour la création, récupération,
|
||||
* mise à jour et suppression des notifications.
|
||||
*
|
||||
* Tous les logs nécessaires pour la traçabilité sont intégrés.
|
||||
*/
|
||||
@ApplicationScoped
|
||||
public class NotificationService {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(NotificationService.class);
|
||||
|
||||
@Inject
|
||||
NotificationRepository notificationRepository;
|
||||
|
||||
@@ -33,6 +39,10 @@ public class NotificationService {
|
||||
@Inject
|
||||
EventsRepository eventsRepository;
|
||||
|
||||
@Inject
|
||||
@Channel("notifications")
|
||||
Emitter<NotificationEvent> notificationEmitter;
|
||||
|
||||
/**
|
||||
* Récupère toutes les notifications d'un utilisateur.
|
||||
*
|
||||
@@ -41,16 +51,16 @@ public class NotificationService {
|
||||
* @throws UserNotFoundException Si l'utilisateur n'existe pas
|
||||
*/
|
||||
public List<Notification> getNotificationsByUserId(UUID userId) {
|
||||
System.out.println("[LOG] Récupération des notifications pour l'utilisateur ID : " + userId);
|
||||
logger.info("[NotificationService] Récupération des notifications pour l'utilisateur ID : " + userId);
|
||||
|
||||
Users user = usersRepository.findById(userId);
|
||||
if (user == null) {
|
||||
System.out.println("[ERROR] Utilisateur non trouvé avec l'ID : " + userId);
|
||||
logger.error("[NotificationService] Utilisateur non trouvé avec l'ID : " + userId);
|
||||
throw new UserNotFoundException("Utilisateur non trouvé avec l'ID : " + userId);
|
||||
}
|
||||
|
||||
List<Notification> notifications = notificationRepository.findByUserId(userId);
|
||||
System.out.println("[LOG] " + notifications.size() + " notification(s) récupérée(s) pour l'utilisateur ID : " + userId);
|
||||
logger.info("[NotificationService] " + notifications.size() + " notification(s) récupérée(s) pour l'utilisateur ID : " + userId);
|
||||
return notifications;
|
||||
}
|
||||
|
||||
@@ -64,11 +74,11 @@ public class NotificationService {
|
||||
* @throws UserNotFoundException Si l'utilisateur n'existe pas
|
||||
*/
|
||||
public List<Notification> getNotificationsByUserIdWithPagination(UUID userId, int page, int size) {
|
||||
System.out.println("[LOG] Récupération paginée des notifications pour l'utilisateur ID : " + userId);
|
||||
logger.info("[NotificationService] Récupération paginée des notifications pour l'utilisateur ID : " + userId);
|
||||
|
||||
Users user = usersRepository.findById(userId);
|
||||
if (user == null) {
|
||||
System.out.println("[ERROR] Utilisateur non trouvé avec l'ID : " + userId);
|
||||
logger.error("[NotificationService] Utilisateur non trouvé avec l'ID : " + userId);
|
||||
throw new UserNotFoundException("Utilisateur non trouvé avec l'ID : " + userId);
|
||||
}
|
||||
|
||||
@@ -93,11 +103,11 @@ public class NotificationService {
|
||||
String type,
|
||||
UUID userId,
|
||||
UUID eventId) {
|
||||
System.out.println("[LOG] Création d'une notification : " + title + " pour l'utilisateur ID : " + userId);
|
||||
logger.info("[NotificationService] Création d'une notification : " + title + " pour l'utilisateur ID : " + userId);
|
||||
|
||||
Users user = usersRepository.findById(userId);
|
||||
if (user == null) {
|
||||
System.out.println("[ERROR] Utilisateur non trouvé avec l'ID : " + userId);
|
||||
logger.error("[NotificationService] Utilisateur non trouvé avec l'ID : " + userId);
|
||||
throw new UserNotFoundException("Utilisateur non trouvé avec l'ID : " + userId);
|
||||
}
|
||||
|
||||
@@ -107,15 +117,50 @@ public class NotificationService {
|
||||
Events event = eventsRepository.findById(eventId);
|
||||
if (event != null) {
|
||||
notification.setEvent(event);
|
||||
System.out.println("[LOG] Notification associée à l'événement ID : " + eventId);
|
||||
logger.info("[NotificationService] Notification associée à l'événement ID : " + eventId);
|
||||
}
|
||||
}
|
||||
|
||||
notificationRepository.persist(notification);
|
||||
System.out.println("[LOG] Notification créée avec succès : " + notification.getId());
|
||||
logger.info("[NotificationService] Notification créée avec succès : " + notification.getId());
|
||||
|
||||
// Publication temps réel via Kafka → WebSocket
|
||||
publishToKafka(notification, user);
|
||||
|
||||
return notification;
|
||||
}
|
||||
|
||||
/**
|
||||
* Publie une notification dans Kafka pour livraison temps réel via WebSocket.
|
||||
*/
|
||||
private void publishToKafka(Notification notification, Users user) {
|
||||
try {
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("notificationId", notification.getId().toString());
|
||||
data.put("title", notification.getTitle());
|
||||
data.put("message", notification.getMessage());
|
||||
data.put("isRead", notification.isRead());
|
||||
data.put("createdAt", notification.getCreatedAt() != null
|
||||
? notification.getCreatedAt().toString() : null);
|
||||
|
||||
if (notification.getEvent() != null) {
|
||||
data.put("eventId", notification.getEvent().getId().toString());
|
||||
data.put("eventTitle", notification.getEvent().getTitle());
|
||||
}
|
||||
|
||||
NotificationEvent event = new NotificationEvent(
|
||||
user.getId().toString(),
|
||||
notification.getType(),
|
||||
data
|
||||
);
|
||||
|
||||
notificationEmitter.send(event);
|
||||
logger.debug("[NotificationService] Notification publiée dans Kafka pour temps réel : " + notification.getId());
|
||||
} catch (Exception e) {
|
||||
logger.warn("[NotificationService] Échec publication Kafka (non bloquant) : " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marque une notification comme lue.
|
||||
*
|
||||
@@ -124,17 +169,17 @@ public class NotificationService {
|
||||
*/
|
||||
@Transactional
|
||||
public Notification markAsRead(UUID notificationId) {
|
||||
System.out.println("[LOG] Marquage de la notification ID : " + notificationId + " comme lue");
|
||||
logger.info("[NotificationService] Marquage de la notification ID : " + notificationId + " comme lue");
|
||||
|
||||
Notification notification = notificationRepository.findById(notificationId);
|
||||
if (notification == null) {
|
||||
System.out.println("[ERROR] Notification non trouvée avec l'ID : " + notificationId);
|
||||
logger.error("[NotificationService] Notification non trouvée avec l'ID : " + notificationId);
|
||||
throw new IllegalArgumentException("Notification non trouvée avec l'ID : " + notificationId);
|
||||
}
|
||||
|
||||
notification.markAsRead();
|
||||
notificationRepository.persist(notification);
|
||||
System.out.println("[LOG] Notification marquée comme lue avec succès");
|
||||
logger.info("[NotificationService] Notification marquée comme lue avec succès");
|
||||
return notification;
|
||||
}
|
||||
|
||||
@@ -147,16 +192,16 @@ public class NotificationService {
|
||||
*/
|
||||
@Transactional
|
||||
public int markAllAsRead(UUID userId) {
|
||||
System.out.println("[LOG] Marquage de toutes les notifications comme lues pour l'utilisateur ID : " + userId);
|
||||
logger.info("[NotificationService] Marquage de toutes les notifications comme lues pour l'utilisateur ID : " + userId);
|
||||
|
||||
Users user = usersRepository.findById(userId);
|
||||
if (user == null) {
|
||||
System.out.println("[ERROR] Utilisateur non trouvé avec l'ID : " + userId);
|
||||
logger.error("[NotificationService] Utilisateur non trouvé avec l'ID : " + userId);
|
||||
throw new UserNotFoundException("Utilisateur non trouvé avec l'ID : " + userId);
|
||||
}
|
||||
|
||||
int updated = notificationRepository.markAllAsReadByUserId(userId);
|
||||
System.out.println("[LOG] " + updated + " notification(s) marquée(s) comme lue(s)");
|
||||
logger.info("[NotificationService] " + updated + " notification(s) marquée(s) comme lue(s)");
|
||||
return updated;
|
||||
}
|
||||
|
||||
@@ -168,16 +213,16 @@ public class NotificationService {
|
||||
*/
|
||||
@Transactional
|
||||
public boolean deleteNotification(UUID notificationId) {
|
||||
System.out.println("[LOG] Suppression de la notification ID : " + notificationId);
|
||||
logger.info("[NotificationService] Suppression de la notification ID : " + notificationId);
|
||||
|
||||
Notification notification = notificationRepository.findById(notificationId);
|
||||
if (notification == null) {
|
||||
System.out.println("[ERROR] Notification non trouvée avec l'ID : " + notificationId);
|
||||
logger.error("[NotificationService] Notification non trouvée avec l'ID : " + notificationId);
|
||||
return false;
|
||||
}
|
||||
|
||||
notificationRepository.delete(notification);
|
||||
System.out.println("[LOG] Notification supprimée avec succès");
|
||||
logger.info("[NotificationService] Notification supprimée avec succès");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -188,11 +233,11 @@ public class NotificationService {
|
||||
* @return La notification trouvée
|
||||
*/
|
||||
public Notification getNotificationById(UUID notificationId) {
|
||||
System.out.println("[LOG] Récupération de la notification ID : " + notificationId);
|
||||
logger.info("[NotificationService] Récupération de la notification ID : " + notificationId);
|
||||
|
||||
Notification notification = notificationRepository.findById(notificationId);
|
||||
if (notification == null) {
|
||||
System.out.println("[ERROR] Notification non trouvée avec l'ID : " + notificationId);
|
||||
logger.error("[NotificationService] Notification non trouvée avec l'ID : " + notificationId);
|
||||
throw new IllegalArgumentException("Notification non trouvée avec l'ID : " + notificationId);
|
||||
}
|
||||
|
||||
|
||||
133
src/main/java/com/lions/dev/service/PasswordResetService.java
Normal file
133
src/main/java/com/lions/dev/service/PasswordResetService.java
Normal file
@@ -0,0 +1,133 @@
|
||||
package com.lions.dev.service;
|
||||
|
||||
import com.lions.dev.entity.auth.PasswordResetToken;
|
||||
import com.lions.dev.entity.users.Users;
|
||||
import com.lions.dev.repository.PasswordResetTokenRepository;
|
||||
import com.lions.dev.repository.UsersRepository;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.transaction.Transactional;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Service de gestion de la réinitialisation de mot de passe.
|
||||
*
|
||||
* Coordonne la création de tokens, l'envoi d'emails et la validation.
|
||||
*/
|
||||
@ApplicationScoped
|
||||
public class PasswordResetService {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(PasswordResetService.class);
|
||||
|
||||
@Inject
|
||||
PasswordResetTokenRepository tokenRepository;
|
||||
|
||||
@Inject
|
||||
UsersRepository usersRepository;
|
||||
|
||||
@Inject
|
||||
EmailService emailService;
|
||||
|
||||
/**
|
||||
* Initie le processus de réinitialisation de mot de passe.
|
||||
* Crée un token et envoie un email à l'utilisateur.
|
||||
*
|
||||
* @param email L'email de l'utilisateur
|
||||
* @return true si l'email a été envoyé (ou simulé), false si l'utilisateur n'existe pas
|
||||
*/
|
||||
@Transactional
|
||||
public boolean initiatePasswordReset(String email) {
|
||||
logger.info("Initiation de réinitialisation de mot de passe pour: " + email);
|
||||
|
||||
// Rechercher l'utilisateur
|
||||
Optional<Users> userOpt = usersRepository.findByEmail(email);
|
||||
if (userOpt.isEmpty()) {
|
||||
logger.info("Aucun utilisateur trouvé avec l'email: " + email);
|
||||
return false;
|
||||
}
|
||||
|
||||
Users user = userOpt.get();
|
||||
|
||||
// Invalider les tokens existants pour cet utilisateur
|
||||
tokenRepository.invalidateAllForUser(user);
|
||||
|
||||
// Créer un nouveau token
|
||||
PasswordResetToken token = new PasswordResetToken(user);
|
||||
tokenRepository.persist(token);
|
||||
|
||||
logger.info("Token de réinitialisation créé pour: " + email + " (expire: " + token.getExpiresAt() + ")");
|
||||
|
||||
// Envoyer l'email
|
||||
try {
|
||||
emailService.sendPasswordResetEmail(user.getEmail(), user.getFirstName(), token.getToken());
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
logger.error("Erreur lors de l'envoi de l'email de réinitialisation", e);
|
||||
// On ne révèle pas l'erreur à l'utilisateur pour des raisons de sécurité
|
||||
return true; // Retourne true quand même pour ne pas révéler si l'email existe
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Valide un token de réinitialisation.
|
||||
*
|
||||
* @param tokenValue La valeur du token
|
||||
* @return Le token s'il est valide, empty sinon
|
||||
*/
|
||||
public Optional<PasswordResetToken> validateToken(String tokenValue) {
|
||||
Optional<PasswordResetToken> tokenOpt = tokenRepository.findValidToken(tokenValue);
|
||||
|
||||
if (tokenOpt.isEmpty()) {
|
||||
logger.warn("Token de réinitialisation invalide ou expiré: " + tokenValue.substring(0, 8) + "...");
|
||||
}
|
||||
|
||||
return tokenOpt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Réinitialise le mot de passe avec un token valide.
|
||||
*
|
||||
* @param tokenValue La valeur du token
|
||||
* @param newPassword Le nouveau mot de passe
|
||||
* @return true si le mot de passe a été réinitialisé, false sinon
|
||||
*/
|
||||
@Transactional
|
||||
public boolean resetPassword(String tokenValue, String newPassword) {
|
||||
Optional<PasswordResetToken> tokenOpt = tokenRepository.findValidToken(tokenValue);
|
||||
|
||||
if (tokenOpt.isEmpty()) {
|
||||
logger.warn("Tentative de réinitialisation avec un token invalide");
|
||||
return false;
|
||||
}
|
||||
|
||||
PasswordResetToken token = tokenOpt.get();
|
||||
Users user = token.getUser();
|
||||
|
||||
// Mettre à jour le mot de passe
|
||||
user.setPassword(newPassword);
|
||||
usersRepository.persist(user);
|
||||
|
||||
// Marquer le token comme utilisé
|
||||
token.markAsUsed();
|
||||
tokenRepository.persist(token);
|
||||
|
||||
// Invalider tous les autres tokens pour cet utilisateur
|
||||
tokenRepository.invalidateAllForUser(user);
|
||||
|
||||
logger.info("Mot de passe réinitialisé avec succès pour: " + user.getEmail());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Nettoie les tokens expirés (à appeler via un scheduler).
|
||||
*
|
||||
* @return Le nombre de tokens supprimés
|
||||
*/
|
||||
@Transactional
|
||||
public long cleanupExpiredTokens() {
|
||||
return tokenRepository.deleteExpiredTokens();
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,7 @@ public class PresenceService {
|
||||
// Broadcast présence aux autres utilisateurs
|
||||
broadcastPresenceToAll(userId, true, user.getLastSeen());
|
||||
|
||||
System.out.println("[PRESENCE] Utilisateur " + userId + " marqué online");
|
||||
Log.debug("[PRESENCE] Utilisateur " + userId + " marqué online");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ public class PresenceService {
|
||||
// Broadcast présence aux autres utilisateurs
|
||||
broadcastPresenceToAll(userId, false, user.getLastSeen());
|
||||
|
||||
System.out.println("[PRESENCE] Utilisateur " + userId + " marqué offline");
|
||||
Log.debug("[PRESENCE] Utilisateur " + userId + " marqué offline");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ public class PresenceService {
|
||||
if (user != null) {
|
||||
user.updatePresence();
|
||||
usersRepository.persist(user);
|
||||
System.out.println("[PRESENCE] Heartbeat reçu pour utilisateur " + userId);
|
||||
Log.debug("[PRESENCE] Heartbeat reçu pour utilisateur " + userId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.lions.dev.service;
|
||||
|
||||
import com.lions.dev.entity.friends.Friendship;
|
||||
import com.lions.dev.entity.friends.FriendshipStatus;
|
||||
import com.lions.dev.entity.social.SocialPost;
|
||||
import com.lions.dev.entity.users.Users;
|
||||
import com.lions.dev.exception.UserNotFoundException;
|
||||
@@ -14,6 +13,7 @@ import org.eclipse.microprofile.reactive.messaging.Emitter;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.transaction.Transactional;
|
||||
import org.jboss.logging.Logger;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -22,15 +22,15 @@ import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Service de gestion des posts sociaux.
|
||||
*
|
||||
*
|
||||
* Ce service contient la logique métier pour la création, récupération,
|
||||
* mise à jour et suppression des posts sociaux.
|
||||
*
|
||||
* Tous les logs nécessaires pour la traçabilité sont intégrés.
|
||||
*/
|
||||
@ApplicationScoped
|
||||
public class SocialPostService {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(SocialPostService.class);
|
||||
|
||||
@Inject
|
||||
SocialPostRepository socialPostRepository;
|
||||
|
||||
@@ -55,7 +55,7 @@ public class SocialPostService {
|
||||
* @return Liste paginée des posts
|
||||
*/
|
||||
public List<SocialPost> getAllPosts(int page, int size) {
|
||||
System.out.println("[LOG] Récupération de tous les posts (page: " + page + ", size: " + size + ")");
|
||||
logger.info("[SocialPostService] Récupération de tous les posts (page: " + page + ", size: " + size + ")");
|
||||
return socialPostRepository.findAllWithPagination(page, size);
|
||||
}
|
||||
|
||||
@@ -67,11 +67,11 @@ public class SocialPostService {
|
||||
* @throws UserNotFoundException Si l'utilisateur n'existe pas
|
||||
*/
|
||||
public List<SocialPost> getPostsByUserId(UUID userId) {
|
||||
System.out.println("[LOG] Récupération des posts pour l'utilisateur ID : " + userId);
|
||||
logger.info("[SocialPostService] Récupération des posts pour l'utilisateur ID : " + userId);
|
||||
|
||||
Users user = usersRepository.findById(userId);
|
||||
if (user == null) {
|
||||
System.out.println("[ERROR] Utilisateur non trouvé avec l'ID : " + userId);
|
||||
logger.error("[SocialPostService] Utilisateur non trouvé avec l'ID : " + userId);
|
||||
throw new UserNotFoundException("Utilisateur non trouvé avec l'ID : " + userId);
|
||||
}
|
||||
|
||||
@@ -89,11 +89,11 @@ public class SocialPostService {
|
||||
*/
|
||||
@Transactional
|
||||
public SocialPost createPost(String content, UUID userId, String imageUrl) {
|
||||
System.out.println("[LOG] Création d'un post par l'utilisateur ID : " + userId);
|
||||
logger.info("[SocialPostService] Création d'un post par l'utilisateur ID : " + userId);
|
||||
|
||||
Users user = usersRepository.findById(userId);
|
||||
if (user == null) {
|
||||
System.out.println("[ERROR] Utilisateur non trouvé avec l'ID : " + userId);
|
||||
logger.error("[SocialPostService] Utilisateur non trouvé avec l'ID : " + userId);
|
||||
throw new UserNotFoundException("Utilisateur non trouvé avec l'ID : " + userId);
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ public class SocialPostService {
|
||||
}
|
||||
|
||||
socialPostRepository.persist(post);
|
||||
System.out.println("[LOG] Post créé avec succès : " + post.getId());
|
||||
logger.info("[SocialPostService] Post créé avec succès : " + post.getId());
|
||||
|
||||
// Créer des notifications pour tous les amis
|
||||
try {
|
||||
@@ -128,9 +128,9 @@ public class SocialPostService {
|
||||
null
|
||||
);
|
||||
}
|
||||
System.out.println("[LOG] Notifications créées pour " + friendships.size() + " ami(s)");
|
||||
logger.info("[SocialPostService] Notifications créées pour " + friendships.size() + " ami(s)");
|
||||
} catch (Exception e) {
|
||||
System.out.println("[ERROR] Erreur lors de la création des notifications : " + e.getMessage());
|
||||
logger.error("[SocialPostService] Erreur lors de la création des notifications : " + e.getMessage());
|
||||
}
|
||||
|
||||
return post;
|
||||
@@ -143,11 +143,11 @@ public class SocialPostService {
|
||||
* @return Le post trouvé
|
||||
*/
|
||||
public SocialPost getPostById(UUID postId) {
|
||||
System.out.println("[LOG] Récupération du post ID : " + postId);
|
||||
logger.info("[SocialPostService] Récupération du post ID : " + postId);
|
||||
|
||||
SocialPost post = socialPostRepository.findById(postId);
|
||||
if (post == null) {
|
||||
System.out.println("[ERROR] Post non trouvé avec l'ID : " + postId);
|
||||
logger.error("[SocialPostService] Post non trouvé avec l'ID : " + postId);
|
||||
throw new IllegalArgumentException("Post non trouvé avec l'ID : " + postId);
|
||||
}
|
||||
|
||||
@@ -164,11 +164,11 @@ public class SocialPostService {
|
||||
*/
|
||||
@Transactional
|
||||
public SocialPost updatePost(UUID postId, String content, String imageUrl) {
|
||||
System.out.println("[LOG] Mise à jour du post ID : " + postId);
|
||||
logger.info("[SocialPostService] Mise à jour du post ID : " + postId);
|
||||
|
||||
SocialPost post = socialPostRepository.findById(postId);
|
||||
if (post == null) {
|
||||
System.out.println("[ERROR] Post non trouvé avec l'ID : " + postId);
|
||||
logger.error("[SocialPostService] Post non trouvé avec l'ID : " + postId);
|
||||
throw new IllegalArgumentException("Post non trouvé avec l'ID : " + postId);
|
||||
}
|
||||
|
||||
@@ -178,7 +178,7 @@ public class SocialPostService {
|
||||
}
|
||||
|
||||
socialPostRepository.persist(post);
|
||||
System.out.println("[LOG] Post mis à jour avec succès");
|
||||
logger.info("[SocialPostService] Post mis à jour avec succès");
|
||||
return post;
|
||||
}
|
||||
|
||||
@@ -190,16 +190,16 @@ public class SocialPostService {
|
||||
*/
|
||||
@Transactional
|
||||
public boolean deletePost(UUID postId) {
|
||||
System.out.println("[LOG] Suppression du post ID : " + postId);
|
||||
logger.info("[SocialPostService] Suppression du post ID : " + postId);
|
||||
|
||||
SocialPost post = socialPostRepository.findById(postId);
|
||||
if (post == null) {
|
||||
System.out.println("[ERROR] Post non trouvé avec l'ID : " + postId);
|
||||
logger.error("[SocialPostService] Post non trouvé avec l'ID : " + postId);
|
||||
return false;
|
||||
}
|
||||
|
||||
socialPostRepository.delete(post);
|
||||
System.out.println("[LOG] Post supprimé avec succès");
|
||||
logger.info("[SocialPostService] Post supprimé avec succès");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -210,7 +210,7 @@ public class SocialPostService {
|
||||
* @return Liste des posts correspondant à la recherche
|
||||
*/
|
||||
public List<SocialPost> searchPosts(String query) {
|
||||
System.out.println("[LOG] Recherche de posts avec la requête : " + query);
|
||||
logger.info("[SocialPostService] Recherche de posts avec la requête : " + query);
|
||||
return socialPostRepository.searchByContent(query);
|
||||
}
|
||||
|
||||
@@ -223,17 +223,35 @@ public class SocialPostService {
|
||||
*/
|
||||
@Transactional
|
||||
public SocialPost likePost(UUID postId, UUID userId) {
|
||||
System.out.println("[LOG] Like du post ID : " + postId + " par utilisateur : " + userId);
|
||||
logger.info("[SocialPostService] Like du post ID : " + postId + " par utilisateur : " + userId);
|
||||
|
||||
SocialPost post = socialPostRepository.findById(postId);
|
||||
if (post == null) {
|
||||
System.out.println("[ERROR] Post non trouvé avec l'ID : " + postId);
|
||||
logger.error("[SocialPostService] Post non trouvé avec l'ID : " + postId);
|
||||
throw new IllegalArgumentException("Post non trouvé avec l'ID : " + postId);
|
||||
}
|
||||
|
||||
post.incrementLikes();
|
||||
socialPostRepository.persist(post);
|
||||
|
||||
// Notification pour l'auteur du post (sauf auto-like)
|
||||
try {
|
||||
Users author = post.getUser();
|
||||
if (author != null && !author.getId().equals(userId)) {
|
||||
Users liker = usersRepository.findById(userId);
|
||||
String likerName = liker != null ? liker.getFirstName() + " " + liker.getLastName() : "Quelqu'un";
|
||||
notificationService.createNotification(
|
||||
"Nouveau like",
|
||||
likerName + " a aimé votre post",
|
||||
"post",
|
||||
author.getId(),
|
||||
null
|
||||
);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("[SocialPostService] Erreur création notification like : " + e.getMessage());
|
||||
}
|
||||
|
||||
// TEMPS RÉEL: Publier dans Kafka (v2.0)
|
||||
try {
|
||||
Map<String, Object> reactionData = new HashMap<>();
|
||||
@@ -252,9 +270,9 @@ public class SocialPostService {
|
||||
);
|
||||
|
||||
reactionEmitter.send(event);
|
||||
System.out.println("[LOG] Réaction like publiée dans Kafka pour post: " + postId);
|
||||
logger.info("[SocialPostService] Réaction like publiée dans Kafka pour post: " + postId);
|
||||
} catch (Exception e) {
|
||||
System.out.println("[ERROR] Erreur publication Kafka: " + e.getMessage());
|
||||
logger.error("[SocialPostService] Erreur publication Kafka: " + e.getMessage());
|
||||
// Ne pas bloquer le like si Kafka échoue
|
||||
}
|
||||
|
||||
@@ -271,17 +289,38 @@ public class SocialPostService {
|
||||
*/
|
||||
@Transactional
|
||||
public SocialPost addComment(UUID postId, UUID userId, String commentContent) {
|
||||
System.out.println("[LOG] Ajout de commentaire au post ID : " + postId + " par utilisateur : " + userId);
|
||||
logger.info("[SocialPostService] Ajout de commentaire au post ID : " + postId + " par utilisateur : " + userId);
|
||||
|
||||
SocialPost post = socialPostRepository.findById(postId);
|
||||
if (post == null) {
|
||||
System.out.println("[ERROR] Post non trouvé avec l'ID : " + postId);
|
||||
logger.error("[SocialPostService] Post non trouvé avec l'ID : " + postId);
|
||||
throw new IllegalArgumentException("Post non trouvé avec l'ID : " + postId);
|
||||
}
|
||||
|
||||
post.incrementComments();
|
||||
socialPostRepository.persist(post);
|
||||
|
||||
// Notification pour l'auteur du post (sauf auto-commentaire)
|
||||
try {
|
||||
Users author = post.getUser();
|
||||
if (author != null && !author.getId().equals(userId)) {
|
||||
Users commenter = usersRepository.findById(userId);
|
||||
String commenterName = commenter != null ? commenter.getFirstName() + " " + commenter.getLastName() : "Quelqu'un";
|
||||
String preview = commentContent != null && commentContent.length() > 60
|
||||
? commentContent.substring(0, 60) + "..."
|
||||
: (commentContent != null ? commentContent : "");
|
||||
notificationService.createNotification(
|
||||
"Nouveau commentaire",
|
||||
commenterName + " a commenté votre post : " + preview,
|
||||
"post",
|
||||
author.getId(),
|
||||
null
|
||||
);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("[SocialPostService] Erreur création notification commentaire : " + e.getMessage());
|
||||
}
|
||||
|
||||
// TEMPS RÉEL: Publier dans Kafka (v2.0)
|
||||
try {
|
||||
Users commenter = usersRepository.findById(userId);
|
||||
@@ -304,9 +343,9 @@ public class SocialPostService {
|
||||
);
|
||||
|
||||
reactionEmitter.send(event);
|
||||
System.out.println("[LOG] Réaction comment publiée dans Kafka pour post: " + postId);
|
||||
logger.info("[SocialPostService] Réaction comment publiée dans Kafka pour post: " + postId);
|
||||
} catch (Exception e) {
|
||||
System.out.println("[ERROR] Erreur publication Kafka: " + e.getMessage());
|
||||
logger.error("[SocialPostService] Erreur publication Kafka: " + e.getMessage());
|
||||
// Ne pas bloquer le commentaire si Kafka échoue
|
||||
}
|
||||
|
||||
@@ -322,11 +361,11 @@ public class SocialPostService {
|
||||
*/
|
||||
@Transactional
|
||||
public SocialPost sharePost(UUID postId, UUID userId) {
|
||||
System.out.println("[LOG] Partage du post ID : " + postId + " par utilisateur : " + userId);
|
||||
logger.info("[SocialPostService] Partage du post ID : " + postId + " par utilisateur : " + userId);
|
||||
|
||||
SocialPost post = socialPostRepository.findById(postId);
|
||||
if (post == null) {
|
||||
System.out.println("[ERROR] Post non trouvé avec l'ID : " + postId);
|
||||
logger.error("[SocialPostService] Post non trouvé avec l'ID : " + postId);
|
||||
throw new IllegalArgumentException("Post non trouvé avec l'ID : " + postId);
|
||||
}
|
||||
|
||||
@@ -348,9 +387,9 @@ public class SocialPostService {
|
||||
);
|
||||
|
||||
reactionEmitter.send(event);
|
||||
System.out.println("[LOG] Réaction share publiée dans Kafka pour post: " + postId);
|
||||
logger.info("[SocialPostService] Réaction share publiée dans Kafka pour post: " + postId);
|
||||
} catch (Exception e) {
|
||||
System.out.println("[ERROR] Erreur publication Kafka: " + e.getMessage());
|
||||
logger.error("[SocialPostService] Erreur publication Kafka: " + e.getMessage());
|
||||
// Ne pas bloquer le partage si Kafka échoue
|
||||
}
|
||||
|
||||
@@ -367,11 +406,11 @@ public class SocialPostService {
|
||||
* @throws UserNotFoundException Si l'utilisateur n'existe pas
|
||||
*/
|
||||
public List<SocialPost> getPostsByFriends(UUID userId, int page, int size) {
|
||||
System.out.println("[LOG] Récupération des posts des amis pour l'utilisateur ID : " + userId);
|
||||
logger.info("[SocialPostService] Récupération des posts des amis pour l'utilisateur ID : " + userId);
|
||||
|
||||
Users user = usersRepository.findById(userId);
|
||||
if (user == null) {
|
||||
System.out.println("[ERROR] Utilisateur non trouvé avec l'ID : " + userId);
|
||||
logger.error("[SocialPostService] Utilisateur non trouvé avec l'ID : " + userId);
|
||||
throw new UserNotFoundException("Utilisateur non trouvé avec l'ID : " + userId);
|
||||
}
|
||||
|
||||
@@ -389,7 +428,7 @@ public class SocialPostService {
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
System.out.println("[LOG] " + friendIds.size() + " ami(s) trouvé(s) pour l'utilisateur ID : " + userId);
|
||||
logger.info("[SocialPostService] " + friendIds.size() + " ami(s) trouvé(s) pour l'utilisateur ID : " + userId);
|
||||
|
||||
// Récupérer les posts de l'utilisateur et de ses amis
|
||||
return socialPostRepository.findPostsByFriends(userId, friendIds, page, size);
|
||||
|
||||
@@ -1,15 +1,24 @@
|
||||
package com.lions.dev.service;
|
||||
|
||||
import com.lions.dev.dto.events.NotificationEvent;
|
||||
import com.lions.dev.entity.friends.Friendship;
|
||||
import com.lions.dev.entity.friends.FriendshipStatus;
|
||||
import com.lions.dev.entity.story.MediaType;
|
||||
import com.lions.dev.entity.story.Story;
|
||||
import com.lions.dev.entity.users.Users;
|
||||
import com.lions.dev.exception.UserNotFoundException;
|
||||
import com.lions.dev.repository.FriendshipRepository;
|
||||
import com.lions.dev.repository.StoryRepository;
|
||||
import com.lions.dev.repository.UsersRepository;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.transaction.Transactional;
|
||||
import org.eclipse.microprofile.reactive.messaging.Channel;
|
||||
import org.eclipse.microprofile.reactive.messaging.Emitter;
|
||||
import org.jboss.logging.Logger;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
@@ -17,18 +26,25 @@ import java.util.UUID;
|
||||
*
|
||||
* Ce service contient la logique métier pour la création, récupération,
|
||||
* mise à jour et suppression des stories.
|
||||
*
|
||||
* Tous les logs nécessaires pour la traçabilité sont intégrés.
|
||||
*/
|
||||
@ApplicationScoped
|
||||
public class StoryService {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(StoryService.class);
|
||||
|
||||
@Inject
|
||||
StoryRepository storyRepository;
|
||||
|
||||
@Inject
|
||||
UsersRepository usersRepository;
|
||||
|
||||
@Inject
|
||||
FriendshipRepository friendshipRepository;
|
||||
|
||||
@Inject
|
||||
@Channel("notifications")
|
||||
Emitter<NotificationEvent> notificationEmitter;
|
||||
|
||||
/**
|
||||
* Récupère toutes les stories actives (non expirées).
|
||||
*
|
||||
@@ -46,7 +62,7 @@ public class StoryService {
|
||||
* @return Liste paginée des stories actives
|
||||
*/
|
||||
public List<Story> getAllActiveStories(int page, int size) {
|
||||
System.out.println("[LOG] Récupération de toutes les stories actives (page: " + page + ", size: " + size + ")");
|
||||
logger.info("[StoryService] Récupération de toutes les stories actives (page: " + page + ", size: " + size + ")");
|
||||
return storyRepository.findAllActive(page, size);
|
||||
}
|
||||
|
||||
@@ -71,11 +87,11 @@ public class StoryService {
|
||||
* @throws UserNotFoundException Si l'utilisateur n'existe pas
|
||||
*/
|
||||
public List<Story> getActiveStoriesByUserId(UUID userId, int page, int size) {
|
||||
System.out.println("[LOG] Récupération des stories actives pour l'utilisateur ID : " + userId + " (page: " + page + ", size: " + size + ")");
|
||||
logger.info("[StoryService] Récupération des stories actives pour l'utilisateur ID : " + userId + " (page: " + page + ", size: " + size + ")");
|
||||
|
||||
Users user = usersRepository.findById(userId);
|
||||
if (user == null) {
|
||||
System.out.println("[ERROR] Utilisateur non trouvé avec l'ID : " + userId);
|
||||
logger.error("[StoryService] Utilisateur non trouvé avec l'ID : " + userId);
|
||||
throw new UserNotFoundException("Utilisateur non trouvé avec l'ID : " + userId);
|
||||
}
|
||||
|
||||
@@ -96,23 +112,23 @@ public class StoryService {
|
||||
*/
|
||||
@Transactional
|
||||
public Story createStory(UUID userId, MediaType mediaType, String mediaUrl, String thumbnailUrl, Integer durationSeconds) {
|
||||
System.out.println("[LOG] Création d'une story par l'utilisateur ID : " + userId);
|
||||
logger.info("[StoryService] Création d'une story par l'utilisateur ID : " + userId);
|
||||
|
||||
// Validation de l'utilisateur
|
||||
Users user = usersRepository.findById(userId);
|
||||
if (user == null) {
|
||||
System.out.println("[ERROR] Utilisateur non trouvé avec l'ID : " + userId);
|
||||
logger.error("[StoryService] Utilisateur non trouvé avec l'ID : " + userId);
|
||||
throw new UserNotFoundException("Utilisateur non trouvé avec l'ID : " + userId);
|
||||
}
|
||||
|
||||
// Validation des paramètres
|
||||
if (mediaUrl == null || mediaUrl.trim().isEmpty()) {
|
||||
System.out.println("[ERROR] L'URL du média est obligatoire");
|
||||
logger.error("[StoryService] L'URL du média est obligatoire");
|
||||
throw new IllegalArgumentException("L'URL du média est obligatoire");
|
||||
}
|
||||
|
||||
if (mediaType == null) {
|
||||
System.out.println("[ERROR] Le type de média est obligatoire");
|
||||
logger.error("[StoryService] Le type de média est obligatoire");
|
||||
throw new IllegalArgumentException("Le type de média est obligatoire");
|
||||
}
|
||||
|
||||
@@ -128,10 +144,52 @@ public class StoryService {
|
||||
}
|
||||
|
||||
storyRepository.persist(story);
|
||||
System.out.println("[LOG] Story créée avec succès : " + story.getId());
|
||||
logger.info("[StoryService] Story créée avec succès : " + story.getId());
|
||||
|
||||
// Notifier les amis en temps réel via Kafka
|
||||
notifyFriendsOfNewStory(user, story);
|
||||
|
||||
return story;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifie les amis d'un utilisateur qu'une nouvelle story a été publiée.
|
||||
*/
|
||||
private void notifyFriendsOfNewStory(Users creator, Story story) {
|
||||
try {
|
||||
List<Friendship> friendships = friendshipRepository.findAcceptedFriendships(creator.getId());
|
||||
String creatorName = creator.getFirstName() + " " + creator.getLastName();
|
||||
|
||||
for (Friendship friendship : friendships) {
|
||||
Users friend = friendship.getUser().getId().equals(creator.getId())
|
||||
? friendship.getFriend()
|
||||
: friendship.getUser();
|
||||
|
||||
if (friend == null || friend.getId() == null) continue;
|
||||
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("storyId", story.getId().toString());
|
||||
data.put("creatorId", creator.getId().toString());
|
||||
data.put("creatorName", creatorName);
|
||||
data.put("creatorProfileImage", creator.getProfileImageUrl());
|
||||
data.put("mediaType", story.getMediaType().toString());
|
||||
data.put("thumbnailUrl", story.getThumbnailUrl());
|
||||
|
||||
NotificationEvent event = new NotificationEvent(
|
||||
friend.getId().toString(),
|
||||
"story_created",
|
||||
data
|
||||
);
|
||||
|
||||
notificationEmitter.send(event);
|
||||
}
|
||||
|
||||
logger.debug("[StoryService] Notification story envoyée à " + friendships.size() + " amis");
|
||||
} catch (Exception e) {
|
||||
logger.warn("[StoryService] Échec notification Kafka story (non bloquant) : " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère une story par son ID.
|
||||
*
|
||||
@@ -140,11 +198,11 @@ public class StoryService {
|
||||
* @throws IllegalArgumentException Si la story n'existe pas
|
||||
*/
|
||||
public Story getStoryById(UUID storyId) {
|
||||
System.out.println("[LOG] Récupération de la story ID : " + storyId);
|
||||
logger.info("[StoryService] Récupération de la story ID : " + storyId);
|
||||
|
||||
Story story = storyRepository.findById(storyId);
|
||||
if (story == null) {
|
||||
System.out.println("[ERROR] Story non trouvée avec l'ID : " + storyId);
|
||||
logger.error("[StoryService] Story non trouvée avec l'ID : " + storyId);
|
||||
throw new IllegalArgumentException("Story non trouvée avec l'ID : " + storyId);
|
||||
}
|
||||
|
||||
@@ -162,19 +220,19 @@ public class StoryService {
|
||||
*/
|
||||
@Transactional
|
||||
public Story markStoryAsViewed(UUID storyId, UUID viewerId) {
|
||||
System.out.println("[LOG] Marquage de la story ID : " + storyId + " comme vue par l'utilisateur ID : " + viewerId);
|
||||
logger.info("[StoryService] Marquage de la story ID : " + storyId + " comme vue par l'utilisateur ID : " + viewerId);
|
||||
|
||||
// Validation de l'utilisateur
|
||||
Users viewer = usersRepository.findById(viewerId);
|
||||
if (viewer == null) {
|
||||
System.out.println("[ERROR] Utilisateur non trouvé avec l'ID : " + viewerId);
|
||||
logger.error("[StoryService] Utilisateur non trouvé avec l'ID : " + viewerId);
|
||||
throw new UserNotFoundException("Utilisateur non trouvé avec l'ID : " + viewerId);
|
||||
}
|
||||
|
||||
// Validation de la story
|
||||
Story story = storyRepository.findById(storyId);
|
||||
if (story == null) {
|
||||
System.out.println("[ERROR] Story non trouvée avec l'ID : " + storyId);
|
||||
logger.error("[StoryService] Story non trouvée avec l'ID : " + storyId);
|
||||
throw new IllegalArgumentException("Story non trouvée avec l'ID : " + storyId);
|
||||
}
|
||||
|
||||
@@ -182,14 +240,47 @@ public class StoryService {
|
||||
boolean isNewView = story.markAsViewed(viewerId);
|
||||
if (isNewView) {
|
||||
storyRepository.persist(story);
|
||||
System.out.println("[LOG] Story marquée comme vue (nouvelle vue)");
|
||||
logger.info("[StoryService] Story marquée comme vue (nouvelle vue)");
|
||||
|
||||
// Notifier le créateur en temps réel
|
||||
notifyCreatorOfStoryView(story, viewer);
|
||||
} else {
|
||||
System.out.println("[LOG] Story déjà vue par cet utilisateur");
|
||||
logger.info("[StoryService] Story déjà vue par cet utilisateur");
|
||||
}
|
||||
|
||||
return story;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifie le créateur d'une story qu'elle a été vue.
|
||||
*/
|
||||
private void notifyCreatorOfStoryView(Story story, Users viewer) {
|
||||
try {
|
||||
Users creator = story.getUser();
|
||||
if (creator == null || creator.getId().equals(viewer.getId())) return;
|
||||
|
||||
String viewerName = viewer.getFirstName() + " " + viewer.getLastName();
|
||||
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("storyId", story.getId().toString());
|
||||
data.put("viewerId", viewer.getId().toString());
|
||||
data.put("viewerName", viewerName);
|
||||
data.put("viewerProfileImage", viewer.getProfileImageUrl());
|
||||
data.put("viewsCount", story.getViewsCount());
|
||||
|
||||
NotificationEvent event = new NotificationEvent(
|
||||
creator.getId().toString(),
|
||||
"story_viewed",
|
||||
data
|
||||
);
|
||||
|
||||
notificationEmitter.send(event);
|
||||
logger.debug("[StoryService] Notification vue story envoyée au créateur : " + creator.getId());
|
||||
} catch (Exception e) {
|
||||
logger.warn("[StoryService] Échec notification vue story (non bloquant) : " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Supprime une story.
|
||||
*
|
||||
@@ -198,16 +289,16 @@ public class StoryService {
|
||||
*/
|
||||
@Transactional
|
||||
public boolean deleteStory(UUID storyId) {
|
||||
System.out.println("[LOG] Suppression de la story ID : " + storyId);
|
||||
logger.info("[StoryService] Suppression de la story ID : " + storyId);
|
||||
|
||||
Story story = storyRepository.findById(storyId);
|
||||
if (story == null) {
|
||||
System.out.println("[ERROR] Story non trouvée avec l'ID : " + storyId);
|
||||
logger.error("[StoryService] Story non trouvée avec l'ID : " + storyId);
|
||||
return false;
|
||||
}
|
||||
|
||||
storyRepository.delete(story);
|
||||
System.out.println("[LOG] Story supprimée avec succès");
|
||||
logger.info("[StoryService] Story supprimée avec succès");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -219,7 +310,7 @@ public class StoryService {
|
||||
*/
|
||||
@Transactional
|
||||
public int deactivateExpiredStories() {
|
||||
System.out.println("[LOG] Désactivation des stories expirées");
|
||||
logger.info("[StoryService] Désactivation des stories expirées");
|
||||
return storyRepository.deactivateExpiredStories();
|
||||
}
|
||||
|
||||
@@ -230,7 +321,7 @@ public class StoryService {
|
||||
* @return Liste des stories les plus vues
|
||||
*/
|
||||
public List<Story> getMostViewedStories(int limit) {
|
||||
System.out.println("[LOG] Récupération des " + limit + " stories les plus vues");
|
||||
logger.info("[StoryService] Récupération des " + limit + " stories les plus vues");
|
||||
return storyRepository.findMostViewedStories(limit);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.lions.dev.repository.UsersRepository;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.transaction.Transactional;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@@ -19,6 +20,8 @@ import java.util.UUID;
|
||||
@ApplicationScoped
|
||||
public class UsersService {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(UsersService.class);
|
||||
|
||||
@Inject
|
||||
UsersRepository usersRepository;
|
||||
|
||||
@@ -66,7 +69,7 @@ public class UsersService {
|
||||
);
|
||||
|
||||
usersRepository.persist(user);
|
||||
System.out.println("[LOG] Utilisateur créé : " + user.getEmail());
|
||||
logger.info("Utilisateur créé : " + user.getEmail());
|
||||
return user;
|
||||
}
|
||||
|
||||
@@ -82,7 +85,7 @@ public class UsersService {
|
||||
public Users updateUser(UUID id, UserCreateRequestDTO userCreateRequestDTO) {
|
||||
Users existingUser = usersRepository.findById(id);
|
||||
if (existingUser == null) {
|
||||
System.out.println("[ERROR] Utilisateur non trouvé avec l'ID : " + id);
|
||||
logger.error("Utilisateur non trouvé avec l'ID : " + id);
|
||||
throw new UserNotFoundException("Utilisateur non trouvé avec l'ID : " + id);
|
||||
}
|
||||
|
||||
@@ -117,7 +120,7 @@ public class UsersService {
|
||||
}
|
||||
|
||||
usersRepository.persist(existingUser);
|
||||
System.out.println("[LOG] Utilisateur mis à jour avec succès : " + existingUser.getEmail());
|
||||
logger.info("Utilisateur mis à jour avec succès : " + existingUser.getEmail());
|
||||
return existingUser;
|
||||
}
|
||||
/**
|
||||
@@ -132,7 +135,7 @@ public class UsersService {
|
||||
public Users updateUserProfileImage(UUID id, String profileImageUrl) {
|
||||
Users existingUser = usersRepository.findById(id);
|
||||
if (existingUser == null) {
|
||||
System.out.println("[ERROR] Utilisateur non trouvé avec l'ID : " + id);
|
||||
logger.error("Utilisateur non trouvé avec l'ID : " + id);
|
||||
throw new UserNotFoundException("Utilisateur non trouvé avec l'ID : " + id);
|
||||
}
|
||||
|
||||
@@ -140,7 +143,7 @@ public class UsersService {
|
||||
existingUser.setProfileImageUrl(profileImageUrl);
|
||||
|
||||
usersRepository.persist(existingUser);
|
||||
System.out.println("[LOG] L'image de profile de l\'Utilisateur mis à jour avec succès : " + existingUser.getEmail());
|
||||
logger.info("Image de profil de l'utilisateur mise à jour avec succès : " + existingUser.getEmail());
|
||||
return existingUser;
|
||||
}
|
||||
|
||||
@@ -166,10 +169,10 @@ public class UsersService {
|
||||
public Users authenticateUser(String email, String password) {
|
||||
Optional<Users> userOptional = usersRepository.findByEmail(email);
|
||||
if (userOptional.isEmpty() || !userOptional.get().verifyPassword(password)) { // v2.0
|
||||
System.out.println("[ERROR] Échec de l'authentification pour l'email : " + email);
|
||||
logger.warn("Échec de l'authentification pour l'email : " + email);
|
||||
throw new UserNotFoundException("Utilisateur ou mot de passe incorrect.");
|
||||
}
|
||||
System.out.println("[LOG] Utilisateur authentifié : " + email);
|
||||
logger.info("Utilisateur authentifié : " + email);
|
||||
return userOptional.get();
|
||||
}
|
||||
|
||||
@@ -192,10 +195,10 @@ public class UsersService {
|
||||
public Users getUserById(UUID id) {
|
||||
Users user = usersRepository.findById(id);
|
||||
if (user == null) {
|
||||
System.out.println("[ERROR] Utilisateur non trouvé avec l'ID : " + id);
|
||||
logger.error("Utilisateur non trouvé avec l'ID : " + id);
|
||||
throw new UserNotFoundException("Utilisateur non trouvé avec l'ID : " + id);
|
||||
}
|
||||
System.out.println("[LOG] Utilisateur trouvé avec l'ID : " + id);
|
||||
logger.debug("Utilisateur trouvé avec l'ID : " + id);
|
||||
return user;
|
||||
}
|
||||
|
||||
@@ -210,13 +213,13 @@ public class UsersService {
|
||||
public void resetPassword(UUID id, String newPassword) {
|
||||
Users user = usersRepository.findById(id);
|
||||
if (user == null) {
|
||||
System.out.println("[ERROR] Utilisateur non trouvé avec l'ID : " + id);
|
||||
logger.error("Utilisateur non trouvé avec l'ID : " + id);
|
||||
throw new UserNotFoundException("Utilisateur non trouvé.");
|
||||
}
|
||||
|
||||
user.setPassword(newPassword); // v2.0 - Hachage automatique
|
||||
usersRepository.persist(user);
|
||||
System.out.println("[LOG] Mot de passe réinitialisé pour l'utilisateur : " + user.getEmail());
|
||||
logger.info("Mot de passe réinitialisé pour l'utilisateur : " + user.getEmail());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -228,9 +231,9 @@ public class UsersService {
|
||||
public boolean deleteUser(UUID id) {
|
||||
boolean deleted = usersRepository.deleteById(id);
|
||||
if (deleted) {
|
||||
System.out.println("[LOG] Utilisateur supprimé avec succès : " + id);
|
||||
logger.info("Utilisateur supprimé avec succès : " + id);
|
||||
} else {
|
||||
System.out.println("[ERROR] Échec de la suppression de l'utilisateur avec l'ID : " + id);
|
||||
logger.error("Échec de la suppression de l'utilisateur avec l'ID : " + id);
|
||||
}
|
||||
return deleted;
|
||||
}
|
||||
@@ -245,10 +248,10 @@ public class UsersService {
|
||||
public Users getUserByEmail(String email) {
|
||||
Optional<Users> userOptional = usersRepository.findByEmail(email);
|
||||
if (userOptional.isEmpty()) {
|
||||
System.out.println("[ERROR] Utilisateur non trouvé avec l'email : " + email);
|
||||
logger.error("Utilisateur non trouvé avec l'email : " + email);
|
||||
throw new UserNotFoundException("Utilisateur non trouvé avec l'email : " + email);
|
||||
}
|
||||
System.out.println("[LOG] Utilisateur trouvé avec l'email : " + email);
|
||||
logger.debug("Utilisateur trouvé avec l'email : " + email);
|
||||
return userOptional.get();
|
||||
}
|
||||
|
||||
@@ -280,7 +283,28 @@ public class UsersService {
|
||||
}
|
||||
user.setRole(newRole);
|
||||
usersRepository.persist(user);
|
||||
System.out.println("[LOG] Rôle attribué à " + user.getEmail() + " : " + newRole);
|
||||
logger.info("Rôle attribué à " + user.getEmail() + " : " + newRole);
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Force ou suspend le compte d'un utilisateur (réservé au super administrateur).
|
||||
* Utilisé pour les managers : isActive = false = suspendu (abonnement expiré / sanction).
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur à modifier.
|
||||
* @param active true = forcer l'activation, false = suspendre.
|
||||
* @return L'utilisateur mis à jour.
|
||||
* @throws UserNotFoundException Si l'utilisateur n'existe pas.
|
||||
*/
|
||||
@Transactional
|
||||
public Users setUserActive(UUID userId, boolean active) {
|
||||
Users user = usersRepository.findById(userId);
|
||||
if (user == null) {
|
||||
throw new UserNotFoundException("Utilisateur non trouvé avec l'ID : " + userId);
|
||||
}
|
||||
user.setActive(active);
|
||||
usersRepository.persist(user);
|
||||
logger.info("Statut actif modifié pour " + user.getEmail() + " : " + active);
|
||||
return user;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,8 @@ public class WavePaymentService {
|
||||
EstablishmentPaymentRepository paymentRepository;
|
||||
@Inject
|
||||
UsersRepository usersRepository;
|
||||
@Inject
|
||||
EmailService emailService;
|
||||
|
||||
@ConfigProperty(name = "wave.api.url", defaultValue = "https://api.wave.com")
|
||||
String waveApiUrl;
|
||||
@@ -184,8 +186,32 @@ public class WavePaymentService {
|
||||
manager.setActive(true);
|
||||
usersRepository.persist(manager);
|
||||
LOG.info("Webhook Wave: établissement et manager activés pour " + establishmentId);
|
||||
try {
|
||||
emailService.sendPaymentConfirmationEmail(
|
||||
manager.getEmail(),
|
||||
manager.getFirstName(),
|
||||
establishment.getName(),
|
||||
sub.getAmountXof() != null ? sub.getAmountXof() : 0,
|
||||
sub.getPlan() != null ? sub.getPlan() : "MONTHLY"
|
||||
);
|
||||
} catch (Exception e) {
|
||||
LOG.warn("Envoi email confirmation paiement échoué: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if ("payment.refunded".equals(eventType)) {
|
||||
sub.setStatus(EstablishmentSubscription.STATUS_CANCELLED);
|
||||
subscriptionRepository.persist(sub);
|
||||
if (establishment != null) {
|
||||
establishment.setIsActive(false);
|
||||
establishmentRepository.persist(establishment);
|
||||
Users manager = establishment.getManager();
|
||||
if (manager != null) {
|
||||
manager.setActive(false);
|
||||
usersRepository.persist(manager);
|
||||
}
|
||||
LOG.info("Webhook Wave: remboursement traité, établissement désactivé pour " + establishmentId);
|
||||
}
|
||||
} else if ("payment.cancelled".equals(eventType) || "payment.expired".equals(eventType)
|
||||
|| "payment.failed".equals(eventType)) {
|
||||
sub.setStatus(EstablishmentSubscription.STATUS_CANCELLED);
|
||||
@@ -199,6 +225,17 @@ public class WavePaymentService {
|
||||
manager.setActive(false);
|
||||
usersRepository.persist(manager);
|
||||
LOG.info("Webhook Wave: établissement et manager suspendus pour " + establishmentId);
|
||||
if ("payment.failed".equals(eventType)) {
|
||||
try {
|
||||
emailService.sendPaymentFailureEmail(
|
||||
manager.getEmail(),
|
||||
manager.getFirstName(),
|
||||
establishment.getName()
|
||||
);
|
||||
} catch (Exception e) {
|
||||
LOG.warn("Envoi email échec paiement échoué: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -207,8 +244,11 @@ public class WavePaymentService {
|
||||
paymentRepository.findByWaveSessionId(sessionId).ifPresent(payment -> {
|
||||
if ("payment.completed".equals(eventType)) {
|
||||
payment.setStatus(EstablishmentPayment.STATUS_COMPLETED);
|
||||
} else if ("payment.cancelled".equals(eventType) || "payment.expired".equals(eventType)) {
|
||||
} else if ("payment.cancelled".equals(eventType) || "payment.expired".equals(eventType)
|
||||
|| "payment.refunded".equals(eventType)) {
|
||||
payment.setStatus(EstablishmentPayment.STATUS_CANCELLED);
|
||||
} else if ("payment.failed".equals(eventType)) {
|
||||
payment.setStatus(EstablishmentPayment.STATUS_FAILED);
|
||||
}
|
||||
paymentRepository.persist(payment);
|
||||
});
|
||||
|
||||
@@ -16,7 +16,6 @@ public class InputConverter {
|
||||
try {
|
||||
return Integer.parseInt(value);
|
||||
} catch (NumberFormatException e) {
|
||||
System.out.println("[ERROR] Impossible de convertir la valeur en entier : " + value);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -33,7 +32,6 @@ public class InputConverter {
|
||||
} else if ("false".equalsIgnoreCase(value)) {
|
||||
return false;
|
||||
}
|
||||
System.out.println("[ERROR] Valeur non valide pour un booléen : " + value);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,6 @@ public class SecureStorage {
|
||||
* @param data Les données à effacer.
|
||||
*/
|
||||
public void clearData(String data) {
|
||||
System.out.println("[LOG] Les données ont été effacées de manière sécurisée.");
|
||||
// En Java, il n'y a pas de suppression directe des données en mémoire. On peut ici
|
||||
// gérer la suppression logique ou l'effacement de données dans un fichier sécurisé.
|
||||
}
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
package com.lions.dev.util;
|
||||
|
||||
/**
|
||||
* Rôles utilisateur de l'application AfterWork.
|
||||
* Hiérarchie : SUPER_ADMIN > ADMIN > MANAGER > USER.
|
||||
*/
|
||||
public final class UserRole {
|
||||
|
||||
private UserRole() {}
|
||||
|
||||
/** Utilisateur standard (participation aux événements, profil, amis). */
|
||||
public static final String USER = "USER";
|
||||
|
||||
/** Responsable d'établissement (gestion de son établissement). */
|
||||
public static final String MANAGER = "MANAGER";
|
||||
|
||||
/** Administrateur (gestion des établissements, modération). */
|
||||
public static final String ADMIN = "ADMIN";
|
||||
|
||||
/**
|
||||
* Super administrateur : tous les droits (gestion des utilisateurs, attribution des rôles,
|
||||
* gestion des établissements, accès aux paiements Wave, etc.).
|
||||
*/
|
||||
public static final String SUPER_ADMIN = "SUPER_ADMIN";
|
||||
|
||||
/**
|
||||
* Vérifie si le rôle a les droits super admin (ou est super admin).
|
||||
*/
|
||||
public static boolean isSuperAdmin(String role) {
|
||||
return SUPER_ADMIN.equals(role);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si le rôle peut gérer les utilisateurs (attribution de rôles, etc.).
|
||||
*/
|
||||
public static boolean canManageUsers(String role) {
|
||||
return SUPER_ADMIN.equals(role) || ADMIN.equals(role);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si le rôle peut gérer les établissements (vérification, modération).
|
||||
*/
|
||||
public static boolean canManageEstablishments(String role) {
|
||||
return SUPER_ADMIN.equals(role) || ADMIN.equals(role);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user