İ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
+412
View File
@@ -0,0 +1,412 @@
<?php
namespace App\Console\Commands;
use App\Services\NaksSyncService;
use Illuminate\Console\Command;
/**
* NAKS Multi-Module Synchronization Command
*
* Synchronizes NAKS data across all project sites for multiple modules:
* - technology: NAKS Technology (naks_certificates)
* - welder: NAKS Welder (naks_welders)
* - consumables: NAKS Consumables (naks_consumables)
* - expert: NAKS Expert (register_of_experts)
* - equipment: NAKS Equipment (welding_equipment)
*
* Usage:
* php artisan naks:sync # Sync all modules from all projects
* php artisan naks:sync --module=welder # Sync only welder module
* php artisan naks:sync --project=101 # Sync specific project only
* php artisan naks:sync --dry-run # Test mode (no changes)
* php artisan naks:sync --force # Ignore last sync ID, sync all
* php artisan naks:sync --status # Show sync status only
* php artisan naks:sync --reset # Reset sync state
*/
class NaksSync extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'naks:sync
{--module=all : Module to sync (all, technology, welder, consumables, expert, equipment)}
{--project= : Filter to sync only a specific project (by name or URL fragment)}
{--dry-run : Run in test mode without making any changes}
{--force : Ignore last synced ID and sync all records}
{--status : Show sync status only, do not sync}
{--reset : Reset sync state for all or specific module/project}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Synchronize NAKS data (certificates, welders, consumables, experts, equipment) from other project sites';
/**
* The sync service instance.
*/
protected NaksSyncService $syncService;
/**
* Create a new command instance.
*/
public function __construct(NaksSyncService $syncService)
{
parent::__construct();
$this->syncService = $syncService;
}
/**
* Execute the console command.
*/
public function handle(): int
{
$module = $this->option('module') ?? 'all';
$project = $this->option('project');
$dryRun = (bool) $this->option('dry-run');
$force = (bool) $this->option('force');
$statusOnly = (bool) $this->option('status');
$reset = (bool) $this->option('reset');
$this->info('╔════════════════════════════════════════════════════════════╗');
$this->info('║ NAKS Multi-Module Cross-Site Synchronization ║');
$this->info('╚════════════════════════════════════════════════════════════╝');
$this->newLine();
// Show available modules
$this->showAvailableModules();
// Handle status display
if ($statusOnly) {
return $this->showStatus($module);
}
// Handle reset
if ($reset) {
return $this->resetSyncState($module, $project);
}
// Validate module
if ($module !== 'all' && !$this->syncService->getModuleConfig($module)) {
$this->error("❌ Invalid module: {$module}");
$this->line("Available modules: all, technology, welder, consumables, expert, equipment");
return self::FAILURE;
}
// Show mode information
if ($dryRun) {
$this->warn('⚠️ DRY RUN MODE - No changes will be made');
$this->newLine();
}
if ($force) {
$this->warn('⚠️ FORCE MODE - Ignoring last sync ID, syncing all records');
$this->newLine();
}
$this->info("📦 Module(s): " . ($module === 'all' ? 'All modules' : $module));
if ($project) {
$this->info("📌 Project filter: {$project}");
}
$this->newLine();
// Check credentials
if (!env('SYNC_ADMIN_EMAIL') || !env('SYNC_ADMIN_PASSWORD')) {
$this->error('❌ SYNC_ADMIN_EMAIL and SYNC_ADMIN_PASSWORD must be set in .env');
return self::FAILURE;
}
// Show available projects
$this->showAvailableProjects();
// Run sync
$this->info('🔄 Starting synchronization...');
$this->newLine();
$startTime = microtime(true);
try {
$stats = $this->syncService->sync($module, $project, $dryRun, $force);
} catch (\Exception $e) {
$this->error('❌ Sync failed: ' . $e->getMessage());
return self::FAILURE;
}
$duration = round(microtime(true) - $startTime, 2);
// Display results
$this->displayResults($stats, $duration, $dryRun);
// Return appropriate exit code
if (!empty($stats['errors']) || !empty($stats['failed_projects'])) {
return self::FAILURE;
}
return self::SUCCESS;
}
/**
* Show available modules
*/
protected function showAvailableModules(): void
{
$modules = $this->syncService->getModules();
$this->info('📦 Available Modules:');
$tableData = [];
foreach ($modules as $key => $config) {
$tableData[] = [
$key,
$config['name'],
$config['table'],
implode(' + ', $config['unique_keys']),
];
}
$this->table(
['Key', 'Name', 'Table', 'Unique Keys'],
$tableData
);
$this->newLine();
}
/**
* Show available projects
*/
protected function showAvailableProjects(): void
{
try {
$projects = $this->syncService->getProjectUrls();
$this->info('📋 Available Projects:');
$this->table(
['Project Name', 'URL'],
array_map(function ($p) {
return [$p['project_name'] ?? 'Unknown', $p['url'] ?? ''];
}, $projects)
);
$this->newLine();
} catch (\Exception $e) {
$this->warn('Could not fetch project list: ' . $e->getMessage());
}
}
/**
* Display sync results
*/
protected function displayResults(array $stats, float $duration, bool $dryRun): void
{
$this->newLine();
$this->info('════════════════════════════════════════════════════════════');
$this->info(' SYNC RESULTS');
$this->info('════════════════════════════════════════════════════════════');
$this->newLine();
// Project summary
$this->table(
['Metric', 'Value'],
[
['Total Projects', $stats['total_projects']],
['Successful Projects', $stats['successful_projects']],
['Failed Projects', count($stats['failed_projects'])],
['Duration', "{$duration}s"],
]
);
// Module-specific results
if (!empty($stats['modules'])) {
$this->newLine();
$this->info('📦 Module Results:');
$moduleData = [];
$totalSynced = 0;
$totalInserted = 0;
$totalUpdated = 0;
$totalSkipped = 0;
$totalPdfs = 0;
foreach ($stats['modules'] as $key => $moduleStats) {
$moduleData[] = [
$moduleStats['name'],
$moduleStats['total_synced'],
$moduleStats['total_inserted'],
$moduleStats['total_updated'],
$moduleStats['total_skipped'],
$moduleStats['total_pdfs_downloaded'],
];
$totalSynced += $moduleStats['total_synced'];
$totalInserted += $moduleStats['total_inserted'];
$totalUpdated += $moduleStats['total_updated'];
$totalSkipped += $moduleStats['total_skipped'];
$totalPdfs += $moduleStats['total_pdfs_downloaded'];
}
// Add totals row
$moduleData[] = [
'─────────',
'─────',
'────────',
'───────',
'───────',
'────',
];
$moduleData[] = [
'TOTAL',
$totalSynced,
$totalInserted,
$totalUpdated,
$totalSkipped,
$totalPdfs,
];
$this->table(
['Module', 'Synced', 'Inserted', 'Updated', 'Skipped', 'PDFs'],
$moduleData
);
}
// Show failed projects
if (!empty($stats['failed_projects'])) {
$this->newLine();
$this->error('❌ Failed Projects:');
foreach ($stats['failed_projects'] as $failed) {
$this->line("{$failed['name']}: {$failed['error']}");
}
}
// Show errors
if (!empty($stats['errors'])) {
$this->newLine();
$this->error('❌ Errors:');
foreach ($stats['errors'] as $error) {
$this->line("{$error}");
}
}
$this->newLine();
if ($dryRun) {
$this->warn('️ This was a DRY RUN - no actual changes were made');
} else {
$hasSyncedRecords = false;
foreach ($stats['modules'] as $moduleStats) {
if ($moduleStats['total_synced'] > 0) {
$hasSyncedRecords = true;
break;
}
}
if ($hasSyncedRecords) {
$this->info('✅ Synchronization completed successfully!');
} else {
$this->info('️ No new records to sync');
}
}
}
/**
* Show sync status
*/
protected function showStatus(string $module): int
{
$this->info('📊 Sync Status:');
$this->newLine();
$tableName = null;
if ($module !== 'all') {
$config = $this->syncService->getModuleConfig($module);
if ($config) {
$tableName = $config['table'];
}
}
$states = $this->syncService->getSyncStates($tableName);
if (empty($states)) {
$this->warn('No sync history found. Run "php artisan naks:sync" to start syncing.');
return self::SUCCESS;
}
$tableData = array_map(function ($state) {
return [
$state->table_name ?? 'Unknown',
$state->source_url ?? 'Unknown',
$state->last_synced_id ?? 0,
$state->synced_count ?? 0,
$state->last_synced_at ?? 'Never',
];
}, $states);
$this->table(
['Table', 'Source URL', 'Last ID', 'Total Synced', 'Last Sync'],
$tableData
);
return self::SUCCESS;
}
/**
* Reset sync state
*/
protected function resetSyncState(string $module, ?string $project): int
{
$tableName = null;
if ($module !== 'all') {
$config = $this->syncService->getModuleConfig($module);
if ($config) {
$tableName = $config['table'];
} else {
$this->error("Invalid module: {$module}");
return self::FAILURE;
}
}
$message = 'Reset sync state for ';
$message .= $module === 'all' ? 'ALL modules' : "module: {$module}";
if ($project) {
$message .= " and project: {$project}";
}
$message .= '?';
if (!$this->confirm($message)) {
$this->info('Operation cancelled.');
return self::SUCCESS;
}
$sourceUrl = null;
if ($project) {
// Find matching project URL
try {
$projects = $this->syncService->getProjectUrls();
foreach ($projects as $p) {
if (str_contains(strtolower($p['url']), strtolower($project)) ||
str_contains(strtolower($p['project_name'] ?? ''), strtolower($project))) {
$sourceUrl = $p['url'];
break;
}
}
if (!$sourceUrl) {
$this->error("Project not found: {$project}");
return self::FAILURE;
}
} catch (\Exception $e) {
$this->error('Could not fetch project list: ' . $e->getMessage());
return self::FAILURE;
}
}
$deleted = $this->syncService->resetSyncState($tableName, $sourceUrl);
$this->info("✅ Reset {$deleted} sync state record(s)");
return self::SUCCESS;
}
}