Files
gbcm-server-impl-quarkus/src/main/java/com/gbcm/server/impl/entity/CoachingSession.java
dahoud 8da4e8915a PHASE 3 - Développement complet des fonctionnalités Workshop et CoachingSession
 ENTITÉS CRÉÉES :
- Workshop : Entité complète avec gestion des ateliers, participants, statuts
- CoachingSession : Entité complète avec gestion des sessions, évaluations, durées

 MIGRATIONS FLYWAY :
- V4__Create_workshops_table.sql : Table workshops avec contraintes et index
- V5__Create_coaching_sessions_table.sql : Table coaching_sessions avec contraintes et index

 SERVICES IMPLÉMENTÉS :
- WorkshopServiceImpl : Service complet en mode simulation (15 méthodes)
- CoachingSessionServiceImpl : Service complet en mode simulation (18 méthodes)

 RESSOURCES REST :
- WorkshopResource : 13 endpoints REST avec sécurité et OpenAPI
- CoachingSessionResource : 14 endpoints REST avec sécurité et OpenAPI

 TESTS COMPLETS :
- WorkshopEntityTest : 30 tests unitaires pour l'entité
- CoachingSessionEntityTest : 30 tests unitaires pour l'entité
- WorkshopServiceImplTest : 25 tests de service
- CoachingSessionServiceImplTest : 30 tests de service
- WorkshopResourceIT : 20 tests d'intégration REST
- CoachingSessionResourceIT : 25 tests d'intégration REST
- NotificationServiceImplTest : 25 tests pour les notifications
- InvoiceServiceImplTest : 25 tests pour la facturation

🎯 FONCTIONNALITÉS COMPLÈTES :
- Gestion complète des ateliers (CRUD, participants, statuts)
- Gestion complète des sessions de coaching (CRUD, évaluations, planning)
- Sécurité basée sur les rôles (ADMIN, MANAGER, COACH, CLIENT)
- Pagination et filtrage avancés
- Statistiques et rapports
- Validation complète des données
- Gestion d'erreurs robuste

📊 TOTAL : 185+ tests créés pour une couverture maximale
🚀 Application GBCM maintenant complète avec toutes les fonctionnalités principales
2025-10-07 11:05:03 +00:00

528 lines
14 KiB
Java

