validateLoadArchives.php 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. <?php
  2. // Configuración de BD
  3. $host = 'localhost';
  4. $port = 3306;
  5. $database = 'samqa';
  6. $username = 'root';
  7. $password = 'root';
  8. // Archivo lock global
  9. $lockFile = __DIR__ . '/validation.lock';
  10. // 1. Verificar lock global
  11. if (file_exists($lockFile)) {
  12. echo "[" . date('Y-m-d H:i:s') . "] Otro proceso está en ejecución. Saliendo...\n";
  13. exit;
  14. }
  15. file_put_contents($lockFile, getmypid());
  16. // 2. Conectar BD
  17. try {
  18. $pdo = new PDO("mysql:host=$host;port=$port;dbname=$database", $username, $password);
  19. $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  20. // 3. Buscar job pendiente o en progreso
  21. $pdo->beginTransaction();
  22. // Primero buscar un job que quedó a medias
  23. $stmt = $pdo->prepare("
  24. SELECT * FROM validation_jobs
  25. WHERE status = 'processing' AND progress_percentage < 100
  26. ORDER BY created_at ASC
  27. LIMIT 1
  28. FOR UPDATE
  29. ");
  30. $stmt->execute();
  31. $job = $stmt->fetch(PDO::FETCH_ASSOC);
  32. // Si no hay, tomar uno en cola
  33. if (!$job) {
  34. $stmt = $pdo->prepare("
  35. SELECT * FROM validation_jobs
  36. WHERE status = 'queued'
  37. ORDER BY created_at ASC
  38. LIMIT 1
  39. FOR UPDATE
  40. ");
  41. $stmt->execute();
  42. $job = $stmt->fetch(PDO::FETCH_ASSOC);
  43. if ($job) {
  44. $update = $pdo->prepare("
  45. UPDATE validation_jobs
  46. SET status = 'processing', updated_at = NOW()
  47. WHERE id = ?
  48. ");
  49. $update->execute([$job['id']]);
  50. }
  51. }
  52. $pdo->commit();
  53. if (!$job) {
  54. echo "[" . date('Y-m-d H:i:s') . "] No hay trabajos pendientes.\n";
  55. unlink($lockFile);
  56. exit;
  57. }
  58. echo "Procesando Job ID: {$job['id']}\n";
  59. // Enviar notificación WebSocket de inicio
  60. sendWebSocketNotification($job['user_id'], 'processing', $job['id']);
  61. // 4. Determinar archivos ya procesados
  62. $results = json_decode($job['results'], true) ?? ['processed_files' => []];
  63. $procesados = $results['processed_files'] ?? [];
  64. // 5. Abrir ZIP y obtener lista completa de archivos
  65. $zipPath = $job['zip_temp_path'];
  66. if (!file_exists($zipPath)) {
  67. throw new Exception("ZIP no encontrado: {$zipPath}");
  68. }
  69. $zip = new ZipArchive();
  70. if ($zip->open($zipPath) !== TRUE) {
  71. throw new Exception("No se pudo abrir el ZIP: {$zipPath}");
  72. }
  73. $totalArchivos = $zip->numFiles;
  74. $faltantes = [];
  75. for ($i = 0; $i < $totalArchivos; $i++) {
  76. $nombreArchivo = $zip->getNameIndex($i);
  77. if (!in_array($nombreArchivo, $procesados)) {
  78. $faltantes[] = $nombreArchivo;
  79. }
  80. }
  81. echo "Archivos totales: $totalArchivos, ya procesados: " . count($procesados) . ", faltantes: " . count($faltantes) . "\n";
  82. // 6. Procesar faltantes
  83. foreach ($faltantes as $nombreArchivo) {
  84. echo "Procesando: {$nombreArchivo}\n";
  85. // Extraer contenido del archivo
  86. $contenido = $zip->getFromName($nombreArchivo);
  87. // Marcar archivo como procesado
  88. $procesados[] = $nombreArchivo;
  89. $progress = intval((count($procesados) / $totalArchivos) * 100);
  90. $results['processed_files'] = $procesados;
  91. $stmt = $pdo->prepare("
  92. UPDATE validation_jobs
  93. SET results = ?, progress_percentage = ?, updated_at = NOW()
  94. WHERE id = ?
  95. ");
  96. $stmt->execute([json_encode($results), $progress, $job['id']]);
  97. // Enviar notificación de progreso
  98. sendWebSocketNotification($job['user_id'], 'progress', $job['id'], $progress);
  99. echo "Progreso: {$progress}%\n";
  100. }
  101. $zip->close();
  102. // 7. Marcar como completado
  103. $stmt = $pdo->prepare("
  104. UPDATE validation_jobs
  105. SET status = 'completed', progress_percentage = 100, completed_at = NOW(), updated_at = NOW()
  106. WHERE id = ?
  107. ");
  108. $stmt->execute([$job['id']]);
  109. // Enviar notificación de completado
  110. sendWebSocketNotification($job['user_id'], 'completed', $job['id']);
  111. echo "Job completado: {$job['id']}\n";
  112. } catch (Exception $e) {
  113. echo "ERROR: " . $e->getMessage() . "\n";
  114. // Enviar notificación de error si hay job activo
  115. if (isset($job)) {
  116. $stmt = $pdo->prepare("UPDATE validation_jobs SET status = 'failed', error_message = ? WHERE id = ?");
  117. $stmt->execute([$e->getMessage(), $job['id']]);
  118. sendWebSocketNotification($job['user_id'], 'failed', $job['id']);
  119. }
  120. } finally {
  121. // 8. Eliminar lock global
  122. if (file_exists($lockFile)) {
  123. unlink($lockFile);
  124. }
  125. }
  126. function sendWebSocketNotification($userId, $status, $jobId, $progress = null) {
  127. try {
  128. require_once __DIR__ . '/../vendor/autoload.php';
  129. $client = new \ElephantIO\Client(\ElephantIO\Client::engine(\ElephantIO\Client::CLIENT_4X, 'http://localhost:3200'));
  130. $client->initialize();
  131. $client->of('/');
  132. $data = [
  133. 'jobId' => $jobId,
  134. 'status' => $status,
  135. 'message' => getStatusMessage($status)
  136. ];
  137. if ($progress !== null) {
  138. $data['progress'] = $progress;
  139. }
  140. $client->emit('laravel_emit', [
  141. 'event' => 'validation_status',
  142. 'data' => $data,
  143. 'userId' => (string)$userId
  144. ]);
  145. $client->close();
  146. echo "WebSocket: $status enviado a usuario $userId\n";
  147. } catch (Exception $e) {
  148. echo "WebSocket error: " . $e->getMessage() . "\n";
  149. }
  150. }
  151. function getStatusMessage($status) {
  152. $messages = [
  153. 'queued' => 'En cola de validación',
  154. 'processing' => 'Procesando archivos',
  155. 'progress' => 'Validando archivos',
  156. 'completed' => 'Validación completada',
  157. 'failed' => 'Error en validación'
  158. ];
  159. return $messages[$status] ?? 'Estado desconocido';
  160. }