GameController.java
package com.archiweb.api;
import com.archiweb.dto.HistoryParticipationDTO;
import com.archiweb.model.Code;
import com.archiweb.model.Participation;
import com.archiweb.model.User;
import com.archiweb.model.Win;
import com.archiweb.repository.CodeRepository;
import com.archiweb.repository.ParticipationRepository;
import com.archiweb.repository.UserRepository;
import com.archiweb.repository.WinRepository;
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.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@RestController
@RequestMapping("/api/game")
@Tag(
name = "Jeu concours",
description = """
Module principal de gestion du **jeu concours Thé Tip Top**.
Ce contrôleur permet :
- La **participation** des utilisateurs via un code valide.
- La **consultation de l’historique des gains** de l’utilisateur connecté.
- La **vérification du statut d’un code** (valide, utilisé ou inexistant).
Tous les endpoints nécessitent un utilisateur connecté et un code valide pour participer.
"""
)
public class GameController {
private final CodeRepository codeRepository;
private final WinRepository winRepository;
private final UserRepository userRepository;
private final ParticipationRepository participationRepository;
public GameController(CodeRepository codeRepository,
WinRepository winRepository,
UserRepository userRepository,ParticipationRepository participationRepository) {
this.codeRepository = codeRepository;
this.winRepository = winRepository;
this.userRepository = userRepository;
this.participationRepository = participationRepository;
}
// =========================================================
// 1️⃣ Participation au jeu concours
// =========================================================
@Operation(
summary = "Participer au jeu concours",
description = """
Permet à un utilisateur authentifié de **participer au jeu concours** en saisissant un code unique.
Si le code est valide, non expiré et non encore utilisé, un **gain** lui est automatiquement attribué.
Le code est alors marqué comme “utilisé” pour empêcher toute réutilisation ultérieure.
""",
responses = {
@ApiResponse(
responseCode = "200",
description = "Participation validée et gain enregistré",
content = @Content(
mediaType = "application/json",
examples = @ExampleObject(
value = """
{
"status": "success",
"message": "Participation enregistrée.",
"gainAttribue": "INFUSEUR",
"timestamp": "2025-10-27T15:05:22"
}
"""
)
)
),
@ApiResponse(
responseCode = "400",
description = "Code invalide ou déjà utilisé",
content = @Content(
mediaType = "application/json",
examples = @ExampleObject(value = "{ \"error\": \"Code invalide ou déjà utilisé.\" }")
)
),
@ApiResponse(
responseCode = "401",
description = "Utilisateur non authentifié",
content = @Content(
mediaType = "application/json",
examples = @ExampleObject(value = "{ \"error\": \"Utilisateur non connecté.\" }")
)
)
}
)
@PostMapping("/participate")
public ResponseEntity<?> participate(@RequestParam String codeValue, Authentication authentication) {
String email = authentication.getName();
Optional<User> optionalUser = userRepository.findByEmail(email);
if (optionalUser.isEmpty()) {
return ResponseEntity.status(401).body("Utilisateur non trouvé.");
}
Optional<Code> optionalCode = codeRepository.findByValeur(codeValue);
if (optionalCode.isEmpty()) {
return ResponseEntity.badRequest().body("Code invalide.");
}
Code code = optionalCode.get();
if (code.isUsed()) {
return ResponseEntity.badRequest().body("Code déjà utilisé.");
}
code.setUsed(true);
code.setUsedAt(LocalDateTime.now());
codeRepository.save(code);
Win win = new Win();
win.setUser(optionalUser.get());
win.setPrizeName(code.getPrizeType().name());
win.setClaimed(false);
winRepository.save(win);
return ResponseEntity.ok("Participation enregistrée. Gain attribué : " + code.getPrizeType().name());
}
// =========================================================
// 2️⃣ Historique des participations et gains
// =========================================================
@Operation(
summary = "Obtenir l’historique des participations",
description = """
Retourne la **liste complète des gains** associés à l’utilisateur actuellement connecté.
Permet à chaque participant de consulter son historique de participations et de gains enregistrés.
""",
responses = {
@ApiResponse(
responseCode = "200",
description = "Historique récupéré avec succès",
content = @Content(
mediaType = "application/json",
examples = @ExampleObject(
value = """
[
{
"id": 1,
"prizeName": "INFUSEUR",
"claimed": false,
"createdAt": "2025-10-21T14:22:11"
},
{
"id": 2,
"prizeName": "COFFRET_39",
"claimed": true,
"claimedAt": "2025-10-24T10:08:05"
}
]
"""
)
)
),
@ApiResponse(
responseCode = "401",
description = "Utilisateur non authentifié",
content = @Content(
mediaType = "application/json",
examples = @ExampleObject(value = "{ \"error\": \"Utilisateur non connecté.\" }")
)
)
}
)
@GetMapping("/history")
public ResponseEntity<List<HistoryParticipationDTO>> getMyHistory(Authentication authentication) {
String email = authentication.getName();
Optional<User> opt = userRepository.findByEmail(email);
if (opt.isEmpty()) {
return ResponseEntity.status(401).build();
}
List<Participation> list = participationRepository.findByUserId(opt.get().getId());
List<HistoryParticipationDTO> dto = list.stream()
.map(p -> new HistoryParticipationDTO(
p.getId(),
p.getCode().getPrizeType().name(), // 🟢 juste le prize type
p.getDateParticipation()
))
.toList();
return ResponseEntity.ok(dto);
}
// =========================================================
// 3️⃣ Vérification du statut d’un code
// =========================================================
@Operation(
summary = "Vérifier le statut d’un code",
description = """
Permet de vérifier si un code saisi est **valide**, **déjà utilisé** ou **inexistant**.
Utilisé principalement pour informer le joueur avant la soumission de sa participation.
""",
responses = {
@ApiResponse(
responseCode = "200",
description = "Statut du code renvoyé avec succès",
content = @Content(
mediaType = "application/json",
examples = @ExampleObject(
value = """
{
"status": "valid",
"message": "Code valide",
"prizeType": "COFFRET_99"
}
"""
)
)
),
@ApiResponse(
responseCode = "400",
description = "Code invalide ou inexistant",
content = @Content(
mediaType = "application/json",
examples = @ExampleObject(value = "Code invalide ou inexistant.")
)
)
}
)
@GetMapping("/status/{codeValue}")
public ResponseEntity<?> getCodeStatus(@PathVariable String codeValue) {
Optional<Code> optionalCode = codeRepository.findByValeur(codeValue);
// Code introuvable
if (optionalCode.isEmpty()) {
return ResponseEntity.badRequest().body(Map.of(
"status", "error",
"message", "Code invalide ou inexistant.",
"valid", false
));
}
Code code = optionalCode.get();
// Code déjà utilisé
if (code.isUsed()) {
return ResponseEntity.ok(Map.of(
"status", "used",
"message", "Code déjà utilisé.",
"valid", false
));
}
// Code valide
Map<String, Object> response = new HashMap<>();
response.put("status", "valid");
response.put("message", "Code valide");
response.put("valid", true);
response.put("prizeType", code.getPrizeType() != null ? code.getPrizeType().name() : null);
return ResponseEntity.ok(response);
}
}