Files
Gnuboard7/app/Jobs/GenerateSitemapJob.php
T
HeuJung face022073 feat(seo): 사이트맵 재생성 진행상황 + 실시간/폴링 UI
관리자 "전체 재생성"을 큐(Full 모드)로 던지고 진행상황을 SEO 탭에서 확인한다.
Reverb ON 이면 방송 채널 구독으로 실시간 갱신, OFF 면 상태 API 폴링으로 폴백한다.

- SitemapProgress 진행상황 스토어(queued→running→writing→completed/failed,
 캐시 TTL 3600, 방송 스로틀) + SitemapManager.getStatus 확장(progress + realtime_enabled)
- GET /api/admin/seo/sitemap/status(core.settings.read) 신규
- 관리자 재생성을 항상 Full 모드로 큐잉 + 진행률 기록, Job 최종 실패 시 progress=failed 고정
- core.admin.seo.sitemap 방송 채널 인증(core.settings.read)
- SEO 탭 진행상황 UI(상태 배지/단계/누적 URL/실시간·폴링 안내) + 폴링 분기
- 재생성 중 누적 URL 표기(replaceAllForContributor 삽입 건수 반환 — count 쿼리 없이 스트림 누적)
- ja 언어팩 진행상황 UI 번역 동기화
2026-07-18 18:22:16 +09:00

120 lines
3.6 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Jobs;
use App\Enums\SitemapGenerationMode;
use App\Seo\SitemapManager;
use App\Seo\SitemapProgress;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
/**
* Sitemap XML 생성 큐 잡
*
* 스케줄러, Artisan 커맨드, 관리자 수동 재생성, 봇 요청 캐시 미스에서 디스패치되며,
* 실제 생성 로직은 SitemapManager 서비스에 위임합니다.
*
* 대용량(수백만 URL) 생성은 수 분~수십 분이 걸리므로 동시 실행을 막고(ShouldBeUnique)
* 타임아웃/재시도 한계를 넉넉히 잡되, 재시도가 무한히 누적되지 않도록 retryUntil 로 제한합니다.
*/
class GenerateSitemapJob implements ShouldBeUnique, ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* @var int 최대 재시도 횟수
*/
public int $tries = 3;
/**
* @var int 타임아웃 (초) — 대용량 스트리밍 생성 대응
*/
public int $timeout = 1800;
/**
* @var int 재시도 대기 시간 (초)
*/
public int $backoff = 120;
/**
* @var int 유니크 락 유지 시간 (초) — 잡이 비정상 종료해도 이 시간 후 자동 해제
*/
public int $uniqueFor = 900;
/**
* GenerateSitemapJob 생성자
*
* @param SitemapGenerationMode $mode 재생성 모드 — SitemapManager 로 전달
*/
public function __construct(public SitemapGenerationMode $mode = SitemapGenerationMode::Auto) {}
/**
* 유니크 락 식별자를 반환합니다.
*
* 봇 요청 캐시 미스마다 디스패치되어도 큐에는 한 건만 남도록 고정 키를 사용합니다.
*
* @return string 유니크 락 키
*/
public function uniqueId(): string
{
return 'seo-sitemap';
}
/**
* 재시도 마감 시각을 반환합니다.
*
* timeout(1800초) × tries(3) 만큼 재시도가 누적되는 것을 막습니다.
*
* @return \DateTime 재시도 마감 시각
*/
public function retryUntil(): \DateTime
{
return now()->addMinutes(30)->toDateTime();
}
/**
* Sitemap 을 생성하고 캐시에 저장합니다.
*
* @param SitemapManager $manager Sitemap 매니저 서비스
*/
public function handle(SitemapManager $manager): void
{
$result = $manager->regenerate($this->mode);
if (($result['status'] ?? null) === 'disabled') {
Log::info('[SEO] Sitemap generation skipped (disabled)');
return;
}
if (! ($result['success'] ?? false)) {
throw new \RuntimeException($result['message'] ?? 'Sitemap regeneration failed');
}
Log::info('[SEO] Sitemap generated and cached', [
'size' => $result['data']['size_bytes'] ?? null,
'ttl' => $result['data']['ttl'] ?? null,
]);
}
/**
* 잡 실패 시 로그를 기록합니다.
*
* @param \Throwable $exception 발생한 예외
*/
public function failed(\Throwable $exception): void
{
// 잡 최종 실패(재시도 소진/타임아웃) 시 진행상황을 'failed' 로 고정 — 무한 running 방지
app(SitemapProgress::class)->fail($exception->getMessage());
Log::error('[SEO] Sitemap generation failed', [
'error' => $exception->getMessage(),
]);
}
}