Files
sam-api/app/Console/Commands/FcmPruneInvalidCommand.php

106 lines
3.4 KiB
PHP
Raw Normal View History

<?php
namespace App\Console\Commands;
use App\Models\PushDeviceToken;
use Illuminate\Console\Command;
class FcmPruneInvalidCommand extends Command
{
protected $signature = 'fcm:prune-invalid
{--days=30 : N일 이상 무효 토큰 삭제}
{--inactive : 비활성(is_active=0) 토큰도 삭제}
{--dry-run : 실제 삭제 없이 대상 수만 출력}
{--force : 확인 없이 강제 실행}';
protected $description = 'FCM 무효 토큰 정리 (soft delete)';
public function handle(): int
{
$days = (int) $this->option('days');
$includeInactive = $this->option('inactive');
$dryRun = $this->option('dry-run');
$this->info('FCM 무효 토큰 정리');
$this->line('');
// 1. 에러가 있는 토큰 (N일 이상)
$errorQuery = PushDeviceToken::withoutGlobalScopes()
->hasError()
->where('last_error_at', '<', now()->subDays($days));
$errorCount = $errorQuery->count();
// 2. 비활성 토큰 (옵션)
$inactiveCount = 0;
if ($includeInactive) {
$inactiveQuery = PushDeviceToken::withoutGlobalScopes()
->where('is_active', false)
->whereNull('last_error') // 에러 없이 비활성화된 것만
->where('updated_at', '<', now()->subDays($days));
$inactiveCount = $inactiveQuery->count();
}
$totalCount = $errorCount + $inactiveCount;
// 현황 출력
$this->table(['구분', '대상 수'], [
["에러 토큰 ({$days}일 이상)", $errorCount],
['비활성 토큰', $includeInactive ? $inactiveCount : '(미포함)'],
['합계', $totalCount],
]);
$this->line('');
if ($totalCount === 0) {
$this->info('정리할 토큰이 없습니다.');
return self::SUCCESS;
}
// Dry-run 모드
if ($dryRun) {
$this->info("🔍 Dry-run: {$totalCount}개 토큰 삭제 예정");
return self::SUCCESS;
}
// 확인
if (! $this->option('force') && ! $this->confirm("정말 {$totalCount}개 토큰을 삭제하시겠습니까?")) {
$this->info('취소되었습니다.');
return self::SUCCESS;
}
// 삭제 실행
$deletedError = 0;
$deletedInactive = 0;
// 에러 토큰 삭제
if ($errorCount > 0) {
$deletedError = PushDeviceToken::withoutGlobalScopes()
->hasError()
->where('last_error_at', '<', now()->subDays($days))
->delete();
$this->info(" 에러 토큰 삭제: {$deletedError}");
}
// 비활성 토큰 삭제
if ($includeInactive && $inactiveCount > 0) {
$deletedInactive = PushDeviceToken::withoutGlobalScopes()
->where('is_active', false)
->whereNull('last_error')
->where('updated_at', '<', now()->subDays($days))
->delete();
$this->info(" 비활성 토큰 삭제: {$deletedInactive}");
}
$this->line('');
$this->info('✅ 총 '.($deletedError + $deletedInactive).'개 토큰 삭제 완료 (soft delete)');
return self::SUCCESS;
}
}