AdminController.java
package com.archiweb.api;
import com.archiweb.service.EmailExportService;
import com.archiweb.service.StatsService;
import com.archiweb.service.DrawService;
import com.archiweb.service.SettingsService;
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.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api/admin")
@Tag(
name = "Administration",
description = """
Module d’administration du jeu concours Thé Tip Top.
Ce contrôleur regroupe les fonctionnalités réservées aux administrateurs :
- Export des emails opt-in pour les campagnes marketing (RGPD).
- Consultation des statistiques globales de participations et de gains.
- Lancement du tirage final automatique du jeu concours.
Tous les endpoints nécessitent le rôle **ADMIN**.
"""
)
public class AdminController {
private final EmailExportService emailExportService;
private final StatsService statsService;
private final DrawService drawService;
private final SettingsService settingsService;
public AdminController(EmailExportService emailExportService,
StatsService statsService,
DrawService drawService,
SettingsService settingsService) {
this.emailExportService = emailExportService;
this.statsService = statsService;
this.drawService = drawService;
this.settingsService = settingsService;
}
// =========================================================
// Endpoint de test pour vérifier les permissions
// =========================================================
@GetMapping("/test-auth")
@Operation(summary = "Test d'authentification admin", description = "Endpoint de test pour vérifier que l'utilisateur a bien le rôle ADMIN")
public ResponseEntity<Map<String, Object>> testAuth(Authentication authentication) {
Map<String, Object> response = new HashMap<>();
response.put("authenticated", authentication != null);
response.put("name", authentication != null ? authentication.getName() : null);
response.put("authorities", authentication != null ?
authentication.getAuthorities().stream()
.map(a -> a.getAuthority())
.collect(java.util.stream.Collectors.toList()) : null);
response.put("hasAdminRole", authentication != null &&
authentication.getAuthorities().stream()
.anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN")));
return ResponseEntity.ok(response);
}
// =========================================================
// Export des emails opt-in
// =========================================================
@Operation(
summary = "Exporter les emails opt-in",
description = """
Permet d’exporter la liste complète des utilisateurs ayant accepté de recevoir des e-mails marketing (`consentGiven = true`).
Le fichier exporté contient les adresses e-mail valides et peut être utilisé pour des campagnes RGPD-compliantes.
""",
responses = {
@ApiResponse(
responseCode = "200",
description = "Export des emails effectué avec succès",
content = @Content(
mediaType = "application/json",
examples = @ExampleObject(
value = """
{
"status": "success",
"count": 145,
"filePath": "/exports/emailing_2025-10-27.csv"
}
"""
)
)
),
@ApiResponse(
responseCode = "403",
description = "Accès refusé : rôle ADMIN requis",
content = @Content(
mediaType = "application/json",
examples = @ExampleObject(
value = "{ \"error\": \"Access denied. ADMIN role required.\" }"
)
)
)
}
)
@PreAuthorize("hasRole('ADMIN')")
@GetMapping("/export-emailing")
public ResponseEntity<?> exportEmails() {
return ResponseEntity.ok(emailExportService.exportEmailing());
}
// =========================================================
// Statistiques globales
// =========================================================
@Operation(
summary = "Obtenir les statistiques globales",
description = """
Retourne un ensemble complet de statistiques sur les participations, les lots gagnés, les boutiques participantes et la période de jeu.
Les paramètres optionnels permettent de filtrer les résultats :
- **lot** : type de lot (ex. INFUSEUR, COFFRET_39)
- **boutique** : identifiant ou nom de la boutique
- **periode** : intervalle temporel (ex. "2025-09-01:2025-09-30")
""",
responses = {
@ApiResponse(
responseCode = "200",
description = "Statistiques récupérées avec succès",
content = @Content(
mediaType = "application/json",
examples = @ExampleObject(
value = """
{
"status": "success",
"totalParticipations": 12054,
"totalGagnants": 354,
"topBoutique": "Boutique Paris 12",
"lotsDistribues": {
"INFUSEUR": 220,
"COFFRET_39": 102,
"COFFRET_99": 32
}
}
"""
)
)
),
@ApiResponse(
responseCode = "403",
description = "Accès refusé : rôle ADMIN requis",
content = @Content(
mediaType = "application/json",
examples = @ExampleObject(value = "{ \"error\": \"Access denied. ADMIN role required.\" }")
)
)
}
)
@PreAuthorize("hasRole('ADMIN')")
@GetMapping("/stats")
public ResponseEntity<?> getStats(@RequestParam(required = false) String lot,
@RequestParam(required = false) String boutique,
@RequestParam(required = false) String periode) {
return ResponseEntity.ok(statsService.getStats(lot, boutique, periode));
}
// =========================================================
// Tirage final du jeu concours
// =========================================================
@Operation(
summary = "Effectuer le tirage final",
description = """
Lance le **tirage automatique du gagnant final** du jeu concours.
Ce processus sélectionne un gagnant parmi toutes les participations valides selon les règles de probabilité définies.
Un paramètre optionnel `seed` peut être fourni pour rendre le tirage reproductible lors des tests.
""",
responses = {
@ApiResponse(
responseCode = "200",
description = "Tirage final effectué avec succès",
content = @Content(
mediaType = "application/json",
examples = @ExampleObject(
value = """
{
"status": "success",
"winner": {
"id": 42,
"email": "gagnant@example.com",
"prize": "COFFRET_99",
"drawDate": "2025-10-27T10:32:45"
}
}
"""
)
)
),
@ApiResponse(
responseCode = "403",
description = "Accès refusé : rôle ADMIN requis",
content = @Content(
mediaType = "application/json",
examples = @ExampleObject(value = "{ \"error\": \"Access denied. ADMIN role required.\" }")
)
)
}
)
@PreAuthorize("hasRole('ADMIN')")
@PostMapping("/draw/final")
public ResponseEntity<?> runFinalDraw(@RequestParam(required = false) Long seed) {
return ResponseEntity.ok(drawService.runFinalDraw(seed));
}
// =========================================================
// Gestion du mode Noël
// =========================================================
@Operation(
summary = "Activer/Désactiver le mode Noël",
description = "Active ou désactive le mode Noël pour le site (accessible à tous les utilisateurs authentifiés)"
)
// Pas de @PreAuthorize - accessible à tous les utilisateurs authentifiés
// Le mode Noël est juste un paramètre visuel, pas besoin de restriction admin
@PostMapping("/settings/christmas-mode")
public ResponseEntity<Map<String, Object>> setChristmasMode(@RequestBody Map<String, Boolean> request) {
Boolean enabled = request.get("enabled");
if (enabled == null) {
Map<String, Object> error = new HashMap<>();
error.put("error", "Le paramètre 'enabled' est requis");
return ResponseEntity.badRequest().body(error);
}
settingsService.setChristmasMode(enabled);
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("enabled", enabled);
response.put("message", enabled ? "Mode Noël activé" : "Mode Noël désactivé");
return ResponseEntity.ok(response);
}
}