İlk temizlik tamamlandı bir önceki projeden
This commit is contained in:
@@ -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}"
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -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}",
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -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}"
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -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}",
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}"
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user