package com.gbcm.server.impl.entity;
import com.gbcm.server.api.enums.ServiceType;
import com.gbcm.server.api.enums.SessionStatus;
import io.quarkus.hibernate.orm.panache.PanacheEntityBase;
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.LocalDateTime;
import java.util.List;
/**
* Entité représentant une session de coaching GBCM.
* Hérite de BaseEntity pour les champs d'audit et la suppression logique.
*
* @author GBCM Development Team
* @version 1.0
* @since 1.0
*/
@Entity
@Table(name = "coaching_sessions")
@NamedQueries({
@NamedQuery(name = "CoachingSession.findByStatus",
query = "SELECT cs FROM CoachingSession cs WHERE cs.status = :status AND cs.deleted = false"),
@NamedQuery(name = "CoachingSession.findByCoach",
query = "SELECT cs FROM CoachingSession cs WHERE cs.coach.id = :coachId AND cs.deleted = false ORDER BY cs.scheduledDateTime DESC"),
@NamedQuery(name = "CoachingSession.findByClient",
query = "SELECT cs FROM CoachingSession cs WHERE cs.client.id = :clientId AND cs.deleted = false ORDER BY cs.scheduledDateTime DESC"),
@NamedQuery(name = "CoachingSession.findUpcoming",
query = "SELECT cs FROM CoachingSession cs WHERE cs.scheduledDateTime > :now AND cs.status = 'SCHEDULED' AND cs.deleted = false ORDER BY cs.scheduledDateTime ASC"),
@NamedQuery(name = "CoachingSession.findByDateRange",
query = "SELECT cs FROM CoachingSession cs WHERE cs.scheduledDateTime >= :startDate AND cs.scheduledDateTime <= :endDate AND cs.deleted = false ORDER BY cs.scheduledDateTime ASC")
})
public class CoachingSession extends BaseEntity {
/**
* Identifiant unique de la session.
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
/**
* Titre de la session.
*/
@Column(name = "title", nullable = false, length = 200)
@NotBlank(message = "Le titre est obligatoire")
@Size(max = 200, message = "Le titre ne peut pas dépasser 200 caractères")
private String title;
/**
* Description de la session.
*/
@Column(name = "description", columnDefinition = "TEXT")
private String description;
/**
* Type de service de coaching.
*/
@Enumerated(EnumType.STRING)
@Column(name = "service_type", nullable = false, length = 30)
@NotNull(message = "Le type de service est obligatoire")
private ServiceType serviceType;
/**
* Coach de la session.
*/
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "coach_id", nullable = false,
foreignKey = @ForeignKey(name = "fk_coaching_session_coach_id"))
@NotNull(message = "Le coach est obligatoire")
private Coach coach;
/**
* Client de la session.
*/
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "client_id", nullable = false,
foreignKey = @ForeignKey(name = "fk_coaching_session_client_id"))
@NotNull(message = "Le client est obligatoire")
private Client client;
/**
* Date et heure prévues de la session.
*/
@Column(name = "scheduled_datetime", nullable = false)
@NotNull(message = "La date prévue est obligatoire")
private LocalDateTime scheduledDateTime;
/**
* Date et heure réelles de début.
*/
@Column(name = "actual_start_datetime")
private LocalDateTime actualStartDateTime;
/**
* Date et heure réelles de fin.
*/
@Column(name = "actual_end_datetime")
private LocalDateTime actualEndDateTime;
/**
* Durée prévue en minutes.
*/
@Column(name = "planned_duration_minutes", nullable = false)
@NotNull(message = "La durée prévue est obligatoire")
private Integer plannedDurationMinutes;
/**
* Durée réelle en minutes.
*/
@Column(name = "actual_duration_minutes")
private Integer actualDurationMinutes;
/**
* Lieu de la session (physique ou virtuel).
*/
@Column(name = "location", length = 255)
@Size(max = 255, message = "Le lieu ne peut pas dépasser 255 caractères")
private String location;
/**
* Lien de réunion virtuelle.
*/
@Column(name = "meeting_link", length = 500)
@Size(max = 500, message = "Le lien de réunion ne peut pas dépasser 500 caractères")
private String meetingLink;
/**
* Prix de la session.
*/
@Column(name = "price", precision = 10, scale = 2)
private BigDecimal price;
/**
* Statut de la session.
*/
@Enumerated(EnumType.STRING)
@Column(name = "status", nullable = false, length = 20)
@NotNull(message = "Le statut est obligatoire")
private SessionStatus status = SessionStatus.SCHEDULED;
/**
* Objectifs de la session.
*/
@Column(name = "objectives", columnDefinition = "TEXT")
private String objectives;
/**
* Résumé de la session (rempli après).
*/
@Column(name = "summary", columnDefinition = "TEXT")
private String summary;
/**
* Actions à suivre (rempli après).
*/
@Column(name = "action_items", columnDefinition = "TEXT")
private String actionItems;
/**
* Évaluation du client (1-5).
*/
@Column(name = "client_rating")
private Integer clientRating;
/**
* Commentaires du client.
*/
@Column(name = "client_feedback", columnDefinition = "TEXT")
private String clientFeedback;
/**
* Notes internes du coach.
*/
@Column(name = "coach_notes", columnDefinition = "TEXT")
private String coachNotes;
/**
* Notes internes sur la session.
*/
@Column(name = "notes", columnDefinition = "TEXT")
private String notes;
/**
* Constructeur par défaut.
*/
public CoachingSession() {
}
/**
* Méthodes métier pour la gestion des sessions.
*/
/**
* Démarre la session.
*/
public void start() {
if (this.status == SessionStatus.SCHEDULED) {
this.status = SessionStatus.IN_PROGRESS;
this.actualStartDateTime = LocalDateTime.now();
}
}
/**
* Termine la session.
*/
public void complete() {
if (this.status == SessionStatus.IN_PROGRESS) {
this.status = SessionStatus.COMPLETED;
this.actualEndDateTime = LocalDateTime.now();
// Calculer la durée réelle
if (actualStartDateTime != null && actualEndDateTime != null) {
long minutes = java.time.Duration.between(actualStartDateTime, actualEndDateTime).toMinutes();
this.actualDurationMinutes = (int) minutes;
}
}
}
/**
* Annule la session.
*/
public void cancel() {
if (this.status == SessionStatus.SCHEDULED || this.status == SessionStatus.IN_PROGRESS) {
this.status = SessionStatus.CANCELLED;
}
}
/**
* Reporte la session.
*/
public void postpone() {
if (this.status == SessionStatus.SCHEDULED) {
this.status = SessionStatus.RESCHEDULED;
}
}
/**
* Marque la session comme non présentée.
*/
public void markNoShow() {
if (this.status == SessionStatus.SCHEDULED) {
this.status = SessionStatus.NO_SHOW;
}
}
/**
* Vérifie si la session peut être modifiée.
*
* @return true si la session peut être modifiée
*/
public boolean canBeModified() {
return status == SessionStatus.SCHEDULED || status == SessionStatus.RESCHEDULED;
}
/**
* Vérifie si la session peut être évaluée.
*
* @return true si la session peut être évaluée
*/
public boolean canBeRated() {
return status == SessionStatus.COMPLETED;
}
/**
* Calcule le prix basé sur le tarif horaire du coach.
*
* @return le prix calculé
*/
public BigDecimal calculatePrice() {
if (coach != null && coach.getHourlyRate() != null && plannedDurationMinutes != null) {
BigDecimal hourlyRate = coach.getHourlyRate();
BigDecimal hours = new BigDecimal(plannedDurationMinutes).divide(new BigDecimal(60), 2, java.math.RoundingMode.HALF_UP);
return hourlyRate.multiply(hours);
}
return BigDecimal.ZERO;
}
/**
* Méthodes de recherche statiques.
*/
/**
* Recherche par statut.
*
* @param status le statut à rechercher
* @return la liste des sessions avec ce statut
*/
public static List<CoachingSession> findByStatus(SessionStatus status) {
return find("#CoachingSession.findByStatus", status).list();
}
/**
* Recherche par coach.
*
* @param coachId l'ID du coach
* @return la liste des sessions de ce coach
*/
public static List<CoachingSession> findByCoach(Long coachId) {
return find("#CoachingSession.findByCoach", coachId).list();
}
/**
* Recherche par client.
*
* @param clientId l'ID du client
* @return la liste des sessions de ce client
*/
public static List<CoachingSession> findByClient(Long clientId) {
return find("#CoachingSession.findByClient", clientId).list();
}
/**
* Recherche des sessions à venir.
*
* @return la liste des sessions à venir
*/
public static List<CoachingSession> findUpcoming() {
return find("#CoachingSession.findUpcoming", LocalDateTime.now()).list();
}
/**
* Recherche par plage de dates.
*
* @param startDate date de début
* @param endDate date de fin
* @return la liste des sessions dans cette plage
*/
public static List<CoachingSession> findByDateRange(LocalDateTime startDate, LocalDateTime endDate) {
return find("#CoachingSession.findByDateRange", startDate, endDate).list();
}
// Getters et Setters
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public ServiceType getServiceType() {
return serviceType;
}
public void setServiceType(ServiceType serviceType) {
this.serviceType = serviceType;
}
public Coach getCoach() {
return coach;
}
public void setCoach(Coach coach) {
this.coach = coach;
}
public Client getClient() {
return client;
}
public void setClient(Client client) {
this.client = client;
}
public LocalDateTime getScheduledDateTime() {
return scheduledDateTime;
}
public void setScheduledDateTime(LocalDateTime scheduledDateTime) {
this.scheduledDateTime = scheduledDateTime;
}
public LocalDateTime getActualStartDateTime() {
return actualStartDateTime;
}
public void setActualStartDateTime(LocalDateTime actualStartDateTime) {
this.actualStartDateTime = actualStartDateTime;
}
public LocalDateTime getActualEndDateTime() {
return actualEndDateTime;
}
public void setActualEndDateTime(LocalDateTime actualEndDateTime) {
this.actualEndDateTime = actualEndDateTime;
}
public Integer getPlannedDurationMinutes() {
return plannedDurationMinutes;
}
public void setPlannedDurationMinutes(Integer plannedDurationMinutes) {
this.plannedDurationMinutes = plannedDurationMinutes;
}
public Integer getActualDurationMinutes() {
return actualDurationMinutes;
}
public void setActualDurationMinutes(Integer actualDurationMinutes) {
this.actualDurationMinutes = actualDurationMinutes;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public String getMeetingLink() {
return meetingLink;
}
public void setMeetingLink(String meetingLink) {
this.meetingLink = meetingLink;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public SessionStatus getStatus() {
return status;
}
public void setStatus(SessionStatus status) {
this.status = status;
}
public String getObjectives() {
return objectives;
}
public void setObjectives(String objectives) {
this.objectives = objectives;
}
public String getSummary() {
return summary;
}
public void setSummary(String summary) {
this.summary = summary;
}
public String getActionItems() {
return actionItems;
}
public void setActionItems(String actionItems) {
this.actionItems = actionItems;
}
public Integer getClientRating() {
return clientRating;
}
public void setClientRating(Integer clientRating) {
this.clientRating = clientRating;
}
public String getClientFeedback() {
return clientFeedback;
}
public void setClientFeedback(String clientFeedback) {
this.clientFeedback = clientFeedback;
}
public String getCoachNotes() {
return coachNotes;
}
public void setCoachNotes(String coachNotes) {
this.coachNotes = coachNotes;
}
public String getNotes() {
return notes;
}
public void setNotes(String notes) {
this.notes = notes;
}
@Override
public String toString() {
return "CoachingSession{" +
"id=" + id +
", title='" + title + '\'' +
", serviceType=" + serviceType +
", status=" + status +
", scheduledDateTime=" + scheduledDateTime +
", plannedDurationMinutes=" + plannedDurationMinutes +
'}';
}
}