649 lines
26 KiB
PHP
649 lines
26 KiB
PHP
<?php
|
||
defined("BASEPATH") or exit("No direct script access allowed");
|
||
|
||
/*
|
||
SQL DDL untuk tabel qr_printout:
|
||
|
||
CREATE TABLE IF NOT EXISTS `qr_printout` (
|
||
`QR_PrintOutID` int(11) NOT NULL AUTO_INCREMENT,
|
||
`QR_PrintOutT_OrderHeaderID` int(11) NOT NULL DEFAULT 0
|
||
COMMENT 'FK ke t_orderheader',
|
||
`QR_PrintOutGroup_ResultID` int(11) NOT NULL DEFAULT 0
|
||
COMMENT 'FK ke m_groupresult (opsional)',
|
||
`QR_PrintOutT_TestID` int(11) NOT NULL DEFAULT 0
|
||
COMMENT 'FK ke m_test (opsional)',
|
||
`QR_PrintOutGroup_ResultName` varchar(250) NOT NULL DEFAULT ''
|
||
COMMENT 'Label group hasil cetak (snapshot)',
|
||
`QR_PrintOutUUID` varchar(36) NOT NULL DEFAULT ''
|
||
COMMENT 'UUID v4 unik per sesi cetak',
|
||
`QR_PrintOutVerifyURL` varchar(500) NOT NULL DEFAULT ''
|
||
COMMENT 'URL QR Code & tujuan upload: https://ds.com/files/{uuid}.pdf (Golang upload ke sini, pasien scan QR buka ini)',
|
||
`QR_PrintOutReportURL` varchar(500) NOT NULL DEFAULT ''
|
||
COMMENT 'URL sumber PDF di PHP app server (Golang HTTP-fetch dari sini untuk diupload ke dedicated server)',
|
||
`QR_PrintOutTempFilePath` varchar(500) NOT NULL DEFAULT ''
|
||
COMMENT 'Path absolut file PDF sementara di PHP server (untuk diambil Golang)',
|
||
`QR_PrintOutUploadStatus` enum('pending','uploaded','failed','failed_permanent') NOT NULL DEFAULT 'pending'
|
||
COMMENT 'Status upload: pending|uploaded|failed (retry<3)|failed_permanent (retry>=3)',
|
||
`QR_PrintOutRetryCount` int(11) NOT NULL DEFAULT 0
|
||
COMMENT 'Berapa kali sudah dicoba upload, maksimal 3',
|
||
`QR_PrintOutLastRetryAt` datetime DEFAULT NULL
|
||
COMMENT 'Waktu percobaan upload terakhir yang gagal',
|
||
`QR_PrintOutUploadedAt` datetime DEFAULT NULL
|
||
COMMENT 'Waktu upload PDF ke dedicated server berhasil',
|
||
`QR_PrintOutCreatedAt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||
COMMENT 'Waktu token QR dibuat',
|
||
`QR_PrintOutCreatedByUserID` int(11) NOT NULL DEFAULT 0
|
||
COMMENT 'UserID yang mencetak laporan',
|
||
`QR_PrintOutIsActive` tinyint(1) NOT NULL DEFAULT 1
|
||
COMMENT '1=aktif, 0=dinonaktifkan (mis. hasil direvisi)',
|
||
PRIMARY KEY (`QR_PrintOutID`),
|
||
UNIQUE KEY `uq_qr_uuid` (`QR_PrintOutUUID`),
|
||
KEY `idx_order_header` (`QR_PrintOutT_OrderHeaderID`),
|
||
KEY `idx_upload_status` (`QR_PrintOutUploadStatus`),
|
||
KEY `idx_retry` (`QR_PrintOutUploadStatus`, `QR_PrintOutRetryCount`)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=latin1
|
||
COMMENT='Token QR Code untuk verifikasi keaslian laporan hasil laboratorium';
|
||
*/
|
||
/**
|
||
* Generateqrreport Library
|
||
*
|
||
* Library untuk generate QR Code pada laporan hasil laboratorium.
|
||
* QR Code berisi URL langsung ke file PDF di dedicated server.
|
||
* Saat QR di-scan, browser langsung membuka file PDF tersebut.
|
||
*
|
||
* Arsitektur:
|
||
*
|
||
* ┌─ PHP App (ini) ──────────────────────────────────────────────┐
|
||
* │ 1. saveQRPrintout(verifyBaseURL='https://ds.com/files') │
|
||
* │ → UUID + verifyURL = https://ds.com/files/{uuid}.pdf │
|
||
* │ verifyURL = URL final PDF = yang di-encode ke QR Code │
|
||
* │ 2. generateQRImageBase64(verifyURL) → embed QR ke PDF cetak │
|
||
* │ 3. saveTempPDF() → simpan PDF sementara di /tmp/ │
|
||
* └──────────────────────────────────────────────────────────────┘
|
||
* │
|
||
* ▼
|
||
* ┌─ Golang Upload Tool ─────────────────────────────────────────┐
|
||
* │ - Poll PHP API: getPendingUploads() │
|
||
* │ - Ambil TempFilePath dari row │
|
||
* │ - Upload PDF ke dedicated server pada path sesuai verifyURL │
|
||
* │ - Berhasil → callback confirmUpload($uuid) │
|
||
* │ - Gagal → callback incrementRetry($uuid) │
|
||
* └──────────────────────────────────────────────────────────────┘
|
||
*
|
||
* ┌─ Dedicated Server ───────────────────────────────────────────┐
|
||
* │ - Serve file PDF statis │
|
||
* │ - URL: https://ds.com/files/{uuid}.pdf │
|
||
* │ - Saat QR di-scan → browser langsung buka PDF ini │
|
||
* └──────────────────────────────────────────────────────────────┘
|
||
*
|
||
* Tabel yang digunakan: qr_printout
|
||
* (lihat: sql/qr_printout_updated.sql untuk DDL lengkap)
|
||
*/
|
||
class Generateqrreport
|
||
{
|
||
/** @var CI_DB_driver */
|
||
protected $db_smartone;
|
||
|
||
/** @var CI_DB_driver */
|
||
protected $db_onedev;
|
||
|
||
function __construct()
|
||
{
|
||
$CI = & get_instance();
|
||
$this->db_smartone = $CI->load->database("default", true);
|
||
$this->db_onedev = $CI->load->database("default", true);
|
||
|
||
$this->_loadQRLib();
|
||
}
|
||
|
||
// =========================================================================
|
||
// PRIVATE HELPERS
|
||
// =========================================================================
|
||
|
||
/**
|
||
* Load phpqrcode library jika belum di-include.
|
||
*/
|
||
private function _loadQRLib()
|
||
{
|
||
if (!class_exists('QRcode', false)) {
|
||
$libPath = APPPATH . 'libraries/qrcode/';
|
||
if (!defined('QR_CACHEABLE'))
|
||
define('QR_CACHEABLE', false);
|
||
if (!defined('QR_CACHE_DIR'))
|
||
define('QR_CACHE_DIR', APPPATH . 'cache/');
|
||
if (!defined('QR_LOG_DIR'))
|
||
define('QR_LOG_DIR', APPPATH . 'logs/');
|
||
if (!defined('QR_FIND_BEST_MASK'))
|
||
define('QR_FIND_BEST_MASK', true);
|
||
if (!defined('QR_FIND_FROM_RANDOM'))
|
||
define('QR_FIND_FROM_RANDOM', false);
|
||
if (!defined('QR_PNG_MAXIMUM_SIZE'))
|
||
define('QR_PNG_MAXIMUM_SIZE', 1024);
|
||
|
||
include_once $libPath . 'phpqrcode.php';
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Generate UUID v4.
|
||
*
|
||
* @param bool $withHyphens true → 36 char 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
|
||
* false → 32 char hex tanpa tanda hubung
|
||
* @return string
|
||
*/
|
||
private function _generateUUID($withHyphens = true)
|
||
{
|
||
$data = random_bytes(16);
|
||
$data[6] = chr(ord($data[6]) & 0x0f | 0x40); // version 4
|
||
$data[8] = chr(ord($data[8]) & 0x3f | 0x80); // variant bits
|
||
|
||
$uuid = vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
|
||
return $withHyphens ? $uuid : str_replace('-', '', $uuid);
|
||
}
|
||
|
||
/**
|
||
* Normalisasi UUID: konversi 32-char (tanpa hyphen) ke format 36-char standar.
|
||
*
|
||
* @param string $uuid
|
||
* @return string lowercase 36-char UUID
|
||
*/
|
||
private function _normalizeUUID($uuid)
|
||
{
|
||
$uuid = trim($uuid);
|
||
if (strlen($uuid) === 32 && strpos($uuid, '-') === false) {
|
||
$uuid = substr($uuid, 0, 8) . '-'
|
||
. substr($uuid, 8, 4) . '-'
|
||
. substr($uuid, 12, 4) . '-'
|
||
. substr($uuid, 16, 4) . '-'
|
||
. substr($uuid, 20);
|
||
}
|
||
return strtolower($uuid);
|
||
}
|
||
|
||
/**
|
||
* Validasi format UUID v4 (8-4-4-4-12).
|
||
*
|
||
* @param string $uuid
|
||
* @return bool
|
||
*/
|
||
private function _validateUUIDFormat($uuid)
|
||
{
|
||
return (bool)preg_match(
|
||
'/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/',
|
||
strtolower($uuid)
|
||
);
|
||
}
|
||
|
||
// =========================================================================
|
||
// PUBLIC METHODS — QR TOKEN
|
||
// =========================================================================
|
||
|
||
/**
|
||
* Buat record QR baru di tabel qr_printout.
|
||
*
|
||
* verifyURL = URL LANGSUNG ke file PDF di dedicated server.
|
||
* URL ini yang di-encode ke gambar QR Code.
|
||
* Golang tool akan upload PDF ke path/URL ini.
|
||
*
|
||
* Contoh:
|
||
* verifyBaseURL = 'https://files.lab.rs.com/reports'
|
||
* verifyURL = 'https://files.lab.rs.com/reports/{uuid}.pdf'
|
||
*
|
||
* @param array $params Wajib:
|
||
* - orderHeaderID (int) FK ke t_orderheader
|
||
* - groupResultName (string) Label group, mis: 'HEMATOLOGI'
|
||
* - verifyBaseURL (string) Base URL folder PDF di dedicated server,
|
||
* contoh: 'https://files.lab.rs.com/reports'
|
||
* Hasilnya: {verifyBaseURL}/{uuid}.pdf
|
||
* Opsional:
|
||
* - groupResultID (int) default 0
|
||
* - testID (int) default 0
|
||
* - createdByUserID (int) default 0
|
||
* @return array [
|
||
* 'success' => bool,
|
||
* 'uuid' => string|null, // 36-char dengan hyphen
|
||
* 'uuid_short' => string|null, // 32-char tanpa hyphen
|
||
* 'verifyURL' => string|null, // URL PDF final = yang di-encode ke QR Code
|
||
* 'qr_printout_id' => int|null,
|
||
* 'message' => string
|
||
* ]
|
||
*/
|
||
public function saveQRPrintout(array $params)
|
||
{
|
||
$required = ['orderHeaderID', 'groupResultName', 'verifyBaseURL', 'QR_PrintOutReportURL'];
|
||
foreach ($required as $field) {
|
||
if (empty($params[$field])) {
|
||
return [
|
||
'success' => false,
|
||
'uuid' => null,
|
||
'uuid_short' => null,
|
||
'verifyURL' => null,
|
||
'qr_printout_id' => null,
|
||
'message' => "Parameter '{$field}' wajib diisi.",
|
||
];
|
||
}
|
||
}
|
||
|
||
$uuid = $this->_generateUUID(true);
|
||
$uuidShort = str_replace('-', '', $uuid);
|
||
|
||
// verifyURL = URL langsung ke PDF: {base}/{uuid}.pdf
|
||
// Golang akan upload PDF ke path ini di dedicated server
|
||
$verifyURL = rtrim($params['verifyBaseURL'], '/') . '/' . str_replace('-', '', $uuid) . '.pdf';
|
||
$groupResultID = (int)($params['groupResultID'] ?? 0);
|
||
$testID = (int)($params['testID'] ?? 0);
|
||
$groupResultName = $params['groupResultName'];
|
||
|
||
$this->db_smartone
|
||
->where('QR_PrintOutT_OrderHeaderID', (int)$params['orderHeaderID'])
|
||
->where('QR_PrintOutGroup_ResultID', $groupResultID)
|
||
->where('QR_PrintOutT_TestID', $testID)
|
||
->where('QR_PrintOutIsActive', 1)
|
||
->update('qr_printout', ['QR_PrintOutIsActive' => 0]);
|
||
|
||
$data = [
|
||
'QR_PrintOutT_OrderHeaderID' => (int)$params['orderHeaderID'],
|
||
'QR_PrintOutGroup_ResultID' => $groupResultID,
|
||
'QR_PrintOutT_TestID' => $testID,
|
||
'QR_PrintOutGroup_ResultName' => $groupResultName,
|
||
'QR_PrintOutUUID' => $uuid,
|
||
'QR_PrintOutVerifyURL' => $verifyURL, // URL final PDF = yang di-encode ke QR Code
|
||
'QR_PrintOutReportURL' => $params['QR_PrintOutReportURL'], // URL sumber PDF di PHP server (Golang fetch dari sini)
|
||
'QR_PrintOutTempFilePath' => '',
|
||
'QR_PrintOutUploadStatus' => 'pending',
|
||
'QR_PrintOutRetryCount' => 0,
|
||
'QR_PrintOutLastRetryAt' => null,
|
||
'QR_PrintOutUploadedAt' => null,
|
||
'QR_PrintOutCreatedAt' => date('Y-m-d H:i:s'),
|
||
'QR_PrintOutCreatedByUserID' => (int)($params['createdByUserID'] ?? 0),
|
||
'QR_PrintOutIsActive' => 1,
|
||
];
|
||
|
||
$this->db_smartone->insert('qr_printout', $data);
|
||
|
||
if ($this->db_smartone->affected_rows() === 0) {
|
||
return [
|
||
'success' => false,
|
||
'uuid' => null,
|
||
'uuid_short' => null,
|
||
'verifyURL' => null,
|
||
'qr_printout_id' => null,
|
||
'message' => 'Gagal menyimpan data QR Printout ke database.',
|
||
];
|
||
}
|
||
|
||
return [
|
||
'success' => true,
|
||
'uuid' => $uuid,
|
||
'uuid_short' => $uuidShort,
|
||
'verifyURL' => $verifyURL, // ini yang di-encode ke gambar QR Code
|
||
'qr_printout_id' => $this->db_smartone->insert_id(),
|
||
'message' => 'OK',
|
||
];
|
||
}
|
||
|
||
/**
|
||
* Nonaktifkan QR Code (misal karena hasil direvisi dan QR lama tidak boleh diakses).
|
||
*
|
||
* @param string $uuid
|
||
* @return bool
|
||
*/
|
||
public function deactivateQR($uuid)
|
||
{
|
||
$uuid = $this->_normalizeUUID($uuid);
|
||
$this->db_smartone->where('QR_PrintOutUUID', $uuid)
|
||
->update('qr_printout', ['QR_PrintOutIsActive' => 0]);
|
||
return $this->db_smartone->affected_rows() > 0;
|
||
}
|
||
|
||
// =========================================================================
|
||
// PUBLIC METHODS — QR IMAGE
|
||
// =========================================================================
|
||
|
||
/**
|
||
* Generate QR Code sebagai Base64 PNG string.
|
||
* Hasil bisa langsung di-embed ke HTML/PDF: <img src="{hasil}">
|
||
*
|
||
* @param string $url URL yang akan di-encode (verifyURL dari saveQRPrintout)
|
||
* @param int $pixelSize Ukuran pixel per modul (1–10), default 5
|
||
* @param string $errorLevel Koreksi error: L, M, Q, H (default H)
|
||
* @return string "data:image/png;base64,..." atau '' jika gagal
|
||
*/
|
||
public function generateQRImageBase64($url, $pixelSize = 5, $errorLevel = 'H')
|
||
{
|
||
$errorLevel = in_array($errorLevel, ['L', 'M', 'Q', 'H']) ? $errorLevel : 'H';
|
||
$pixelSize = min(max((int)$pixelSize, 1), 10);
|
||
|
||
ob_start();
|
||
QRcode::png($url, false, $errorLevel, $pixelSize, 2);
|
||
$imgData = ob_get_clean();
|
||
|
||
return empty($imgData) ? '' : 'data:image/png;base64,' . base64_encode($imgData);
|
||
}
|
||
|
||
/**
|
||
* Generate QR Code dan simpan sebagai file PNG di disk.
|
||
*
|
||
* @param string $url URL yang akan di-encode
|
||
* @param string $savePath Path lengkap tujuan, misal: FCPATH.'qrcodes/abc.png'
|
||
* @param int $pixelSize Ukuran pixel per modul (1–10), default 5
|
||
* @param string $errorLevel Koreksi error: L, M, Q, H (default H)
|
||
* @return bool true jika berhasil disimpan
|
||
*/
|
||
public function generateQRImageFile($url, $savePath, $pixelSize = 5, $errorLevel = 'H')
|
||
{
|
||
$errorLevel = in_array($errorLevel, ['L', 'M', 'Q', 'H']) ? $errorLevel : 'H';
|
||
$pixelSize = min(max((int)$pixelSize, 1), 10);
|
||
|
||
$dir = dirname($savePath);
|
||
if (!is_dir($dir)) {
|
||
mkdir($dir, 0755, true);
|
||
}
|
||
|
||
QRcode::png($url, $savePath, $errorLevel, $pixelSize, 2);
|
||
return file_exists($savePath);
|
||
}
|
||
|
||
// =========================================================================
|
||
// PUBLIC METHODS — PDF TEMP & GOLANG CALLBACK
|
||
// =========================================================================
|
||
|
||
// =========================================================================
|
||
// PUBLIC METHODS — GOLANG UPLOAD TOOL SUPPORT
|
||
// =========================================================================
|
||
|
||
/**
|
||
* Ambil daftar record yang BELUM diupload ke dedicated server.
|
||
* Dipakai oleh Golang upload tool sebagai polling.
|
||
*
|
||
* Hanya mengambil record dengan:
|
||
* - UploadStatus = 'pending' ATAU 'failed' (masih bisa retry)
|
||
* - RetryCount < 3
|
||
* - IsActive = 1
|
||
* - QR_PrintOutReportURL tidak kosong (PDF sudah tersedia di PHP server)
|
||
*
|
||
* Field penting untuk Golang dari setiap row:
|
||
* - QR_PrintOutUUID : identifier
|
||
* - QR_PrintOutVerifyURL : URL TUJUAN upload di dedicated server
|
||
* - QR_PrintOutReportURL : URL SUMBER PDF di PHP server (Golang fetch dari sini)
|
||
* - QR_PrintOutTempFilePath: path lokal file PDF (opsional, jika Golang akses lokal)
|
||
*
|
||
* @param int $limit Jumlah maksimal record (default 10)
|
||
* @return array
|
||
*/
|
||
public function getPendingUploads($limit = 10)
|
||
{
|
||
return $this->db_smartone
|
||
->where_in('QR_PrintOutUploadStatus', ['pending', 'failed'])
|
||
->where('QR_PrintOutRetryCount <', 3)
|
||
->where('QR_PrintOutIsActive', 1)
|
||
->where('QR_PrintOutReportURL !=', '') // pastikan PDF sudah siap
|
||
->order_by('QR_PrintOutCreatedAt', 'ASC')
|
||
->limit((int)$limit)
|
||
->get('qr_printout')
|
||
->result_array();
|
||
}
|
||
|
||
/**
|
||
* Catat bahwa satu percobaan upload gagal dan tambah retry count.
|
||
*
|
||
* - Jika RetryCount setelah increment < 3 → status tetap 'failed' (akan dicoba lagi)
|
||
* - Jika RetryCount setelah increment >= 3 → status menjadi 'failed_permanent'
|
||
* (tidak akan diambil oleh getPendingUploads() lagi)
|
||
*
|
||
* Dipanggil oleh Golang tool saat upload ke dedicated server gagal.
|
||
*
|
||
* @param string $uuid
|
||
* @return array [
|
||
* 'success' => bool,
|
||
* 'retry_count' => int, // retry count setelah increment
|
||
* 'is_permanent' => bool, // true = sudah melebihi batas, tidak akan di-retry lagi
|
||
* 'message' => string
|
||
* ]
|
||
*/
|
||
public function incrementRetry($uuid)
|
||
{
|
||
$uuid = $this->_normalizeUUID($uuid);
|
||
|
||
if (!$this->_validateUUIDFormat($uuid)) {
|
||
return ['success' => false, 'retry_count' => 0, 'is_permanent' => false,
|
||
'message' => 'Format UUID tidak valid.'];
|
||
}
|
||
|
||
$qrRow = $this->db_smartone
|
||
->select('QR_PrintOutRetryCount, QR_PrintOutUploadStatus')
|
||
->where('QR_PrintOutUUID', $uuid)
|
||
->get('qr_printout')
|
||
->row_array();
|
||
|
||
if (!$qrRow) {
|
||
return ['success' => false, 'retry_count' => 0, 'is_permanent' => false,
|
||
'message' => 'UUID tidak ditemukan.'];
|
||
}
|
||
|
||
$newRetryCount = (int)$qrRow['QR_PrintOutRetryCount'] + 1;
|
||
$isPermanent = $newRetryCount >= 3;
|
||
$newStatus = $isPermanent ? 'failed_permanent' : 'failed';
|
||
|
||
$this->db_smartone->where('QR_PrintOutUUID', $uuid)
|
||
->update('qr_printout', [
|
||
'QR_PrintOutRetryCount' => $newRetryCount,
|
||
'QR_PrintOutLastRetryAt' => date('Y-m-d H:i:s'),
|
||
'QR_PrintOutUploadStatus' => $newStatus,
|
||
]);
|
||
|
||
return [
|
||
'success' => true,
|
||
'retry_count' => $newRetryCount,
|
||
'is_permanent' => $isPermanent,
|
||
'message' => $isPermanent
|
||
? "Retry count mencapai {$newRetryCount}. Status menjadi 'failed_permanent'. Upload otomatis dihentikan."
|
||
: "Retry count: {$newRetryCount}/3. Akan dicoba kembali.",
|
||
];
|
||
}
|
||
|
||
/**
|
||
* Minta upload ulang (re-upload) untuk satu record.
|
||
*
|
||
* Gunakan fungsi ini ketika:
|
||
* (a) File PDF di dedicated server sudah dihapus / kadaluarsa, ATAU
|
||
* (b) Record sebelumnya 'failed_permanent' dan ingin dicoba ulang secara manual
|
||
*
|
||
* Fungsi ini akan:
|
||
* - Reset UploadStatus → 'pending'
|
||
* - Reset RetryCount → 0
|
||
* - Kosongkan ReportURL (file lama di dedicated server sudah tidak valid)
|
||
* - Wajib diikuti dengan saveTempPDF() untuk menyiapkan file PDF baru
|
||
*
|
||
* @param string $uuid
|
||
* @return array ['success' => bool, 'message' => string]
|
||
*/
|
||
public function requestReUpload($uuid)
|
||
{
|
||
$uuid = $this->_normalizeUUID($uuid);
|
||
|
||
if (!$this->_validateUUIDFormat($uuid)) {
|
||
return ['success' => false, 'message' => 'Format UUID tidak valid.'];
|
||
}
|
||
|
||
$exists = $this->db_smartone
|
||
->where('QR_PrintOutUUID', $uuid)
|
||
->count_all_results('qr_printout');
|
||
|
||
if ($exists === 0) {
|
||
return ['success' => false, 'message' => 'UUID tidak ditemukan.'];
|
||
}
|
||
|
||
$this->db_smartone->where('QR_PrintOutUUID', $uuid)
|
||
->update('qr_printout', [
|
||
'QR_PrintOutUploadStatus' => 'pending',
|
||
'QR_PrintOutRetryCount' => 0,
|
||
'QR_PrintOutLastRetryAt' => null,
|
||
'QR_PrintOutUploadedAt' => null,
|
||
'QR_PrintOutTempFilePath' => '', // harus diisi ulang via saveTempPDF()
|
||
]);
|
||
|
||
if ($this->db_smartone->affected_rows() === 0) {
|
||
return ['success' => false, 'message' => 'Gagal update database.'];
|
||
}
|
||
|
||
return [
|
||
'success' => true,
|
||
'message' => 'Record di-reset ke pending. Panggil saveTempPDF() untuk menyiapkan PDF baru.',
|
||
];
|
||
}
|
||
|
||
|
||
|
||
/**
|
||
* Konfirmasi bahwa Golang berhasil upload PDF ke dedicated server.
|
||
*
|
||
* URL QR_PrintOutVerifyURL tidak berubah (sudah fix sejak saveQRPrintout).
|
||
* Fungsi ini hanya mengubah status → 'uploaded'.
|
||
*
|
||
* Alur Golang:
|
||
* 1. Poll getPendingUploads() → dapat row dengan:
|
||
* QR_PrintOutReportURL (fetch PDF dari PHP server via HTTP)
|
||
* QR_PrintOutVerifyURL (upload PDF ke dedicated server di path ini)
|
||
* 2. Download PDF dari QR_PrintOutReportURL
|
||
* 3. Upload ke dedicated server sesuai QR_PrintOutVerifyURL
|
||
* 4. Callback PHP: confirmUpload($uuid)
|
||
*
|
||
* @param string $uuid UUID record qr_printout
|
||
* @return array ['success' => bool, 'message' => string]
|
||
*/
|
||
public function confirmUpload($uuid)
|
||
{
|
||
$uuid = $this->_normalizeUUID($uuid);
|
||
|
||
if (!$this->_validateUUIDFormat($uuid)) {
|
||
return ['success' => false, 'message' => 'Format UUID tidak valid.'];
|
||
}
|
||
|
||
$exists = $this->db_smartone
|
||
->where('QR_PrintOutUUID', $uuid)
|
||
->count_all_results('qr_printout');
|
||
|
||
if ($exists === 0) {
|
||
return ['success' => false, 'message' => 'UUID tidak ditemukan di database.'];
|
||
}
|
||
|
||
$this->db_smartone->where('QR_PrintOutUUID', $uuid)
|
||
->update('qr_printout', [
|
||
'QR_PrintOutUploadStatus' => 'uploaded',
|
||
'QR_PrintOutUploadedAt' => date('Y-m-d H:i:s'),
|
||
]);
|
||
|
||
if ($this->db_smartone->affected_rows() === 0) {
|
||
return ['success' => false, 'message' => 'Gagal update database.'];
|
||
}
|
||
|
||
return ['success' => true, 'message' => 'OK'];
|
||
}
|
||
|
||
/**
|
||
* Tandai upload gagal permanen tanpa menambah retry count.
|
||
* Gunakan ini hanya untuk override manual (mis. admin force-fail).
|
||
* Untuk kasus normal, gunakan incrementRetry() agar retry count terlacak.
|
||
*
|
||
* @param string $uuid
|
||
* @return bool
|
||
*/
|
||
public function markUploadFailed($uuid)
|
||
{
|
||
$uuid = $this->_normalizeUUID($uuid);
|
||
$this->db_smartone->where('QR_PrintOutUUID', $uuid)
|
||
->update('qr_printout', [
|
||
'QR_PrintOutUploadStatus' => 'failed_permanent',
|
||
'QR_PrintOutRetryCount' => 3,
|
||
]);
|
||
return $this->db_smartone->affected_rows() > 0;
|
||
}
|
||
|
||
/**
|
||
* Reset URL report (untuk upload ulang jika laporan direvisi).
|
||
* @deprecated Gunakan requestReUpload() yang lebih lengkap.
|
||
*
|
||
* @param string $uuid
|
||
* @return array ['success' => bool, 'message' => string]
|
||
*/
|
||
public function resetReportURL($uuid)
|
||
{
|
||
return $this->requestReUpload($uuid);
|
||
}
|
||
|
||
// =========================================================================
|
||
// PUBLIC METHODS — VERIFIKASI (dipakai dedicated server / controller publik)
|
||
// =========================================================================
|
||
|
||
/**
|
||
* Ambil URL PDF report berdasarkan UUID.
|
||
*
|
||
* Dipakai jika PHP app perlu tahu URL PDF (mis. untuk redirect).
|
||
* Scan tracking dihandle oleh dedicated server.
|
||
*
|
||
* @param string $uuid UUID (36 atau 32 char)
|
||
* @return array [
|
||
* 'success' => bool,
|
||
* 'report_url' => string|null,
|
||
* 'qr_info' => array|null,
|
||
* 'message' => string
|
||
* ]
|
||
*/
|
||
public function getReportURL($uuid)
|
||
{
|
||
$uuid = $this->_normalizeUUID($uuid);
|
||
|
||
if (!$this->_validateUUIDFormat($uuid)) {
|
||
return ['success' => false, 'report_url' => null, 'qr_info' => null,
|
||
'message' => 'Format UUID tidak valid.'];
|
||
}
|
||
|
||
$qrRow = $this->db_smartone
|
||
->where('QR_PrintOutUUID', $uuid)
|
||
->where('QR_PrintOutIsActive', 1)
|
||
->get('qr_printout')
|
||
->row_array();
|
||
|
||
if (!$qrRow) {
|
||
return ['success' => false, 'report_url' => null, 'qr_info' => null,
|
||
'message' => 'QR Code tidak ditemukan atau sudah tidak aktif.'];
|
||
}
|
||
|
||
// verifyURL = URL langsung ke PDF, hanya valid jika sudah ter-upload
|
||
if ($qrRow['QR_PrintOutUploadStatus'] !== 'uploaded') {
|
||
$status = $qrRow['QR_PrintOutUploadStatus'];
|
||
$msg = $status === 'failed_permanent'
|
||
? 'Upload PDF gagal permanen. Hubungi admin laboratorium.'
|
||
: 'Report PDF sedang diproses. Coba beberapa saat lagi.';
|
||
return ['success' => false, 'report_url' => null, 'qr_info' => $qrRow, 'message' => $msg];
|
||
}
|
||
|
||
return [
|
||
'success' => true,
|
||
'report_url' => $qrRow['QR_PrintOutVerifyURL'], // URL langsung ke PDF
|
||
'qr_info' => $qrRow,
|
||
'message' => 'OK',
|
||
];
|
||
}
|
||
|
||
// =========================================================================
|
||
// LEGACY / UTILITY
|
||
// =========================================================================
|
||
|
||
function clean_mysqli_connection($dbc)
|
||
{
|
||
while (mysqli_more_results($dbc)) {
|
||
if (mysqli_next_result($dbc)) {
|
||
$result = mysqli_use_result($dbc);
|
||
if (get_class($result) == 'mysqli_stmt') {
|
||
mysqli_stmt_free_result($result);
|
||
}
|
||
else {
|
||
unset($result);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|