Files
2026-08-29 15:53:53 +03:00

63 lines
4.0 KiB
PHP
Raw Permalink 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\Services;
use App\Enums\KeywordStatus;
use App\Enums\ProjectStatus;
use App\Enums\SerpCheckStatus;
use App\Jobs\RunSerpCheck;
use App\Jobs\SubmitSerpCheck;
use App\Models\Keyword;
use App\Models\SerpCheck;
use App\Models\User;
use App\Serp\Data\SerpQuery;
use App\Serp\Operations\ActivationManager;
use App\Serp\SerpProviderRegistry;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;
final class SerpCheckCreator
{
public function __construct(private SerpProviderRegistry $providers, private ActivationManager $activation) {}
public function create(User $user, Keyword $keyword, bool $dispatch = true, string $origin = 'manual'): SerpCheck
{
$keyword->refresh()->loadMissing('project');
if ($keyword->project->user_id !== $user->id || $keyword->status !== KeywordStatus::Active || $keyword->archived_at !== null || $keyword->project->status !== ProjectStatus::Active || $keyword->project->archived_at !== null) {
throw ValidationException::withMessages(['serp' => 'Bu anahtar kelime için kontrol başlatılamaz.']);
}if (! config('serp.enabled')) {
throw ValidationException::withMessages(['serp' => 'SERP kontrol hizmeti henüz yapılandırılmamış.']);
}$provider = $this->providers->resolve();
if ($provider->key() === 'dataforseo' && ! $this->activation->allowsNormal()) {
throw ValidationException::withMessages(['serp' => 'SERP kontrol hizmeti şu anda kullanılamıyor.']);
}$query = new SerpQuery($keyword->phrase, $keyword->search_engine->value, $keyword->country_code, $keyword->language_code, $keyword->device->value, 10);
if (! $provider->supports($query)) {
throw ValidationException::withMessages(['serp' => 'Seçili hedef yapılandırılmış sağlayıcı tarafından desteklenmiyor.']);
}if (! in_array($origin, ['manual', 'scheduled'], true)) {
throw ValidationException::withMessages(['serp' => 'Geçersiz kontrol kaynağı.']);
}$mode = $provider->key() === 'dataforseo' ? ($origin === 'manual' ? (string) config('serp.manual_mode') : (string) config('serp.automatic_mode')) : 'live';
if (! in_array($mode, ['live', 'standard'], true)) {
throw ValidationException::withMessages(['serp' => 'Geçersiz yürütme modu.']);
}
return Cache::lock('serp-create:'.$keyword->id, 10)->block(3, function () use ($user, $keyword, $dispatch, $origin, $mode) {
if ($keyword->serpChecks()->whereIn('status', ['pending', 'running'])->exists()) {
throw ValidationException::withMessages(['serp' => 'Bu anahtar kelime için devam eden bir kontrol var.']);
}$cool = (int) config('serp.keyword_cooldown_minutes');
if ($keyword->serpChecks()->where('created_at', '>', now()->subMinutes($cool))->exists()) {
throw ValidationException::withMessages(['serp' => 'Yeni kontrol için bekleme süresi henüz dolmadı.']);
}$used = SerpCheck::whereHas('keyword.project', fn ($q) => $q->where('user_id', $user->id))->where('created_at', '>=', now()->startOfDay())->count();
if ($used >= (int) config('serp.daily_user_quota')) {
throw ValidationException::withMessages(['serp' => 'Günlük SERP kontrol kotanıza ulaştınız.']);
}$check = DB::transaction(fn () => $keyword->serpChecks()->create(['phrase' => $keyword->phrase, 'status' => SerpCheckStatus::Pending, 'active_slot' => 'active', 'search_engine' => $keyword->search_engine, 'country_code' => $keyword->country_code, 'language_code' => $keyword->language_code, 'device' => $keyword->device, 'provider' => config('serp.provider'), 'execution_mode' => $mode, 'origin' => $origin, 'result_count' => 0]));
if ($dispatch) {
$job = $mode === 'standard' ? new SubmitSerpCheck($check->ulid) : new RunSerpCheck($check->ulid);
dispatch($job->onQueue(config('serp.queue')))->afterCommit();
}
return $check;
});
}
}