db_onedev = $this->load->database('onedev', true); $this->db_log = $this->load->database('one_lab_log', true); } public function index() { echo "QR report uploader tool"; } public function run($limit = 10) { $limit = (int)$limit; if ($limit <= 0) { $limit = 10; } $endpointUrl = $this->get_active_endpoint_url(); if ($endpointUrl === '') { $this->reply_result([ 'status' => 'ERR', 'message' => 'QR_ReportEndpointUrl aktif tidak ditemukan.', ]); return; } $rows = $this->get_pending_rows($limit); $summary = [ 'endpoint_url' => $endpointUrl, 'limit' => $limit, 'total_pending' => count($rows), 'uploaded' => 0, 'failed' => 0, 'failed_permanent' => 0, 'skipped' => 0, 'details' => [], ]; foreach ($rows as $row) { $result = $this->process_row($row, $endpointUrl); $summary['details'][] = $result; if ($result['status'] === 'uploaded') { $summary['uploaded']++; } elseif ($result['status'] === 'failed') { $summary['failed']++; } elseif ($result['status'] === 'failed_permanent') { $summary['failed_permanent']++; } else { $summary['skipped']++; } } $this->reply_result([ 'status' => 'OK', 'data' => $summary, ]); } private function get_pending_rows($limit) { $sql = "SELECT QR_PrintOutID, QR_PrintOutT_OrderHeaderID, QR_PrintOutUUID, QR_PrintOutVerifyURL, QR_PrintOutReportURL, QR_PrintOutUploadStatus, QR_PrintOutRetryCount FROM qr_printout WHERE QR_PrintOutIsActive = 1 AND (QR_PrintOutUploadStatus = 'pending' OR QR_PrintOutUploadStatus = 'failed') AND QR_PrintOutRetryCount < 3 AND QR_PrintOutReportURL IS NOT NULL AND QR_PrintOutReportURL != '' ORDER BY QR_PrintOutCreatedAt ASC LIMIT ?"; $qry = $this->db_onedev->query($sql, [$limit]); if (!$qry) { $this->reply_result([ 'status' => 'ERR', 'message' => 'Gagal mengambil daftar pending.', 'db_error' => $this->db_onedev->error(), ]); exit; } return $qry->result_array(); } private function get_active_endpoint_url() { $sql = "SELECT QR_ReportEndpointUrl FROM qr_report_endpoint WHERE QR_ReportEndpointIsActive = 'Y' LIMIT 1"; $qry = $this->db_onedev->query($sql); if (!$qry) { return ''; } $row = $qry->row_array(); if (!$row || empty($row['QR_ReportEndpointUrl'])) { return ''; } return rtrim($row['QR_ReportEndpointUrl'], '/') . '/'; } private function process_row($row, $endpointUrl) { $printoutID = (int)$row['QR_PrintOutID']; $orderHeaderID = (int)$row['QR_PrintOutT_OrderHeaderID']; $reportUrl = $row['QR_PrintOutReportURL']; $pdfContent = $this->download_file($reportUrl); if ($pdfContent === false) { return $this->mark_failed($row, $orderHeaderID, '', 'DOWNLOAD_FAILED'); } if (strpos($pdfContent, '%PDF') === false) { return $this->mark_failed($row, $orderHeaderID, '', 'INVALID_PDF'); } $payload = json_encode([ 'qrcode' => $row['QR_PrintOutUUID'], 'url' => $row['QR_PrintOutReportURL'], 'type' => 'pdf', 'name' => $row['QR_PrintOutUUID'] . '.pdf', 'base64_file' => base64_encode($pdfContent), ], JSON_UNESCAPED_SLASHES); $response = $this->post_json($endpointUrl . 'upload', $payload, "secure-token-libCBxciByZXBvcnQ="); $decoded = json_decode($response, true); if (is_array($decoded) && isset($decoded['status']) && $decoded['status'] === 'OK') { $this->log_insert_qr($printoutID, $orderHeaderID, $payload, $response); $this->db_onedev->query( "UPDATE qr_printout SET QR_PrintOutUploadStatus = 'uploaded', QR_PrintOutUploadedAt = NOW() WHERE QR_PrintOutID = ?", [$printoutID] ); return [ 'printout_id' => $printoutID, 'order_header_id' => $orderHeaderID, 'status' => 'uploaded', 'message' => 'Upload berhasil', ]; } return $this->mark_failed($row, $orderHeaderID, $payload, $response); } private function mark_failed($row, $orderHeaderID, $payload, $response) { $printoutID = (int)$row['QR_PrintOutID']; $retry = (int)$row['QR_PrintOutRetryCount'] + 1; $newStatus = $retry >= 3 ? 'failed_permanent' : 'failed'; if ($payload !== '' || $response !== '') { $this->log_insert_qr($printoutID, $orderHeaderID, $payload, $response); } $this->db_onedev->query( "UPDATE qr_printout SET QR_PrintOutUploadStatus = ?, QR_PrintOutRetryCount = ?, QR_PrintOutLastRetryAt = NOW() WHERE QR_PrintOutID = ?", [$newStatus, $retry, $printoutID] ); return [ 'printout_id' => $printoutID, 'order_header_id' => $orderHeaderID, 'status' => $newStatus, 'message' => is_string($response) ? $response : 'Upload gagal', ]; } private function log_insert_qr($printoutID, $orderHeaderID, $json, $response) { $sql = "INSERT INTO one_lab_log.log_qr_printout( Log_QR_PrintOutQR_PrintOutID, Log_QR_PrintOutT_OrderHeaderID, Log_QR_PrintOutJSON, Log_QR_PrintOutResponse, Log_QR_PrintOutDateTime ) VALUES(?,?,?,?,NOW())"; $this->db_onedev->query($sql, [ $printoutID, $orderHeaderID, $json, $response, ]); } private function post_json($url, $data, $token) { $ch = curl_init($url); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); curl_setopt($ch, CURLOPT_TIMEOUT, 30); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Content-Type: application/json', 'Content-Length: ' . strlen($data), "Authorization: Bearer {$token}", ]); $result = curl_exec($ch); if (curl_error($ch) !== '') { $error = curl_error($ch); curl_close($ch); return "ERROR API [{$url}] : {$error}"; } curl_close($ch); return $result; } private function download_file($url) { $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, 30); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); $result = curl_exec($ch); if (curl_error($ch)) { curl_close($ch); return false; } curl_close($ch); return $result; } private function reply_result($result) { if ($this->input->is_cli_request()) { echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . PHP_EOL; return; } echo json_encode($result); } }