the-tip-top-backend/scripts/generate-tickets-bulk.js
2025-11-17 23:47:54 +01:00

202 lines
6.3 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Script pour générer des tickets en masse
* Usage: node scripts/generate-tickets-bulk.js [nombre]
* Exemple: node scripts/generate-tickets-bulk.js 500000
*/
import 'dotenv/config';
import { pool } from '../db.js';
const colors = {
reset: '\x1b[0m',
green: '\x1b[32m',
red: '\x1b[31m',
yellow: '\x1b[33m',
blue: '\x1b[34m',
cyan: '\x1b[36m',
};
const log = {
success: (msg) => console.log(`${colors.green}${msg}${colors.reset}`),
error: (msg) => console.log(`${colors.red}${msg}${colors.reset}`),
info: (msg) => console.log(`${colors.blue} ${msg}${colors.reset}`),
warn: (msg) => console.log(`${colors.yellow}⚠️ ${msg}${colors.reset}`),
section: (msg) => console.log(`\n${colors.cyan}${'='.repeat(60)}${colors.reset}\n${colors.cyan}${msg}${colors.reset}\n${colors.cyan}${'='.repeat(60)}${colors.reset}`),
};
/**
* Générer un code ticket aléatoire unique
*/
function generateTicketCode() {
const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'; // Sans I, O, 0, 1
let code = '';
for (let i = 0; i < 10; i++) {
code += chars.charAt(Math.floor(Math.random() * chars.length));
}
return code;
}
/**
* Fonction principale de génération
*/
async function generateTickets(quantity) {
log.section('GÉNÉRATION DE TICKETS EN MASSE');
console.log(`Quantité demandée: ${quantity.toLocaleString('fr-FR')} tickets\n`);
try {
// 1. Récupérer tous les lots actifs avec leurs probabilités
log.info('Récupération des lots actifs...');
const prizesResult = await pool.query(
`SELECT id, name, probability, stock
FROM prizes
WHERE is_active = TRUE AND stock > 0
ORDER BY probability DESC`
);
const prizes = prizesResult.rows;
if (prizes.length === 0) {
log.error('Aucun lot actif avec du stock disponible');
process.exit(1);
}
log.success(`${prizes.length} lots actifs trouvés`);
console.log('\nDistribution des probabilités:');
prizes.forEach(prize => {
console.log(`${prize.name}: ${(parseFloat(prize.probability) * 100).toFixed(2)}%`);
});
// 2. Calculer la somme totale des probabilités
const totalProbability = prizes.reduce((sum, prize) => sum + parseFloat(prize.probability), 0);
if (totalProbability === 0) {
log.error('La somme des probabilités est 0');
process.exit(1);
}
log.info(`\nProbabilité totale: ${totalProbability}`);
// 3. Générer les tickets
log.section('GÉNÉRATION DES CODES');
const tickets = [];
const codes = new Set();
const prizeDistribution = {};
const startTime = Date.now();
for (let i = 0; i < quantity; i++) {
// Générer un code unique
let code;
do {
code = generateTicketCode();
} while (codes.has(code));
codes.add(code);
// Sélectionner un lot selon la probabilité
const random = Math.random() * totalProbability;
let cumulativeProbability = 0;
let selectedPrize = prizes[0];
for (const prize of prizes) {
cumulativeProbability += parseFloat(prize.probability);
if (random <= cumulativeProbability) {
selectedPrize = prize;
break;
}
}
tickets.push({
code,
prizeId: selectedPrize.id,
prizeName: selectedPrize.name,
});
// Compter la distribution
prizeDistribution[selectedPrize.name] = (prizeDistribution[selectedPrize.name] || 0) + 1;
// Log de progression tous les 10000 tickets
if ((i + 1) % 10000 === 0) {
const progress = ((i + 1) / quantity * 100).toFixed(2);
console.log(`${(i + 1).toLocaleString('fr-FR')}/${quantity.toLocaleString('fr-FR')} (${progress}%)`);
}
}
const generationTime = ((Date.now() - startTime) / 1000).toFixed(2);
log.success(`${tickets.length.toLocaleString('fr-FR')} codes générés en ${generationTime}s`);
// 4. Afficher la distribution
log.section('DISTRIBUTION DES LOTS');
console.log('\nRésultat de la génération:');
Object.entries(prizeDistribution).forEach(([prizeName, count]) => {
const percentage = ((count / quantity) * 100).toFixed(2);
console.log(`${prizeName}: ${count.toLocaleString('fr-FR')} (${percentage}%)`);
});
// 5. Insérer les tickets en batch
log.section('INSERTION EN BASE DE DONNÉES');
const batchSize = 1000;
let inserted = 0;
const insertStartTime = Date.now();
for (let i = 0; i < tickets.length; i += batchSize) {
const batch = tickets.slice(i, i + batchSize);
// Construire les values pour l'insertion
const values = batch.map((_, idx) => {
const baseIndex = idx;
return `($${baseIndex * 2 + 1}, $${baseIndex * 2 + 2})`;
}).join(', ');
const params = batch.flatMap(t => [t.code, t.prizeId]);
await pool.query(
`INSERT INTO tickets (code, prize_id) VALUES ${values}`,
params
);
inserted += batch.length;
const progress = ((inserted / tickets.length) * 100).toFixed(2);
console.log(`${inserted.toLocaleString('fr-FR')}/${tickets.length.toLocaleString('fr-FR')} tickets insérés (${progress}%)`);
}
const insertTime = ((Date.now() - insertStartTime) / 1000).toFixed(2);
const totalTime = ((Date.now() - startTime) / 1000).toFixed(2);
log.section('RÉSUMÉ');
log.success(`${inserted.toLocaleString('fr-FR')} tickets générés et insérés avec succès!`);
console.log(`\nTemps total: ${totalTime}s`);
console.log(` • Génération codes: ${generationTime}s`);
console.log(` • Insertion DB: ${insertTime}s`);
console.log(`\nVitesse: ${Math.round(inserted / parseFloat(totalTime))} tickets/seconde`);
// 6. Statistiques finales
const totalTickets = await pool.query('SELECT COUNT(*) FROM tickets');
console.log(`\n📊 Total tickets en base: ${parseInt(totalTickets.rows[0].count).toLocaleString('fr-FR')}`);
} catch (error) {
log.error(`Erreur: ${error.message}`);
console.error(error);
process.exit(1);
} finally {
await pool.end();
}
}
// Lancer le script
const args = process.argv.slice(2);
const quantity = parseInt(args[0]) || 500000; // Par défaut: 500K tickets
if (quantity < 1 || quantity > 10000000) {
log.error('La quantité doit être entre 1 et 10,000,000');
process.exit(1);
}
generateTickets(quantity);