diff --git a/app/app/Jobs/Autopodbor/RunAutopodborSearchJob.php b/app/app/Jobs/Autopodbor/RunAutopodborSearchJob.php new file mode 100644 index 00000000..4aa0783a --- /dev/null +++ b/app/app/Jobs/Autopodbor/RunAutopodborSearchJob.php @@ -0,0 +1,88 @@ +runId); + + // Выставляем tenant-контекст сессионно — все запросы (включая вложенные транзакции charge) видят GUC + DB::statement('SET app.current_tenant_id = '.$run->tenant_id); + + $run->update(['status' => 'running', 'started_at' => now()]); + + try { + $p = $run->params; + $max = (int) (SystemSettings::get('autopodbor_max_competitors') ?? 15); + + $res = $agent->findCompetitors(new FindCompetitorsRequest( + regionCode: (int) $run->region_code, + examples: $p['examples'] ?? [], + aboutSelf: $p['about_self'] ?? [], + includeFederal: (bool) ($p['include_federal'] ?? false), + maxCompetitors: $max, + )); + + $unique = $dedup->dedupCompetitors($res->competitors); + + if ($unique === []) { + $run->update(['status' => 'empty', 'finished_at' => now()]); + return; + } + + foreach (array_slice($unique, 0, $max) as $c) { + AutopodborCompetitor::create([ + 'tenant_id' => $run->tenant_id, + 'search_run_id' => $run->id, + 'name' => $c['name'], + 'description' => $c['description'] ?? null, + 'is_federal' => (bool) ($c['is_federal'] ?? false), + 'relevance_pct' => $c['relevance_pct'] ?? null, + 'origin' => 'auto', + 'site_url' => $c['site_url'] ?? null, + 'directory_urls' => $c['directory_urls'] ?? [], + 'provenance' => $c['provenance'] ?? [], + 'dedup_key' => $c['dedup_key'], + ]); + } + + $price = (string) (SystemSettings::get('autopodbor_price_search_rub') ?? '0'); + $charge->chargeForRun($run, $price); + + $run->update(['status' => 'done', 'finished_at' => now()]); + } catch (\Throwable $e) { + $run->update([ + 'status' => 'failed', + 'error_code' => substr($e->getMessage(), 0, 64), + 'finished_at' => now(), + ]); + throw $e; + } + } +} diff --git a/app/tests/Doubles/EmptyCompetitorAgent.php b/app/tests/Doubles/EmptyCompetitorAgent.php new file mode 100644 index 00000000..279ccfca --- /dev/null +++ b/app/tests/Doubles/EmptyCompetitorAgent.php @@ -0,0 +1,29 @@ +call([new RunAutopodborSearchJob($runId), 'handle']); +} + +it('успешный подбор: сохраняет конкурентов, списывает, status=done', function () { + $tenant = Tenant::factory()->create(['balance_rub' => '100000.00']); + DB::statement("SET app.current_tenant_id = ".$tenant->id); + SystemSetting::updateOrCreate(['key' => 'autopodbor_price_search_rub'], ['value' => '500', 'type' => 'decimal']); + SystemSetting::updateOrCreate(['key' => 'autopodbor_max_competitors'], ['value' => '15', 'type' => 'int']); + $run = AutopodborRun::create([ + 'tenant_id' => $tenant->id, + 'kind' => 'search', + 'status' => 'queued', + 'region_code' => 16, + 'params' => ['examples' => ['okna.ru'], 'about_self' => [], 'include_federal' => true], + ]); + + runSearchJob($run->id); + + expect($run->fresh()->status)->toBe('done') + ->and($run->fresh()->price_rub_charged)->toBe('500.00') + ->and(AutopodborCompetitor::where('search_run_id', $run->id)->count())->toBeGreaterThan(0) + ->and((string) $tenant->fresh()->balance_rub)->toBe('99500.00'); +}); + +it('пустой результат: status=empty, без списания', function () { + app()->bind(CompetitorAgent::class, \Tests\Doubles\EmptyCompetitorAgent::class); + $tenant = Tenant::factory()->create(['balance_rub' => '100000.00']); + DB::statement("SET app.current_tenant_id = ".$tenant->id); + SystemSetting::updateOrCreate(['key' => 'autopodbor_price_search_rub'], ['value' => '500', 'type' => 'decimal']); + $run = AutopodborRun::create([ + 'tenant_id' => $tenant->id, + 'kind' => 'search', + 'status' => 'queued', + 'region_code' => 16, + 'params' => ['examples' => [], 'about_self' => [], 'include_federal' => false], + ]); + + runSearchJob($run->id); + + expect($run->fresh()->status)->toBe('empty') + ->and($run->fresh()->price_rub_charged)->toBeNull() + ->and((string) $tenant->fresh()->balance_rub)->toBe('100000.00'); +});