ajout des champs date de naissance et gender sur l'inscription, modification de la permition de lancer le jeu concour par l'administrateur, envoie des mail a chaque lot remporter
This commit is contained in:
@@ -1,23 +1,41 @@
|
||||
const prisma = require("../config/prisma");
|
||||
|
||||
// Le concours traverse cinq états, et non deux : confondre « pas encore
|
||||
// ouvert » avec « terminé » produit des messages faux côté joueur et laisse
|
||||
// passer le tirage au sort avant le début du jeu.
|
||||
const PHASES = {
|
||||
NOT_CONFIGURED: "NOT_CONFIGURED",
|
||||
SCHEDULED: "SCHEDULED",
|
||||
OPEN: "OPEN",
|
||||
CLAIM_ONLY: "CLAIM_ONLY",
|
||||
CLOSED: "CLOSED",
|
||||
};
|
||||
|
||||
const GAME_DURATION_DAYS = 30;
|
||||
const CLAIM_EXTENSION_DAYS = 30;
|
||||
const DAY_MS = 24 * 60 * 60 * 1000;
|
||||
|
||||
// Fenêtre pendant laquelle un joueur peut à la fois tester son code et
|
||||
// réclamer son lot : 30 jours de jeu + 30 jours supplémentaires à compter
|
||||
// de startDate. Tant qu'aucune GameSettings n'existe, le jeu est considéré
|
||||
// comme non démarré et donc ouvert (comportement de dev par défaut).
|
||||
async function getGameWindow() {
|
||||
// Deux échéances distinctes à partir de startDate :
|
||||
// - participationDeadline (30 j) : dernier moment pour tester un code ;
|
||||
// - claimDeadline (60 j) : dernier moment pour retirer un lot déjà gagné.
|
||||
// Les 30 jours supplémentaires servent uniquement au retrait, pas au jeu.
|
||||
// Tant qu'aucune GameSettings n'existe, le jeu est considéré comme non
|
||||
// configuré et donc ouvert (comportement de dev par défaut).
|
||||
async function getGameWindow(now = new Date()) {
|
||||
const settings = await prisma.gameSettings.findFirst({ orderBy: { id: "asc" } });
|
||||
|
||||
// Tant que l'administrateur n'a pas fixé de date, le concours n'existe pas :
|
||||
// rien n'est jouable et aucun tirage n'est possible. Ouvrir par défaut
|
||||
// reviendrait à faire gagner des lots dans un jeu qui n'a jamais été lancé.
|
||||
if (!settings) {
|
||||
return {
|
||||
configured: false,
|
||||
phase: PHASES.NOT_CONFIGURED,
|
||||
startDate: null,
|
||||
participationDeadline: null,
|
||||
claimDeadline: null,
|
||||
isOpen: true,
|
||||
isParticipationOpen: false,
|
||||
isOpen: false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -26,26 +44,57 @@ async function getGameWindow() {
|
||||
settings.startDate.getTime() + (GAME_DURATION_DAYS + CLAIM_EXTENSION_DAYS) * DAY_MS
|
||||
);
|
||||
|
||||
const hasStarted = now >= settings.startDate;
|
||||
const isParticipationOpen = hasStarted && now <= participationDeadline;
|
||||
const isOpen = hasStarted && now <= claimDeadline;
|
||||
|
||||
let phase;
|
||||
if (!hasStarted) phase = PHASES.SCHEDULED;
|
||||
else if (isParticipationOpen) phase = PHASES.OPEN;
|
||||
else if (isOpen) phase = PHASES.CLAIM_ONLY;
|
||||
else phase = PHASES.CLOSED;
|
||||
|
||||
return {
|
||||
configured: true,
|
||||
phase,
|
||||
startDate: settings.startDate,
|
||||
participationDeadline,
|
||||
claimDeadline,
|
||||
isOpen: new Date() <= claimDeadline,
|
||||
hasStarted,
|
||||
isParticipationOpen,
|
||||
isOpen,
|
||||
};
|
||||
}
|
||||
|
||||
// Bascule en CANCELLED les gains PENDING non réclamés une fois la fenêtre
|
||||
// fermée. Appelé au fil de l'eau (pas de cron) avant toute lecture/écriture
|
||||
// de claims, ce qui suffit pour ce projet.
|
||||
async function expireOverdueClaims() {
|
||||
const window = await getGameWindow();
|
||||
if (window.isOpen) return;
|
||||
|
||||
await prisma.claim.updateMany({
|
||||
where: { status: "PENDING" },
|
||||
data: { status: "CANCELLED" },
|
||||
});
|
||||
// L'expiration d'un gain est *dérivée* de la fenêtre de jeu, jamais écrite en
|
||||
// base : un PENDING dont le délai de retrait est passé est présenté comme
|
||||
// CANCELLED, mais la ligne reste intacte. Corriger une startDate erronée
|
||||
// suffit donc à rétablir les gains, et aucune lecture ne modifie la base.
|
||||
function effectiveClaimStatus(status, gameWindow) {
|
||||
if (status === "PENDING" && !gameWindow.isOpen) return "CANCELLED";
|
||||
return status;
|
||||
}
|
||||
|
||||
module.exports = { getGameWindow, expireOverdueClaims };
|
||||
// Message destiné au joueur qui tente de jouer hors de la fenêtre : chaque
|
||||
// phase a sa propre explication, avec la date utile quand elle existe.
|
||||
function participationClosedMessage(gameWindow) {
|
||||
switch (gameWindow.phase) {
|
||||
case PHASES.NOT_CONFIGURED:
|
||||
return "Le jeu-concours n'est pas encore ouvert. Revenez prochainement.";
|
||||
case PHASES.SCHEDULED:
|
||||
return `Le jeu-concours ouvrira le ${gameWindow.startDate.toLocaleDateString("fr-FR")}.`;
|
||||
case PHASES.CLAIM_ONLY:
|
||||
return "La période de participation est terminée. Vous pouvez encore retirer les lots déjà gagnés.";
|
||||
default:
|
||||
return "Le jeu-concours est terminé. Vous ne pouvez plus tester de code ni réclamer de lot.";
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getGameWindow,
|
||||
effectiveClaimStatus,
|
||||
participationClosedMessage,
|
||||
PHASES,
|
||||
GAME_DURATION_DAYS,
|
||||
CLAIM_EXTENSION_DAYS,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user