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
This commit is contained in:
527
src/main/java/com/gbcm/server/impl/entity/CoachingSession.java
Normal file
527
src/main/java/com/gbcm/server/impl/entity/CoachingSession.java
Normal file
@@ -0,0 +1,527 @@
|
||||
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 +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user