From 204675716bf37f1b3ba35c1a3c1037f7f8af2b1f Mon Sep 17 00:00:00 2001 From: Daeng Deni Mardaeni Date: Sun, 11 May 2025 14:46:16 +0700 Subject: [PATCH] feat(webstatement): refactor job sinkronisasi BiayaKartu - Mengubah pelaksanaan scheduling delayed job untuk UpdateAtmCardBranchCurrencyJob. - Memperbarui logging untuk proses sinkronisasi kartu ATM. Signed-off-by: Daeng Deni Mardaeni --- app/Jobs/BiayaKartu.php | 580 ++++++++++++++++++++-------------------- 1 file changed, 295 insertions(+), 285 deletions(-) diff --git a/app/Jobs/BiayaKartu.php b/app/Jobs/BiayaKartu.php index b805a72..5cc0fcb 100644 --- a/app/Jobs/BiayaKartu.php +++ b/app/Jobs/BiayaKartu.php @@ -1,323 +1,333 @@ periode = $periode ?? Carbon::now()->format('Y-m'); - } + use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; - /** - * Execute the job. - */ - public function handle(): void - { - set_time_limit(self::MAX_EXECUTION_TIME); + /** + * Database constants + */ + private const DB_TABLE = 'IST77.CMS_VCARD'; + private const BATCH_SIZE = 1000; + private const MAX_EXECUTION_TIME = 86400; // 24 jam dalam detik - // Inisialisasi atau ambil log sinkronisasi - $this->initSyncLog(); + /** + * Log model untuk menyimpan status sinkronisasi + */ + protected $syncLog; - try { - // Tandai sinkronisasi dimulai - $this->updateSyncLogStart(); + /** + * Periode yang sedang disinkronkan (YYYY-MM) + */ + protected $periode; - // Proses sinkronisasi - $this->syncAtmCards(); + /** + * Statistik sinkronisasi + */ + protected $totalRecords = 0; + protected $successRecords = 0; + protected $failedRecords = 0; - // Jadwalkan job untuk update branch dan currency - $this->scheduleUpdateBranchCurrencyJobs(); - - // Tandai sinkronisasi selesai - $this->updateSyncLogSuccess(); - } catch (Exception $e) { - // Catat error - $this->updateSyncLogFailed($e->getMessage()); - - Log::error('BiayaKartu: Sinkronisasi gagal: ' . $e->getMessage(), [ - 'file' => $e->getFile(), - 'line' => $e->getLine(), - 'periode' => $this->periode - ]); + /** + * Create a new job instance. + */ + public function __construct($periode = null) + { + // Jika periode tidak diberikan, gunakan bulan saat ini + $this->periode = $periode ?? Carbon::now()->format('Y-m'); } - } - /** - * Inisialisasi atau ambil log sinkronisasi - */ - private function initSyncLog(): void - { - // Cek apakah sudah ada log untuk periode ini - $this->syncLog = KartuSyncLog::where('periode', $this->periode)->first(); + /** + * Execute the job. + */ + public function handle() + : void + { + set_time_limit(self::MAX_EXECUTION_TIME); - // Jika belum ada, buat log baru - if (!$this->syncLog) { - $this->syncLog = KartuSyncLog::create([ - 'periode' => $this->periode, - 'is_sync' => false, - 'is_csv' => false, - 'is_ftp' => false, - 'total_records' => 0, - 'success_records' => 0, - 'failed_records' => 0, - ]); + // Inisialisasi atau ambil log sinkronisasi + $this->initSyncLog(); + + try { + // Tandai sinkronisasi dimulai + $this->updateSyncLogStart(); + + // Proses sinkronisasi + $this->syncAtmCards(); + + // Jadwalkan job untuk update branch dan currency + $this->scheduleUpdateBranchCurrencyJobs(); + + // Tandai sinkronisasi selesai + $this->updateSyncLogSuccess(); + } catch (Exception $e) { + // Catat error + $this->updateSyncLogFailed($e->getMessage()); + + Log::error('BiayaKartu: Sinkronisasi gagal: ' . $e->getMessage(), [ + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'periode' => $this->periode + ]); + } } - } - /** - * Update log sinkronisasi saat dimulai - */ - private function updateSyncLogStart(): void - { - $this->syncLog->update([ - 'sync_notes' => 'Sinkronisasi data kartu untuk periode ' . $this->periode . ' dimulai', - 'is_sync' => false, - 'sync_at' => null, - ]); + /** + * Inisialisasi atau ambil log sinkronisasi + */ + private function initSyncLog() + : void + { + // Cek apakah sudah ada log untuk periode ini + $this->syncLog = KartuSyncLog::where('periode', $this->periode)->first(); - Log::info('Memulai sinkronisasi data kartu untuk periode ' . $this->periode); - } + // Jika belum ada, buat log baru + if (!$this->syncLog) { + $this->syncLog = KartuSyncLog::create([ + 'periode' => $this->periode, + 'is_sync' => false, + 'is_csv' => false, + 'is_ftp' => false, + 'total_records' => 0, + 'success_records' => 0, + 'failed_records' => 0, + ]); + } + } - /** - * Update log sinkronisasi saat berhasil - */ - private function updateSyncLogSuccess(): void - { - $this->syncLog->update([ - 'is_sync' => true, - 'sync_at' => Carbon::now(), - 'sync_notes' => 'Sinkronisasi selesai: ' . $this->totalRecords . ' total, ' - . $this->successRecords . ' berhasil, ' - . $this->failedRecords . ' gagal', - 'total_records' => $this->totalRecords, - 'success_records' => $this->successRecords, - 'failed_records' => $this->failedRecords, - ]); + /** + * Update log sinkronisasi saat dimulai + */ + private function updateSyncLogStart() + : void + { + $this->syncLog->update([ + 'sync_notes' => 'Sinkronisasi data kartu untuk periode ' . $this->periode . ' dimulai', + 'is_sync' => false, + 'sync_at' => null, + ]); - Log::info('Sinkronisasi kartu ATM berhasil diselesaikan untuk periode ' . $this->periode); - } + Log::info('Memulai sinkronisasi data kartu untuk periode ' . $this->periode); + } - /** - * Update log sinkronisasi saat gagal - */ - private function updateSyncLogFailed($errorMessage): void - { - $this->syncLog->update([ - 'is_sync' => false, - 'sync_notes' => 'Sinkronisasi gagal: ' . $errorMessage, - 'total_records' => $this->totalRecords, - 'success_records' => $this->successRecords, - 'failed_records' => $this->failedRecords, - ]); - } + /** + * Synchronize ATM cards data from Oracle database + * + * @return void + */ + private function syncAtmCards() + : void + { + try { + $offset = 0; + $hasMoreRecords = true; - /** - * Synchronize ATM cards data from Oracle database - * - * @return void - */ - private function syncAtmCards(): void - { - try { - $offset = 0; - $hasMoreRecords = true; + while ($hasMoreRecords) { + $cards = $this->fetchCardBatch($offset); + $this->processCardBatch($cards); - while ($hasMoreRecords) { - $cards = $this->fetchCardBatch($offset); - $this->processCardBatch($cards); + // Update total records + $this->totalRecords += count($cards); - // Update total records - $this->totalRecords += count($cards); + // Update progress di log + if ($offset % 5000 === 0) { + $this->syncLog->update([ + 'sync_notes' => 'Sinkronisasi dalam proses: ' . $this->totalRecords . ' data diproses, ' + . $this->successRecords . ' berhasil, ' + . $this->failedRecords . ' gagal', + 'total_records' => $this->totalRecords, + 'success_records' => $this->successRecords, + 'failed_records' => $this->failedRecords, + ]); + } - // Update progress di log - if ($offset % 5000 === 0) { - $this->syncLog->update([ - 'sync_notes' => 'Sinkronisasi dalam proses: ' . $this->totalRecords . ' data diproses, ' - . $this->successRecords . ' berhasil, ' - . $this->failedRecords . ' gagal', - 'total_records' => $this->totalRecords, - 'success_records' => $this->successRecords, - 'failed_records' => $this->failedRecords, - ]); + $hasMoreRecords = count($cards) === self::BATCH_SIZE; + $offset += self::BATCH_SIZE; + } + } catch (Exception $e) { + Log::error('BiayaKartu: syncAtmCards: ' . $e->getMessage(), [ + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'periode' => $this->periode + ]); + + throw $e; + } + } + + /** + * Fetch a batch of ATM cards from the database + * + * @param int $offset + * + * @return \Illuminate\Support\Collection + */ + private function fetchCardBatch(int $offset) + { + return DB::connection('oracle') + ->table(self::DB_TABLE) + ->select('CRDNO', 'ACCFLAG', 'CRACC1', 'CRACC2', 'CRACC3', 'CRACC4', 'CRACC5', 'CRACCNAM1', 'CRACCNAM2', 'CRACCNAM3', 'CRACCNAM4', 'CRACCNAM5', 'CRSTS', 'CTTYPE', 'CTDESC', 'CRDATE', 'LASTUPDATE') + ->join('IST77.CMS_VCARDTYP', 'IST77.CMS_VCARD.CRTYPE', '=', 'IST77.CMS_VCARDTYP.CTTYPE') + ->where('crsts', 1) + ->whereNotNull('ACCFLAG') + ->where('ACCFLAG', '>', 0) + ->skip($offset) + ->take(self::BATCH_SIZE) + ->get(); + } + + /** + * Process a batch of ATM cards + * + * @param \Illuminate\Support\Collection $cards + * + * @return void + */ + private function processCardBatch($cards) + : void + { + foreach ($cards as $card) { + try { + // Perbarui data kartu dasar + $cardData = $this->getCardBaseData($card); + Atmcard::updateOrCreate(['crdno' => $card->crdno], $cardData); + + // Tambah hitungan sukses + $this->successRecords++; + } catch (Exception $e) { + // Tambah hitungan gagal + $this->failedRecords++; + + Log::warning("Gagal memproses kartu {$card->crdno}: " . $e->getMessage()); + } + } + } + + /** + * Get base data for card update + * + * @param object $card + * + * @return array + */ + private function getCardBaseData(object $card) + : array + { + return [ + 'accflag' => $card->accflag, + 'cracc1' => $card->cracc1, + 'cracc2' => $card->cracc2, + 'cracc3' => $card->cracc3, + 'cracc4' => $card->cracc4, + 'cracc5' => $card->cracc5, + 'craccnam1' => $card->craccnam1, + 'craccnam2' => $card->craccnam2, + 'craccnam3' => $card->craccnam3, + 'craccnam4' => $card->craccnam4, + 'craccnam5' => $card->craccnam5, + 'crsts' => $card->crsts, + 'cttype' => $card->cttype, + 'ctdesc' => $card->ctdesc, + 'crdate' => $card->crdate, + 'last_update' => $card->lastupdate, + ]; + } + + /** + * Schedule update branch and currency jobs for cards that need update + * + * @return void + */ + private function scheduleUpdateBranchCurrencyJobs() + : void + { + try { + // Ambil semua kartu yang perlu diperbarui branch dan currency + $cards = Atmcard::where('crsts', 1) + ->whereNotNull('accflag') + ->where('accflag', '!=', '') + ->where(function ($query) { + $query->whereNull('branch') + ->orWhere('branch', '') + ->orWhereNull('currency') + ->orWhere('currency', ''); + }) + ->get(); + + $totalCards = $cards->count(); + Log::info("Menjadwalkan {$totalCards} job pembaruan branch dan currency"); + + // Update log + $this->syncLog->update([ + 'sync_notes' => $this->syncLog->sync_notes . "\nMenjadwalkan {$totalCards} job pembaruan branch dan currency" + ]); + + foreach ($cards as $card) { + foreach ($cards as $index => $card) { + UpdateAtmCardBranchCurrencyJob::dispatch($card, $this->syncLog->id) + ->delay(now()->addSeconds(1 * ($index + 1))); + } } - $hasMoreRecords = count($cards) === self::BATCH_SIZE; - $offset += self::BATCH_SIZE; - } - } catch (Exception $e) { - Log::error('BiayaKartu: syncAtmCards: ' . $e->getMessage(), [ - 'file' => $e->getFile(), - 'line' => $e->getLine(), - 'periode' => $this->periode - ]); - - throw $e; - } - } - - /** - * Fetch a batch of ATM cards from the database - * - * @param int $offset - * - * @return \Illuminate\Support\Collection - */ - private function fetchCardBatch(int $offset) - { - return DB::connection('oracle') - ->table(self::DB_TABLE) - ->select('CRDNO', 'ACCFLAG', 'CRACC1', 'CRACC2', 'CRACC3', 'CRACC4', 'CRACC5', 'CRACCNAM1', 'CRACCNAM2', 'CRACCNAM3', 'CRACCNAM4', 'CRACCNAM5', 'CRSTS','CTTYPE','CTDESC','CRDATE','LAST_UPDATE') - ->join('IST77.CMS_VCARDTYP', 'IST77.CMS_VCARD.CRTYPE', '=','IST77.CMS_VCARD.CTTYPE') - ->where('crsts', 1) - ->whereNotNull('ACCFLAG') - ->where('ACCFLAG', '>', 0) - ->skip($offset) - ->take(self::BATCH_SIZE) - ->get(); - } - - /** - * Process a batch of ATM cards - * - * @param \Illuminate\Support\Collection $cards - * - * @return void - */ - private function processCardBatch($cards): void - { - foreach ($cards as $card) { - try { - // Perbarui data kartu dasar - $cardData = $this->getCardBaseData($card); - Atmcard::updateOrCreate(['crdno' => $card->crdno], $cardData); - - // Tambah hitungan sukses - $this->successRecords++; + Log::info('Semua job pembaruan branch dan currency telah dijadwalkan'); } catch (Exception $e) { - // Tambah hitungan gagal - $this->failedRecords++; + Log::error('Gagal menjadwalkan job pembaruan branch dan currency: ' . $e->getMessage(), [ + 'file' => $e->getFile(), + 'line' => $e->getLine() + ]); - Log::warning("Gagal memproses kartu {$card->crdno}: " . $e->getMessage()); + $this->syncLog->update([ + 'sync_notes' => $this->syncLog->sync_notes . "\nError: Gagal menjadwalkan job pembaruan branch dan currency: " . $e->getMessage() + ]); } } - } - /** - * Schedule update branch and currency jobs for cards that need update - * - * @return void - */ - private function scheduleUpdateBranchCurrencyJobs(): void - { - try { - // Ambil semua kartu yang perlu diperbarui branch dan currency - $cards = Atmcard::where('crsts', 1) - ->whereNotNull('accflag') - ->where('accflag', '!=', '') - ->where(function($query) { - $query->whereNull('branch') - ->orWhere('branch', '') - ->orWhereNull('currency') - ->orWhere('currency', ''); - }) - ->get(); - - $totalCards = $cards->count(); - Log::info("Menjadwalkan {$totalCards} job pembaruan branch dan currency"); - - // Update log + /** + * Update log sinkronisasi saat berhasil + */ + private function updateSyncLogSuccess() + : void + { $this->syncLog->update([ - 'sync_notes' => $this->syncLog->sync_notes . "\nMenjadwalkan {$totalCards} job pembaruan branch dan currency" + 'is_sync' => true, + 'sync_at' => Carbon::now(), + 'sync_notes' => 'Sinkronisasi selesai: ' . $this->totalRecords . ' total, ' + . $this->successRecords . ' berhasil, ' + . $this->failedRecords . ' gagal', + 'total_records' => $this->totalRecords, + 'success_records' => $this->successRecords, + 'failed_records' => $this->failedRecords, ]); - foreach ($cards as $card) { - // Jadwalkan job dengan delay untuk menghindari terlalu banyak request bersamaan - UpdateAtmCardBranchCurrencyJob::dispatch($card, $this->syncLog->id) - ->delay(now()->addSeconds(rand(1, 300))); // Random delay antara 1-300 detik - } - - Log::info('Semua job pembaruan branch dan currency telah dijadwalkan'); - } catch (Exception $e) { - Log::error('Gagal menjadwalkan job pembaruan branch dan currency: ' . $e->getMessage(), [ - 'file' => $e->getFile(), - 'line' => $e->getLine() - ]); + Log::info('Sinkronisasi kartu ATM berhasil diselesaikan untuk periode ' . $this->periode); + } + /** + * Update log sinkronisasi saat gagal + */ + private function updateSyncLogFailed($errorMessage) + : void + { $this->syncLog->update([ - 'sync_notes' => $this->syncLog->sync_notes . "\nError: Gagal menjadwalkan job pembaruan branch dan currency: " . $e->getMessage() + 'is_sync' => false, + 'sync_notes' => 'Sinkronisasi gagal: ' . $errorMessage, + 'total_records' => $this->totalRecords, + 'success_records' => $this->successRecords, + 'failed_records' => $this->failedRecords, ]); } } - - /** - * Get base data for card update - * - * @param object $card - * - * @return array - */ - private function getCardBaseData(object $card): array - { - return [ - 'accflag' => $card->accflag, - 'cracc1' => $card->cracc1, - 'cracc2' => $card->cracc2, - 'cracc3' => $card->cracc3, - 'cracc4' => $card->cracc4, - 'cracc5' => $card->cracc5, - 'craccnam1' => $card->craccnam1, - 'craccnam2' => $card->craccnam2, - 'craccnam3' => $card->craccnam3, - 'craccnam4' => $card->craccnam4, - 'craccnam5' => $card->craccnam5, - 'crsts' => $card->crsts, - 'cttype' => $card->cttype, - 'ctdesc' => $card->ctdesc, - 'crdate' => $card->crdate, - 'last_update' => $card->lastupdate, - ]; - } -}