Task 1.1 - DTOs et Interfaces API de base

- Création de tous les DTOs d'authentification (LoginRequestDTO, LoginResponseDTO, TokenDTO, PasswordResetDTO)
- Création des DTOs utilisateur (UserDTO, CreateUserDTO, UpdateUserDTO)
- Création des DTOs communs (PagedResultDTO, ErrorResponseDTO, SuccessResponseDTO)
- Création de toutes les classes d'exception (GBCMException, AuthenticationException, AuthorizationException, ValidationException, ResourceNotFoundException, BusinessRuleException)
- Création des enums métier (ServiceType, WorkshopPackage, PaymentStatus, SessionStatus, InvoiceStatus)
- Amélioration de l'interface AuthService avec documentation complète
- Création de l'interface UserService avec tous les endpoints CRUD
- Documentation Javadoc complète en français sur toutes les classes
- Annotations OpenAPI/Swagger sur toutes les interfaces
- Validation Jakarta sur tous les DTOs
- Compilation réussie du module API
This commit is contained in:
dahoud
2025-10-06 19:53:31 +00:00
parent 95c2552c26
commit f77776820e
24 changed files with 3955 additions and 55 deletions

28
pom.xml
View File

@@ -39,6 +39,13 @@
<version>${jakarta.ws.rs.version}</version> <version>${jakarta.ws.rs.version}</version>
</dependency> </dependency>
<!-- Jakarta Annotation (for @RolesAllowed) -->
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>2.1.1</version>
</dependency>
<!-- Jackson Annotations --> <!-- Jackson Annotations -->
<dependency> <dependency>
<groupId>com.fasterxml.jackson.core</groupId> <groupId>com.fasterxml.jackson.core</groupId>
@@ -70,26 +77,7 @@
<version>3.11.0</version> <version>3.11.0</version>
</plugin> </plugin>
<!-- OpenAPI Generator Plugin -->
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>7.0.1</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>${project.basedir}/src/main/resources/META-INF/openapi.yaml</inputSpec>
<generatorName>java</generatorName>
<configOptions>
<sourceFolder>src/gen/java/main</sourceFolder>
</configOptions>
</configuration>
</execution>
</executions>
</plugin>
</plugins> </plugins>
</build> </build>
</project> </project>

View File

