/** * 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);