package com.gbcm.server.impl.entity; import com.gbcm.server.api.enums.ServiceType; import jakarta.persistence.*; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; import java.math.BigDecimal; import java.time.LocalDate; import java.time.LocalTime; import java.util.List; import java.util.Set; /** * Entité représentant un coach de la plateforme GBCM. * Un coach est associé à un utilisateur et peut offrir différents services. * * @author GBCM Development Team * @version 1.0 * @since 1.0 */ @Entity @Table(name = "coaches", indexes = { @Index(name = "idx_coaches_user_id", columnList = "user_id", unique = true), @Index(name = "idx_coaches_status", columnList = "status"), @Index(name = "idx_coaches_specialization", columnList = "specialization"), @Index(name = "idx_coaches_deleted", columnList = "deleted") }) @NamedQueries({ @NamedQuery(name = "Coach.findByUserId", query = "SELECT c FROM Coach c WHERE c.user.id = :userId AND c.deleted = false"), @NamedQuery(name = "Coach.findByStatus", query = "SELECT c FROM Coach c WHERE c.status = :status AND c.deleted = false"), @NamedQuery(name = "Coach.findBySpecialization", query = "SELECT c FROM Coach c WHERE c.specialization = :specialization AND c.deleted = false"), @NamedQuery(name = "Coach.findAvailableCoaches", query = "SELECT c FROM Coach c WHERE c.status = 'ACTIVE' AND c.availableForBooking = true AND c.deleted = false") }) public class Coach extends BaseEntity { /** * Identifiant unique du coach. */ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; /** * Utilisateur associé à ce coach. * Relation one-to-one obligatoire. */ @OneToOne(fetch = FetchType.LAZY, optional = false) @JoinColumn(name = "user_id", nullable = false, unique = true, foreignKey = @ForeignKey(name = "fk_coaches_user_id")) @NotNull(message = "L'utilisateur associé est obligatoire") private User user; /** * Spécialisation principale du coach. */ @Column(name = "specialization", nullable = false, length = 100) @NotBlank(message = "La spécialisation est obligatoire") @Size(max = 100, message = "La spécialisation ne peut pas dépasser 100 caractères") private String specialization; /** * Biographie professionnelle du coach. */ @Column(name = "bio", columnDefinition = "TEXT") private String bio; /** * Années d'expérience du coach. */ @Column(name = "years_of_experience") private Integer yearsOfExperience; /** * Certifications du coach. */ @Column(name = "certifications", columnDefinition = "TEXT") private String certifications; /** * Langues parlées par le coach. */ @Column(name = "languages", length = 255) @Size(max = 255, message = "Les langues ne peuvent pas dépasser 255 caractères") private String languages; /** * Tarif horaire du coach pour les sessions individuelles. */ @Column(name = "hourly_rate", precision = 10, scale = 2) private BigDecimal hourlyRate; /** * Statut du coach. */ @Enumerated(EnumType.STRING) @Column(name = "status", nullable = false, length = 20) @NotNull(message = "Le statut est obligatoire") private CoachStatus status = CoachStatus.ACTIVE; /** * Disponibilité pour les réservations. */ @Column(name = "available_for_booking", nullable = false) private boolean availableForBooking = true; /** * Types de services offerts par le coach. */ @ElementCollection(targetClass = ServiceType.class) @CollectionTable(name = "coach_service_types", joinColumns = @JoinColumn(name = "coach_id"), foreignKey = @ForeignKey(name = "fk_coach_service_types_coach_id")) @Column(name = "service_type", length = 30) @Enumerated(EnumType.STRING) private Set serviceTypes; /** * Heures de travail - début. */ @Column(name = "working_hours_start") private LocalTime workingHoursStart; /** * Heures de travail - fin. */ @Column(name = "working_hours_end") private LocalTime workingHoursEnd; /** * Jours de travail (format: MONDAY,TUESDAY,WEDNESDAY...). */ @Column(name = "working_days", length = 100) @Size(max = 100, message = "Les jours de travail ne peuvent pas dépasser 100 caractères") private String workingDays; /** * Fuseau horaire du coach. */ @Column(name = "timezone", length = 50) @Size(max = 50, message = "Le fuseau horaire ne peut pas dépasser 50 caractères") private String timezone; /** * Date de début d'activité comme coach. */ @Column(name = "start_date") private LocalDate startDate; /** * Date de fin d'activité comme coach. */ @Column(name = "end_date") private LocalDate endDate; /** * Note moyenne du coach (calculée à partir des évaluations). */ @Column(name = "average_rating", precision = 3, scale = 2) private BigDecimal averageRating; /** * Nombre total d'évaluations reçues. */ @Column(name = "total_ratings", nullable = false) private int totalRatings = 0; /** * Nombre total de sessions effectuées. */ @Column(name = "total_sessions", nullable = false) private int totalSessions = 0; /** * Revenus totaux générés. */ @Column(name = "total_revenue", precision = 15, scale = 2) private BigDecimal totalRevenue = BigDecimal.ZERO; /** * Notes internes sur le coach. */ @Column(name = "notes", columnDefinition = "TEXT") private String notes; /** * Constructeur par défaut. */ public Coach() { super(); } /** * Constructeur avec les champs obligatoires. * * @param user l'utilisateur associé * @param specialization la spécialisation du coach */ public Coach(User user, String specialization) { this(); this.user = user; this.specialization = specialization; this.startDate = LocalDate.now(); } /** * Active le coach. */ public void activate() { this.status = CoachStatus.ACTIVE; this.availableForBooking = true; } /** * Désactive le coach. */ public void deactivate() { this.status = CoachStatus.INACTIVE; this.availableForBooking = false; } /** * Met le coach en congé. */ public void setOnLeave() { this.status = CoachStatus.ON_LEAVE; this.availableForBooking = false; } /** * Termine l'activité du coach. */ public void terminate() { this.status = CoachStatus.TERMINATED; this.availableForBooking = false; this.endDate = LocalDate.now(); } /** * Met à jour la note moyenne après une nouvelle évaluation. * * @param newRating la nouvelle note reçue */ public void updateRating(BigDecimal newRating) { if (newRating != null && newRating.compareTo(BigDecimal.ZERO) > 0) { if (averageRating == null) { averageRating = newRating; totalRatings = 1; } else { BigDecimal totalScore = averageRating.multiply(new BigDecimal(totalRatings)); totalScore = totalScore.add(newRating); totalRatings++; averageRating = totalScore.divide(new BigDecimal(totalRatings), 2, java.math.RoundingMode.HALF_UP); } } } /** * Incrémente le nombre de sessions effectuées. */ public void incrementSessionCount() { this.totalSessions++; } /** * Ajoute des revenus au total. * * @param amount le montant à ajouter */ public void addRevenue(BigDecimal amount) { if (amount != null && amount.compareTo(BigDecimal.ZERO) > 0) { this.totalRevenue = this.totalRevenue.add(amount); } } /** * Vérifie si le coach est disponible pour les réservations. * * @return true si disponible, false sinon */ public boolean isAvailableForBooking() { return availableForBooking && status == CoachStatus.ACTIVE; } /** * Vérifie si le coach offre un type de service spécifique. * * @param serviceType le type de service à vérifier * @return true si le coach offre ce service, false sinon */ public boolean offersService(ServiceType serviceType) { return serviceTypes != null && serviceTypes.contains(serviceType); } /** * Méthode de recherche par ID utilisateur. * * @param userId l'ID de l'utilisateur * @return le coach trouvé ou null */ public static Coach findByUserId(Long userId) { return find("#Coach.findByUserId", userId).firstResult(); } /** * Méthode de recherche par statut. * * @param status le statut à rechercher * @return la liste des coaches avec ce statut */ public static List findByStatus(CoachStatus status) { return find("#Coach.findByStatus", status).list(); } /** * Méthode de recherche par spécialisation. * * @param specialization la spécialisation à rechercher * @return la liste des coaches avec cette spécialisation */ public static List findBySpecialization(String specialization) { return find("#Coach.findBySpecialization", specialization).list(); } /** * Méthode de recherche des coaches disponibles. * * @return la liste des coaches disponibles pour réservation */ public static List findAvailableCoaches() { return find("#Coach.findAvailableCoaches").list(); } // Getters et Setters public Long getId() { return id; } public void setId(Long id) { this.id = id; } public User getUser() { return user; } public void setUser(User user) { this.user = user; } public String getSpecialization() { return specialization; } public void setSpecialization(String specialization) { this.specialization = specialization; } public String getBio() { return bio; } public void setBio(String bio) { this.bio = bio; } public Integer getYearsOfExperience() { return yearsOfExperience; } public void setYearsOfExperience(Integer yearsOfExperience) { this.yearsOfExperience = yearsOfExperience; } public String getCertifications() { return certifications; } public void setCertifications(String certifications) { this.certifications = certifications; } public String getLanguages() { return languages; } public void setLanguages(String languages) { this.languages = languages; } public BigDecimal getHourlyRate() { return hourlyRate; } public void setHourlyRate(BigDecimal hourlyRate) { this.hourlyRate = hourlyRate; } public CoachStatus getStatus() { return status; } public void setStatus(CoachStatus status) { this.status = status; } public void setAvailableForBooking(boolean availableForBooking) { this.availableForBooking = availableForBooking; } public Set getServiceTypes() { return serviceTypes; } public void setServiceTypes(Set serviceTypes) { this.serviceTypes = serviceTypes; } public LocalTime getWorkingHoursStart() { return workingHoursStart; } public void setWorkingHoursStart(LocalTime workingHoursStart) { this.workingHoursStart = workingHoursStart; } public LocalTime getWorkingHoursEnd() { return workingHoursEnd; } public void setWorkingHoursEnd(LocalTime workingHoursEnd) { this.workingHoursEnd = workingHoursEnd; } public String getWorkingDays() { return workingDays; } public void setWorkingDays(String workingDays) { this.workingDays = workingDays; } public String getTimezone() { return timezone; } public void setTimezone(String timezone) { this.timezone = timezone; } public LocalDate getStartDate() { return startDate; } public void setStartDate(LocalDate startDate) { this.startDate = startDate; } public LocalDate getEndDate() { return endDate; } public void setEndDate(LocalDate endDate) { this.endDate = endDate; } public BigDecimal getAverageRating() { return averageRating; } public void setAverageRating(BigDecimal averageRating) { this.averageRating = averageRating; } public int getTotalRatings() { return totalRatings; } public void setTotalRatings(int totalRatings) { this.totalRatings = totalRatings; } public int getTotalSessions() { return totalSessions; } public void setTotalSessions(int totalSessions) { this.totalSessions = totalSessions; } public BigDecimal getTotalRevenue() { return totalRevenue; } public void setTotalRevenue(BigDecimal totalRevenue) { this.totalRevenue = totalRevenue; } public String getNotes() { return notes; } public void setNotes(String notes) { this.notes = notes; } @Override public String toString() { return "Coach{" + "id=" + id + ", specialization='" + specialization + '\'' + ", status=" + status + ", availableForBooking=" + availableForBooking + ", averageRating=" + averageRating + ", totalSessions=" + totalSessions + ", totalRevenue=" + totalRevenue + '}'; } /** * Énumération des statuts de coach. */ public enum CoachStatus { /** * Coach actif et disponible. */ ACTIVE, /** * Coach inactif temporairement. */ INACTIVE, /** * Coach en congé. */ ON_LEAVE, /** * Coach dont le contrat est terminé. */ TERMINATED } }