ContactController.java
package com.archiweb.api;
import com.archiweb.model.ContactMessage;
import com.archiweb.model.ContactMessageReply;
import com.archiweb.repository.ContactMessageRepository;
import com.archiweb.repository.ContactMessageReplyRepository;
import com.archiweb.service.EmailService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.ExampleObject;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@RestController
@RequestMapping("/api/contact")
@RequiredArgsConstructor
@Tag(
name = "Contact",
description = """
Gestion des messages de contact.
Permet aux utilisateurs d'envoyer des messages de contact et aux administrateurs de les consulter.
"""
)
public class ContactController {
private final ContactMessageRepository contactMessageRepository;
private final ContactMessageReplyRepository contactMessageReplyRepository;
private final EmailService emailService;
@Operation(
summary = "Envoyer un message de contact",
description = """
Permet à un utilisateur d'envoyer un message de contact.
Le message sera stocké en base de données et pourra être consulté par les administrateurs.
""",
requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
description = "Données du message de contact",
required = true,
content = @Content(
schema = @Schema(implementation = ContactRequest.class),
examples = @ExampleObject(
value = """
{
"name": "Jean Dupont",
"email": "jean.dupont@example.com",
"message": "Bonjour, j'aimerais avoir des informations sur vos produits."
}
"""
)
)
),
responses = {
@ApiResponse(
responseCode = "200",
description = "Message envoyé avec succès",
content = @Content(
mediaType = "application/json",
examples = @ExampleObject(
value = """
{
"message": "Votre message a été envoyé avec succès. Nous vous répondrons dans les plus brefs délais."
}
"""
)
)
),
@ApiResponse(
responseCode = "400",
description = "Données invalides"
)
}
)
@PostMapping("/send")
public ResponseEntity<?> sendMessage(@RequestBody ContactRequest request) {
// Validation
if (request.getName() == null || request.getName().trim().isEmpty()) {
return ResponseEntity.badRequest().body(Map.of("error", "Le nom est requis"));
}
if (request.getEmail() == null || request.getEmail().trim().isEmpty()) {
return ResponseEntity.badRequest().body(Map.of("error", "L'email est requis"));
}
if (request.getMessage() == null || request.getMessage().trim().isEmpty()) {
return ResponseEntity.badRequest().body(Map.of("error", "Le message est requis"));
}
// Créer et sauvegarder le message
ContactMessage contactMessage = ContactMessage.builder()
.name(request.getName().trim())
.email(request.getEmail().trim().toLowerCase())
.message(request.getMessage().trim())
.read(false)
.createdAt(LocalDateTime.now())
.build();
contactMessageRepository.save(contactMessage);
return ResponseEntity.ok(Map.of(
"message", "Votre message a été envoyé avec succès. Nous vous répondrons dans les plus brefs délais."
));
}
@Operation(
summary = "Récupérer tous les messages de contact (paginé)",
description = """
Récupère tous les messages de contact avec pagination.
Accessible uniquement aux administrateurs.
Les messages sont triés par date de création (plus récents en premier).
""",
responses = {
@ApiResponse(
responseCode = "200",
description = "Liste des messages récupérée avec succès"
),
@ApiResponse(
responseCode = "403",
description = "Accès refusé - Réservé aux administrateurs"
)
}
)
@GetMapping("/messages")
@PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_EMPLOYEE')")
public ResponseEntity<Page<ContactMessage>> getAllMessages(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size
) {
Pageable pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "createdAt"));
Page<ContactMessage> messages = contactMessageRepository.findAllByOrderByCreatedAtDesc(pageable);
return ResponseEntity.ok(messages);
}
@Operation(
summary = "Récupérer les messages non lus",
description = """
Récupère tous les messages de contact qui n'ont pas encore été lus.
Accessible uniquement aux administrateurs.
""",
responses = {
@ApiResponse(
responseCode = "200",
description = "Liste des messages non lus récupérée avec succès"
),
@ApiResponse(
responseCode = "403",
description = "Accès refusé - Réservé aux administrateurs"
)
}
)
@GetMapping("/messages/unread")
@PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_EMPLOYEE')")
public ResponseEntity<List<ContactMessage>> getUnreadMessages() {
List<ContactMessage> messages = contactMessageRepository.findByReadFalseOrderByCreatedAtDesc();
return ResponseEntity.ok(messages);
}
@Operation(
summary = "Compter les messages non lus",
description = """
Retourne le nombre de messages non lus.
Accessible uniquement aux administrateurs.
""",
responses = {
@ApiResponse(
responseCode = "200",
description = "Nombre de messages non lus"
),
@ApiResponse(
responseCode = "403",
description = "Accès refusé - Réservé aux administrateurs"
)
}
)
@GetMapping("/messages/unread/count")
@PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_EMPLOYEE')")
public ResponseEntity<Map<String, Long>> getUnreadCount() {
long count = contactMessageRepository.countByReadFalse();
return ResponseEntity.ok(Map.of("count", count));
}
@Operation(
summary = "Marquer un message comme lu",
description = """
Marque un message de contact comme lu.
Accessible uniquement aux administrateurs.
""",
responses = {
@ApiResponse(
responseCode = "200",
description = "Message marqué comme lu"
),
@ApiResponse(
responseCode = "404",
description = "Message non trouvé"
),
@ApiResponse(
responseCode = "403",
description = "Accès refusé - Réservé aux administrateurs"
)
}
)
@PatchMapping("/messages/{id}/read")
@PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_EMPLOYEE')")
public ResponseEntity<?> markAsRead(@PathVariable Long id) {
Optional<ContactMessage> messageOpt = contactMessageRepository.findById(id);
if (messageOpt.isEmpty()) {
return ResponseEntity.notFound().build();
}
ContactMessage message = messageOpt.get();
message.setRead(true);
message.setReadAt(LocalDateTime.now());
contactMessageRepository.save(message);
return ResponseEntity.ok(Map.of("message", "Message marqué comme lu"));
}
@Operation(
summary = "Récupérer un message par ID",
description = """
Récupère un message de contact par son ID.
Accessible uniquement aux administrateurs.
""",
responses = {
@ApiResponse(
responseCode = "200",
description = "Message récupéré avec succès"
),
@ApiResponse(
responseCode = "404",
description = "Message non trouvé"
),
@ApiResponse(
responseCode = "403",
description = "Accès refusé - Réservé aux administrateurs"
)
}
)
@GetMapping("/messages/{id}")
@PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_EMPLOYEE')")
public ResponseEntity<ContactMessage> getMessage(@PathVariable Long id) {
Optional<ContactMessage> messageOpt = contactMessageRepository.findById(id);
if (messageOpt.isEmpty()) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(messageOpt.get());
}
@Operation(
summary = "Récupérer la conversation complète",
description = """
Récupère le message original et toutes les réponses associées, dans l'ordre chronologique.
Accessible uniquement aux administrateurs et employés.
""",
responses = {
@ApiResponse(
responseCode = "200",
description = "Conversation récupérée avec succès"
),
@ApiResponse(
responseCode = "404",
description = "Message non trouvé"
),
@ApiResponse(
responseCode = "403",
description = "Accès refusé - Réservé aux administrateurs et employés"
)
}
)
@GetMapping("/messages/{id}/conversation")
@PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_EMPLOYEE')")
public ResponseEntity<?> getConversation(@PathVariable Long id) {
Optional<ContactMessage> messageOpt = contactMessageRepository.findById(id);
if (messageOpt.isEmpty()) {
return ResponseEntity.notFound().build();
}
ContactMessage message = messageOpt.get();
List<ContactMessageReply> replies = contactMessageReplyRepository.findByContactMessageIdOrderByCreatedAtAsc(id);
Map<String, Object> conversation = Map.of(
"originalMessage", Map.of(
"id", message.getId(),
"name", message.getName(),
"email", message.getEmail(),
"message", message.getMessage(),
"createdAt", message.getCreatedAt(),
"isFromUser", true
),
"replies", replies.stream().map(reply -> Map.of(
"id", reply.getId(),
"subject", reply.getSubject(),
"message", reply.getMessage(),
"isFromUser", reply.isFromUser(),
"createdAt", reply.getCreatedAt()
)).toList()
);
return ResponseEntity.ok(conversation);
}
@Operation(
summary = "Répondre à un message de contact",
description = """
Envoie une réponse par email à l'expéditeur d'un message de contact.
Accessible uniquement aux administrateurs et employés.
""",
requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
description = "Contenu de la réponse",
required = true,
content = @Content(
schema = @Schema(implementation = ReplyRequest.class),
examples = @ExampleObject(
value = """
{
"subject": "Re: Votre demande",
"message": "Bonjour, merci pour votre message. Nous vous répondrons dans les plus brefs délais."
}
"""
)
)
),
responses = {
@ApiResponse(
responseCode = "200",
description = "Réponse envoyée avec succès"
),
@ApiResponse(
responseCode = "404",
description = "Message non trouvé"
),
@ApiResponse(
responseCode = "403",
description = "Accès refusé - Réservé aux administrateurs et employés"
)
}
)
@PostMapping("/messages/{id}/reply")
@PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_EMPLOYEE')")
public ResponseEntity<?> replyToMessage(@PathVariable Long id, @RequestBody ReplyRequest request) {
Optional<ContactMessage> messageOpt = contactMessageRepository.findById(id);
if (messageOpt.isEmpty()) {
return ResponseEntity.notFound().build();
}
ContactMessage contactMessage = messageOpt.get();
// Validation
if (request.getSubject() == null || request.getSubject().trim().isEmpty()) {
return ResponseEntity.badRequest().body(Map.of("error", "Le sujet est requis"));
}
if (request.getMessage() == null || request.getMessage().trim().isEmpty()) {
return ResponseEntity.badRequest().body(Map.of("error", "Le message est requis"));
}
try {
// Construire le message de réponse avec le message original
String replyMessage = String.format(
"Bonjour %s,\n\n%s\n\n---\nMessage original :\n%s",
contactMessage.getName(),
request.getMessage().trim(),
contactMessage.getMessage()
);
// Sauvegarder la réponse dans la base de données
ContactMessageReply reply = ContactMessageReply.builder()
.contactMessage(contactMessage)
.subject(request.getSubject().trim())
.message(request.getMessage().trim())
.isFromUser(false) // Réponse de l'admin/employé
.build();
contactMessageReplyRepository.save(reply);
// Envoyer l'email de réponse
emailService.sendEmail(
contactMessage.getEmail(),
request.getSubject().trim(),
replyMessage
);
return ResponseEntity.ok(Map.of(
"message", "Réponse envoyée avec succès à " + contactMessage.getEmail(),
"replyId", reply.getId()
));
} catch (Exception e) {
return ResponseEntity.status(500).body(Map.of(
"error", "Erreur lors de l'envoi de la réponse : " + e.getMessage()
));
}
}
@Data
public static class ContactRequest {
@Schema(description = "Nom de l'expéditeur", example = "Jean Dupont", requiredMode = Schema.RequiredMode.REQUIRED)
private String name;
@Schema(description = "Email de l'expéditeur", example = "jean.dupont@example.com", requiredMode = Schema.RequiredMode.REQUIRED)
private String email;
@Schema(description = "Message", example = "Bonjour, j'aimerais avoir des informations sur vos produits.", requiredMode = Schema.RequiredMode.REQUIRED)
private String message;
}
@Data
public static class ReplyRequest {
@Schema(description = "Sujet de la réponse", example = "Re: Votre demande", requiredMode = Schema.RequiredMode.REQUIRED)
private String subject;
@Schema(description = "Message de réponse", example = "Bonjour, merci pour votre message...", requiredMode = Schema.RequiredMode.REQUIRED)
private String message;
}
}