validateLoadArchives.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  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, en progreso o fallido
  21. $pdo->beginTransaction();
  22. // Primero buscar un job que quedó a medias
  23. $stmt = $pdo->prepare("
  24. SELECT * FROM S002V01TVAJO
  25. WHERE VAJO_ESTA = 'processing' AND VAJO_PRPE < 100
  26. ORDER BY VAJO_FERE ASC
  27. LIMIT 1
  28. FOR UPDATE
  29. ");
  30. $stmt->execute();
  31. $job = $stmt->fetch(PDO::FETCH_ASSOC);
  32. // Si no hay, buscar jobs fallidos para reintentar
  33. if (!$job) {
  34. $stmt = $pdo->prepare("
  35. SELECT * FROM S002V01TVAJO
  36. WHERE VAJO_ESTA = 'failed'
  37. ORDER BY VAJO_FERE ASC
  38. LIMIT 1
  39. FOR UPDATE
  40. ");
  41. $stmt->execute();
  42. $job = $stmt->fetch(PDO::FETCH_ASSOC);
  43. if ($job) {
  44. echo "Reintentando job fallido: {$job['VAJO_IDJO']}\n";
  45. $update = $pdo->prepare("
  46. UPDATE S002V01TVAJO
  47. SET VAJO_ESTA = 'processing', VAJO_ERME = NULL, VAJO_FEMO = NOW()
  48. WHERE VAJO_IDJO = ?
  49. ");
  50. $update->execute([$job['VAJO_IDJO']]);
  51. }
  52. }
  53. // Si no hay, tomar uno en cola
  54. if (!$job) {
  55. $stmt = $pdo->prepare("
  56. SELECT * FROM S002V01TVAJO
  57. WHERE VAJO_ESTA = 'queued'
  58. ORDER BY VAJO_FERE ASC
  59. LIMIT 1
  60. FOR UPDATE
  61. ");
  62. $stmt->execute();
  63. $job = $stmt->fetch(PDO::FETCH_ASSOC);
  64. if ($job) {
  65. $update = $pdo->prepare("
  66. UPDATE S002V01TVAJO
  67. SET VAJO_ESTA = 'processing', VAJO_FEMO = NOW()
  68. WHERE VAJO_IDJO = ?
  69. ");
  70. $update->execute([$job['VAJO_IDJO']]);
  71. }
  72. }
  73. $pdo->commit();
  74. if (!$job) {
  75. echo "[" . date('Y-m-d H:i:s') . "] No hay trabajos pendientes.\n";
  76. unlink($lockFile);
  77. exit;
  78. }
  79. echo "Procesando Job ID: {$job['VAJO_IDJO']}\n";
  80. // Enviar notificación WebSocket de inicio
  81. sendWebSocketNotification($job['VAJO_IDUS'], 'processing', $job['VAJO_IDJO']);
  82. // 4. Determinar archivos ya procesados
  83. $results = json_decode($job['VAJO_RESU'], true) ?? ['processed_files' => []];
  84. $procesados = $results['processed_files'] ?? [];
  85. // 5. Buscar ruta del ZIP en base de datos
  86. $stmt = $pdo->prepare("SELECT ARTE_UBTE FROM s002v01tarte WHERE ARTE_IDAR = ?");
  87. $stmt->execute([$job['VAJO_ZITE']]);
  88. $zipData = $stmt->fetch(PDO::FETCH_ASSOC);
  89. if (!$zipData) {
  90. throw new Exception("ZIP ID no encontrado: {$job['VAJO_ZITE']}");
  91. }
  92. $zipPath = $zipData['ARTE_UBTE'];
  93. if (!file_exists($zipPath)) {
  94. throw new Exception("ZIP no encontrado: {$zipPath}");
  95. }
  96. // Guardar IDs de archivos temporales
  97. $tempFiles = [
  98. 'excel' => getEncrypt($job['VAJO_EXTE']),
  99. 'zip' => getEncrypt($job['VAJO_ZITE'])
  100. ];
  101. $individualTempFiles = [];
  102. $zip = new ZipArchive();
  103. if ($zip->open($zipPath) !== TRUE) {
  104. throw new Exception("No se pudo abrir el ZIP: {$zipPath}");
  105. }
  106. // Crear carpeta de extracción temporal
  107. $extractDir = __DIR__ . '/../storage/app/tempFiles/extracted_' . time();
  108. if (!mkdir($extractDir, 0755, true)) {
  109. throw new Exception("No se pudo crear directorio de extracción: {$extractDir}");
  110. }
  111. // Obtener solo archivos (no directorios)
  112. $archivos = [];
  113. for ($i = 0; $i < $zip->numFiles; $i++) {
  114. $stat = $zip->statIndex($i);
  115. $nombreArchivo = $stat['name'];
  116. // Solo procesar archivos, no directorios
  117. if (substr($nombreArchivo, -1) !== '/') {
  118. $archivos[] = basename($nombreArchivo); // Solo nombre sin ruta
  119. }
  120. }
  121. $totalArchivos = count($archivos);
  122. $faltantes = array_diff($archivos, $procesados);
  123. echo "Archivos totales: $totalArchivos, ya procesados: " . count($procesados) . ", faltantes: " . count($faltantes) . "\n";
  124. // 6. Procesar faltantes
  125. foreach ($faltantes as $nombreArchivo) {
  126. echo "Procesando: {$nombreArchivo}\n";
  127. // Extraer archivo del ZIP
  128. $archivoExtraido = $extractDir . '/' . $nombreArchivo;
  129. $contenido = null;
  130. // Buscar el archivo en el ZIP (puede estar en subdirectorios)
  131. for ($i = 0; $i < $zip->numFiles; $i++) {
  132. $stat = $zip->statIndex($i);
  133. if (basename($stat['name']) === $nombreArchivo) {
  134. $contenido = $zip->getFromIndex($i);
  135. break;
  136. }
  137. }
  138. if ($contenido === null) {
  139. echo "Archivo no encontrado en ZIP: {$nombreArchivo}\n";
  140. continue;
  141. }
  142. // Guardar archivo extraído
  143. file_put_contents($archivoExtraido, $contenido);
  144. // Enviar archivo al endpoint usando multipart/form-data
  145. $ch = curl_init('http://192.168.2.21:8000/api/upload-temp-file');
  146. curl_setopt($ch, CURLOPT_POST, true);
  147. curl_setopt($ch, CURLOPT_POSTFIELDS, [
  148. 'file' => new CURLFile($archivoExtraido, mime_content_type($archivoExtraido), $nombreArchivo),
  149. 'id_user' => $job['VAJO_IDUS'],
  150. 'linea' => $job['VAJO_NULI']
  151. ]);
  152. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  153. $response = curl_exec($ch);
  154. $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  155. curl_close($ch);
  156. // Eliminar archivo temporal
  157. unlink($archivoExtraido);
  158. // Solo marcar como procesado si el endpoint respondió exitosamente
  159. if ($httpCode >= 200 && $httpCode < 300) {
  160. $responseData = json_decode($response, true);
  161. if (isset($responseData['response']['idArchivo'])) {
  162. $individualTempFiles[] = [
  163. 'original_name' => $nombreArchivo,
  164. 'temp_id' => $responseData['response']['idArchivo']
  165. ];
  166. }
  167. $procesados[] = $nombreArchivo;
  168. $progress = intval((count($procesados) / $totalArchivos) * 100);
  169. $results['processed_files'] = $procesados;
  170. $stmt = $pdo->prepare("
  171. UPDATE S002V01TVAJO
  172. SET VAJO_RESU = ?, VAJO_PRPE = ?, VAJO_FEMO = NOW()
  173. WHERE VAJO_IDJO = ?
  174. ");
  175. $stmt->execute([json_encode($results), $progress, $job['VAJO_IDJO']]);
  176. // Enviar notificación de progreso
  177. sendWebSocketNotification($job['VAJO_IDUS'], 'progress', $job['VAJO_IDJO'], $progress);
  178. //echo "Progreso: {$progress}%\n";
  179. } else {
  180. echo "Error al procesar archivo {$nombreArchivo}: HTTP {$httpCode}\n";
  181. throw new Exception("Error al procesar archivo: {$nombreArchivo}");
  182. }
  183. }
  184. $zip->close();
  185. // Eliminar directorio de extracción
  186. rmdir($extractDir);
  187. // 7. Marcar como completado
  188. $stmt = $pdo->prepare("
  189. UPDATE S002V01TVAJO
  190. SET VAJO_ESTA = 'completed', VAJO_PRPE = 100, VAJO_FECO = NOW(), VAJO_FEMO = NOW()
  191. WHERE VAJO_IDJO = ?
  192. ");
  193. $stmt->execute([$job['VAJO_IDJO']]);
  194. // Enviar notificación de completado con archivos temporales
  195. $completionData = [
  196. 'temp_files' => $tempFiles,
  197. 'individual_temp_files' => $individualTempFiles
  198. ];
  199. sendWebSocketNotification($job['VAJO_IDUS'], 'completed', $job['VAJO_IDJO'], 100, $completionData);
  200. echo "Job completado: {$job['VAJO_IDJO']}\n";
  201. } catch (Exception $e) {
  202. echo "ERROR: " . $e->getMessage() . "\n";
  203. // Enviar notificación de error si hay job activo
  204. if (isset($job)) {
  205. $stmt = $pdo->prepare("UPDATE S002V01TVAJO SET VAJO_ESTA = 'failed', VAJO_ERME = ? WHERE VAJO_IDJO = ?");
  206. $stmt->execute([$e->getMessage(), $job['VAJO_IDJO']]);
  207. sendWebSocketNotification($job['VAJO_IDUS'], 'failed', $job['VAJO_IDJO']);
  208. }
  209. } finally {
  210. // 8. Limpiar y eliminar lock global
  211. if (isset($extractDir) && is_dir($extractDir)) {
  212. $files = glob("$extractDir/*");
  213. foreach ($files as $file) {
  214. if (is_file($file)) {
  215. unlink($file);
  216. }
  217. }
  218. rmdir($extractDir);
  219. }
  220. if (file_exists($lockFile)) {
  221. unlink($lockFile);
  222. }
  223. }
  224. function sendWebSocketNotification($userId, $status, $jobId, $progress = 0, $completionData = []) {
  225. try {
  226. require_once __DIR__ . '/../vendor/autoload.php';
  227. $client = new \ElephantIO\Client(\ElephantIO\Client::engine(\ElephantIO\Client::CLIENT_4X, 'http://localhost:3200'));
  228. $client->initialize();
  229. $client->of('/');
  230. $data = [
  231. 'jobId' => $jobId,
  232. 'status' => $status,
  233. 'message' => getStatusMessage($status)
  234. ];
  235. if ($progress !== null) {
  236. $data['progress'] = $progress;
  237. }
  238. if (!empty($completionData)) {
  239. $data = array_merge($data, $completionData);
  240. }
  241. $client->emit('laravel_emit', [
  242. 'event' => 'validation_status',
  243. 'data' => $data,
  244. 'completionData' => $completionData,
  245. 'userId' => (string)$userId
  246. ]);
  247. $client->close();
  248. //echo "WebSocket: $status enviado a usuario $userId\n";
  249. } catch (Exception $e) {
  250. echo "WebSocket error: " . $e->getMessage() . "\n";
  251. }
  252. }
  253. function getStatusMessage($status) {
  254. $messages = [
  255. 'queued' => 'En cola de validación',
  256. 'processing' => 'Procesando archivos',
  257. 'progress' => 'Validando archivos',
  258. 'completed' => 'Validación completada',
  259. 'failed' => 'Error en validación'
  260. ];
  261. return $messages[$status] ?? 'Estado desconocido';
  262. }
  263. function getEncrypt($value){
  264. $ch = curl_init('http://192.168.2.21:8000/api/encrypt');
  265. curl_setopt($ch, CURLOPT_POST, true);
  266. curl_setopt($ch, CURLOPT_POSTFIELDS, ['value' => $value]);
  267. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  268. $response = curl_exec($ch);
  269. curl_close($ch);
  270. $responseData = json_decode($response, true);
  271. return $responseData['response']['encrypted'];
  272. }