Files
lionsdev-client-impl-quarkus/src/main/java/dev/lions/models/Project.java

215 lines
6.4 KiB
Java

package dev.lions.models;
import jakarta.persistence.*;
import jakarta.validation.constraints.*;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
/**
* Entité représentant un projet dans le système.
* Gère les informations complètes d'un projet, incluant ses métadonnées,
* technologies, témoignages et statut.
*/
@Entity
@Table(
name = "projects",
indexes = {
@Index(name = "idx_project_completion_date", columnList = "completionDate"),
@Index(name = "idx_project_featured", columnList = "featured")
}
)
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Project implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@NotNull(message = "L'identifiant du projet est obligatoire")
@Pattern(regexp = "^[a-zA-Z0-9-_]+$", message = "L'identifiant ne doit contenir que des lettres, chiffres, tirets et underscores")
private String id;
@Column(nullable = false, length = 100)
@NotNull(message = "Le titre du projet est obligatoire")
@Size(min = 3, max = 100, message = "Le titre doit contenir entre 3 et 100 caractères")
private String title;
@Column(nullable = false, length = 500)
@NotNull(message = "La description du projet est obligatoire")
@Size(min = 10, max = 500, message = "La description doit contenir entre 10 et 500 caractères")
private String description;
@Column(nullable = false, length = 250)
@NotNull(message = "La description courte est obligatoire")
@Size(min = 10, max = 250, message = "La description courte doit contenir entre 10 et 250 caractères")
private String shortDescription;
@Column(nullable = false)
@NotNull(message = "L'URL de l'image est obligatoire")
@Pattern(regexp = "^[^<>\"']*$", message = "L'URL de l'image contient des caractères non autorisés")
private String imageUrl;
@Column(length = 100)
@Pattern(regexp = "^[^<>\"']*$", message = "Le nom du client contient des caractères non autorisés")
private String clientName;
@Column(nullable = false)
@PastOrPresent(message = "La date de réalisation ne peut pas être dans le futur")
private LocalDateTime completionDate;
@ElementCollection
@CollectionTable(
name = "project_tags",
joinColumns = @JoinColumn(name = "project_id")
)
@Column(name = "tag", length = 50)
@Builder.Default
private List<@Pattern(regexp = "^[a-zA-Z0-9-_]+$") String> tags = new ArrayList<>();
@ElementCollection
@CollectionTable(
name = "project_technologies",
joinColumns = @JoinColumn(name = "project_id")
)
@Column(name = "technology", length = 50)
@Builder.Default
private List<@Pattern(regexp = "^[a-zA-Z0-9-_. ]+$") String> technologies = new ArrayList<>();
@Column(length = 1000)
@Size(max = 1000, message = "La description du challenge ne doit pas dépasser 1000 caractères")
private String challenge;
@Column(length = 1000)
@Size(max = 1000, message = "La description de la solution ne doit pas dépasser 1000 caractères")
private String solution;
@Column(length = 1000)
@Size(max = 1000, message = "La description des résultats ne doit pas dépasser 1000 caractères")
private String results;
@ElementCollection
@CollectionTable(
name = "project_testimonials",
joinColumns = @JoinColumn(name = "project_id")
)
@Column(name = "testimonial", length = 1000)
@Builder.Default
private List<@Size(max = 1000) String> testimonials = new ArrayList<>();
@Builder.Default
private boolean featured = false;
@Version
private Long version;
@Column(name = "created_at", nullable = false, updatable = false)
@CreationTimestamp
private LocalDateTime createdAt;
@Column(name = "updated_at")
@UpdateTimestamp
private LocalDateTime updatedAt;
/**
* Récupère les tags de manière sécurisée.
*
* @return Liste immuable des tags
*/
public List<String> getTags() {
return Collections.unmodifiableList(tags);
}
/**
* Récupère les technologies de manière sécurisée.
*
* @return Liste immuable des technologies
*/
public List<String> getTechnologies() {
return Collections.unmodifiableList(technologies);
}
/**
* Récupère les témoignages de manière sécurisée.
*
* @return Liste immuable des témoignages
*/
public List<String> getTestimonials() {
return Collections.unmodifiableList(testimonials);
}
/**
* Ajoute un tag au projet.
*
* @param tag Tag à ajouter
* @return true si le tag a été ajouté
*/
public boolean addTag(String tag) {
if (tag != null && !tag.isEmpty() && !tags.contains(tag)) {
return tags.add(tag.trim().toLowerCase());
}
return false;
}
/**
* Ajoute une technologie au projet.
*
* @param technology Technologie à ajouter
* @return true si la technologie a été ajoutée
*/
public boolean addTechnology(String technology) {
if (technology != null && !technology.isEmpty() && !technologies.contains(technology)) {
return technologies.add(technology.trim());
}
return false;
}
/**
* Ajoute un témoignage au projet.
*
* @param testimonial Témoignage à ajouter
* @return true si le témoignage a été ajouté
*/
public boolean addTestimonial(String testimonial) {
if (testimonial != null && !testimonial.isEmpty()) {
return testimonials.add(testimonial.trim());
}
return false;
}
/**
* Récupère le premier témoignage s'il existe.
*
* @return Optional contenant le premier témoignage
*/
public Optional<String> getFirstTestimonial() {
return testimonials.isEmpty() ? Optional.empty() :
Optional.of(testimonials.get(0));
}
/**
* Vérifie si le projet est complet et prêt à être publié.
*
* @return true si le projet est complet
*/
public boolean isComplete() {
return id != null && !id.isEmpty() &&
title != null && !title.isEmpty() &&
description != null && !description.isEmpty() &&
imageUrl != null && !imageUrl.isEmpty() &&
completionDate != null;
}
}