@@ -1,72 +1,148 @@
package com.gbcm.server.api.dto.auth; package com.gbcm.server.api.dto.auth;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Email; import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size; import jakarta.validation.constraints.Size;
/** /**
* DTO pour les requêtes de connexion * DTO pour les requêtes de connexion utilisateur.
* Contient les informations d'authentification nécessaires pour se connecter à la plateforme GBCM.
*
* @author GBCM Development Team
* @version 1.0
* @since 1.0
*/ */
@Schema(description = "Requête de connexion utilisateur") @Schema(description = "Requête de connexion utilisateur à la plateforme GBCM")
public class LoginRequestDTO { public class LoginRequestDTO {
@Schema(description = "Adresse email de l'utilisateur", example = "user@gbcm.com") /**
* Adresse email de l'utilisateur.
* Doit être un email valide et non vide.
*/
@Schema(description = "Adresse email de l'utilisateur",
example = "john.doe@gbcm.com",
required = true,
maxLength = 255)
@JsonProperty("email") @JsonProperty("email")
@NotBlank(message = "L'email est obligatoire") @NotBlank(message = "L'email est obligatoire")
@Email(message = "Format d'email invalide") @Email(message = "Format d'email invalide")
private String email; private String email;
@Schema(description = "Mot de passe", example = "password123") /**
* Mot de passe de l'utilisateur.
* Doit contenir entre 6 et 100 caractères.
*/
@Schema(description = "Mot de passe de l'utilisateur",
example = "motdepasse123",
required = true,
minLength = 6,
maxLength = 100)
@JsonProperty("password") @JsonProperty("password")
@NotBlank(message = "Le mot de passe est obligatoire") @NotBlank(message = "Le mot de passe est obligatoire")
@Size(min = 6, max = 100, message = "Le mot de passe doit contenir entre 6 et 100 caractères") @Size(min = 6, max = 100, message = "Le mot de passe doit contenir entre 6 et 100 caractères")
private String password; private String password;
@Schema(description = "Se souvenir de moi", example = "true") /**
* Indicateur pour maintenir la session active plus longtemps.
* Si true, le token JWT aura une durée de vie étendue.
*/
@Schema(description = "Se souvenir de moi pour une session prolongée",
example = "false",
defaultValue = "false")
@JsonProperty("rememberMe") @JsonProperty("rememberMe")
private boolean rememberMe = false; private boolean rememberMe = false;
// Constructeurs /**
* Constructeur par défaut.
*/
public LoginRequestDTO() {} public LoginRequestDTO() {}
/**
* Constructeur avec email et mot de passe.
*
* @param email l'adresse email de l'utilisateur
* @param password le mot de passe de l'utilisateur
*/
public LoginRequestDTO(String email, String password) { public LoginRequestDTO(String email, String password) {
this.email = email; this.email = email;
this.password = password; this.password = password;
} }
/**
* Constructeur complet.
*
* @param email l'adresse email de l'utilisateur
* @param password le mot de passe de l'utilisateur
* @param rememberMe indicateur pour session prolongée
*/
public LoginRequestDTO(String email, String password, boolean rememberMe) { public LoginRequestDTO(String email, String password, boolean rememberMe) {
this.email = email; this.email = email;
this.password = password; this.password = password;
this.rememberMe = rememberMe; this.rememberMe = rememberMe;
} }
// Getters et Setters /**
* Retourne l'adresse email de l'utilisateur.
*
* @return l'adresse email
*/
public String getEmail() { public String getEmail() {
return email; return email;
} }
/**
* Définit l'adresse email de l'utilisateur.
*
* @param email l'adresse email à définir
*/
public void setEmail(String email) { public void setEmail(String email) {
this.email = email; this.email = email;
} }
/**
* Retourne le mot de passe de l'utilisateur.
*
* @return le mot de passe
*/
public String getPassword() { public String getPassword() {
return password; return password;
} }
/**
* Définit le mot de passe de l'utilisateur.
*
* @param password le mot de passe à définir
*/
public void setPassword(String password) { public void setPassword(String password) {
this.password = password; this.password = password;
} }
/**
* Indique si l'option "se souvenir de moi" est activée.
*
* @return true si l'option est activée, false sinon
*/
public boolean isRememberMe() { public boolean isRememberMe() {
return rememberMe; return rememberMe;
} }
/**
* Définit l'option "se souvenir de moi".
*
* @param rememberMe true pour activer l'option, false sinon
*/
public void setRememberMe(boolean rememberMe) { public void setRememberMe(boolean rememberMe) {
this.rememberMe = rememberMe; this.rememberMe = rememberMe;
} }
/**
* Représentation textuelle de l'objet (sans le mot de passe pour la sécurité).
*
* @return une chaîne représentant l'objet
*/
@Override @Override
public String toString() { public String toString() {
return "LoginRequestDTO{" + return "LoginRequestDTO{" +

View File

@@ -1,49 +1,100 @@
package com.gbcm.server.api.dto.auth; package com.gbcm.server.api.dto.auth;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.gbcm.server.api.dto.user.UserDTO;
import io.swagger.v3.oas.annotations.media.Schema;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.gbcm.server.api.dto.user.UserDTO;
import io.swagger.v3.oas.annotations.media.Schema;
/** /**
* DTO pour les réponses de connexion * DTO pour les réponses de connexion utilisateur.
* Contient le résultat de l'authentification avec les tokens et informations utilisateur.
*
* @author GBCM Development Team
* @version 1.0
* @since 1.0
*/ */
@Schema(description = "Réponse de connexion utilisateur") @Schema(description = "Réponse de connexion utilisateur à la plateforme GBCM")
public class LoginResponseDTO { public class LoginResponseDTO {
@Schema(description = "Succès de la connexion", example = "true") /**
* Indicateur de succès de la connexion.
*/
@Schema(description = "Succès de la connexion",
example = "true",
required = true)
@JsonProperty("success") @JsonProperty("success")
private boolean success; private boolean success;
@Schema(description = "Message de réponse", example = "Connexion réussie") /**
* Message descriptif du résultat de la connexion.
*/
@Schema(description = "Message de réponse",
example = "Connexion réussie",
required = true)
@JsonProperty("message") @JsonProperty("message")
private String message; private String message;
@Schema(description = "Token d'authentification JWT") /**
* Token JWT pour l'authentification des requêtes API.
* Présent seulement en cas de connexion réussie.
*/
@Schema(description = "Token d'authentification JWT",
example = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...")
@JsonProperty("token") @JsonProperty("token")
private String token; private String token;
@Schema(description = "Date d'expiration du token") /**
* Date et heure d'expiration du token.
* Présent seulement en cas de connexion réussie.
*/
@Schema(description = "Date d'expiration du token",
example = "2024-12-31T23:59:59")
@JsonProperty("expiresAt") @JsonProperty("expiresAt")
private LocalDateTime expiresAt; private LocalDateTime expiresAt;
/**
* Informations détaillées de l'utilisateur connecté.
* Présent seulement en cas de connexion réussie.
*/
@Schema(description = "Informations utilisateur") @Schema(description = "Informations utilisateur")
@JsonProperty("user") @JsonProperty("user")
private UserDTO user; private UserDTO user;
@Schema(description = "Token de rafraîchissement") /**
* Token de rafraîchissement pour renouveler le token principal.
* Présent seulement si "rememberMe" était activé.
*/
@Schema(description = "Token de rafraîchissement",
example = "refresh_token_abc123")
@JsonProperty("refreshToken") @JsonProperty("refreshToken")
private String refreshToken; private String refreshToken;
// Constructeurs /**
* Constructeur par défaut.
*/
public LoginResponseDTO() {} public LoginResponseDTO() {}
/**
* Constructeur avec succès et message.
*
* @param success indicateur de succès
* @param message message descriptif
*/
public LoginResponseDTO(boolean success, String message) { public LoginResponseDTO(boolean success, String message) {
this.success = success; this.success = success;
this.message = message; this.message = message;
} }
/**
* Crée une réponse de connexion réussie avec token et utilisateur.
*
* @param token le token JWT généré
* @param expiresAt la date d'expiration du token
* @param user les informations de l'utilisateur connecté
* @return une réponse de succès configurée
*/
public static LoginResponseDTO success(String token, LocalDateTime expiresAt, UserDTO user) { public static LoginResponseDTO success(String token, LocalDateTime expiresAt, UserDTO user) {
LoginResponseDTO response = new LoginResponseDTO(true, "Connexion réussie"); LoginResponseDTO response = new LoginResponseDTO(true, "Connexion réussie");
response.setToken(token); response.setToken(token);
@@ -52,6 +103,27 @@ public class LoginResponseDTO {
return response; return response;
} }
/**
* Crée une réponse de connexion réussie avec token, utilisateur et refresh token.
*
* @param token le token JWT généré
* @param expiresAt la date d'expiration du token
* @param user les informations de l'utilisateur connecté
* @param refreshToken le token de rafraîchissement
* @return une réponse de succès configurée
*/
public static LoginResponseDTO success(String token, LocalDateTime expiresAt, UserDTO user, String refreshToken) {
LoginResponseDTO response = success(token, expiresAt, user);
response.setRefreshToken(refreshToken);
return response;
}
/**
* Crée une réponse d'échec de connexion.
*
* @param message le message d'erreur
* @return une réponse d'échec configurée
*/
public static LoginResponseDTO failure(String message) { public static LoginResponseDTO failure(String message) {
return new LoginResponseDTO(false, message); return new LoginResponseDTO(false, message);
} }

View File

@@ -0,0 +1,214 @@
package com.gbcm.server.api.dto.auth;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
/**
* DTO pour les demandes de réinitialisation de mot de passe.
* Utilisé pour les étapes de demande et de confirmation de réinitialisation.
*
* @author GBCM Development Team
* @version 1.0
* @since 1.0
*/
@Schema(description = "Demande de réinitialisation de mot de passe")
public class PasswordResetDTO {
/**
* Adresse email de l'utilisateur demandant la réinitialisation.
* Obligatoire pour la demande initiale.
*/
@Schema(description = "Adresse email de l'utilisateur",
example = "john.doe@gbcm.com")
@JsonProperty("email")
@Email(message = "Format d'email invalide")
private String email;
/**
* Token de réinitialisation reçu par email.
* Obligatoire pour confirmer la réinitialisation.
*/
@Schema(description = "Token de réinitialisation reçu par email",
example = "abc123def456")
@JsonProperty("resetToken")
private String resetToken;
/**
* Nouveau mot de passe choisi par l'utilisateur.
* Obligatoire pour confirmer la réinitialisation.
*/
@Schema(description = "Nouveau mot de passe",
example = "nouveauMotDePasse123")
@JsonProperty("newPassword")
@Size(min = 6, max = 100, message = "Le mot de passe doit contenir entre 6 et 100 caractères")
private String newPassword;
/**
* Confirmation du nouveau mot de passe.
* Doit être identique au nouveau mot de passe.
*/
@Schema(description = "Confirmation du nouveau mot de passe",
example = "nouveauMotDePasse123")
@JsonProperty("confirmPassword")
@Size(min = 6, max = 100, message = "La confirmation doit contenir entre 6 et 100 caractères")
private String confirmPassword;
/**
* Constructeur par défaut.
*/
public PasswordResetDTO() {}
/**
* Constructeur pour demande de réinitialisation.
*
* @param email l'adresse email de l'utilisateur
*/
public PasswordResetDTO(String email) {
this.email = email;
}
/**
* Constructeur pour confirmation de réinitialisation.
*
* @param resetToken le token de réinitialisation
* @param newPassword le nouveau mot de passe
* @param confirmPassword la confirmation du mot de passe
*/
public PasswordResetDTO(String resetToken, String newPassword, String confirmPassword) {
this.resetToken = resetToken;
this.newPassword = newPassword;
this.confirmPassword = confirmPassword;
}
/**
* Constructeur complet.
*
* @param email l'adresse email
* @param resetToken le token de réinitialisation
* @param newPassword le nouveau mot de passe
* @param confirmPassword la confirmation du mot de passe
*/
public PasswordResetDTO(String email, String resetToken, String newPassword, String confirmPassword) {
this.email = email;
this.resetToken = resetToken;
this.newPassword = newPassword;
this.confirmPassword = confirmPassword;
}
/**
* Retourne l'adresse email de l'utilisateur.
*
* @return l'adresse email
*/
public String getEmail() {
return email;
}
/**
* Définit l'adresse email de l'utilisateur.
*
* @param email l'adresse email à définir
*/
public void setEmail(String email) {
this.email = email;
}
/**
* Retourne le token de réinitialisation.
*
* @return le token de réinitialisation
*/
public String getResetToken() {
return resetToken;
}
/**
* Définit le token de réinitialisation.
*
* @param resetToken le token de réinitialisation à définir
*/
public void setResetToken(String resetToken) {
this.resetToken = resetToken;
}
/**
* Retourne le nouveau mot de passe.
*
* @return le nouveau mot de passe
*/
public String getNewPassword() {
return newPassword;
}
/**
* Définit le nouveau mot de passe.
*
* @param newPassword le nouveau mot de passe à définir
*/
public void setNewPassword(String newPassword) {
this.newPassword = newPassword;
}
/**
* Retourne la confirmation du mot de passe.
*
* @return la confirmation du mot de passe
*/
public String getConfirmPassword() {
return confirmPassword;
}
/**
* Définit la confirmation du mot de passe.
*
* @param confirmPassword la confirmation à définir
*/
public void setConfirmPassword(String confirmPassword) {
this.confirmPassword = confirmPassword;
}
/**
* Vérifie si les mots de passe correspondent.
*
* @return true si les mots de passe correspondent, false sinon
*/
public boolean isPasswordConfirmed() {
return newPassword != null && newPassword.equals(confirmPassword);
}
/**
* Vérifie si c'est une demande de réinitialisation (email seulement).
*
* @return true si c'est une demande, false sinon
*/
public boolean isResetRequest() {
return email != null && resetToken == null && newPassword == null;
}
/**
* Vérifie si c'est une confirmation de réinitialisation (avec token et mot de passe).
*
* @return true si c'est une confirmation, false sinon
*/
public boolean isResetConfirmation() {
return resetToken != null && newPassword != null;
}
/**
* Représentation textuelle de l'objet (sans les mots de passe pour la sécurité).
*
* @return une chaîne représentant l'objet
*/
@Override
public String toString() {
return "PasswordResetDTO{" +
"email='" + email + '\'' +
", hasResetToken=" + (resetToken != null) +
", hasNewPassword=" + (newPassword != null) +
", hasConfirmPassword=" + (confirmPassword != null) +
'}';
}
}

View File

@@ -0,0 +1,229 @@
package com.gbcm.server.api.dto.auth;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDateTime;
/**
* DTO représentant un token JWT avec ses métadonnées.
* Utilisé pour transporter les informations de token entre le client et le serveur.
*
* @author GBCM Development Team
* @version 1.0
* @since 1.0
*/
@Schema(description = "Token JWT avec ses métadonnées")
public class TokenDTO {
/**
* Le token JWT lui-même.
* Utilisé pour l'authentification des requêtes API.
*/
@Schema(description = "Token JWT pour l'authentification",
example = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
required = true)
@JsonProperty("token")
@NotBlank(message = "Le token ne peut pas être vide")
private String token;
/**
* Type du token (généralement "Bearer").
*/
@Schema(description = "Type du token",
example = "Bearer",
required = true)
@JsonProperty("tokenType")
@NotBlank(message = "Le type de token ne peut pas être vide")
private String tokenType = "Bearer";
/**
* Date et heure d'expiration du token.
*/
@Schema(description = "Date et heure d'expiration du token",
example = "2024-12-31T23:59:59",
required = true)
@JsonProperty("expiresAt")
@NotNull(message = "La date d'expiration est obligatoire")
private LocalDateTime expiresAt;
/**
* Token de rafraîchissement pour renouveler le token principal.
* Optionnel, présent seulement si "rememberMe" était activé.
*/
@Schema(description = "Token de rafraîchissement",
example = "refresh_token_abc123")
@JsonProperty("refreshToken")
private String refreshToken;
/**
* Durée de vie du token en secondes.
*/
@Schema(description = "Durée de vie du token en secondes",
example = "3600")
@JsonProperty("expiresIn")
private Long expiresIn;
/**
* Constructeur par défaut.
*/
public TokenDTO() {}
/**
* Constructeur avec token et expiration.
*
* @param token le token JWT
* @param expiresAt la date d'expiration
*/
public TokenDTO(String token, LocalDateTime expiresAt) {
this.token = token;
this.expiresAt = expiresAt;
this.expiresIn = calculateExpiresIn(expiresAt);
}
/**
* Constructeur complet.
*
* @param token le token JWT
* @param tokenType le type de token
* @param expiresAt la date d'expiration
* @param refreshToken le token de rafraîchissement
*/
public TokenDTO(String token, String tokenType, LocalDateTime expiresAt, String refreshToken) {
this.token = token;
this.tokenType = tokenType;
this.expiresAt = expiresAt;
this.refreshToken = refreshToken;
this.expiresIn = calculateExpiresIn(expiresAt);
}
/**
* Calcule la durée de vie en secondes jusqu'à l'expiration.
*
* @param expiresAt la date d'expiration
* @return la durée en secondes, ou null si expiresAt est null
*/
private Long calculateExpiresIn(LocalDateTime expiresAt) {
if (expiresAt == null) {
return null;
}
return java.time.Duration.between(LocalDateTime.now(), expiresAt).getSeconds();
}
/**
* Retourne le token JWT.
*
* @return le token JWT
*/
public String getToken() {
return token;
}
/**
* Définit le token JWT.
*
* @param token le token JWT à définir
*/
public void setToken(String token) {
this.token = token;
}
/**
* Retourne le type de token.
*
* @return le type de token
*/
public String getTokenType() {
return tokenType;
}
/**
* Définit le type de token.
*
* @param tokenType le type de token à définir
*/
public void setTokenType(String tokenType) {
this.tokenType = tokenType;
}
/**
* Retourne la date d'expiration du token.
*
* @return la date d'expiration
*/
public LocalDateTime getExpiresAt() {
return expiresAt;
}
/**
* Définit la date d'expiration du token.
*
* @param expiresAt la date d'expiration à définir
*/
public void setExpiresAt(LocalDateTime expiresAt) {
this.expiresAt = expiresAt;
this.expiresIn = calculateExpiresIn(expiresAt);
}
/**
* Retourne le token de rafraîchissement.
*
* @return le token de rafraîchissement
*/
public String getRefreshToken() {
return refreshToken;
}
/**
* Définit le token de rafraîchissement.
*
* @param refreshToken le token de rafraîchissement à définir
*/
public void setRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
}
/**
* Retourne la durée de vie du token en secondes.
*
* @return la durée de vie en secondes
*/
public Long getExpiresIn() {
return expiresIn;
}
/**
* Définit la durée de vie du token en secondes.
*
* @param expiresIn la durée de vie en secondes
*/
public void setExpiresIn(Long expiresIn) {
this.expiresIn = expiresIn;
}
/**
* Vérifie si le token est expiré.
*
* @return true si le token est expiré, false sinon
*/
public boolean isExpired() {
return expiresAt != null && LocalDateTime.now().isAfter(expiresAt);
}
/**
* Représentation textuelle de l'objet (sans les tokens pour la sécurité).
*
* @return une chaîne représentant l'objet
*/
@Override
public String toString() {
return "TokenDTO{" +
"tokenType='" + tokenType + '\'' +
", expiresAt=" + expiresAt +
", expiresIn=" + expiresIn +
", hasRefreshToken=" + (refreshToken != null) +
'}';
}
}

View File

@@ -0,0 +1,333 @@
package com.gbcm.server.api.dto.common;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
/**
* DTO pour les réponses d'erreur standardisées.
* Utilisé pour retourner des informations d'erreur cohérentes à travers toute l'API.
*
* @author GBCM Development Team
* @version 1.0
* @since 1.0
*/
@Schema(description = "Réponse d'erreur standardisée de l'API GBCM")
public class ErrorResponseDTO {
/**
* Code d'erreur unique pour identifier le type d'erreur.
*/
@Schema(description = "Code d'erreur unique",
example = "VALIDATION_ERROR",
required = true)
@JsonProperty("errorCode")
@NotBlank(message = "Le code d'erreur est obligatoire")
private String errorCode;
/**
* Message d'erreur principal en français.
*/
@Schema(description = "Message d'erreur principal",
example = "Les données fournies ne sont pas valides",
required = true)
@JsonProperty("message")
@NotBlank(message = "Le message d'erreur est obligatoire")
private String message;
/**
* Description détaillée de l'erreur.
*/
@Schema(description = "Description détaillée de l'erreur",
example = "Le champ 'email' doit être une adresse email valide")
@JsonProperty("details")
private String details;
/**
* Timestamp de l'erreur.
*/
@Schema(description = "Timestamp de l'erreur",
example = "2024-01-25T10:30:00",
required = true)
@JsonProperty("timestamp")
@NotNull(message = "Le timestamp est obligatoire")
private LocalDateTime timestamp;
/**
* Chemin de la requête qui a causé l'erreur.
*/
@Schema(description = "Chemin de la requête",
example = "/api/v1/users")
@JsonProperty("path")
private String path;
/**
* Code de statut HTTP.
*/
@Schema(description = "Code de statut HTTP",
example = "400")
@JsonProperty("status")
private Integer status;
/**
* Liste des erreurs de validation spécifiques.
*/
@Schema(description = "Liste des erreurs de validation")
@JsonProperty("validationErrors")
private List<ValidationErrorDTO> validationErrors;
/**
* Métadonnées additionnelles sur l'erreur.
*/
@Schema(description = "Métadonnées additionnelles")
@JsonProperty("metadata")
private Map<String, Object> metadata;
/**
* Constructeur par défaut.
*/
public ErrorResponseDTO() {
this.timestamp = LocalDateTime.now();
}
/**
* Constructeur avec code d'erreur et message.
*
* @param errorCode le code d'erreur
* @param message le message d'erreur
*/
public ErrorResponseDTO(String errorCode, String message) {
this();
this.errorCode = errorCode;
this.message = message;
}
/**
* Constructeur complet.
*
* @param errorCode le code d'erreur
* @param message le message d'erreur
* @param details les détails de l'erreur
* @param path le chemin de la requête
* @param status le code de statut HTTP
*/
public ErrorResponseDTO(String errorCode, String message, String details, String path, Integer status) {
this(errorCode, message);
this.details = details;
this.path = path;
this.status = status;
}
/**
* Crée une réponse d'erreur de validation.
*
* @param message le message d'erreur principal
* @param validationErrors la liste des erreurs de validation
* @return une réponse d'erreur de validation
*/
public static ErrorResponseDTO validationError(String message, List<ValidationErrorDTO> validationErrors) {
ErrorResponseDTO error = new ErrorResponseDTO("VALIDATION_ERROR", message);
error.setValidationErrors(validationErrors);
error.setStatus(400);
return error;
}
/**
* Crée une réponse d'erreur d'authentification.
*
* @param message le message d'erreur
* @return une réponse d'erreur d'authentification
*/
public static ErrorResponseDTO authenticationError(String message) {
ErrorResponseDTO error = new ErrorResponseDTO("AUTHENTICATION_ERROR", message);
error.setStatus(401);
return error;
}
/**
* Crée une réponse d'erreur d'autorisation.
*
* @param message le message d'erreur
* @return une réponse d'erreur d'autorisation
*/
public static ErrorResponseDTO authorizationError(String message) {
ErrorResponseDTO error = new ErrorResponseDTO("AUTHORIZATION_ERROR", message);
error.setStatus(403);
return error;
}
/**
* Crée une réponse d'erreur de ressource non trouvée.
*
* @param message le message d'erreur
* @return une réponse d'erreur de ressource non trouvée
*/
public static ErrorResponseDTO notFoundError(String message) {
ErrorResponseDTO error = new ErrorResponseDTO("RESOURCE_NOT_FOUND", message);
error.setStatus(404);
return error;
}
/**
* Crée une réponse d'erreur interne du serveur.
*
* @param message le message d'erreur
* @return une réponse d'erreur interne
*/
public static ErrorResponseDTO internalError(String message) {
ErrorResponseDTO error = new ErrorResponseDTO("INTERNAL_ERROR", message);
error.setStatus(500);
return error;
}
// Getters et Setters
public String getErrorCode() {
return errorCode;
}
public void setErrorCode(String errorCode) {
this.errorCode = errorCode;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getDetails() {
return details;
}
public void setDetails(String details) {
this.details = details;
}
public LocalDateTime getTimestamp() {
return timestamp;
}
public void setTimestamp(LocalDateTime timestamp) {
this.timestamp = timestamp;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public List<ValidationErrorDTO> getValidationErrors() {
return validationErrors;
}
public void setValidationErrors(List<ValidationErrorDTO> validationErrors) {
this.validationErrors = validationErrors;
}
public Map<String, Object> getMetadata() {
return metadata;
}
public void setMetadata(Map<String, Object> metadata) {
this.metadata = metadata;
}
@Override
public String toString() {
return "ErrorResponseDTO{" +
"errorCode='" + errorCode + '\'' +
", message='" + message + '\'' +
", timestamp=" + timestamp +
", path='" + path + '\'' +
", status=" + status +
'}';
}
/**
* DTO pour les erreurs de validation spécifiques.
*/
@Schema(description = "Erreur de validation spécifique")
public static class ValidationErrorDTO {
/**
* Nom du champ en erreur.
*/
@Schema(description = "Nom du champ en erreur", example = "email")
@JsonProperty("field")
private String field;
/**
* Valeur rejetée.
*/
@Schema(description = "Valeur rejetée", example = "invalid-email")
@JsonProperty("rejectedValue")
private Object rejectedValue;
/**
* Message d'erreur pour ce champ.
*/
@Schema(description = "Message d'erreur pour ce champ", example = "Format d'email invalide")
@JsonProperty("message")
private String message;
public ValidationErrorDTO() {}
public ValidationErrorDTO(String field, Object rejectedValue, String message) {
this.field = field;
this.rejectedValue = rejectedValue;
this.message = message;
}
public String getField() {
return field;
}
public void setField(String field) {
this.field = field;
}
public Object getRejectedValue() {
return rejectedValue;
}
public void setRejectedValue(Object rejectedValue) {
this.rejectedValue = rejectedValue;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public String toString() {
return "ValidationErrorDTO{" +
"field='" + field + '\'' +
", rejectedValue=" + rejectedValue +
", message='" + message + '\'' +
'}';
}
}
}

View File

@@ -0,0 +1,339 @@
package com.gbcm.server.api.dto.common;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import java.util.List;
/**
* DTO générique pour les résultats paginés.
* Encapsule une liste d'éléments avec les métadonnées de pagination.
*
* @param <T> le type des éléments contenus dans la page
* @author GBCM Development Team
* @version 1.0
* @since 1.0
*/
@Schema(description = "Résultat paginé contenant une liste d'éléments et les métadonnées de pagination")
public class PagedResultDTO<T> {
/**
* Liste des éléments de la page courante.
*/
@Schema(description = "Liste des éléments de la page courante",
required = true)
@JsonProperty("content")
@NotNull(message = "Le contenu ne peut pas être null")
private List<T> content;
/**
* Numéro de la page courante (commence à 0).
*/
@Schema(description = "Numéro de la page courante (commence à 0)",
example = "0",
required = true)
@JsonProperty("page")
@Min(value = 0, message = "Le numéro de page doit être >= 0")
private int page;
/**
* Taille de la page (nombre d'éléments par page).
*/
@Schema(description = "Taille de la page (nombre d'éléments par page)",
example = "20",
required = true)
@JsonProperty("size")
@Min(value = 1, message = "La taille de page doit être >= 1")
private int size;
/**
* Nombre total d'éléments disponibles.
*/
@Schema(description = "Nombre total d'éléments disponibles",
example = "150",
required = true)
@JsonProperty("totalElements")
@Min(value = 0, message = "Le nombre total d'éléments doit être >= 0")
private long totalElements;
/**
* Nombre total de pages disponibles.
*/
@Schema(description = "Nombre total de pages disponibles",
example = "8",
required = true)
@JsonProperty("totalPages")
@Min(value = 0, message = "Le nombre total de pages doit être >= 0")
private int totalPages;
/**
* Indique si c'est la première page.
*/
@Schema(description = "Indique si c'est la première page",
example = "true")
@JsonProperty("first")
private boolean first;
/**
* Indique si c'est la dernière page.
*/
@Schema(description = "Indique si c'est la dernière page",
example = "false")
@JsonProperty("last")
private boolean last;
/**
* Nombre d'éléments dans la page courante.
*/
@Schema(description = "Nombre d'éléments dans la page courante",
example = "20")
@JsonProperty("numberOfElements")
private int numberOfElements;
/**
* Indique si la page est vide.
*/
@Schema(description = "Indique si la page est vide",
example = "false")
@JsonProperty("empty")
private boolean empty;
/**
* Constructeur par défaut.
*/
public PagedResultDTO() {}
/**
* Constructeur avec tous les paramètres.
*
* @param content la liste des éléments
* @param page le numéro de page
* @param size la taille de page
* @param totalElements le nombre total d'éléments
*/
public PagedResultDTO(List<T> content, int page, int size, long totalElements) {
this.content = content;
this.page = page;
this.size = size;
this.totalElements = totalElements;
this.numberOfElements = content != null ? content.size() : 0;
this.empty = this.numberOfElements == 0;
this.totalPages = size > 0 ? (int) Math.ceil((double) totalElements / size) : 0;
this.first = page == 0;
this.last = page >= totalPages - 1;
}
/**
* Crée un résultat paginé vide.
*
* @param <T> le type des éléments
* @return un résultat paginé vide
*/
public static <T> PagedResultDTO<T> empty() {
return new PagedResultDTO<>(List.of(), 0, 0, 0);
}
/**
* Crée un résultat paginé avec une seule page.
*
* @param content la liste des éléments
* @param <T> le type des éléments
* @return un résultat paginé avec une seule page
*/
public static <T> PagedResultDTO<T> of(List<T> content) {
return new PagedResultDTO<>(content, 0, content.size(), content.size());
}
/**
* Retourne la liste des éléments de la page courante.
*
* @return la liste des éléments
*/
public List<T> getContent() {
return content;
}
/**
* Définit la liste des éléments de la page courante.
*
* @param content la liste des éléments à définir
*/
public void setContent(List<T> content) {
this.content = content;
this.numberOfElements = content != null ? content.size() : 0;
this.empty = this.numberOfElements == 0;
}
/**
* Retourne le numéro de la page courante.
*
* @return le numéro de page
*/
public int getPage() {
return page;
}
/**
* Définit le numéro de la page courante.
*
* @param page le numéro de page à définir
*/
public void setPage(int page) {
this.page = page;
this.first = page == 0;
this.last = page >= totalPages - 1;
}
/**
* Retourne la taille de la page.
*
* @return la taille de page
*/
public int getSize() {
return size;
}
/**
* Définit la taille de la page.
*
* @param size la taille de page à définir
*/
public void setSize(int size) {
this.size = size;
this.totalPages = size > 0 ? (int) Math.ceil((double) totalElements / size) : 0;
this.last = page >= totalPages - 1;
}
/**
* Retourne le nombre total d'éléments.
*
* @return le nombre total d'éléments
*/
public long getTotalElements() {
return totalElements;
}
/**
* Définit le nombre total d'éléments.
*
* @param totalElements le nombre total d'éléments à définir
*/
public void setTotalElements(long totalElements) {
this.totalElements = totalElements;
this.totalPages = size > 0 ? (int) Math.ceil((double) totalElements / size) : 0;
this.last = page >= totalPages - 1;
}
/**
* Retourne le nombre total de pages.
*
* @return le nombre total de pages
*/
public int getTotalPages() {
return totalPages;
}
/**
* Définit le nombre total de pages.
*
* @param totalPages le nombre total de pages à définir
*/
public void setTotalPages(int totalPages) {
this.totalPages = totalPages;
this.last = page >= totalPages - 1;
}
/**
* Indique si c'est la première page.
*
* @return true si c'est la première page, false sinon
*/
public boolean isFirst() {
return first;
}
/**
* Définit si c'est la première page.
*
* @param first true si c'est la première page, false sinon
*/
public void setFirst(boolean first) {
this.first = first;
}
/**
* Indique si c'est la dernière page.
*
* @return true si c'est la dernière page, false sinon
*/
public boolean isLast() {
return last;
}
/**
* Définit si c'est la dernière page.
*
* @param last true si c'est la dernière page, false sinon
*/
public void setLast(boolean last) {
this.last = last;
}
/**
* Retourne le nombre d'éléments dans la page courante.
*
* @return le nombre d'éléments dans la page courante
*/
public int getNumberOfElements() {
return numberOfElements;
}
/**
* Définit le nombre d'éléments dans la page courante.
*
* @param numberOfElements le nombre d'éléments à définir
*/
public void setNumberOfElements(int numberOfElements) {
this.numberOfElements = numberOfElements;
this.empty = numberOfElements == 0;
}
/**
* Indique si la page est vide.
*
* @return true si la page est vide, false sinon
*/
public boolean isEmpty() {
return empty;
}
/**
* Définit si la page est vide.
*
* @param empty true si la page est vide, false sinon
*/
public void setEmpty(boolean empty) {
this.empty = empty;
}
/**
* Représentation textuelle de l'objet.
*
* @return une chaîne représentant l'objet
*/
@Override
public String toString() {
return "PagedResultDTO{" +
"page=" + page +
", size=" + size +
", totalElements=" + totalElements +
", totalPages=" + totalPages +
", numberOfElements=" + numberOfElements +
", first=" + first +
", last=" + last +
", empty=" + empty +
'}';
}
}

View File

@@ -0,0 +1,305 @@
package com.gbcm.server.api.dto.common;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDateTime;
import java.util.Map;
/**
* DTO pour les réponses de succès standardisées.
* Utilisé pour retourner des confirmations d'opérations réussies avec des données optionnelles.
*
* @param <T> le type des données retournées
* @author GBCM Development Team
* @version 1.0
* @since 1.0
*/
@Schema(description = "Réponse de succès standardisée de l'API GBCM")
public class SuccessResponseDTO<T> {
/**
* Indicateur de succès (toujours true pour cette classe).
*/
@Schema(description = "Indicateur de succès",
example = "true",
required = true)
@JsonProperty("success")
@NotNull(message = "L'indicateur de succès est obligatoire")
private boolean success = true;
/**
* Message de succès en français.
*/
@Schema(description = "Message de succès",
example = "Opération réalisée avec succès",
required = true)
@JsonProperty("message")
@NotBlank(message = "Le message de succès est obligatoire")
private String message;
/**
* Données retournées par l'opération.
*/
@Schema(description = "Données retournées par l'opération")
@JsonProperty("data")
private T data;
/**
* Timestamp de la réponse.
*/
@Schema(description = "Timestamp de la réponse",
example = "2024-01-25T10:30:00",
required = true)
@JsonProperty("timestamp")
@NotNull(message = "Le timestamp est obligatoire")
private LocalDateTime timestamp;
/**
* Code de statut HTTP.
*/
@Schema(description = "Code de statut HTTP",
example = "200")
@JsonProperty("status")
private Integer status;
/**
* Métadonnées additionnelles.
*/
@Schema(description = "Métadonnées additionnelles")
@JsonProperty("metadata")
private Map<String, Object> metadata;
/**
* Constructeur par défaut.
*/
public SuccessResponseDTO() {
this.timestamp = LocalDateTime.now();
}
/**
* Constructeur avec message.
*
* @param message le message de succès
*/
public SuccessResponseDTO(String message) {
this();
this.message = message;
}
/**
* Constructeur avec message et données.
*
* @param message le message de succès
* @param data les données à retourner
*/
public SuccessResponseDTO(String message, T data) {
this(message);
this.data = data;
}
/**
* Constructeur complet.
*
* @param message le message de succès
* @param data les données à retourner
* @param status le code de statut HTTP
*/
public SuccessResponseDTO(String message, T data, Integer status) {
this(message, data);
this.status = status;
}
/**
* Crée une réponse de succès simple.
*
* @param message le message de succès
* @param <T> le type des données
* @return une réponse de succès
*/
public static <T> SuccessResponseDTO<T> of(String message) {
return new SuccessResponseDTO<>(message);
}
/**
* Crée une réponse de succès avec données.
*
* @param message le message de succès
* @param data les données à retourner
* @param <T> le type des données
* @return une réponse de succès avec données
*/
public static <T> SuccessResponseDTO<T> of(String message, T data) {
return new SuccessResponseDTO<>(message, data);
}
/**
* Crée une réponse de création réussie (201).
*
* @param message le message de succès
* @param data les données créées
* @param <T> le type des données
* @return une réponse de création réussie
*/
public static <T> SuccessResponseDTO<T> created(String message, T data) {
return new SuccessResponseDTO<>(message, data, 201);
}
/**
* Crée une réponse de mise à jour réussie.
*
* @param message le message de succès
* @param data les données mises à jour
* @param <T> le type des données
* @return une réponse de mise à jour réussie
*/
public static <T> SuccessResponseDTO<T> updated(String message, T data) {
return new SuccessResponseDTO<>(message, data, 200);
}
/**
* Crée une réponse de suppression réussie.
*
* @param message le message de succès
* @return une réponse de suppression réussie
*/
public static SuccessResponseDTO<Void> deleted(String message) {
return new SuccessResponseDTO<>(message, null, 200);
}
/**
* Crée une réponse sans contenu (204).
*
* @param message le message de succès
* @return une réponse sans contenu
*/
public static SuccessResponseDTO<Void> noContent(String message) {
return new SuccessResponseDTO<>(message, null, 204);
}
/**
* Retourne l'indicateur de succès.
*
* @return true (toujours pour cette classe)
*/
public boolean isSuccess() {
return success;
}
/**
* Définit l'indicateur de succès.
*
* @param success l'indicateur de succès
*/
public void setSuccess(boolean success) {
this.success = success;
}
/**
* Retourne le message de succès.
*
* @return le message de succès
*/
public String getMessage() {
return message;
}
/**
* Définit le message de succès.
*
* @param message le message de succès à définir
*/
public void setMessage(String message) {
this.message = message;
}
/**
* Retourne les données de la réponse.
*
* @return les données
*/
public T getData() {
return data;
}
/**
* Définit les données de la réponse.
*
* @param data les données à définir
*/
public void setData(T data) {
this.data = data;
}
/**
* Retourne le timestamp de la réponse.
*
* @return le timestamp
*/
public LocalDateTime getTimestamp() {
return timestamp;
}
/**
* Définit le timestamp de la réponse.
*
* @param timestamp le timestamp à définir
*/
public void setTimestamp(LocalDateTime timestamp) {
this.timestamp = timestamp;
}
/**
* Retourne le code de statut HTTP.
*
* @return le code de statut
*/
public Integer getStatus() {
return status;
}
/**
* Définit le code de statut HTTP.
*
* @param status le code de statut à définir
*/
public void setStatus(Integer status) {
this.status = status;
}
/**
* Retourne les métadonnées additionnelles.
*
* @return les métadonnées
*/
public Map<String, Object> getMetadata() {
return metadata;
}
/**
* Définit les métadonnées additionnelles.
*
* @param metadata les métadonnées à définir
*/
public void setMetadata(Map<String, Object> metadata) {
this.metadata = metadata;
}
/**
* Représentation textuelle de l'objet.
*
* @return une chaîne représentant l'objet
*/
@Override
public String toString() {
return "SuccessResponseDTO{" +
"success=" + success +
", message='" + message + '\'' +
", timestamp=" + timestamp +
", status=" + status +
", hasData=" + (data != null) +
'}';
}
}

View File

@@ -0,0 +1,278 @@
package com.gbcm.server.api.dto.user;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.gbcm.server.api.enums.UserRole;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
/**
* DTO pour la création d'un nouvel utilisateur.
* Contient toutes les informations nécessaires pour créer un compte utilisateur.
*
* @author GBCM Development Team
* @version 1.0
* @since 1.0
*/
@Schema(description = "Données pour la création d'un nouvel utilisateur")
public class CreateUserDTO {
/**
* Prénom de l'utilisateur.
*/
@Schema(description = "Prénom de l'utilisateur",
example = "John",
required = true,
maxLength = 50)
@JsonProperty("firstName")
@NotBlank(message = "Le prénom est obligatoire")
@Size(max = 50, message = "Le prénom ne peut pas dépasser 50 caractères")
private String firstName;
/**
* Nom de famille de l'utilisateur.
*/
@Schema(description = "Nom de famille de l'utilisateur",
example = "Doe",
required = true,
maxLength = 50)
@JsonProperty("lastName")
@NotBlank(message = "Le nom est obligatoire")
@Size(max = 50, message = "Le nom ne peut pas dépasser 50 caractères")
private String lastName;
/**
* Adresse email unique de l'utilisateur.
* Sera utilisée pour l'authentification.
*/
@Schema(description = "Adresse email unique de l'utilisateur",
example = "john.doe@gbcm.com",
required = true,
maxLength = 255)
@JsonProperty("email")
@NotBlank(message = "L'email est obligatoire")
@Email(message = "Format d'email invalide")
@Size(max = 255, message = "L'email ne peut pas dépasser 255 caractères")
private String email;
/**
* Mot de passe initial de l'utilisateur.
* Sera haché avant stockage en base.
*/
@Schema(description = "Mot de passe initial de l'utilisateur",
example = "motdepasse123",
required = true,
minLength = 6,
maxLength = 100)
@JsonProperty("password")
@NotBlank(message = "Le mot de passe est obligatoire")
@Size(min = 6, max = 100, message = "Le mot de passe doit contenir entre 6 et 100 caractères")
private String password;
/**
* Numéro de téléphone de l'utilisateur.
* Format international recommandé.
*/
@Schema(description = "Numéro de téléphone de l'utilisateur",
example = "+1-404-555-0123",
maxLength = 20)
@JsonProperty("phone")
@Size(max = 20, message = "Le téléphone ne peut pas dépasser 20 caractères")
private String phone;
/**
* Rôle de l'utilisateur dans le système.
* Détermine les permissions initiales.
*/
@Schema(description = "Rôle de l'utilisateur dans le système",
required = true)
@JsonProperty("role")
@NotNull(message = "Le rôle est obligatoire")
private UserRole role;
/**
* Statut d'activation initial du compte.
* Par défaut, les comptes sont créés actifs.
*/
@Schema(description = "Statut d'activation initial du compte",
example = "true",
defaultValue = "true")
@JsonProperty("active")
private boolean active = true;
/**
* Constructeur par défaut.
*/
public CreateUserDTO() {}
/**
* Constructeur avec les champs obligatoires.
*
* @param firstName le prénom de l'utilisateur
* @param lastName le nom de famille de l'utilisateur
* @param email l'adresse email de l'utilisateur
* @param password le mot de passe initial
* @param role le rôle de l'utilisateur
*/
public CreateUserDTO(String firstName, String lastName, String email, String password, UserRole role) {
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.password = password;
this.role = role;
}
/**
* Retourne le prénom de l'utilisateur.
*
* @return le prénom
*/
public String getFirstName() {
return firstName;
}
/**
* Définit le prénom de l'utilisateur.
*
* @param firstName le prénom à définir
*/
public void setFirstName(String firstName) {
this.firstName = firstName;
}
/**
* Retourne le nom de famille de l'utilisateur.
*
* @return le nom de famille
*/
public String getLastName() {
return lastName;
}
/**
* Définit le nom de famille de l'utilisateur.
*
* @param lastName le nom de famille à définir
*/
public void setLastName(String lastName) {
this.lastName = lastName;
}
/**
* Retourne l'adresse email de l'utilisateur.
*
* @return l'adresse email
*/
public String getEmail() {
return email;
}
/**
* Définit l'adresse email de l'utilisateur.
*
* @param email l'adresse email à définir
*/
public void setEmail(String email) {
this.email = email;
}
/**
* Retourne le mot de passe de l'utilisateur.
*
* @return le mot de passe
*/
public String getPassword() {
return password;
}
/**
* Définit le mot de passe de l'utilisateur.
*
* @param password le mot de passe à définir
*/
public void setPassword(String password) {
this.password = password;
}
/**
* Retourne le numéro de téléphone de l'utilisateur.
*
* @return le numéro de téléphone
*/
public String getPhone() {
return phone;
}
/**
* Définit le numéro de téléphone de l'utilisateur.
*
* @param phone le numéro de téléphone à définir
*/
public void setPhone(String phone) {
this.phone = phone;
}
/**
* Retourne le rôle de l'utilisateur.
*
* @return le rôle
*/
public UserRole getRole() {
return role;
}
/**
* Définit le rôle de l'utilisateur.
*
* @param role le rôle à définir
*/
public void setRole(UserRole role) {
this.role = role;
}
/**
* Indique si le compte est actif.
*
* @return true si le compte est actif, false sinon
*/
public boolean isActive() {
return active;
}
/**
* Définit le statut d'activation du compte.
*
* @param active true pour activer le compte, false sinon
*/
public void setActive(boolean active) {
this.active = active;
}
/**
* Retourne le nom complet de l'utilisateur.
*
* @return le nom complet (prénom + nom)
*/
public String getFullName() {
return firstName + " " + lastName;
}
/**
* Représentation textuelle de l'objet (sans le mot de passe pour la sécurité).
*
* @return une chaîne représentant l'objet
*/
@Override
public String toString() {
return "CreateUserDTO{" +
"firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", email='" + email + '\'' +
", phone='" + phone + '\'' +
", role=" + role +
", active=" + active +
'}';
}
}

View File

@@ -0,0 +1,245 @@
package com.gbcm.server.api.dto.user;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.gbcm.server.api.enums.UserRole;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.Size;
/**
* DTO pour la mise à jour d'un utilisateur existant.
* Tous les champs sont optionnels, seuls les champs fournis seront mis à jour.
*
* @author GBCM Development Team
* @version 1.0
* @since 1.0
*/
@Schema(description = "Données pour la mise à jour d'un utilisateur existant")
public class UpdateUserDTO {
/**
* Nouveau prénom de l'utilisateur.
* Optionnel - si fourni, remplace le prénom existant.
*/
@Schema(description = "Nouveau prénom de l'utilisateur",
example = "John",
maxLength = 50)
@JsonProperty("firstName")
@Size(max = 50, message = "Le prénom ne peut pas dépasser 50 caractères")
private String firstName;
/**
* Nouveau nom de famille de l'utilisateur.
* Optionnel - si fourni, remplace le nom existant.
*/
@Schema(description = "Nouveau nom de famille de l'utilisateur",
example = "Doe",
maxLength = 50)
@JsonProperty("lastName")
@Size(max = 50, message = "Le nom ne peut pas dépasser 50 caractères")
private String lastName;
/**
* Nouvelle adresse email de l'utilisateur.
* Optionnel - si fourni, doit être unique dans le système.
*/
@Schema(description = "Nouvelle adresse email de l'utilisateur",
example = "john.doe@gbcm.com",
maxLength = 255)
@JsonProperty("email")
@Email(message = "Format d'email invalide")
@Size(max = 255, message = "L'email ne peut pas dépasser 255 caractères")
private String email;
/**
* Nouveau numéro de téléphone de l'utilisateur.
* Optionnel - format international recommandé.
*/
@Schema(description = "Nouveau numéro de téléphone de l'utilisateur",
example = "+1-404-555-0123",
maxLength = 20)
@JsonProperty("phone")
@Size(max = 20, message = "Le téléphone ne peut pas dépasser 20 caractères")
private String phone;
/**
* Nouveau rôle de l'utilisateur dans le système.
* Optionnel - modifie les permissions de l'utilisateur.
*/
@Schema(description = "Nouveau rôle de l'utilisateur dans le système")
@JsonProperty("role")
private UserRole role;
/**
* Nouveau statut d'activation du compte.
* Optionnel - permet d'activer/désactiver le compte.
*/
@Schema(description = "Nouveau statut d'activation du compte",
example = "true")
@JsonProperty("active")
private Boolean active;
/**
* Constructeur par défaut.
*/
public UpdateUserDTO() {}
/**
* Constructeur avec prénom et nom.
*
* @param firstName le nouveau prénom
* @param lastName le nouveau nom de famille
*/
public UpdateUserDTO(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
/**
* Retourne le nouveau prénom de l'utilisateur.
*
* @return le nouveau prénom, ou null si non modifié
*/
public String getFirstName() {
return firstName;
}
/**
* Définit le nouveau prénom de l'utilisateur.
*
* @param firstName le nouveau prénom à définir
*/
public void setFirstName(String firstName) {
this.firstName = firstName;
}
/**
* Retourne le nouveau nom de famille de l'utilisateur.
*
* @return le nouveau nom de famille, ou null si non modifié
*/
public String getLastName() {
return lastName;
}
/**
* Définit le nouveau nom de famille de l'utilisateur.
*
* @param lastName le nouveau nom de famille à définir
*/
public void setLastName(String lastName) {
this.lastName = lastName;
}
/**
* Retourne la nouvelle adresse email de l'utilisateur.
*
* @return la nouvelle adresse email, ou null si non modifiée
*/
public String getEmail() {
return email;
}
/**
* Définit la nouvelle adresse email de l'utilisateur.
*
* @param email la nouvelle adresse email à définir
*/
public void setEmail(String email) {
this.email = email;
}
/**
* Retourne le nouveau numéro de téléphone de l'utilisateur.
*
* @return le nouveau numéro de téléphone, ou null si non modifié
*/
public String getPhone() {
return phone;
}
/**
* Définit le nouveau numéro de téléphone de l'utilisateur.
*
* @param phone le nouveau numéro de téléphone à définir
*/
public void setPhone(String phone) {
this.phone = phone;
}
/**
* Retourne le nouveau rôle de l'utilisateur.
*
* @return le nouveau rôle, ou null si non modifié
*/
public UserRole getRole() {
return role;
}
/**
* Définit le nouveau rôle de l'utilisateur.
*
* @param role le nouveau rôle à définir
*/
public void setRole(UserRole role) {
this.role = role;
}
/**
* Retourne le nouveau statut d'activation du compte.
*
* @return le nouveau statut, ou null si non modifié
*/
public Boolean getActive() {
return active;
}
/**
* Définit le nouveau statut d'activation du compte.
*
* @param active le nouveau statut à définir
*/
public void setActive(Boolean active) {
this.active = active;
}
/**
* Vérifie si au moins un champ est fourni pour la mise à jour.
*
* @return true si au moins un champ est non null, false sinon
*/
public boolean hasUpdates() {
return firstName != null || lastName != null || email != null ||
phone != null || role != null || active != null;
}
/**
* Retourne le nom complet si prénom et nom sont fournis.
*
* @return le nom complet, ou null si prénom ou nom manquant
*/
public String getFullName() {
if (firstName != null && lastName != null) {
return firstName + " " + lastName;
}
return null;
}
/**
* Représentation textuelle de l'objet.
*
* @return une chaîne représentant l'objet
*/
@Override
public String toString() {
return "UpdateUserDTO{" +
"firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", email='" + email + '\'' +
", phone='" + phone + '\'' +
", role=" + role +
", active=" + active +
'}';
}
}

View File

@@ -1,62 +1,124 @@
package com.gbcm.server.api.dto.user; package com.gbcm.server.api.dto.user;
import java.time.LocalDateTime;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.gbcm.server.api.enums.UserRole; import com.gbcm.server.api.enums.UserRole;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Email; import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import java.time.LocalDateTime;
/** /**
* DTO pour les informations utilisateur * DTO pour les informations utilisateur de la plateforme GBCM.
* Contient toutes les données nécessaires pour représenter un utilisateur côté client.
*
* @author GBCM Development Team
* @version 1.0
* @since 1.0
*/ */
@Schema(description = "Informations utilisateur") @Schema(description = "Informations utilisateur de la plateforme GBCM")
public class UserDTO { public class UserDTO {
@Schema(description = "Identifiant unique", example = "1") /**
* Identifiant unique de l'utilisateur.
*/
@Schema(description = "Identifiant unique de l'utilisateur",
example = "1",
accessMode = Schema.AccessMode.READ_ONLY)
@JsonProperty("id") @JsonProperty("id")
private Long id; private Long id;
@Schema(description = "Prénom", example = "John") /**
* Prénom de l'utilisateur.
*/
@Schema(description = "Prénom de l'utilisateur",
example = "John",
required = true,
maxLength = 50)
@JsonProperty("firstName") @JsonProperty("firstName")
@NotBlank(message = "Le prénom est obligatoire") @NotBlank(message = "Le prénom est obligatoire")
private String firstName; private String firstName;
@Schema(description = "Nom de famille", example = "Doe") /**
* Nom de famille de l'utilisateur.
*/
@Schema(description = "Nom de famille de l'utilisateur",
example = "Doe",
required = true,
maxLength = 50)
@JsonProperty("lastName") @JsonProperty("lastName")
@NotBlank(message = "Le nom est obligatoire") @NotBlank(message = "Le nom est obligatoire")
private String lastName; private String lastName;
@Schema(description = "Adresse email", example = "john.doe@gbcm.com") /**
* Adresse email unique de l'utilisateur.
* Utilisée pour l'authentification et les communications.
*/
@Schema(description = "Adresse email unique de l'utilisateur",
example = "john.doe@gbcm.com",
required = true,
maxLength = 255)
@JsonProperty("email") @JsonProperty("email")
@NotBlank(message = "L'email est obligatoire") @NotBlank(message = "L'email est obligatoire")
@Email(message = "Format d'email invalide") @Email(message = "Format d'email invalide")
private String email; private String email;
@Schema(description = "Numéro de téléphone", example = "+1234567890") /**
* Numéro de téléphone de l'utilisateur.
* Format international recommandé.
*/
@Schema(description = "Numéro de téléphone de l'utilisateur",
example = "+1-404-555-0123",
maxLength = 20)
@JsonProperty("phone") @JsonProperty("phone")
private String phone; private String phone;
@Schema(description = "Rôle utilisateur") /**
* Rôle de l'utilisateur dans le système.
* Détermine les permissions et l'accès aux fonctionnalités.
*/
@Schema(description = "Rôle de l'utilisateur dans le système",
required = true)
@JsonProperty("role") @JsonProperty("role")
@NotNull(message = "Le rôle est obligatoire") @NotNull(message = "Le rôle est obligatoire")
private UserRole role; private UserRole role;
@Schema(description = "Statut actif", example = "true") /**
* Statut d'activation du compte utilisateur.
* Les comptes inactifs ne peuvent pas se connecter.
*/
@Schema(description = "Statut d'activation du compte",
example = "true",
defaultValue = "true")
@JsonProperty("active") @JsonProperty("active")
private boolean active = true; private boolean active = true;
@Schema(description = "Date de création") /**
* Date et heure de création du compte.
*/
@Schema(description = "Date et heure de création du compte",
example = "2024-01-15T10:30:00",
accessMode = Schema.AccessMode.READ_ONLY)
@JsonProperty("createdAt") @JsonProperty("createdAt")
private LocalDateTime createdAt; private LocalDateTime createdAt;
@Schema(description = "Date de dernière modification") /**
* Date et heure de dernière modification du profil.
*/
@Schema(description = "Date et heure de dernière modification",
example = "2024-01-20T14:45:00",
accessMode = Schema.AccessMode.READ_ONLY)
@JsonProperty("updatedAt") @JsonProperty("updatedAt")
private LocalDateTime updatedAt; private LocalDateTime updatedAt;
@Schema(description = "Date de dernière connexion") /**
* Date et heure de dernière connexion réussie.
*/
@Schema(description = "Date et heure de dernière connexion",
example = "2024-01-25T09:15:00",
accessMode = Schema.AccessMode.READ_ONLY)
@JsonProperty("lastLoginAt") @JsonProperty("lastLoginAt")
private LocalDateTime lastLoginAt; private LocalDateTime lastLoginAt;

View File

@@ -0,0 +1,236 @@
package com.gbcm.server.api.enums;
import com.fasterxml.jackson.annotation.JsonValue;
/**
* Énumération des statuts de facture dans le système GBCM.
* Définit les différents états possibles d'une facture.
*
* @author GBCM Development Team
* @version 1.0
* @since 1.0
*/
public enum InvoiceStatus {
/**
* Facture en brouillon, pas encore envoyée.
*/
DRAFT("draft", "Brouillon", "La facture est en cours de préparation"),
/**
* Facture envoyée au client.
*/
SENT("sent", "Envoyée", "La facture a été envoyée au client"),
/**
* Facture payée intégralement.
*/
PAID("paid", "Payée", "La facture a été payée intégralement"),
/**
* Facture partiellement payée.
*/
PARTIALLY_PAID("partially_paid", "Partiellement payée",
"La facture a été partiellement payée"),
/**
* Facture en retard (dépassé la date d'échéance).
*/
OVERDUE("overdue", "En retard", "La facture a dépassé la date d'échéance"),
/**
* Facture annulée.
*/
CANCELLED("cancelled", "Annulée", "La facture a été annulée"),
/**
* Facture remboursée.
*/
REFUNDED("refunded", "Remboursée", "La facture a été remboursée"),
/**
* Facture en litige.
*/
DISPUTED("disputed", "En litige", "La facture fait l'objet d'un litige");
/**
* Code unique du statut de facture.
*/
private final String code;
/**
* Nom d'affichage du statut.
*/
private final String displayName;
/**
* Description détaillée du statut.
*/
private final String description;
/**
* Constructeur de l'énumération.
*
* @param code le code unique du statut
* @param displayName le nom d'affichage
* @param description la description détaillée
*/
InvoiceStatus(String code, String displayName, String description) {
this.code = code;
this.displayName = displayName;
this.description = description;
}
/**
* Retourne le code unique du statut de facture.
*
* @return le code du statut
*/
@JsonValue
public String getCode() {
return code;
}
/**
* Retourne le nom d'affichage du statut.
*
* @return le nom d'affichage
*/
public String getDisplayName() {
return displayName;
}
/**
* Retourne la description détaillée du statut.
*
* @return la description
*/
public String getDescription() {
return description;
}
/**
* Trouve un statut de facture par son code.
*
* @param code le code à rechercher
* @return le statut correspondant
* @throws IllegalArgumentException si le code n'est pas trouvé
*/
public static InvoiceStatus fromCode(String code) {
if (code == null) {
throw new IllegalArgumentException("Le code ne peut pas être null");
}
for (InvoiceStatus status : values()) {
if (status.code.equals(code)) {
return status;
}
}
throw new IllegalArgumentException("Statut de facture non trouvé pour le code: " + code);
}
/**
* Vérifie si la facture est en brouillon.
*
* @return true si la facture est en brouillon, false sinon
*/
public boolean isDraft() {
return this == DRAFT;
}
/**
* Vérifie si la facture est finalisée (envoyée ou plus).
*
* @return true si la facture est finalisée, false sinon
*/
public boolean isFinalized() {
return this != DRAFT;
}
/**
* Vérifie si la facture est payée (intégralement ou partiellement).
*
* @return true si la facture est payée, false sinon
*/
public boolean isPaid() {
return this == PAID || this == PARTIALLY_PAID;
}
/**
* Vérifie si la facture est entièrement payée.
*
* @return true si la facture est entièrement payée, false sinon
*/
public boolean isFullyPaid() {
return this == PAID;
}
/**
* Vérifie si la facture est en attente de paiement.
*
* @return true si la facture est en attente de paiement, false sinon
*/
public boolean isPendingPayment() {
return this == SENT || this == PARTIALLY_PAID || this == OVERDUE;
}
/**
* Vérifie si la facture peut être modifiée.
*
* @return true si la facture peut être modifiée, false sinon
*/
public boolean canBeModified() {
return this == DRAFT;
}
/**
* Vérifie si la facture peut être annulée.
*
* @return true si la facture peut être annulée, false sinon
*/
public boolean canBeCancelled() {
return this == DRAFT || this == SENT || this == OVERDUE;
}
/**
* Vérifie si la facture peut être remboursée.
*
* @return true si la facture peut être remboursée, false sinon
*/
public boolean canBeRefunded() {
return this == PAID || this == PARTIALLY_PAID;
}
/**
* Vérifie si la facture nécessite une action urgente.
*
* @return true si une action urgente est nécessaire, false sinon
*/
public boolean requiresUrgentAction() {
return this == OVERDUE || this == DISPUTED;
}
/**
* Vérifie si la facture génère des revenus.
*
* @return true si la facture génère des revenus, false sinon
*/
public boolean generatesRevenue() {
return this == PAID || this == PARTIALLY_PAID;
}
/**
* Représentation textuelle de l'énumération.
*
* @return une chaîne représentant le statut de facture
*/
@Override
public String toString() {
return "InvoiceStatus{" +
"code='" + code + '\'' +
", displayName='" + displayName + '\'' +
", description='" + description + '\'' +
'}';
}
}

View File

@@ -0,0 +1,196 @@
package com.gbcm.server.api.enums;
import com.fasterxml.jackson.annotation.JsonValue;
/**
* Énumération des statuts de paiement dans le système GBCM.
* Définit les différents états possibles d'un paiement.
*
* @author GBCM Development Team
* @version 1.0
* @since 1.0
*/
public enum PaymentStatus {
/**
* Paiement en attente de traitement.
*/
PENDING("pending", "En attente", "Le paiement est en cours de traitement"),
/**
* Paiement effectué avec succès.
*/
PAID("paid", "Payé", "Le paiement a été effectué avec succès"),
/**
* Paiement en retard (dépassé la date d'échéance).
*/
OVERDUE("overdue", "En retard", "Le paiement a dépassé la date d'échéance"),
/**
* Paiement annulé.
*/
CANCELLED("cancelled", "Annulé", "Le paiement a été annulé"),
/**
* Paiement échoué.
*/
FAILED("failed", "Échoué", "Le paiement a échoué lors du traitement"),
/**
* Paiement remboursé.
*/
REFUNDED("refunded", "Remboursé", "Le paiement a été remboursé"),
/**
* Paiement partiellement remboursé.
*/
PARTIALLY_REFUNDED("partially_refunded", "Partiellement remboursé",
"Le paiement a été partiellement remboursé");
/**
* Code unique du statut de paiement.
*/
private final String code;
/**
* Nom d'affichage du statut.
*/
private final String displayName;
/**
* Description détaillée du statut.
*/
private final String description;
/**
* Constructeur de l'énumération.
*
* @param code le code unique du statut
* @param displayName le nom d'affichage
* @param description la description détaillée
*/
PaymentStatus(String code, String displayName, String description) {
this.code = code;
this.displayName = displayName;
this.description = description;
}
/**
* Retourne le code unique du statut de paiement.
*
* @return le code du statut
*/
@JsonValue
public String getCode() {
return code;
}
/**
* Retourne le nom d'affichage du statut.
*
* @return le nom d'affichage
*/
public String getDisplayName() {
return displayName;
}
/**
* Retourne la description détaillée du statut.
*
* @return la description
*/
public String getDescription() {
return description;
}
/**
* Trouve un statut de paiement par son code.
*
* @param code le code à rechercher
* @return le statut correspondant
* @throws IllegalArgumentException si le code n'est pas trouvé
*/
public static PaymentStatus fromCode(String code) {
if (code == null) {
throw new IllegalArgumentException("Le code ne peut pas être null");
}
for (PaymentStatus status : values()) {
if (status.code.equals(code)) {
return status;
}
}
throw new IllegalArgumentException("Statut de paiement non trouvé pour le code: " + code);
}
/**
* Vérifie si le paiement est finalisé (succès ou échec définitif).
*
* @return true si le paiement est finalisé, false sinon
*/
public boolean isFinal() {
return this == PAID || this == CANCELLED || this == FAILED ||
this == REFUNDED || this == PARTIALLY_REFUNDED;
}
/**
* Vérifie si le paiement est en cours de traitement.
*
* @return true si le paiement est en cours, false sinon
*/
public boolean isPending() {
return this == PENDING;
}
/**
* Vérifie si le paiement est réussi.
*
* @return true si le paiement est réussi, false sinon
*/
public boolean isSuccessful() {
return this == PAID;
}
/**
* Vérifie si le paiement a échoué.
*
* @return true si le paiement a échoué, false sinon
*/
public boolean isFailed() {
return this == FAILED || this == CANCELLED;
}
/**
* Vérifie si le paiement nécessite une action.
*
* @return true si une action est nécessaire, false sinon
*/
public boolean requiresAction() {
return this == PENDING || this == OVERDUE;
}
/**
* Vérifie si le paiement peut être remboursé.
*
* @return true si le paiement peut être remboursé, false sinon
*/
public boolean canBeRefunded() {
return this == PAID || this == PARTIALLY_REFUNDED;
}
/**
* Représentation textuelle de l'énumération.
*
* @return une chaîne représentant le statut de paiement
*/
@Override
public String toString() {
return "PaymentStatus{" +
"code='" + code + '\'' +
", displayName='" + displayName + '\'' +
", description='" + description + '\'' +
'}';
}
}

View File

@@ -0,0 +1,156 @@
package com.gbcm.server.api.enums;
import com.fasterxml.jackson.annotation.JsonValue;
/**
* Énumération des types de services offerts par GBCM.
* Définit les différents services disponibles sur la plateforme.
*
* @author GBCM Development Team
* @version 1.0
* @since 1.0
*/
public enum ServiceType {
/**
* Ateliers stratégiques - Programmes de 2 ans avec différents niveaux.
*/
STRATEGIC_WORKSHOP("strategic_workshop", "Atelier Stratégique",
"Programme de développement stratégique sur 2 ans"),
/**
* Coaching individuel - Sessions one-on-one avec un coach.
*/
ONE_ON_ONE_COACHING("one_on_one_coaching", "Coaching Individuel",
"Sessions de coaching personnalisées en tête-à-tête"),
/**
* Coaching à la demande - Abonnement mensuel pour coaching flexible.
*/
ON_DEMAND_COACHING("on_demand_coaching", "Coaching à la Demande",
"Abonnement mensuel pour coaching flexible"),
/**
* Projets spéciaux - Consulting personnalisé pour besoins spécifiques.
*/
SPECIAL_PROJECT("special_project", "Projet Spécial",
"Consulting personnalisé pour besoins spécifiques");
/**
* Code unique du type de service.
*/
private final String code;
/**
* Nom d'affichage du type de service.
*/
private final String displayName;
/**
* Description détaillée du type de service.
*/
private final String description;
/**
* Constructeur de l'énumération.
*
* @param code le code unique du service
* @param displayName le nom d'affichage
* @param description la description détaillée
*/
ServiceType(String code, String displayName, String description) {
this.code = code;
this.displayName = displayName;
this.description = description;
}
/**
* Retourne le code unique du type de service.
*
* @return le code du service
*/
@JsonValue
public String getCode() {
return code;
}
/**
* Retourne le nom d'affichage du type de service.
*
* @return le nom d'affichage
*/
public String getDisplayName() {
return displayName;
}
/**
* Retourne la description détaillée du type de service.
*
* @return la description
*/
public String getDescription() {
return description;
}
/**
* Trouve un type de service par son code.
*
* @param code le code à rechercher
* @return le type de service correspondant
* @throws IllegalArgumentException si le code n'est pas trouvé
*/
public static ServiceType fromCode(String code) {
if (code == null) {
throw new IllegalArgumentException("Le code ne peut pas être null");
}
for (ServiceType type : values()) {
if (type.code.equals(code)) {
return type;
}
}
throw new IllegalArgumentException("Type de service non trouvé pour le code: " + code);
}
/**
* Vérifie si le service nécessite un abonnement.
*
* @return true si le service nécessite un abonnement, false sinon
*/
public boolean requiresSubscription() {
return this == ON_DEMAND_COACHING || this == STRATEGIC_WORKSHOP;
}
/**
* Vérifie si le service est facturable à l'heure.
*
* @return true si le service est facturable à l'heure, false sinon
*/
public boolean isHourlyBilling() {
return this == ONE_ON_ONE_COACHING;
}
/**
* Vérifie si le service a un prix fixe.
*
* @return true si le service a un prix fixe, false sinon
*/
public boolean hasFixedPrice() {
return this == STRATEGIC_WORKSHOP || this == ON_DEMAND_COACHING;
}
/**
* Représentation textuelle de l'énumération.
*
* @return une chaîne représentant le type de service
*/
@Override
public String toString() {
return "ServiceType{" +
"code='" + code + '\'' +
", displayName='" + displayName + '\'' +
", description='" + description + '\'' +
'}';
}
}

View File

@@ -0,0 +1,213 @@
package com.gbcm.server.api.enums;
import com.fasterxml.jackson.annotation.JsonValue;
/**
* Énumération des statuts de session de coaching dans le système GBCM.
* Définit les différents états possibles d'une session de coaching.
*
* @author GBCM Development Team
* @version 1.0
* @since 1.0
*/
public enum SessionStatus {
/**
* Session programmée mais pas encore commencée.
*/
SCHEDULED("scheduled", "Programmée", "La session est programmée et confirmée"),
/**
* Session en cours.
*/
IN_PROGRESS("in_progress", "En cours", "La session est actuellement en cours"),
/**
* Session terminée avec succès.
*/
COMPLETED("completed", "Terminée", "La session s'est terminée avec succès"),
/**
* Session annulée par le client ou le coach.
*/
CANCELLED("cancelled", "Annulée", "La session a été annulée"),
/**
* Client absent à la session (no-show).
*/
NO_SHOW("no_show", "Absence", "Le client ne s'est pas présenté à la session"),
/**
* Session reportée à une date ultérieure.
*/
RESCHEDULED("rescheduled", "Reportée", "La session a été reportée"),
/**
* Session en attente de confirmation.
*/
PENDING_CONFIRMATION("pending_confirmation", "En attente de confirmation",
"La session est en attente de confirmation");
/**
* Code unique du statut de session.
*/
private final String code;
/**
* Nom d'affichage du statut.
*/
private final String displayName;
/**
* Description détaillée du statut.
*/
private final String description;
/**
* Constructeur de l'énumération.
*
* @param code le code unique du statut
* @param displayName le nom d'affichage
* @param description la description détaillée
*/
SessionStatus(String code, String displayName, String description) {
this.code = code;
this.displayName = displayName;
this.description = description;
}
/**
* Retourne le code unique du statut de session.
*
* @return le code du statut
*/
@JsonValue
public String getCode() {
return code;
}
/**
* Retourne le nom d'affichage du statut.
*
* @return le nom d'affichage
*/
public String getDisplayName() {
return displayName;
}
/**
* Retourne la description détaillée du statut.
*
* @return la description
*/
public String getDescription() {
return description;
}
/**
* Trouve un statut de session par son code.
*
* @param code le code à rechercher
* @return le statut correspondant
* @throws IllegalArgumentException si le code n'est pas trouvé
*/
public static SessionStatus fromCode(String code) {
if (code == null) {
throw new IllegalArgumentException("Le code ne peut pas être null");
}
for (SessionStatus status : values()) {
if (status.code.equals(code)) {
return status;
}
}
throw new IllegalArgumentException("Statut de session non trouvé pour le code: " + code);
}
/**
* Vérifie si la session est active (programmée ou en cours).
*
* @return true si la session est active, false sinon
*/
public boolean isActive() {
return this == SCHEDULED || this == IN_PROGRESS || this == PENDING_CONFIRMATION;
}
/**
* Vérifie si la session est terminée (complétée, annulée, ou no-show).
*
* @return true si la session est terminée, false sinon
*/
public boolean isFinished() {
return this == COMPLETED || this == CANCELLED || this == NO_SHOW;
}
/**
* Vérifie si la session peut être modifiée.
*
* @return true si la session peut être modifiée, false sinon
*/
public boolean canBeModified() {
return this == SCHEDULED || this == PENDING_CONFIRMATION;
}
/**
* Vérifie si la session peut être annulée.
*
* @return true si la session peut être annulée, false sinon
*/
public boolean canBeCancelled() {
return this == SCHEDULED || this == PENDING_CONFIRMATION;
}
/**
* Vérifie si la session peut être reportée.
*
* @return true si la session peut être reportée, false sinon
*/
public boolean canBeRescheduled() {
return this == SCHEDULED || this == PENDING_CONFIRMATION;
}
/**
* Vérifie si la session nécessite une confirmation.
*
* @return true si une confirmation est nécessaire, false sinon
*/
public boolean requiresConfirmation() {
return this == PENDING_CONFIRMATION;
}
/**
* Vérifie si la session est facturable.
*
* @return true si la session est facturable, false sinon
*/
public boolean isBillable() {
return this == COMPLETED || this == NO_SHOW;
}
/**
* Vérifie si la session compte comme réalisée pour les statistiques.
*
* @return true si la session compte comme réalisée, false sinon
*/
public boolean countsAsCompleted() {
return this == COMPLETED;
}
/**
* Représentation textuelle de l'énumération.
*
* @return une chaîne représentant le statut de session
*/
@Override
public String toString() {
return "SessionStatus{" +
"code='" + code + '\'' +
", displayName='" + displayName + '\'' +
", description='" + description + '\'' +
'}';
}
}

View File

@@ -0,0 +1,224 @@
package com.gbcm.server.api.enums;
import java.math.BigDecimal;
import com.fasterxml.jackson.annotation.JsonValue;
/**
* Énumération des packages d'ateliers stratégiques GBCM.
* Définit les trois niveaux de packages avec leurs prix et caractéristiques.
*
* @author GBCM Development Team
* @version 1.0
* @since 1.0
*/
public enum WorkshopPackage {
/**
* Package Silver - Niveau de base.
* Prix: $3,000 pour 2 ans
*/
SILVER("silver", "Silver", "Package de base pour les ateliers stratégiques",
new BigDecimal("3000.00"), 24, "Accès aux ateliers de base et ressources essentielles"),
/**
* Package Gold - Niveau intermédiaire.
* Prix: $4,000 pour 2 ans
*/
GOLD("gold", "Gold", "Package intermédiaire avec services étendus",
new BigDecimal("4000.00"), 24, "Accès complet aux ateliers + sessions de coaching mensuelles"),
/**
* Package Platinum - Niveau premium.
* Prix: $5,000 pour 2 ans
*/
PLATINUM("platinum", "Platinum", "Package premium avec tous les services",
new BigDecimal("5000.00"), 24, "Accès VIP complet + coaching illimité + support prioritaire");
/**
* Code unique du package.
*/
private final String code;
/**
* Nom d'affichage du package.
*/
private final String displayName;
/**
* Description courte du package.
*/
private final String description;
/**
* Prix total du package en USD.
*/
private final BigDecimal price;
/**
* Durée du package en mois.
*/
private final int durationMonths;
/**
* Description détaillée des services inclus.
*/
private final String features;
/**
* Constructeur de l'énumération.
*
* @param code le code unique du package
* @param displayName le nom d'affichage
* @param description la description courte
* @param price le prix total
* @param durationMonths la durée en mois
* @param features la description des services inclus
*/
WorkshopPackage(String code, String displayName, String description,
BigDecimal price, int durationMonths, String features) {
this.code = code;
this.displayName = displayName;
this.description = description;
this.price = price;
this.durationMonths = durationMonths;
this.features = features;
}
/**
* Retourne le code unique du package.
*
* @return le code du package
*/
@JsonValue
public String getCode() {
return code;
}
/**
* Retourne le nom d'affichage du package.
*
* @return le nom d'affichage
*/
public String getDisplayName() {
return displayName;
}
/**
* Retourne la description courte du package.
*
* @return la description
*/
public String getDescription() {
return description;
}
/**
* Retourne le prix total du package.
*
* @return le prix en USD
*/
public BigDecimal getPrice() {
return price;
}
/**
* Retourne la durée du package en mois.
*
* @return la durée en mois
*/
public int getDurationMonths() {
return durationMonths;
}
/**
* Retourne la description des services inclus.
*
* @return la description des services
*/
public String getFeatures() {
return features;
}
/**
* Calcule le prix mensuel du package.
*
* @return le prix mensuel en USD
*/
public BigDecimal getMonthlyPrice() {
return price.divide(new BigDecimal(durationMonths), 2, java.math.RoundingMode.HALF_UP);
}
/**
* Trouve un package par son code.
*
* @param code le code à rechercher
* @return le package correspondant
* @throws IllegalArgumentException si le code n'est pas trouvé
*/
public static WorkshopPackage fromCode(String code) {
if (code == null) {
throw new IllegalArgumentException("Le code ne peut pas être null");
}
for (WorkshopPackage pkg : values()) {
if (pkg.code.equalsIgnoreCase(code)) {
return pkg;
}
}
throw new IllegalArgumentException("Package non trouvé pour le code: " + code);
}
/**
* Vérifie si c'est un package premium.
*
* @return true si c'est le package Platinum, false sinon
*/
public boolean isPremium() {
return this == PLATINUM;
}
/**
* Vérifie si le package inclut du coaching.
*
* @return true si le package inclut du coaching, false sinon
*/
public boolean includesCoaching() {
return this == GOLD || this == PLATINUM;
}
/**
* Vérifie si le package inclut un support prioritaire.
*
* @return true si le package inclut un support prioritaire, false sinon
*/
public boolean hasPrioritySupport() {
return this == PLATINUM;
}
/**
* Compare les packages par prix (ordre croissant).
*
* @param other l'autre package à comparer
* @return résultat de la comparaison (-1, 0, 1)
*/
public int compareByPrice(WorkshopPackage other) {
return this.price.compareTo(other.price);
}
/**
* Représentation textuelle de l'énumération.
*
* @return une chaîne représentant le package
*/
@Override
public String toString() {
return "WorkshopPackage{" +
"code='" + code + '\'' +
", displayName='" + displayName + '\'' +
", price=" + price +
", durationMonths=" + durationMonths +
'}';
}
}

View File

@@ -0,0 +1,52 @@
package com.gbcm.server.api.exceptions;
/**
* Exception levée lors d'erreurs d'authentification.
* Utilisée pour les cas d'identifiants invalides, tokens expirés, etc.
*
* @author GBCM Development Team
* @version 1.0
* @since 1.0
*/
public class AuthenticationException extends GBCMException {
/**
* Constructeur avec message.
*
* @param message le message d'erreur d'authentification
*/
public AuthenticationException(String message) {
super(message, "AUTHENTICATION_ERROR");
}
/**
* Constructeur avec message et cause.
*
* @param message le message d'erreur d'authentification
* @param cause la cause de l'exception
*/
public AuthenticationException(String message, Throwable cause) {
super(message, "AUTHENTICATION_ERROR", cause);
}
/**
* Constructeur avec message et code d'erreur spécifique.
*
* @param message le message d'erreur d'authentification
* @param errorCode le code d'erreur spécifique
*/
public AuthenticationException(String message, String errorCode) {
super(message, errorCode);
}
/**
* Constructeur complet.
*
* @param message le message d'erreur d'authentification
* @param errorCode le code d'erreur spécifique
* @param cause la cause de l'exception
*/
public AuthenticationException(String message, String errorCode, Throwable cause) {
super(message, errorCode, cause);
}
}

View File

@@ -0,0 +1,52 @@
package com.gbcm.server.api.exceptions;
/**
* Exception levée lors d'erreurs d'autorisation.
* Utilisée quand un utilisateur authentifié n'a pas les permissions nécessaires.
*
* @author GBCM Development Team
* @version 1.0
* @since 1.0
*/
public class AuthorizationException extends GBCMException {
/**
* Constructeur avec message.
*
* @param message le message d'erreur d'autorisation
*/
public AuthorizationException(String message) {
super(message, "AUTHORIZATION_ERROR");
}
/**
* Constructeur avec message et cause.
*
* @param message le message d'erreur d'autorisation
* @param cause la cause de l'exception
*/
public AuthorizationException(String message, Throwable cause) {
super(message, "AUTHORIZATION_ERROR", cause);
}
/**
* Constructeur avec message et code d'erreur spécifique.
*
* @param message le message d'erreur d'autorisation
* @param errorCode le code d'erreur spécifique
*/
public AuthorizationException(String message, String errorCode) {
super(message, errorCode);
}
/**
* Constructeur complet.
*
* @param message le message d'erreur d'autorisation
* @param errorCode le code d'erreur spécifique
* @param cause la cause de l'exception
*/
public AuthorizationException(String message, String errorCode, Throwable cause) {
super(message, errorCode, cause);
}
}

View File

@@ -0,0 +1,73 @@
package com.gbcm.server.api.exceptions;
/**
* Exception levée lors de violations des règles métier GBCM.
* Utilisée pour les cas où une opération viole une règle business spécifique.
*
* @author GBCM Development Team
* @version 1.0
* @since 1.0
*/
public class BusinessRuleException extends GBCMException {
/**
* Nom de la règle métier violée.
*/
private final String ruleName;
/**
* Constructeur avec message.
*
* @param message le message d'erreur de règle métier
*/
public BusinessRuleException(String message) {
super(message, "BUSINESS_RULE_VIOLATION");
this.ruleName = null;
}
/**
* Constructeur avec message et nom de règle.
*
* @param message le message d'erreur de règle métier
* @param ruleName le nom de la règle violée
*/
public BusinessRuleException(String message, String ruleName) {
super(message, "BUSINESS_RULE_VIOLATION");
this.ruleName = ruleName;
}
/**
* Constructeur avec message, nom de règle et cause.
*
* @param message le message d'erreur de règle métier
* @param ruleName le nom de la règle violée
* @param cause la cause de l'exception
*/
public BusinessRuleException(String message, String ruleName, Throwable cause) {
super(message, "BUSINESS_RULE_VIOLATION", cause);
this.ruleName = ruleName;
}
/**
* Retourne le nom de la règle métier violée.
*
* @return le nom de la règle
*/
public String getRuleName() {
return ruleName;
}
/**
* Représentation textuelle de l'exception.
*
* @return une chaîne représentant l'exception
*/
@Override
public String toString() {
return "BusinessRuleException{" +
"errorCode='" + getErrorCode() + '\'' +
", message='" + getMessage() + '\'' +
", ruleName='" + ruleName + '\'' +
'}';
}
}

View File

@@ -0,0 +1,83 @@
package com.gbcm.server.api.exceptions;
/**
* Exception de base pour toutes les exceptions métier de la plateforme GBCM.
* Toutes les autres exceptions spécifiques héritent de cette classe.
*
* @author GBCM Development Team
* @version 1.0
* @since 1.0
*/
public class GBCMException extends Exception {
/**
* Code d'erreur unique pour identifier le type d'exception.
*/
private final String errorCode;
/**
* Constructeur avec message.
*
* @param message le message d'erreur
*/
public GBCMException(String message) {
super(message);
this.errorCode = "GBCM_ERROR";
}
/**
* Constructeur avec message et code d'erreur.
*
* @param message le message d'erreur
* @param errorCode le code d'erreur unique
*/
public GBCMException(String message, String errorCode) {
super(message);
this.errorCode = errorCode;
}
/**
* Constructeur avec message et cause.
*
* @param message le message d'erreur
* @param cause la cause de l'exception
*/
public GBCMException(String message, Throwable cause) {
super(message, cause);
this.errorCode = "GBCM_ERROR";
}
/**
* Constructeur complet.
*
* @param message le message d'erreur
* @param errorCode le code d'erreur unique
* @param cause la cause de l'exception
*/
public GBCMException(String message, String errorCode, Throwable cause) {
super(message, cause);
this.errorCode = errorCode;
}
/**
* Retourne le code d'erreur unique.
*
* @return le code d'erreur
*/
public String getErrorCode() {
return errorCode;
}
/**
* Représentation textuelle de l'exception.
*
* @return une chaîne représentant l'exception
*/
@Override
public String toString() {
return "GBCMException{" +
"errorCode='" + errorCode + '\'' +
", message='" + getMessage() + '\'' +
'}';
}
}

View File

@@ -0,0 +1,104 @@
package com.gbcm.server.api.exceptions;
/**
* Exception levée quand une ressource demandée n'est pas trouvée.
* Utilisée pour les cas où un utilisateur, client, session, etc. n'existe pas.
*
* @author GBCM Development Team
* @version 1.0
* @since 1.0
*/
public class ResourceNotFoundException extends GBCMException {
/**
* Type de ressource non trouvée.
*/
private final String resourceType;
/**
* Identifiant de la ressource non trouvée.
*/
private final Object resourceId;
/**
* Constructeur avec message.
*
* @param message le message d'erreur
*/
public ResourceNotFoundException(String message) {
super(message, "RESOURCE_NOT_FOUND");
this.resourceType = null;
this.resourceId = null;
}
/**
* Constructeur avec type et identifiant de ressource.
*
* @param resourceType le type de ressource (ex: "User", "Client")
* @param resourceId l'identifiant de la ressource
*/
public ResourceNotFoundException(String resourceType, Object resourceId) {
super(String.format("%s avec l'identifiant '%s' non trouvé", resourceType, resourceId),
"RESOURCE_NOT_FOUND");
this.resourceType = resourceType;
this.resourceId = resourceId;
}
/**
* Constructeur avec message, type et identifiant.
*
* @param message le message d'erreur personnalisé
* @param resourceType le type de ressource
* @param resourceId l'identifiant de la ressource
*/
public ResourceNotFoundException(String message, String resourceType, Object resourceId) {
super(message, "RESOURCE_NOT_FOUND");
this.resourceType = resourceType;
this.resourceId = resourceId;
}
/**
* Constructeur avec message et cause.
*
* @param message le message d'erreur
* @param cause la cause de l'exception
*/
public ResourceNotFoundException(String message, Throwable cause) {
super(message, "RESOURCE_NOT_FOUND", cause);
this.resourceType = null;
this.resourceId = null;
}
/**
* Retourne le type de ressource non trouvée.
*
* @return le type de ressource
*/
public String getResourceType() {
return resourceType;
}
/**
* Retourne l'identifiant de la ressource non trouvée.
*
* @return l'identifiant de la ressource
*/
public Object getResourceId() {
return resourceId;
}
/**
* Représentation textuelle de l'exception.
*
* @return une chaîne représentant l'exception
*/
@Override
public String toString() {
return "ResourceNotFoundException{" +
"errorCode='" + getErrorCode() + '\'' +
", message='" + getMessage() + '\'' +
", resourceType='" + resourceType + '\'' +
", resourceId=" + resourceId +
'}';
}
}

View File

@@ -0,0 +1,95 @@
package com.gbcm.server.api.exceptions;
import java.util.List;
import java.util.Map;
/**
* Exception levée lors d'erreurs de validation des données.
* Contient des détails sur les champs en erreur et leurs messages.
*
* @author GBCM Development Team
* @version 1.0
* @since 1.0
*/
public class ValidationException extends GBCMException {
/**
* Map des erreurs de validation par champ.
*/
private final Map<String, List<String>> fieldErrors;
/**
* Constructeur avec message.
*
* @param message le message d'erreur de validation
*/
public ValidationException(String message) {
super(message, "VALIDATION_ERROR");
this.fieldErrors = Map.of();
}
/**
* Constructeur avec message et erreurs de champs.
*
* @param message le message d'erreur de validation
* @param fieldErrors les erreurs par champ
*/
public ValidationException(String message, Map<String, List<String>> fieldErrors) {
super(message, "VALIDATION_ERROR");
this.fieldErrors = fieldErrors != null ? fieldErrors : Map.of();
}
/**
* Constructeur avec message, erreurs de champs et cause.
*
* @param message le message d'erreur de validation
* @param fieldErrors les erreurs par champ
* @param cause la cause de l'exception
*/
public ValidationException(String message, Map<String, List<String>> fieldErrors, Throwable cause) {
super(message, "VALIDATION_ERROR", cause);
this.fieldErrors = fieldErrors != null ? fieldErrors : Map.of();
}
/**
* Retourne les erreurs de validation par champ.
*
* @return une map des erreurs par champ
*/
public Map<String, List<String>> getFieldErrors() {
return fieldErrors;
}
/**
* Vérifie si l'exception contient des erreurs de champs.
*
* @return true si des erreurs de champs sont présentes, false sinon
*/
public boolean hasFieldErrors() {
return !fieldErrors.isEmpty();
}
/**
* Retourne les erreurs pour un champ spécifique.
*
* @param fieldName le nom du champ
* @return la liste des erreurs pour ce champ, ou une liste vide
*/
public List<String> getFieldErrors(String fieldName) {
return fieldErrors.getOrDefault(fieldName, List.of());
}
/**
* Représentation textuelle de l'exception.
*
* @return une chaîne représentant l'exception
*/
@Override
public String toString() {
return "ValidationException{" +
"errorCode='" + getErrorCode() + '\'' +
", message='" + getMessage() + '\'' +
", fieldErrorsCount=" + fieldErrors.size() +
'}';
}
}

View File

@@ -2,24 +2,36 @@ package com.gbcm.server.api.interfaces;
import com.gbcm.server.api.dto.auth.LoginRequestDTO; import com.gbcm.server.api.dto.auth.LoginRequestDTO;
import com.gbcm.server.api.dto.auth.LoginResponseDTO; import com.gbcm.server.api.dto.auth.LoginResponseDTO;
import com.gbcm.server.api.dto.auth.PasswordResetDTO;
import com.gbcm.server.api.dto.common.SuccessResponseDTO;
import com.gbcm.server.api.dto.user.UserDTO; import com.gbcm.server.api.dto.user.UserDTO;
import com.gbcm.server.api.exceptions.AuthenticationException; import com.gbcm.server.api.exceptions.AuthenticationException;
import com.gbcm.server.api.exceptions.GBCMException; import com.gbcm.server.api.exceptions.GBCMException;
import jakarta.validation.Valid;
import jakarta.ws.rs.*; import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.MediaType;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
/** /**
* Interface de service pour l'authentification * Interface de service pour l'authentification et l'autorisation.
* Fournit tous les endpoints nécessaires pour la gestion des sessions utilisateur,
* l'authentification JWT et la réinitialisation des mots de passe.
*
* @author GBCM Development Team
* @version 1.0
* @since 1.0
*/ */
@Path("/auth") @Path("/auth")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@Tag(name = "Authentication", description = "Services d'authentification et autorisation") @Tag(name = "Authentication", description = "Services d'authentification et autorisation GBCM")
public interface AuthService { public interface AuthService {
/** /**

View File

@@ -0,0 +1,263 @@
package com.gbcm.server.api.interfaces;
import com.gbcm.server.api.dto.common.PagedResultDTO;
import com.gbcm.server.api.dto.common.SuccessResponseDTO;
import com.gbcm.server.api.dto.user.CreateUserDTO;
import com.gbcm.server.api.dto.user.UpdateUserDTO;
import com.gbcm.server.api.dto.user.UserDTO;
import com.gbcm.server.api.enums.UserRole;
import com.gbcm.server.api.exceptions.AuthorizationException;
import com.gbcm.server.api.exceptions.GBCMException;
import com.gbcm.server.api.exceptions.ResourceNotFoundException;
import com.gbcm.server.api.exceptions.ValidationException;
import jakarta.annotation.security.RolesAllowed;
import jakarta.validation.Valid;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
/**
* Interface de service pour la gestion des utilisateurs.
* Fournit tous les endpoints CRUD pour les utilisateurs de la plateforme GBCM.
*
* @author GBCM Development Team
* @version 1.0
* @since 1.0
*/
@Path("/users")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Tag(name = "Users", description = "Gestion des utilisateurs GBCM")
public interface UserService {
/**
* Récupère la liste paginée des utilisateurs.
*/
@GET
@RolesAllowed({"ADMIN", "MANAGER"})
@Operation(
summary = "Liste des utilisateurs",
description = "Récupère la liste paginée des utilisateurs avec filtres optionnels"
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Liste récupérée avec succès",
content = @Content(schema = @Schema(implementation = PagedResultDTO.class))),
@ApiResponse(responseCode = "401", description = "Non authentifié"),
@ApiResponse(responseCode = "403", description = "Accès refusé - permissions insuffisantes")
})
PagedResultDTO<UserDTO> getUsers(
@Parameter(description = "Numéro de page (commence à 0)", example = "0")
@QueryParam("page") @DefaultValue("0") int page,
@Parameter(description = "Taille de page", example = "20")
@QueryParam("size") @DefaultValue("20") int size,
@Parameter(description = "Tri (ex: firstName,asc ou email,desc)")
@QueryParam("sort") String sort,
@Parameter(description = "Filtre par rôle")
@QueryParam("role") UserRole role,
@Parameter(description = "Filtre par statut actif")
@QueryParam("active") Boolean active,
@Parameter(description = "Recherche par nom ou email")
@QueryParam("search") String search
) throws AuthorizationException;
/**
* Récupère un utilisateur par son ID.
*/
@GET
@Path("/{id}")
@RolesAllowed({"ADMIN", "MANAGER", "COACH"})
@Operation(
summary = "Utilisateur par ID",
description = "Récupère les détails d'un utilisateur par son identifiant"
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Utilisateur trouvé",
content = @Content(schema = @Schema(implementation = UserDTO.class))),
@ApiResponse(responseCode = "404", description = "Utilisateur non trouvé"),
@ApiResponse(responseCode = "401", description = "Non authentifié"),
@ApiResponse(responseCode = "403", description = "Accès refusé")
})
UserDTO getUserById(
@Parameter(description = "ID de l'utilisateur", required = true, example = "1")
@PathParam("id") Long id
) throws ResourceNotFoundException, AuthorizationException;
/**
* Crée un nouvel utilisateur.
*/
@POST
@RolesAllowed({"ADMIN"})
@Operation(
summary = "Créer un utilisateur",
description = "Crée un nouvel utilisateur dans le système"
)
@ApiResponses({
@ApiResponse(responseCode = "201", description = "Utilisateur créé avec succès",
content = @Content(schema = @Schema(implementation = UserDTO.class))),
@ApiResponse(responseCode = "400", description = "Données invalides"),
@ApiResponse(responseCode = "409", description = "Email déjà utilisé"),
@ApiResponse(responseCode = "401", description = "Non authentifié"),
@ApiResponse(responseCode = "403", description = "Accès refusé - seuls les admins peuvent créer des utilisateurs")
})
UserDTO createUser(
@Parameter(description = "Données du nouvel utilisateur", required = true)
@Valid CreateUserDTO createUserDTO
) throws ValidationException, GBCMException, AuthorizationException;
/**
* Met à jour un utilisateur existant.
*/
@PUT
@Path("/{id}")
@RolesAllowed({"ADMIN", "MANAGER"})
@Operation(
summary = "Mettre à jour un utilisateur",
description = "Met à jour les informations d'un utilisateur existant"
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Utilisateur mis à jour avec succès",
content = @Content(schema = @Schema(implementation = UserDTO.class))),
@ApiResponse(responseCode = "404", description = "Utilisateur non trouvé"),
@ApiResponse(responseCode = "400", description = "Données invalides"),
@ApiResponse(responseCode = "409", description = "Email déjà utilisé par un autre utilisateur"),
@ApiResponse(responseCode = "401", description = "Non authentifié"),
@ApiResponse(responseCode = "403", description = "Accès refusé")
})
UserDTO updateUser(
@Parameter(description = "ID de l'utilisateur", required = true, example = "1")
@PathParam("id") Long id,
@Parameter(description = "Données de mise à jour", required = true)
@Valid UpdateUserDTO updateUserDTO
) throws ResourceNotFoundException, ValidationException, GBCMException, AuthorizationException;
/**
* Supprime un utilisateur (soft delete).
*/
@DELETE
@Path("/{id}")
@RolesAllowed({"ADMIN"})
@Operation(
summary = "Supprimer un utilisateur",
description = "Supprime un utilisateur du système (soft delete)"
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Utilisateur supprimé avec succès"),
@ApiResponse(responseCode = "404", description = "Utilisateur non trouvé"),
@ApiResponse(responseCode = "401", description = "Non authentifié"),
@ApiResponse(responseCode = "403", description = "Accès refusé - seuls les admins peuvent supprimer des utilisateurs")
})
SuccessResponseDTO<Void> deleteUser(
@Parameter(description = "ID de l'utilisateur", required = true, example = "1")
@PathParam("id") Long id
) throws ResourceNotFoundException, AuthorizationException;
/**
* Active ou désactive un utilisateur.
*/
@PUT
@Path("/{id}/status")
@RolesAllowed({"ADMIN", "MANAGER"})
@Operation(
summary = "Changer le statut d'un utilisateur",
description = "Active ou désactive un compte utilisateur"
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Statut modifié avec succès",
content = @Content(schema = @Schema(implementation = UserDTO.class))),
@ApiResponse(responseCode = "404", description = "Utilisateur non trouvé"),
@ApiResponse(responseCode = "401", description = "Non authentifié"),
@ApiResponse(responseCode = "403", description = "Accès refusé")
})
UserDTO changeUserStatus(
@Parameter(description = "ID de l'utilisateur", required = true, example = "1")
@PathParam("id") Long id,
@Parameter(description = "Nouveau statut", required = true, example = "true")
@FormParam("active") boolean active
) throws ResourceNotFoundException, AuthorizationException;
/**
* Récupère le profil de l'utilisateur connecté.
*/
@GET
@Path("/profile")
@RolesAllowed({"ADMIN", "MANAGER", "COACH", "CLIENT", "PROSPECT"})
@Operation(
summary = "Profil utilisateur",
description = "Récupère le profil de l'utilisateur actuellement connecté"
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Profil récupéré avec succès",
content = @Content(schema = @Schema(implementation = UserDTO.class))),
@ApiResponse(responseCode = "401", description = "Non authentifié")
})
UserDTO getCurrentUserProfile(
@Parameter(description = "Token d'authentification", required = true)
@HeaderParam("Authorization") String authToken
) throws AuthorizationException;
/**
* Met à jour le profil de l'utilisateur connecté.
*/
@PUT
@Path("/profile")
@RolesAllowed({"ADMIN", "MANAGER", "COACH", "CLIENT", "PROSPECT"})
@Operation(
summary = "Mettre à jour le profil",
description = "Met à jour le profil de l'utilisateur actuellement connecté"
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Profil mis à jour avec succès",
content = @Content(schema = @Schema(implementation = UserDTO.class))),
@ApiResponse(responseCode = "400", description = "Données invalides"),
@ApiResponse(responseCode = "409", description = "Email déjà utilisé"),
@ApiResponse(responseCode = "401", description = "Non authentifié")
})
UserDTO updateCurrentUserProfile(
@Parameter(description = "Token d'authentification", required = true)
@HeaderParam("Authorization") String authToken,
@Parameter(description = "Données de mise à jour du profil", required = true)
@Valid UpdateUserDTO updateUserDTO
) throws ValidationException, GBCMException, AuthorizationException;
/**
* Recherche d'utilisateurs par critères.
*/
@GET
@Path("/search")
@RolesAllowed({"ADMIN", "MANAGER", "COACH"})
@Operation(
summary = "Rechercher des utilisateurs",
description = "Recherche d'utilisateurs par différents critères"
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Résultats de recherche",
content = @Content(schema = @Schema(implementation = PagedResultDTO.class))),
@ApiResponse(responseCode = "401", description = "Non authentifié"),
@ApiResponse(responseCode = "403", description = "Accès refusé")
})
PagedResultDTO<UserDTO> searchUsers(
@Parameter(description = "Terme de recherche", required = true)
@QueryParam("q") String query,
@Parameter(description = "Numéro de page", example = "0")
@QueryParam("page") @DefaultValue("0") int page,
@Parameter(description = "Taille de page", example = "20")
@QueryParam("size") @DefaultValue("20") int size
) throws AuthorizationException;
}