CodeController.java
package com.archiweb.api;
import com.archiweb.dto.CheckCodeRequest;
import com.archiweb.dto.CreateCodeRequest;
import com.archiweb.dto.UpdateCodeRequest;
import com.archiweb.model.Code;
import com.archiweb.model.PrizeType;
import com.archiweb.repository.CodeRepository;
import com.archiweb.service.CodeService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
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 org.springframework.data.domain.Page;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api/codes")
@Tag(
name = "Codes",
description = """
Module de gestion et de vérification des codes du jeu concours.
Ce contrôleur permet :
- la **vérification** d’un code client (valide, expiré, déjà utilisé, etc.),
- la **création** et la **mise à jour** de codes par un administrateur,
- la **consultation** des codes existants (filtrés et paginés),
- la **suppression** des codes obsolètes.
"""
)
public class CodeController {
private final CodeRepository repo;
private final CodeService codeService;
public CodeController(CodeRepository repo, CodeService codeService) {
this.repo = repo;
this.codeService = codeService;
}
// ================================================================
// Vérification du service
// ================================================================
@GetMapping("/health")
@Operation(
summary = "Vérifier l'état du service Codes",
description = "Permet de tester la disponibilité du module Codes. Renvoie une réponse 200 si le service est actif.",
responses = {
@ApiResponse(responseCode = "200", description = "Service opérationnel")
}
)
public String ping() {
return "API Codes opérationnelle ✅";
}
// ================================================================
// Vérification de validité d’un code
// ================================================================
@PostMapping("/check")
@Operation(
summary = "Vérifier la validité d’un code",
description = """
Vérifie la validité d’un code saisi par un utilisateur.
Le service renvoie des informations précises selon l’état du code :
- **200 OK** : code valide → gain associé retourné.
- **409 Conflict** : code déjà utilisé.
- **410 Gone** : code expiré.
- **404 Not Found** : code inconnu.
- **400 Bad Request** : code vide ou invalide.
""",
requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
required = true,
description = "Requête contenant la valeur du code à vérifier",
content = @Content(
schema = @Schema(implementation = CheckCodeRequest.class),
examples = @ExampleObject(value = """
{
"valeur": "CODE123ABC"
}
""")
)
),
responses = {
@ApiResponse(responseCode = "200", description = "Code valide et gain associé",
content = @Content(
mediaType = "application/json",
examples = @ExampleObject(value = """
{
"status": "success",
"message": "Félicitations, vous avez gagné : INFUSEUR",
"valid": true,
"prizeType": "INFUSEUR"
}
""")
)),
@ApiResponse(responseCode = "404", description = "Code inconnu"),
@ApiResponse(responseCode = "409", description = "Code déjà utilisé"),
@ApiResponse(responseCode = "410", description = "Code expiré"),
@ApiResponse(responseCode = "400", description = "Code vide ou invalide")
}
)
public ResponseEntity<Map<String, Object>> checkCode(@RequestBody CheckCodeRequest request) {
String valeur = request.getValeur();
if (valeur == null || valeur.trim().isEmpty()) {
Map<String, Object> resp = new HashMap<>();
resp.put("status", "error");
resp.put("message", "Code vide ou invalide.");
resp.put("valid", false);
return ResponseEntity.badRequest().body(resp);
}
return repo.findByValeur(valeur.trim().toUpperCase())
.map(code -> {
Map<String, Object> resp = new HashMap<>();
if (code.getExpiresAt() != null && code.getExpiresAt().isBefore(LocalDateTime.now())) {
resp.put("status", "expired");
resp.put("message", "Ce code est expiré.");
resp.put("valid", false);
return ResponseEntity.status(HttpStatus.GONE).body(resp);
}
if (code.isUsed()) {
resp.put("status", "used");
resp.put("message", "Ce code a déjà été utilisé.");
resp.put("valid", false);
return ResponseEntity.status(HttpStatus.CONFLICT).body(resp);
}
resp.put("status", "success");
resp.put("message", "Félicitations, vous avez gagné : " + code.getPrizeType());
resp.put("valid", true);
resp.put("prizeType", code.getPrizeType().name());
return ResponseEntity.ok(resp);
})
.orElseGet(() -> {
Map<String, Object> resp = new HashMap<>();
resp.put("status", "error");
resp.put("message", "Code inconnu.");
resp.put("valid", false);
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(resp);
});
}
// ================================================================
// Récupérer un code spécifique
// ================================================================
@GetMapping("/{valeur}")
@Operation(
summary = "Obtenir un code spécifique",
description = """
Permet d’obtenir les informations détaillées d’un code à partir de sa valeur.
Cet endpoint est principalement utilisé par les administrateurs ou le support.
""",
responses = {
@ApiResponse(responseCode = "200", description = "Code trouvé avec succès",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = Code.class))),
@ApiResponse(responseCode = "404", description = "Code introuvable")
}
)
public ResponseEntity<Object> getCode(@PathVariable String valeur) {
return repo.findByValeur(valeur.toUpperCase())
.<ResponseEntity<Object>>map(ResponseEntity::ok)
.orElseGet(() -> ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(Map.of("message", "Code introuvable")));
}
// ================================================================
// Lister les codes avec filtres
// ================================================================
@GetMapping
@Operation(
summary = "Lister les codes (paginés et filtrés)",
description = """
Renvoie la liste des codes existants avec pagination et filtres optionnels :
- **prizeType** : type de lot (INFUSEUR, DETOX_100G…)
- **used** : filtrer selon leur état d’utilisation
- **page / size** : paramètres de pagination
""",
responses = {
@ApiResponse(responseCode = "200", description = "Liste des codes récupérée avec succès",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = Page.class)))
}
)
public ResponseEntity<Page<Code>> getCodes(
@Parameter(description = "Numéro de page (par défaut 0)") @RequestParam(defaultValue = "0") int page,
@Parameter(description = "Taille de la page (par défaut 20)") @RequestParam(defaultValue = "20") int size,
@Parameter(description = "Filtrer par type de lot (ex: INFUSEUR)") @RequestParam(required = false) PrizeType prizeType,
@Parameter(description = "Filtrer selon l’état d’utilisation du code") @RequestParam(required = false) Boolean used
) {
Page<Code> result = codeService.getCodesFiltered(prizeType, used, page, size);
return ResponseEntity.ok(result);
}
// ================================================================
// Créer un nouveau code
// ================================================================
@PostMapping
@Operation(
summary = "Créer un nouveau code",
description = """
Ajoute un nouveau code dans la base de données.
Si la valeur existe déjà, une erreur 409 est renvoyée.
""",
requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
required = true,
description = "Informations du code à créer",
content = @Content(
schema = @Schema(implementation = CreateCodeRequest.class),
examples = @ExampleObject(value = """
{
"valeur": "NEWCODE123",
"prizeType": "INFUSEUR",
"expiresAt": "2025-12-31T23:59:59"
}
""")
)
),
responses = {
@ApiResponse(responseCode = "201", description = "Code créé avec succès"),
@ApiResponse(responseCode = "409", description = "Valeur de code déjà existante")
}
)
public ResponseEntity<?> createCode(@RequestBody CreateCodeRequest req) {
if (repo.findByValeur(req.getValeur()).isPresent()) {
return ResponseEntity.status(HttpStatus.CONFLICT)
.body(Map.of("message", "Valeur déjà existante"));
}
Code code = new Code();
code.setValeur(req.getValeur().toUpperCase());
code.setPrizeType(req.getPrizeType());
code.setExpiresAt(req.getExpiresAt() != null ? req.getExpiresAt() : LocalDateTime.now().plusDays(60));
code.setUsed(false);
code.setUsedAt(null);
return ResponseEntity.status(HttpStatus.CREATED).body(repo.save(code));
}
// ================================================================
// Mettre à jour un code existant
// ================================================================
@PutMapping("/{id}")
@Operation(
summary = "Mettre à jour un code existant",
description = """
Met à jour les informations d’un code existant :
- type de lot
- date d’expiration
- état d’utilisation
""",
responses = {
@ApiResponse(responseCode = "200", description = "Code mis à jour avec succès"),
@ApiResponse(responseCode = "404", description = "Code introuvable")
}
)
public ResponseEntity<?> updateCode(@PathVariable Long id, @RequestBody UpdateCodeRequest req) {
return repo.findById(id)
.map(existing -> {
existing.setPrizeType(req.getPrizeType());
existing.setExpiresAt(req.getExpiresAt());
existing.setUsed(req.isUsed());
existing.setUsedAt(req.getUsedAt());
repo.save(existing);
return ResponseEntity.ok(Map.of(
"message", "Code mis à jour avec succès",
"code", existing
));
})
.orElseGet(() -> ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(Map.of("message", "Code introuvable")));
}
// ================================================================
// Supprimer un code
// ================================================================
@DeleteMapping("/{id}")
@Operation(
summary = "Supprimer un code",
description = "Supprime un code existant de la base de données en fonction de son identifiant unique.",
responses = {
@ApiResponse(responseCode = "200", description = "Code supprimé avec succès"),
@ApiResponse(responseCode = "404", description = "Code introuvable")
}
)
public ResponseEntity<?> deleteCode(@PathVariable Long id) {
if (!repo.existsById(id)) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(Map.of("message", "Code introuvable"));
}
repo.deleteById(id);
return ResponseEntity.ok(Map.of("message", "Code supprimé avec succès"));
}
}