202 lines
6.3 KiB
JavaScript
202 lines
6.3 KiB
JavaScript
/**
|
||
* 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);
|