🗑️ job(CleanupInspeksiDataJob): Menambahkan fungsi untuk cleanup data inspeksi dengan queueable retry dan transaction aman

Menambahkan exponential backoff (60, 120, 300 detik), timeout 5 menit, dan logging rinci untuk audit cleanup data inspeksi tanpa dokument_id

- Implementasi job queue dengan retry otomatis menggunakan exponential backoff strategy
- Tambahkan timeout 5 menit untuk mencegah hanging jobs
- Gunakan database transaction untuk memastikan atomicity operasi soft delete
- Logging komprehensif untuk audit trail proses cleanup
- Validasi data lengkap (dengan dokument_id) sebelum proses cleanup
- Soft delete data lama tanpa dokument_id hanya jika data baru tersedia
- Pencegahan penghapusan data yang baru saja ditemukan dengan kondisi where('id', '!=', $newInspeksi->id)
- Error handling dengan rollback transaction dan re-throw exception untuk retry mechanism
- Dokumentasi kode yang jelas dengan PHPDoc untuk setiap method
- Menggunakan Laravel best practices dengan ShouldQueue, Dispatchable, InteractsWithQueue, Queueable, SerializesModels traits
This commit is contained in:
Daeng Deni Mardaeni
2025-12-09 15:22:37 +07:00
parent d9d8eaafcd
commit bef7bcfa8f

View File

@@ -0,0 +1,156 @@
<?php
namespace Modules\Lpj\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Modules\Lpj\Models\Inspeksi;
/**
* Job untuk membersihkan data inspeksi yang tidak memiliki dokument_id
*
* Case: Jika ada data inspeksi yang masuk dengan permohonan_id yang sama
* tetapi memiliki dokument_id dan user created_by yang sama, maka
* data lama (tanpa dokument_id) akan di-soft delete
*/
class CleanupInspeksiDataJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $timeout = 300; // 5 menit
public $tries = 3;
public $maxExceptions = 3;
public $backoff = [60, 120, 300]; // Exponential backoff dalam detik
protected int $permohonanId;
protected int $createdBy;
protected ?int $dokumentId;
/**
* Create a new job instance.
*
* @param int $permohonanId
* @param int $createdBy
* @param int|null $dokumentId
*/
public function __construct(int $permohonanId, int $createdBy, ?int $dokumentId = null)
{
$this->permohonanId = $permohonanId;
$this->createdBy = $createdBy;
$this->dokumentId = $dokumentId;
}
/**
* Execute the job.
*
* @return void
* @throws \Exception
*/
public function handle(): void
{
Log::info('CleanupInspeksiDataJob: Memulai proses cleanup data inspeksi', [
'permohonan_id' => $this->permohonanId,
'created_by' => $this->createdBy,
'dokument_id' => $this->dokumentId
]);
DB::beginTransaction();
try {
// Cari data inspeksi yang memiliki dokument_id (data baru)
$newInspeksi = Inspeksi::where('permohonan_id', $this->permohonanId)
->where('created_by', $this->createdBy)
->whereNotNull('dokument_id')
->whereNull('deleted_at')
->first();
if (!$newInspeksi) {
Log::warning('CleanupInspeksiDataJob: Tidak ditemukan data inspeksi baru dengan dokument_id', [
'permohonan_id' => $this->permohonanId,
'created_by' => $this->createdBy
]);
DB::rollBack();
return;
}
Log::info('CleanupInspeksiDataJob: Data inspeksi baru ditemukan', [
'inspeksi_id' => $newInspeksi->id,
'dokument_id' => $newInspeksi->dokument_id
]);
// Cari data inspeksi lama yang tidak memiliki dokument_id
$oldInspeksiList = Inspeksi::where('permohonan_id', $this->permohonanId)
->where('created_by', $this->createdBy)
->whereNull('dokument_id')
->whereNull('deleted_at')
->where('id', '!=', $newInspeksi->id) // Jangan hapus data yang baru saja ditemukan
->get();
if ($oldInspeksiList->isEmpty()) {
Log::info('CleanupInspeksiDataJob: Tidak ditemukan data inspeksi lama tanpa dokument_id', [
'permohonan_id' => $this->permohonanId,
'created_by' => $this->createdBy
]);
DB::commit();
return;
}
$deletedCount = 0;
foreach ($oldInspeksiList as $oldInspeksi) {
// Soft delete data lama
$oldInspeksi->delete(); // Menggunakan soft delete karena model menggunakan SoftDeletes trait
Log::info('CleanupInspeksiDataJob: Data inspeksi lama berhasil di-soft delete', [
'old_inspeksi_id' => $oldInspeksi->id,
'permohonan_id' => $oldInspeksi->permohonan_id,
'created_by' => $oldInspeksi->created_by,
'deleted_at' => now()->toDateTimeString()
]);
$deletedCount++;
}
DB::commit();
Log::info('CleanupInspeksiDataJob: Proses cleanup selesai', [
'permohonan_id' => $this->permohonanId,
'created_by' => $this->createdBy,
'deleted_count' => $deletedCount,
'new_inspeksi_id' => $newInspeksi->id
]);
} catch (\Exception $e) {
DB::rollBack();
Log::error('CleanupInspeksiDataJob: Terjadi error saat proses cleanup', [
'permohonan_id' => $this->permohonanId,
'created_by' => $this->createdBy,
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
throw $e;
}
}
/**
* Handle a job failure.
*
* @param \Throwable $exception
* @return void
*/
public function failed(\Throwable $exception): void
{
Log::error('CleanupInspeksiDataJob: Job gagal dieksekusi', [
'permohonan_id' => $this->permohonanId,
'created_by' => $this->createdBy,
'error' => $exception->getMessage(),
'trace' => $exception->getTraceAsString()
]);
}
}