- 차원 테이블: dim_client, dim_product 마이그레이션 + SCD Type 2 동기화 (DimensionSyncService) - 재고 통계: stat_inventory_daily + InventoryStatService (stocks, stock_transactions, inspections) - 견적/영업 통계: stat_quote_pipeline_daily + QuoteStatService (quotes, biddings, sales_prospects) - 인사/근태 통계: stat_hr_attendance_daily + HrStatService (attendances, leaves, user_tenants) - KPI/알림: stat_kpi_targets, stat_alerts + KpiAlertService + StatCheckKpiAlertsCommand - StatAggregatorService에 inventory, quote, hr 도메인 추가 (총 6개 도메인) - 스케줄러: stat:check-kpi-alerts 매일 09:00 등록 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
133 lines
5.6 KiB
PHP
133 lines
5.6 KiB
PHP
<?php
|
|
|
|
use App\Models\ApiRequestLog;
|
|
use Illuminate\Foundation\Inspiring;
|
|
use Illuminate\Support\Facades\Artisan;
|
|
use Illuminate\Support\Facades\Schedule;
|
|
|
|
Artisan::command('inspire', function () {
|
|
$this->comment(Inspiring::quote());
|
|
})->purpose('Display an inspiring quote');
|
|
|
|
// API 요청 로그 정리 커맨드
|
|
Artisan::command('api-log:prune', function () {
|
|
$deleted = ApiRequestLog::pruneOldLogs();
|
|
$this->info("✅ {$deleted}개의 API 요청 로그가 삭제되었습니다.");
|
|
})->purpose('하루 지난 API 요청 로그 삭제');
|
|
|
|
// 스케줄러 정의 (Laravel 12 표준 방식)
|
|
|
|
// 매일 새벽 03:00에 API 요청 로그 정리 (하루치만 보관)
|
|
Schedule::command('api-log:prune')
|
|
->dailyAt('03:00')
|
|
->appendOutputTo(storage_path('logs/scheduler.log'))
|
|
->onSuccess(function () {
|
|
\Illuminate\Support\Facades\Log::info('✅ api-log:prune 스케줄러 실행 성공', ['time' => now()]);
|
|
})
|
|
->onFailure(function () {
|
|
\Illuminate\Support\Facades\Log::error('❌ api-log:prune 스케줄러 실행 실패', ['time' => now()]);
|
|
});
|
|
|
|
// 매일 새벽 03:10에 감사 로그 정리 (환경값 기반 보관기간)
|
|
Schedule::command('audit:prune')
|
|
->dailyAt('03:10')
|
|
->appendOutputTo(storage_path('logs/scheduler.log'))
|
|
->onSuccess(function () {
|
|
\Illuminate\Support\Facades\Log::info('✅ audit:prune 스케줄러 실행 성공', ['time' => now()]);
|
|
})
|
|
->onFailure(function () {
|
|
\Illuminate\Support\Facades\Log::error('❌ audit:prune 스케줄러 실행 실패', ['time' => now()]);
|
|
});
|
|
|
|
// 매일 새벽 03:20에 만료된 토큰 정리 (만료 후 24시간 보관)
|
|
Schedule::command('sanctum:prune-expired --hours=24')
|
|
->dailyAt('03:20')
|
|
->appendOutputTo(storage_path('logs/scheduler.log'))
|
|
->onSuccess(function () {
|
|
\Illuminate\Support\Facades\Log::info('✅ sanctum:prune-expired 스케줄러 실행 성공', ['time' => now()]);
|
|
})
|
|
->onFailure(function () {
|
|
\Illuminate\Support\Facades\Log::error('❌ sanctum:prune-expired 스케줄러 실행 실패', ['time' => now()]);
|
|
});
|
|
|
|
// 매일 새벽 03:30에 7일 이상 된 임시 파일 정리
|
|
Schedule::command('storage:cleanup-temp')
|
|
->dailyAt('03:30')
|
|
->appendOutputTo(storage_path('logs/scheduler.log'))
|
|
->onSuccess(function () {
|
|
\Illuminate\Support\Facades\Log::info('✅ storage:cleanup-temp 스케줄러 실행 성공', ['time' => now()]);
|
|
})
|
|
->onFailure(function () {
|
|
\Illuminate\Support\Facades\Log::error('❌ storage:cleanup-temp 스케줄러 실행 실패', ['time' => now()]);
|
|
});
|
|
|
|
// 매일 새벽 03:40에 휴지통 파일 영구 삭제 (30일 이상)
|
|
Schedule::command('storage:cleanup-trash')
|
|
->dailyAt('03:40')
|
|
->appendOutputTo(storage_path('logs/scheduler.log'))
|
|
->onSuccess(function () {
|
|
\Illuminate\Support\Facades\Log::info('✅ storage:cleanup-trash 스케줄러 실행 성공', ['time' => now()]);
|
|
})
|
|
->onFailure(function () {
|
|
\Illuminate\Support\Facades\Log::error('❌ storage:cleanup-trash 스케줄러 실행 실패', ['time' => now()]);
|
|
});
|
|
|
|
// 매일 새벽 03:50에 만료된 공유 링크 정리
|
|
Schedule::command('storage:cleanup-links')
|
|
->dailyAt('03:50')
|
|
->appendOutputTo(storage_path('logs/scheduler.log'))
|
|
->onSuccess(function () {
|
|
\Illuminate\Support\Facades\Log::info('✅ storage:cleanup-links 스케줄러 실행 성공', ['time' => now()]);
|
|
})
|
|
->onFailure(function () {
|
|
\Illuminate\Support\Facades\Log::error('❌ storage:cleanup-links 스케줄러 실행 실패', ['time' => now()]);
|
|
});
|
|
|
|
// 매일 새벽 04:00에 용량 사용 히스토리 기록
|
|
Schedule::command('storage:record-usage')
|
|
->dailyAt('04:00')
|
|
->appendOutputTo(storage_path('logs/scheduler.log'))
|
|
->onSuccess(function () {
|
|
\Illuminate\Support\Facades\Log::info('✅ storage:record-usage 스케줄러 실행 성공', ['time' => now()]);
|
|
})
|
|
->onFailure(function () {
|
|
\Illuminate\Support\Facades\Log::error('❌ storage:record-usage 스케줄러 실행 실패', ['time' => now()]);
|
|
});
|
|
|
|
// ─── 통계 집계 (sam_stat DB) ───
|
|
|
|
// 매일 새벽 02:00에 일간 통계 집계 (전일 데이터)
|
|
Schedule::command('stat:aggregate-daily')
|
|
->dailyAt('02:00')
|
|
->withoutOverlapping()
|
|
->appendOutputTo(storage_path('logs/scheduler.log'))
|
|
->onSuccess(function () {
|
|
\Illuminate\Support\Facades\Log::info('✅ stat:aggregate-daily 스케줄러 실행 성공', ['time' => now()]);
|
|
})
|
|
->onFailure(function () {
|
|
\Illuminate\Support\Facades\Log::error('❌ stat:aggregate-daily 스케줄러 실행 실패', ['time' => now()]);
|
|
});
|
|
|
|
// 매월 1일 새벽 03:00에 월간 통계 집계 (전월 데이터)
|
|
Schedule::command('stat:aggregate-monthly')
|
|
->monthlyOn(1, '03:00')
|
|
->withoutOverlapping()
|
|
->appendOutputTo(storage_path('logs/scheduler.log'))
|
|
->onSuccess(function () {
|
|
\Illuminate\Support\Facades\Log::info('✅ stat:aggregate-monthly 스케줄러 실행 성공', ['time' => now()]);
|
|
})
|
|
->onFailure(function () {
|
|
\Illuminate\Support\Facades\Log::error('❌ stat:aggregate-monthly 스케줄러 실행 실패', ['time' => now()]);
|
|
});
|
|
|
|
// 매일 오전 09:00에 KPI 목표 대비 알림 체크
|
|
Schedule::command('stat:check-kpi-alerts')
|
|
->dailyAt('09:00')
|
|
->appendOutputTo(storage_path('logs/scheduler.log'))
|
|
->onSuccess(function () {
|
|
\Illuminate\Support\Facades\Log::info('✅ stat:check-kpi-alerts 스케줄러 실행 성공', ['time' => now()]);
|
|
})
|
|
->onFailure(function () {
|
|
\Illuminate\Support\Facades\Log::error('❌ stat:check-kpi-alerts 스케줄러 실행 실패', ['time' => now()]);
|
|
});
|