İlk temizlik tamamlandı bir önceki projeden

This commit is contained in:
Ümit Tunç
2026-04-28 21:14:25 +03:00
commit f80443aec0
10000 changed files with 959965 additions and 0 deletions
+170
View File
@@ -0,0 +1,170 @@
<?php
namespace App\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\Storage;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Str;
class CacheBladeViewJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* The number of seconds the job can run before timing out.
*
* @var int
*/
public $timeout = 60 * 60; // 1 hour
/**
* The number of times the job may be attempted.
*
* @var int
*/
public $tries = 3;
/**
* The maximum number of unhandled exceptions to allow before failing.
*
* @var int
*/
public $maxExceptions = 1;
protected $viewPath;
protected $cacheFileName;
public $uniqueId;
/**
* Create a new job instance.
*/
public function __construct(string $viewPath, string $cacheFileName)
{
$this->viewPath = $viewPath;
$this->cacheFileName = $cacheFileName;
// 1. Bu iş için benzersiz bir ID oluştur
$this->uniqueId = Str::uuid()->toString();
// 2. Bu viewPath ve cacheFileName için "geçerli en son iş" olarak bunu işaretle
// Cache süresi job timeout süresinden biraz fazla olsun
$lockKey = "job_lock_{$viewPath}_{$cacheFileName}";
Cache::put($lockKey, $this->uniqueId, 3660);
// 3. Öncelikli kuyruğa ata
$this->onQueue('cache');
}
/**
* Execute the job.
*/
public function handle(): void
{
ini_set('memory_limit', '-1');
set_time_limit(0);
$lockKey = "job_lock_{$this->viewPath}_{$this->cacheFileName}";
// 4. KONTROL: Cache'deki son ID benimki mi?
// Eğer benden sonra başka bir job geldiyse, cache'deki ID değişmiştir.
$latestJobId = Cache::get($lockKey);
if ($latestJobId !== $this->uniqueId) {
Log::info("CacheBladeViewJob CANCELLED: Newer job detected for {$this->viewPath} -> {$this->cacheFileName}", [
'my_id' => $this->uniqueId,
'latest_id' => $latestJobId, // null ise cache silinmiş veya süresi dolmuş demektir
'lock_key' => $lockKey
]);
return; // İşlemi sessizce bitir
}
$startTime = microtime(true);
Log::info("=== CacheBladeViewJob STARTED ===", [
'view_path' => $this->viewPath,
'cache_file_name' => $this->cacheFileName,
'timestamp' => now(),
'memory_start' => memory_get_usage(true) / 1024 / 1024 . ' MB'
]);
try {
// Cache klasörünü oluştur (yoksa)
if (!Storage::exists('cache')) {
Storage::makeDirectory('cache');
}
// Render öncesi tekrar kontrol (Uzun sürebileceği için)
if (Cache::get($lockKey) !== $this->uniqueId) {
Log::info("CacheBladeViewJob CANCELLED (Pre-render): Newer job detected", [
'view_path' => $this->viewPath,
'cache_file_name' => $this->cacheFileName
]);
return;
}
// View içeriğini oluştur
$viewContent = view($this->viewPath)->render();
// Cache dosyasını kaydet
Storage::put("cache/{$this->cacheFileName}.blade.php", $viewContent);
// Son güncelleme zamanını kaydet
Storage::put("cache/{$this->cacheFileName}_last_updated.txt", date('Y-m-d H:i:s'));
// Cache'leri temizle - DİKKAT: cache:clear komutu çalışan diğer jobların lock bilgilerini sildiği için kaldırıldı.
// Artisan::call('cache:clear');
// Artisan::call('config:clear');
Artisan::call('view:clear');
$endTime = microtime(true);
$executionTime = $endTime - $startTime;
$memoryUsage = memory_get_peak_usage(true) / 1024 / 1024;
Log::info("=== CacheBladeViewJob COMPLETED ===", [
'view_path' => $this->viewPath,
'cache_file_name' => $this->cacheFileName,
'execution_time_seconds' => round($executionTime, 2),
'memory_peak_mb' => round($memoryUsage, 2),
'timestamp' => now()
]);
} catch (\Exception $e) {
Log::error("CacheBladeViewJob FAILED", [
'view_path' => $this->viewPath,
'cache_file_name' => $this->cacheFileName,
'error_message' => $e->getMessage(),
'error_trace' => $e->getTraceAsString(),
'timestamp' => now()
]);
throw $e;
}
}
/**
* Get the job's description for Telescope.
*/
public function getDescription(): string
{
return "Cache Blade View: {$this->viewPath} -> {$this->cacheFileName}";
}
/**
* Get the tags that should be assigned to the job.
*/
public function tags(): array
{
return [
"CacheBladeView",
"View:{$this->viewPath}",
"Cache:{$this->cacheFileName}"
];
}
}
+160
View File
@@ -0,0 +1,160 @@
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
/**
* Batch Excel'den yapılan toplu işlemler sonrası tüm trigger'ları
* tek bir job içinde çalıştırır. Her kayıt için ayrı job oluşturulur.
* Tekrarlı aynı veride sadece bir kez çalışır (ShouldBeUnique).
*/
class ExecuteBatchExcelTriggersJob implements ShouldQueue, ShouldBeUnique
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $timeout = 1800; // 30 minutes for large batches
public $tries = 1;
public $maxExceptions = 1;
public $uniqueFor = 1800; // Kilit süresi (30 dk)
protected $tableName;
protected $ids;
protected $requestData;
protected $changedColumns;
/**
* @param string $tableName Table name
* @param array $ids Array of ['id' => X] records
* @param array $requestData Cached request data
*/
public function __construct(string $tableName, array $ids, array $requestData, array $changedColumns = [])
{
$this->tableName = $tableName;
$this->ids = $ids;
$this->requestData = $requestData;
$this->changedColumns = $changedColumns;
$this->onQueue('save-trigger');
}
public function uniqueId()
{
// Chunk'taki tüm ID'lerin hash'ini kullan (chunk çakışmasını önle)
$idList = array_map(fn($item) => $item['id'] ?? 0, $this->ids);
sort($idList);
return $this->tableName . '_' . md5(implode(',', $idList));
}
public function handle(): void
{
$startTime = microtime(true);
$totalIds = count($this->ids);
Log::info("=== ExecuteBatchExcelTriggersJob STARTED ===", [
'table_name' => $this->tableName,
'total_ids' => $totalIds,
]);
if (!\Illuminate\Support\Facades\Schema::hasTable($this->tableName)) {
Log::critical("ExecuteBatchExcelTriggersJob FATAL ERROR: Table '{$this->tableName}' does not exist. ExecuteBatchExcelTriggersJob cannot proceed.", [
'table_name' => $this->tableName,
'ids_count' => count($this->ids)
]);
return;
}
$isLogTestType = in_array($this->tableName, log_test_types());
$isWeldLogs = ($this->tableName == "weld_logs");
foreach ($this->ids as $index => $recordIdData) {
try {
$recordId = (int) ($recordIdData['id'] ?? 0);
if ($recordId <= 0) {
Log::warning("ExecuteBatchExcelTriggersJob: Invalid record ID", ['data' => $recordIdData]);
continue;
}
Log::info("ExecuteBatchExcelTriggersJob: Start processing ID: {$recordId}");
$recordData = db($this->tableName)->where('id', $recordId)->first();
if (!$recordData) {
Log::info("ExecuteBatchExcelTriggersJob: ID {$recordId} not found in table '{$this->tableName}', skipping.");
continue;
}
// 1. log_test_types: logToWeldlogUpdate + logToWPQUpdate
if ($isLogTestType) {
Log::info("ExecuteBatchExcelTriggersJob: Running logToWeldlogUpdate...");
$triggerData = (array) $recordData;
if (function_exists('logToWeldlogUpdate')) {
logToWeldlogUpdate($triggerData);
}
Log::info("ExecuteBatchExcelTriggersJob: Running logToWPQUpdate...");
if (function_exists('logToWPQUpdate')) {
logToWPQUpdate($triggerData, $this->tableName);
}
}
// 2. weld_logs: materialGroupUpdater
if ($isWeldLogs && function_exists('materialGroupUpdater')) {
Log::info("ExecuteBatchExcelTriggersJob: Running materialGroupUpdater...");
materialGroupUpdater($recordId);
}
// 3. SaveTrigger dosyasını çalıştır
Log::info("ExecuteBatchExcelTriggersJob: Running SaveTrigger ({$this->tableName})...");
// include edilen dosya kendi global state'ini bozmasın diye taze bir scope kuralım (anonim fonksiyon ile de yapılabilirdi ama require/include olduğu için değişkenleri manuel veriyoruz)
$tableName = $this->tableName;
$request = is_array($this->requestData) ? $this->requestData : [];
$request['key'] = $recordIdData;
$key = $recordIdData;
$values = null;
$beforeData = null; // Batch Excel'de gerçek beforeData yok
$changedColumns = $this->changedColumns; // Hangi sütunlar Excel'den güncellendi
$action = "update";
$path = app_path("Http/Controllers/SaveTrigger/{$this->tableName}.php");
if (file_exists($path)) {
include($path);
}
Log::info("ExecuteBatchExcelTriggersJob: SaveTrigger done.");
} catch (\Throwable $th) {
Log::error("ExecuteBatchExcelTriggersJob loop error", [
'table_name' => $this->tableName,
'record_id' => $recordIdData['id'] ?? null,
'error' => $th->getMessage(),
'trace' => $th->getTraceAsString(),
]);
// Diğer kayıtlara devam et
continue;
}
}
$executionTime = microtime(true) - $startTime;
Log::info("=== ExecuteBatchExcelTriggersJob COMPLETED ===", [
'table_name' => $this->tableName,
'total_ids' => $totalIds,
'execution_time_seconds' => round($executionTime, 2),
]);
}
public function tags(): array
{
$tableName = $this->tableName ?? 'unknown';
$idsCount = is_array($this->ids) ? count($this->ids) : 0;
return [
"BatchExcelTriggers:{$tableName}",
"Count:{$idsCount}",
];
}
}
@@ -0,0 +1,91 @@
<?php
namespace App\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\Log;
class ExecuteMaterialGroupUpdaterJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* The number of seconds the job can run before timing out.
*/
public $timeout = 300;
/**
* The number of times the job may be attempted.
*/
public $tries = 1;
public $maxExceptions = 1;
protected $weldLogId;
/**
* Create a new job instance.
*
* @param int $weldLogId Weld log record ID
*/
public function __construct(int $weldLogId)
{
$this->weldLogId = $weldLogId;
$this->onQueue('save-trigger');
}
/**
* Execute the job.
*/
public function handle(): void
{
Log::info("=== ExecuteMaterialGroupUpdaterJob STARTED ===", [
'weld_log_id' => $this->weldLogId,
'timestamp' => now(),
]);
try {
if (function_exists('materialGroupUpdater')) {
$result = materialGroupUpdater($this->weldLogId);
Log::info("ExecuteMaterialGroupUpdaterJob completed", [
'weld_log_id' => $this->weldLogId,
'result' => $result,
]);
} else {
Log::warning("materialGroupUpdater function not found", [
'weld_log_id' => $this->weldLogId,
]);
}
} catch (\Throwable $th) {
Log::error("ExecuteMaterialGroupUpdaterJob failed", [
'weld_log_id' => $this->weldLogId,
'error' => $th->getMessage(),
'trace' => $th->getTraceAsString()
]);
throw $th;
}
Log::info("=== ExecuteMaterialGroupUpdaterJob COMPLETED ===", [
'weld_log_id' => $this->weldLogId,
'timestamp' => now(),
]);
}
/**
* Get the tags that should be assigned to the job.
*/
public function tags(): array
{
return [
"MaterialGroupUpdater",
"WeldLog:{$this->weldLogId}",
];
}
}
+228
View File
@@ -0,0 +1,228 @@
<?php
namespace App\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\Http\Request;
use Illuminate\Support\Facades\Log;
class ExecuteSaveTriggerJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* The number of seconds the job can run before timing out.
*
* @var int
*/
public $timeout = 600; // 10 minutes
/**
* The number of times the job may be attempted.
*
* @var int
*/
public $tries = 1; // Only try once, no retry on failure
/**
* The maximum number of unhandled exceptions to allow before failing.
*
* @var int
*/
public $maxExceptions = 1;
protected $tableName;
protected $requestData;
protected $key;
protected $values;
protected $beforeData;
protected $action;
/**
* Create a new job instance.
*/
public function __construct(string $tableName, array $requestData, $key = null, $values = null, $beforeData = null, $action = null)
{
$this->tableName = $tableName;
$this->requestData = $requestData;
$this->key = $key;
$this->values = $values;
$this->beforeData = $beforeData;
$this->action = $action;
// Determine queue priority based on table and operation
$queueName = $this->determineQueuePriority($tableName, $values);
// Set the queue for this job
$this->onQueue($queueName);
}
/**
* Determine the queue priority based on table and data
* Material group operations get higher priority
*/
private function determineQueuePriority(string $tableName, $values): string
{
// High priority for material group related operations in weld_logs
if ($tableName === 'weld_logs' && $values) {
$materialGroupFields = [
'steel_grade_p',
'steel_grade_b',
'material_group_p',
'material_group_b',
'ru_material_group_1',
'ru_material_group_2'
];
// Check if any material group field is being updated
foreach ($materialGroupFields as $field) {
if (isset($values[$field])) {
Log::info("High priority queue assigned for material group operation", [
'table' => $tableName,
'field' => $field,
'queue' => 'high-priority-save-trigger'
]);
return 'high-priority-save-trigger';
}
}
}
// Default priority
return 'save-trigger';
}
/**
* Execute the job.
*/
public function handle(): void
{
$startTime = microtime(true);
Log::info("=== ExecuteSaveTriggerJob STARTED ===", [
'table_name' => $this->tableName,
'key' => $this->key,
'action' => $this->action,
'timestamp' => now(),
'memory_start' => memory_get_usage(true) / 1024 / 1024 . ' MB'
]);
$path = app_path("Http/Controllers/SaveTrigger/{$this->tableName}.php");
if (file_exists($path)) {
// Job içinde değişkenleri global olarak erişilebilir hale getir
$tableName = $this->tableName;
$request = $this->requestData;
$key = $this->key;
$values = $this->values;
$beforeData = $this->beforeData;
$action = $this->action;
Log::info("SaveTrigger file found, executing...", [
'path' => $path,
'table' => $tableName
]);
// Include dosyasını çalıştır
include($path);
} else {
Log::warning("SaveTrigger dosyası bulunamadı", [
'expected_path' => $path,
'table_name' => $this->tableName
]);
}
// Dispatch report generation if applicable
$this->dispatchReportGeneration();
$endTime = microtime(true);
$executionTime = $endTime - $startTime;
$memoryUsage = memory_get_peak_usage(true) / 1024 / 1024;
Log::info("=== ExecuteSaveTriggerJob COMPLETED ===", [
'table_name' => $this->tableName,
'key' => $this->key,
'execution_time_seconds' => round($executionTime, 2),
'memory_peak_mb' => round($memoryUsage, 2),
'timestamp' => now()
]);
}
/**
* Check and dispatch report generation for relevant templates
*/
protected function dispatchReportGeneration()
{
try {
// Only proceed if we have a valid key (ID)
$recordId = $this->key['id'] ?? null;
if (!$recordId) {
return;
}
// Find templates that might be related to this table
$templates = \Illuminate\Support\Facades\DB::table("document_templates")
->where("y", "1")
->get();
foreach ($templates as $template) {
$fields = json_decode($template->fields);
if (!isset($fields->editorMaster)) {
continue;
}
$masterSql = $fields->editorMaster;
// Check conditions:
// 1. Contains tableName
// 2. Contains ORDER BY (case insensitive)
if (stripos($masterSql, $this->tableName) !== false && stripos($masterSql, 'ORDER BY') !== false) {
// Clone işleminde rapor oluşturmayı engelle
if($this->action === "clone") {
continue;
}
Log::debug("Dispatching RenderReportJob for template", [
'template_id' => $template->id,
'table' => $this->tableName,
'record_id' => $recordId
]);
\App\Jobs\RenderReportJob::dispatch($template->id, $recordId, $this->tableName);
}
}
} catch (\Exception $e) {
Log::error("Error in dispatchReportGeneration: " . $e->getMessage());
}
}
/**
* Get the job's description for Telescope.
*/
public function getDescription(): string
{
$action = $this->key ? 'Update' : 'Create';
return "Execute Save Trigger for {$this->tableName} table - {$action} operation";
}
/**
* Get the tags that should be assigned to the job.
*/
public function tags(): array
{
$action = $this->action;
return [
"SaveTrigger:{$this->tableName}",
"Action:{$action}",
"Table:{$this->tableName}"
];
}
}
+86
View File
@@ -0,0 +1,86 @@
<?php
namespace App\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\Log;
class ExecuteWeldlogSyncJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $timeout = 300;
public $tries = 1;
public $maxExceptions = 1;
protected $tableName;
protected $recordId;
/**
* @param string $tableName Log test type table name
* @param int $recordId Record ID in the table
*/
public function __construct(string $tableName, int $recordId)
{
$this->tableName = $tableName;
$this->recordId = $recordId;
$this->onQueue('save-trigger');
}
public function handle(): void
{
Log::info("=== ExecuteWeldlogSyncJob STARTED ===", [
'table_name' => $this->tableName,
'record_id' => $this->recordId,
]);
try {
// Kayıt verisini kuyruk içinde çek (response'u yavaşlatmaz)
$recordData = db($this->tableName)->where('id', $this->recordId)->first();
if (!$recordData) {
Log::warning("ExecuteWeldlogSyncJob: Record not found", [
'table_name' => $this->tableName,
'record_id' => $this->recordId,
]);
return;
}
$data = (array) $recordData;
if (function_exists('logToWeldlogUpdate')) {
logToWeldlogUpdate($data);
}
if (function_exists('logToWPQUpdate')) {
logToWPQUpdate($data, $this->tableName);
}
} catch (\Throwable $th) {
Log::error("ExecuteWeldlogSyncJob failed", [
'table_name' => $this->tableName,
'record_id' => $this->recordId,
'error' => $th->getMessage(),
]);
throw $th;
}
Log::info("=== ExecuteWeldlogSyncJob COMPLETED ===", [
'table_name' => $this->tableName,
'record_id' => $this->recordId,
]);
}
public function tags(): array
{
return [
"WeldlogSync:{$this->tableName}",
"RecordId:{$this->recordId}",
];
}
}
+277
View File
@@ -0,0 +1,277 @@
<?php
namespace App\Jobs;
use App\Services\RegisterCreator\RegisterCreatorService;
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\Log;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Cache;
use Exception;
class RegisterCreatorJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public array $jobData;
public int $userId;
/**
* The number of times the job may be attempted.
*/
public int $tries = 3;
/**
* The number of seconds the job can run before timing out.
*/
public int $timeout = 3600; // 1 hour
/**
* Create a new job instance.
*/
public function __construct(array $jobData, int $userId = null)
{
$this->jobData = $jobData;
$this->userId = $userId;
// Set the queue for this job
$this->onQueue('register-creator');
}
/**
* Execute the job.
*/
public function handle(): void
{
$lineData = $this->jobData['line_data'] ?? [];
$documents = $this->jobData['documents'] ?? [];
$settings = $this->jobData['settings'] ?? [];
$jobId = $this->jobData['job_id'] ?? uniqid('rc_', true);
$registerColumnBased = $settings['register_column_based'] ?? 'line_number';
$lineIdentifier = $lineData[$registerColumnBased] ?? 'unknown';
Log::info("RegisterCreatorJob started", [
'job_id' => $jobId,
'line' => $lineIdentifier,
'attempt' => $this->attempts(),
'user_id' => $this->userId
]);
// Check if job is cancelled (deleted from queue cache)
$queue = Cache::get('register-creator-queue-2', []);
if (!isset($queue[$jobId])) {
Log::info("RegisterCreatorJob cancelled by user (job ID not found in cache)", ['job_id' => $jobId]);
return;
}
try {
// Reset statistics before processing - FIX FOR WORKER STATE ISSUE
\App\Services\RegisterCreator\ExcelRowHandler::resetStatistics();
Log::debug("ExcelRowHandler statistics reset for job", [
'job_id' => $jobId,
'line' => $lineIdentifier
]);
// Set locale
setlocale(LC_ALL, 'ru_RU.utf8');
\App::setLocale("ru");
// Increase limits
set_time_limit(-1);
ini_set('max_execution_time', -1);
ini_set('memory_limit', '2048M');
// Set user in settings for tracking
if ($this->userId) {
$settings['tracking_user_id'] = $this->userId;
}
// Create service instance
$service = new RegisterCreatorService();
// Process the line
$result = $service->processLine($lineData, $documents, array_merge($settings, [
'job_id' => $jobId
]));
Log::info("RegisterCreatorJob completed successfully", [
'job_id' => $jobId,
'line' => $lineIdentifier,
'duration' => $result['duration'] ?? 0,
'statistics' => $result['statistics'] ?? []
]);
// Dispatch cache update for register creator view
// Dispatch cache update for register creator view
try {
\App\Jobs\CacheBladeViewJob::dispatch('admin-ajax.register-no-cache', 'register-creator');
Log::info("RegisterCreatorJob: Cache update dispatched");
} catch (\Exception $e) {
Log::warning('RegisterCreatorJob: Failed to dispatch cache update', ['error' => $e->getMessage()]);
}
// Send success notification email if configured
$this->sendSuccessNotification($settings, $lineIdentifier, $result);
} catch (\Throwable $th) {
// Detailed error logging
$errorDetails = [
'job_id' => $jobId,
'line' => $lineIdentifier,
'error_message' => $th->getMessage(),
'error_type' => get_class($th),
'file' => $th->getFile(),
'line_number' => $th->getLine(),
'attempt' => $this->attempts(),
'max_tries' => $this->tries,
'trace' => $th->getTraceAsString(),
'input_data' => [
'line_data_keys' => array_keys($lineData),
'documents_count' => count($documents),
'settings_keys' => array_keys($settings)
]
];
Log::error("RegisterCreatorJob failed", $errorDetails);
// Write error to file for easier debugging
try {
$errorLogPath = "register_creator_errors/" . date('Y-m-d') . "/";
if (!Storage::exists($errorLogPath)) {
Storage::makeDirectory($errorLogPath);
}
$errorFileName = $errorLogPath . "{$jobId}_error.json";
$errorContent = json_encode([
'timestamp' => now()->toDateTimeString(),
'job_id' => $jobId,
'line_identifier' => $lineIdentifier,
'error' => [
'type' => get_class($th),
'message' => $th->getMessage(),
'file' => $th->getFile(),
'line' => $th->getLine(),
'trace' => explode("\n", $th->getTraceAsString())
],
'input_data' => [
'line_data' => $lineData,
'documents' => $documents,
'settings' => $settings
],
'context' => [
'attempt' => $this->attempts(),
'max_tries' => $this->tries,
'memory_usage' => round(memory_get_usage(true) / 1024 / 1024, 2) . ' MB',
'peak_memory' => round(memory_get_peak_usage(true) / 1024 / 1024, 2) . ' MB'
]
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
Storage::put($errorFileName, $errorContent);
Log::info("Error details saved to file: {$errorFileName}");
} catch (\Throwable $logError) {
Log::warning("Could not write error to file", [
'error' => $logError->getMessage()
]);
}
// Send failure notification
$this->sendFailureNotification($settings, $lineIdentifier, $th);
// Re-throw exception to mark job as failed
throw $th;
} finally {
// Always reset statistics after job completion - CRITICAL FIX
// This ensures that each job starts with a clean state even when worker reuses the same process
\App\Services\RegisterCreator\ExcelRowHandler::resetStatistics();
Log::debug("ExcelRowHandler statistics reset after job completion", [
'job_id' => $jobId ?? 'unknown',
'line' => $lineIdentifier ?? 'unknown'
]);
}
}
/**
* Handle a job failure.
*/
public function failed(\Throwable $exception): void
{
$lineData = $this->jobData['line_data'] ?? [];
$settings = $this->jobData['settings'] ?? [];
$jobId = $this->jobData['job_id'] ?? 'unknown';
$registerColumnBased = $settings['register_column_based'] ?? 'line_number';
$lineIdentifier = $lineData[$registerColumnBased] ?? 'unknown';
Log::critical("RegisterCreatorJob permanently failed", [
'job_id' => $jobId,
'line' => $lineIdentifier,
'error' => $exception->getMessage(),
'attempts' => $this->attempts()
]);
// Update progress to failed state
\Cache::put("register-creator-progress-{$jobId}", [
'status' => 'failed',
'progress' => 0,
'description' => "Failed: {$exception->getMessage()}",
'line_data' => $lineIdentifier,
'failed_at' => now()->toDateTimeString()
], now()->addDays(7));
}
/**
* Send success notification email
*/
private function sendSuccessNotification(array $settings, string $lineIdentifier, array $result): void
{
try {
$userEmail = $settings['user_email'] ?? null;
if (!$userEmail) {
return;
}
// You can implement email notification here
Log::debug("Success notification would be sent to {$userEmail} for line {$lineIdentifier}");
} catch (\Throwable $th) {
Log::warning("Could not send success notification", [
'error' => $th->getMessage()
]);
}
}
/**
* Send failure notification email
*/
private function sendFailureNotification(array $settings, string $lineIdentifier, \Throwable $exception): void
{
try {
$userEmail = $settings['user_email'] ?? null;
if (!$userEmail) {
return;
}
// You can implement email notification here
Log::debug("Failure notification would be sent to {$userEmail} for line {$lineIdentifier}");
} catch (\Throwable $th) {
Log::warning("Could not send failure notification", [
'error' => $th->getMessage()
]);
}
}
}
+183
View File
@@ -0,0 +1,183 @@
<?php
namespace App\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 Illuminate\Support\Facades\Cache;
use Illuminate\Support\Str;
use Illuminate\Http\Request;
class RenderReportJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $templateId;
protected $recordId;
protected $tableName;
public $uniqueId;
/**
* The number of seconds the job can run before timing out.
*
* @var int
*/
public $timeout = 300; // 5 minutes
/**
* The number of seconds after which the job's unique lock will be released.
* This should be longer than the job timeout.
*
* @var int
*/
public $uniqueFor = 360; // 6 minutes (timeout + 1 minute)
/**
* Create a new job instance.
*
* @param int $templateId
* @param int $recordId
* @param string $tableName
*/
public function __construct($templateId, $recordId, $tableName)
{
$this->templateId = $templateId;
$this->recordId = $recordId;
$this->tableName = $tableName;
// Create unique ID for this job instance
$this->uniqueId = Str::uuid()->toString();
// Mark this as the latest job for this templateId
// Cache duration should be longer than job timeout
Cache::put("job_lock_render_report_{$templateId}", $this->uniqueId, $this->uniqueFor);
// Low priority as requested
$this->onQueue('low');
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
// Check if this job is still the latest one for this templateId
// If a newer job has been queued, cancel this one
$latestJobId = Cache::get("job_lock_render_report_{$this->templateId}");
if ($latestJobId !== $this->uniqueId) {
Log::info("RenderReportJob CANCELLED: Newer job detected for template_id {$this->templateId}", [
'my_id' => $this->uniqueId,
'latest_id' => $latestJobId,
'template_id' => $this->templateId
]);
return; // Silently exit
}
Log::debug("=== RenderReportJob STARTED ===", [
'template_id' => $this->templateId,
'record_id' => $this->recordId,
'table_name' => $this->tableName,
'unique_id' => $this->uniqueId
]);
try {
$documentTemplate = DB::table("document_templates")->where("id", $this->templateId)->first();
if (!$documentTemplate) {
Log::error("RenderReportJob: Template not found", ['id' => $this->templateId]);
return;
}
$fields = json_decode($documentTemplate->fields);
if (!isset($fields->editorMaster)) {
Log::error("RenderReportJob: editorMaster not found in template fields");
return;
}
// Construct query to fetch specific record using the master query
// We use the master query directly with LIMIT 1 as requested,
// relying on the master query's internal ordering/randomization if present.
$masterSql = trim($fields->editorMaster);
// Remove trailing semicolon if present
if (substr($masterSql, -1) == ';') {
$masterSql = substr($masterSql, 0, -1);
}
// User instruction: use master query directly with limit 1
// We ignore $this->recordId here as requested
$sql = $masterSql . " LIMIT 1";
try {
$result = DB::select($sql);
} catch (\Exception $e) {
Log::error("RenderReportJob: Error executing master SQL", [
'error' => $e->getMessage(),
'sql' => $sql
]);
throw $e;
}
if (empty($result)) {
Log::warning("RenderReportJob: No records found in master query result", [
'template_id' => $this->templateId,
'sql' => $sql
]);
return;
}
$rowData = $result[0];
$rowDataArray = is_object($rowData) ? (array)$rowData : $rowData;
// Prepare POST data as expected by the view
// Note: This modifies the global $_POST which is risky in some environments but
// the view seems to rely on it. In a Job, this is isolated to this process.
$_POST = [
'sqlCodeMaster' => $fields->editorMaster,
'sqlCodeDetail' => $fields->editorDetail ?? "",
'templateRowNo' => $fields->templateRowNo ?? 25,
'documentId' => $documentTemplate->id,
'fileNameTemplate' => $fields->fileNameTemplate ?? 'report_{line_number}',
'rowData' => $rowDataArray,
'isMultipleMode' => isset($fields->isMultipleMode) ? $fields->isMultipleMode : false,
'repeatedRows' => isset($fields->repeatedRows) ? json_encode($fields->repeatedRows) : json_encode([]),
'isSingleMode' => isset($fields->isSingleMode) ? $fields->isSingleMode : true,
'repeatRowCheckbox' => isset($fields->repeatRowCheckbox) ? $fields->repeatRowCheckbox : false,
'repeatedTableCount' => isset($fields->repeatedTableCount) ? $fields->repeatedTableCount : 1
];
// Check again before rendering (rendering can take a long time)
if (Cache::get("job_lock_render_report_{$this->templateId}") !== $this->uniqueId) {
Log::info("RenderReportJob CANCELLED (Pre-render): Newer job detected for template_id {$this->templateId}", [
'template_id' => $this->templateId
]);
return;
}
// Render the view
Log::debug("RenderReportJob: Rendering view...");
$startTime = microtime(true);
// We use view()->render() just like the cron job
view('admin-ajax.report-builder-pdf-generator')->render();
$executionTime = round((microtime(true) - $startTime) * 1000, 2);
Log::debug("RenderReportJob: View rendered successfully", ['time_ms' => $executionTime]);
} catch (\Exception $e) {
Log::error("RenderReportJob: Error processing report", [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
throw $e;
}
}
}
+151
View File
@@ -0,0 +1,151 @@
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
class SpoolStatusChangerJob implements ShouldQueue, ShouldBeUnique
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* The number of seconds the job can run before timing out.
*
* @var int
*/
public $timeout = 120; // 2 minutes
/**
* The number of seconds after which the job's unique lock will be released.
*
* @var int
*/
public $uniqueFor = 300;
/**
* The number of times the job may be attempted.
*
* @var int
*/
public $tries = 2; // Try twice on failure
/**
* The maximum number of unhandled exceptions to allow before failing.
*
* @var int
*/
public $maxExceptions = 2;
protected $isoNumber;
protected $spoolNumber;
/**
* Create a new job instance.
*/
public function __construct(?string $isoNumber = null, ?string $spoolNumber = null)
{
$this->isoNumber = $isoNumber;
$this->spoolNumber = $spoolNumber;
// Set high priority queue
$this->onQueue('high-priority-save-trigger');
}
/**
* Get the unique ID for the job.
*/
public function uniqueId(): string
{
return md5($this->isoNumber . '_' . $this->spoolNumber);
}
/**
* Execute the job.
*/
public function handle(): void
{
$startTime = microtime(true);
Log::info("=== SpoolStatusChangerJob STARTED ===", [
'iso_number' => $this->isoNumber,
'spool_number' => $this->spoolNumber,
'timestamp' => now(),
'memory_start' => memory_get_usage(true) / 1024 / 1024 . ' MB'
]);
try {
if ($this->isoNumber !== null) {
if ($this->spoolNumber !== null) {
spoolStatusChanger($this->isoNumber, $this->spoolNumber);
} else {
spoolStatusChanger($this->isoNumber);
}
Log::info("Spool status updated successfully", [
'iso_number' => $this->isoNumber,
'spool_number' => $this->spoolNumber
]);
} else {
Log::warning("SpoolStatusChangerJob called with null iso_number", [
'spool_number' => $this->spoolNumber
]);
}
} catch (\Exception $e) {
Log::error("SpoolStatusChangerJob failed", [
'iso_number' => $this->isoNumber,
'spool_number' => $this->spoolNumber,
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
throw $e; // Re-throw to trigger retry mechanism
}
$endTime = microtime(true);
$executionTime = $endTime - $startTime;
$memoryUsage = memory_get_peak_usage(true) / 1024 / 1024;
Log::info("=== SpoolStatusChangerJob COMPLETED ===", [
'iso_number' => $this->isoNumber,
'spool_number' => $this->spoolNumber,
'execution_time_seconds' => round($executionTime, 2),
'memory_peak_mb' => round($memoryUsage, 2),
'timestamp' => now()
]);
}
/**
* Get the job's description for Telescope.
*/
public function getDescription(): string
{
return "Update spool status for ISO: {$this->isoNumber}" .
($this->spoolNumber ? ", Spool: {$this->spoolNumber}" : "");
}
/**
* Get the tags that should be assigned to the job.
*/
public function tags(): array
{
$tags = ["SpoolStatusChanger"];
if ($this->isoNumber) {
$tags[] = "ISO:{$this->isoNumber}";
}
if ($this->spoolNumber) {
$tags[] = "Spool:{$this->spoolNumber}";
}
return $tags;
}
}
+100
View File
@@ -0,0 +1,100 @@
<?php
namespace App\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\Log;
class SyncTriggerJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* The number of seconds the job can run before timing out.
*
* @var int
*/
public $timeout = 600; // 10 minutes
/**
* The number of times the job may be attempted.
*
* @var int
*/
public $tries = 2;
protected $syncPath;
protected $id;
/**
* Create a new job instance.
*
* @param string $syncPath The blade view path to render
* @param mixed $id The record ID to pass to the view
*/
public function __construct(string $syncPath, $id)
{
$this->syncPath = $syncPath;
$this->id = $id;
// Use the 'save-trigger' queue or a dedicated 'sync' queue
$this->onQueue('save-trigger');
}
/**
* Execute the job.
*/
public function handle(): void
{
Log::info("=== SyncTriggerJob STARTED ===", [
'sync_path' => $this->syncPath,
'id' => $this->id,
'timestamp' => now()
]);
try {
// Render the view to trigger its internal logic
// We use render() which will execute the PHP code inside the Blade file
$output = view($this->syncPath, ['id' => $this->id])->render();
Log::info("=== SyncTriggerJob COMPLETED ===", [
'sync_path' => $this->syncPath,
'id' => $this->id,
'timestamp' => now()
]);
} catch (\Exception $e) {
Log::error("SyncTriggerJob FAILED", [
'sync_path' => $this->syncPath,
'id' => $this->id,
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
throw $e;
}
}
/**
* Get the job's description for Telescope.
*/
public function getDescription(): string
{
return "Sync Trigger: {$this->syncPath} for ID {$this->id}";
}
/**
* Get the tags that should be assigned to the job.
*/
public function tags(): array
{
return [
"SyncTrigger",
"View:{$this->syncPath}",
"ID:{$this->id}"
];
}
}
+139
View File
@@ -0,0 +1,139 @@
<?php
namespace App\Jobs;
use App\Services\TpCreator\TpCreatorService;
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\Log;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Cache;
class TpCreatorJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public array $jobData;
public int $userId;
/**
* The number of times the job may be attempted.
*/
public int $tries = 3;
/**
* The number of seconds the job can run before timing out.
*/
public int $timeout = 3600; // 1 hour
/**
* Create a new job instance.
*/
public function __construct(array $jobData, int $userId = null)
{
$this->jobData = $jobData;
$this->userId = $userId;
// Set the queue for this job
$this->onQueue('register-creator');
}
/**
* Execute the job.
*/
public function handle(): void
{
$lineData = $this->jobData['line_data'] ?? [];
$documents = $this->jobData['documents'] ?? [];
$settings = $this->jobData['settings'] ?? [];
$jobId = $this->jobData['job_id'] ?? uniqid('tp_', true);
$lineIdentifier = $lineData['test_package_number'] ?? 'unknown';
Log::info("TpCreatorJob started", [
'job_id' => $jobId,
'line' => $lineIdentifier,
'attempt' => $this->attempts(),
'user_id' => $this->userId
]);
// Check if job is cancelled (deleted from queue cache)
$queue = Cache::get('tp-creator-queue', []);
if (!isset($queue[$jobId])) {
Log::info("TpCreatorJob cancelled by user (job ID not found in cache)", ['job_id' => $jobId]);
return;
}
try {
setlocale(LC_ALL, 'ru_RU.utf8');
\App::setLocale("ru");
set_time_limit(-1);
ini_set('max_execution_time', -1);
ini_set('memory_limit', '2048M');
$service = new TpCreatorService();
$result = $service->processLine($lineData, $documents, array_merge($settings, ['job_id' => $jobId]));
Log::info("TpCreatorJob completed successfully", ['job_id' => $jobId, 'line' => $lineIdentifier]);
$queue = Cache::get('tp-creator-queue', []);
if(isset($queue[$jobId])) {
$queue[$jobId]['progress'] = 100;
$queue[$jobId]['description'] = "Completed";
$queue[$jobId]['status'] = "completed";
Cache::put('tp-creator-queue', $queue, now()->addHours(24));
}
} catch (\Throwable $th) {
$errorDetails = [
'job_id' => $jobId,
'line' => $lineIdentifier,
'error_message' => $th->getMessage(),
'file' => $th->getFile(),
'line_number' => $th->getLine(),
'trace' => $th->getTraceAsString()
];
Log::error("TpCreatorJob failed", $errorDetails);
// Update cache status to failed
$queue = Cache::get('tp-creator-queue', []);
if(isset($queue[$jobId])) {
$queue[$jobId]['status'] = "failed";
$queue[$jobId]['description'] = "Failed: " . $th->getMessage();
Cache::put('tp-creator-queue', $queue, now()->addHours(24));
}
// Save detailed error to file for debugging
try {
$errorLogPath = "tp_creator_errors/" . date('Y-m-d') . "/";
if (!Storage::exists($errorLogPath)) {
Storage::makeDirectory($errorLogPath);
}
$errorFileName = $errorLogPath . "{$jobId}_error.json";
Storage::put($errorFileName, json_encode($errorDetails, JSON_PRETTY_PRINT));
} catch (\Throwable $e) {
// Ignore storage errors
}
throw $th;
}
}
/**
* Handle a job failure.
*/
public function failed(\Throwable $exception): void
{
$jobId = $this->jobData['job_id'] ?? 'unknown';
Log::error("TpCreatorJob permanently failed: " . $exception->getMessage());
$queue = Cache::get('tp-creator-queue', []);
if(isset($queue[$jobId])) {
$queue[$jobId]['status'] = "failed";
$queue[$jobId]['description'] = "Permanently Failed: " . $exception->getMessage();
Cache::put('tp-creator-queue', $queue, now()->addHours(24));
}
}
}
+102
View File
@@ -0,0 +1,102 @@
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use App\Services\NaksSyncService;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
class TriggerNaksSyncJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $payload;
/**
* Create a new job instance.
*
* @param array $payload ['module' => string, 'source_project' => string]
* @return void
*/
public function __construct(array $payload)
{
$this->payload = $payload;
}
/**
* Execute the job.
*
* @return void
*/
public function handle(NaksSyncService $syncService)
{
$logPrefix = "[TriggerNaksSyncJob]";
Log::info("$logPrefix Starting sync trigger propagation", $this->payload);
try {
// Get all project URLs
$projects = $syncService->getProjectUrls();
$currentUrl = config('app.url');
foreach ($projects as $project) {
// Normalize URLs for comparison
$targetUrl = rtrim($project['url'], '/');
$myUrl = rtrim($currentUrl, '/');
// Skip self
if ($this->urlsMatch($targetUrl, $myUrl)) {
continue;
}
$this->triggerProject($targetUrl, $syncService);
}
} catch (\Exception $e) {
Log::error("$logPrefix Failed to propagate trigger: " . $e->getMessage());
}
}
protected function triggerProject($baseUrl, NaksSyncService $syncService)
{
$endpoint = "$baseUrl/api/naks/trigger-sync";
Log::info("[TriggerNaksSyncJob] Sending trigger to: $endpoint");
try {
// We need to authenticate to convert the request
// Or assumes the endpoint is protected by Sanctum and we act as a User.
// Using NaksSyncService's auth logic
$token = $syncService->authenticate($baseUrl);
if (!$token) {
Log::warning("[TriggerNaksSyncJob] Auth failed for $baseUrl");
return;
}
$response = Http::withToken($token)
->timeout(10)
->post($endpoint, $this->payload);
if ($response->successful()) {
Log::info("[TriggerNaksSyncJob] Successfully triggered $baseUrl");
} else {
Log::warning("[TriggerNaksSyncJob] Failed response from $baseUrl: " . $response->status());
}
} catch (\Exception $e) {
Log::error("[TriggerNaksSyncJob] Exception calling $baseUrl: " . $e->getMessage());
}
}
protected function urlsMatch($url1, $url2)
{
$host1 = parse_url($url1, PHP_URL_HOST);
$host2 = parse_url($url2, PHP_URL_HOST);
return $host1 === $host2;
}
}
+139
View File
@@ -0,0 +1,139 @@
<?php
namespace App\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;
class UpdateHandoverDatesJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $folderDates;
protected $registerColumnPath;
/**
* Create a new job instance.
*
* @param array $folderDates
* @param string $registerColumnPath
*/
public function __construct(array $folderDates, string $registerColumnPath)
{
$this->folderDates = $folderDates;
$this->registerColumnPath = $registerColumnPath;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
try {
Log::info('UpdateHandoverDatesJob started', [
'folder_count' => count($this->folderDates),
'register_column_path' => $this->registerColumnPath
]);
$startTime = microtime(true);
$updatedCount = 0;
// Batch size için küçük gruplar halinde işle
$batchSize = 100;
$folderBatches = array_chunk($this->folderDates, $batchSize, true);
foreach ($folderBatches as $batch) {
$cases = [];
$revisionCases = [];
$projectNames = [];
foreach ($batch as $projectName => $folderDate) {
$folderDateFormatted = date('Y-m-d', strtotime($folderDate));
// Tarih formatını doğrula
if (!$folderDateFormatted || $folderDateFormatted === '1970-01-01') {
Log::warning("Invalid date format for project: {$projectName}, date: {$folderDate}");
continue;
}
// SQL injection'ı önlemek için project name'i escape et
$escapedProjectName = addslashes($projectName);
$projectNames[] = $escapedProjectName;
// CASE statements için hazırla
$cases[] = "WHEN project = '{$escapedProjectName}' THEN '{$folderDateFormatted}'";
$revisionCases[] = "WHEN project = '{$escapedProjectName}' AND revision_date IS NULL THEN '{$folderDateFormatted}'";
}
if (!empty($projectNames)) {
// Tek sorguda birden fazla project'i güncelle
$caseStatement = implode(' ', $cases);
$revisionCaseStatement = implode(' ', $revisionCases);
$projectList = "'" . implode("','", $projectNames) . "'";
$affected = DB::update("
UPDATE handovers
SET
created_date = CASE {$caseStatement} ELSE created_date END,
revision_date = CASE
{$revisionCaseStatement}
ELSE revision_date
END,
updated_at = NOW()
WHERE project IN ({$projectList})
");
$updatedCount += $affected;
Log::info("Processed batch", [
'projects_in_batch' => count($projectNames),
'affected_rows' => $affected
]);
}
// Her batch'ten sonra kısa bir mola (memory ve CPU için)
usleep(10000); // 10ms mola
}
$endTime = microtime(true);
$executionTime = round($endTime - $startTime, 2);
Log::info('UpdateHandoverDatesJob completed successfully', [
'total_updated_records' => $updatedCount,
'execution_time_seconds' => $executionTime,
'average_time_per_record' => $updatedCount > 0 ? round($executionTime / $updatedCount, 4) : 0
]);
} catch (\Exception $e) {
Log::error('UpdateHandoverDatesJob failed', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
// Job'ı tekrar queue'ya atmak için exception'ı yeniden fırlat
throw $e;
}
}
/**
* Handle a job failure.
*
* @param \Throwable $exception
* @return void
*/
public function failed(\Throwable $exception)
{
Log::error('UpdateHandoverDatesJob permanently failed', [
'error' => $exception->getMessage(),
'folder_dates_count' => count($this->folderDates)
]);
}
}