diff --git a/application/controllers/tools/Qr_report_uploader.php b/application/controllers/tools/Qr_report_uploader.php new file mode 100644 index 00000000..d4170cdf --- /dev/null +++ b/application/controllers/tools/Qr_report_uploader.php @@ -0,0 +1,260 @@ +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); + } +}