215 lines
6.4 KiB
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;
|
|
}
|
|
} |