feat(автоподбор): интерфейс движка CompetitorAgent + заглушка + binding
- CompetitorAgent interface: findCompetitors / studyCompetitor / resolveByName - FakeCompetitorAgent: 4 конкурента, 5 источников, 1 кандидат по имени - AutopodborServiceProvider: bind(CompetitorAgent → FakeCompetitorAgent) - Регистрация провайдера в bootstrap/providers.php (Laravel 11+) - Pest.php: extend TestCase для Unit/Autopodbor (контейнер в Unit-тестах) - Тест: 1/1 PASS, 10 assertions Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Services\Autopodbor\Agent\CompetitorAgent;
|
||||
use App\Services\Autopodbor\Agent\FakeCompetitorAgent;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class AutopodborServiceProvider extends ServiceProvider
|
||||
{
|
||||
public function register(): void
|
||||
{
|
||||
// v1: заглушка. Реальный движок биндится здесь, когда будет готов.
|
||||
$this->app->bind(CompetitorAgent::class, FakeCompetitorAgent::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Autopodbor\Agent;
|
||||
|
||||
use App\Services\Autopodbor\Agent\Dto\{FindCompetitorsRequest, FindCompetitorsResult, StudyCompetitorRequest, StudyCompetitorResult, ResolveByNameRequest, ResolveByNameResult};
|
||||
|
||||
interface CompetitorAgent
|
||||
{
|
||||
public function findCompetitors(FindCompetitorsRequest $r): FindCompetitorsResult;
|
||||
public function studyCompetitor(StudyCompetitorRequest $r): StudyCompetitorResult;
|
||||
public function resolveByName(ResolveByNameRequest $r): ResolveByNameResult;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Autopodbor\Agent;
|
||||
|
||||
use App\Services\Autopodbor\Agent\Dto\{FindCompetitorsRequest, FindCompetitorsResult, StudyCompetitorRequest, StudyCompetitorResult, ResolveByNameRequest, ResolveByNameResult};
|
||||
|
||||
final class FakeCompetitorAgent implements CompetitorAgent
|
||||
{
|
||||
public function findCompetitors(FindCompetitorsRequest $r): FindCompetitorsResult
|
||||
{
|
||||
return new FindCompetitorsResult([
|
||||
['name' => 'Окна Комфорт', 'description' => 'Пластиковые окна и остекление балконов под ключ.', 'is_federal' => false, 'relevance_pct' => 100, 'site_url' => 'okna-komfort-kzn.ru', 'directory_urls' => ['https://2gis.ru/firm/1'], 'provenance' => ['via' => 'similar-pages']],
|
||||
['name' => 'Пластика Окон', 'description' => 'Окна ПВХ, лоджии, входные группы.', 'is_federal' => false, 'relevance_pct' => 96, 'site_url' => 'plastika-okon-kzn.ru', 'directory_urls' => ['https://2gis.ru/firm/2'], 'provenance' => ['via' => 'similar-pages']],
|
||||
['name' => 'Фабрика Окон', 'description' => 'Федеральная сеть окон ПВХ, филиал в регионе.', 'is_federal' => true, 'relevance_pct' => 84, 'site_url' => 'fabrika-okon.ru', 'directory_urls' => ['https://2gis.ru/firm/3'], 'provenance' => ['via' => 'similar-pages']],
|
||||
['name' => 'Балкон-Сервис 16', 'description' => 'Остекление балконов; окна частично.', 'is_federal' => false, 'relevance_pct' => 61, 'site_url' => null, 'directory_urls' => ['https://yandex.ru/maps/4', 'https://2gis.ru/firm/4'], 'provenance' => ['via' => 'similar-pages']],
|
||||
]);
|
||||
}
|
||||
|
||||
public function studyCompetitor(StudyCompetitorRequest $r): StudyCompetitorResult
|
||||
{
|
||||
return new StudyCompetitorResult([
|
||||
['signal_type' => 'site', 'identifier' => 'okna-komfort-kzn.ru', 'phone_kind' => null, 'provenance_url' => 'https://2gis.ru/firm/1', 'provenance_label' => '2ГИС — карточка компании'],
|
||||
['signal_type' => 'site', 'identifier' => 'okna-komfort.pro', 'phone_kind' => null, 'provenance_url' => 'https://yandex.ru/maps/1', 'provenance_label' => 'Яндекс.Карты — сайт в контактах'],
|
||||
['signal_type' => 'call', 'identifier' => '78432001122', 'phone_kind' => 'real', 'provenance_url' => 'https://2gis.ru/firm/1', 'provenance_label' => '2ГИС — карточка компании'],
|
||||
['signal_type' => 'call', 'identifier' => '78432009988', 'phone_kind' => 'substitute', 'provenance_url' => 'https://okna-komfort-kzn.ru', 'provenance_label' => 'номер в шапке (коллтрекинг)'],
|
||||
['signal_type' => 'call', 'identifier' => '79172001122', 'phone_kind' => 'real', 'provenance_url' => 'https://yandex.ru/maps/1', 'provenance_label' => 'Яндекс.Карты — карточка компании'],
|
||||
]);
|
||||
}
|
||||
|
||||
public function resolveByName(ResolveByNameRequest $r): ResolveByNameResult
|
||||
{
|
||||
return new ResolveByNameResult([
|
||||
['name' => $r->name, 'description' => 'Найдено по названию (заглушка).', 'site_url' => 'okna-komfort-kzn.ru', 'directory_urls' => ['https://2gis.ru/firm/1'], 'provenance' => ['via' => 'name-search']],
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -4,4 +4,5 @@ use App\Providers\AppServiceProvider;
|
||||
|
||||
return [
|
||||
AppServiceProvider::class,
|
||||
App\Providers\AutopodborServiceProvider::class,
|
||||
];
|
||||
|
||||
@@ -30,6 +30,10 @@ uses(SharesAdminPdo::class)->in('Feature');
|
||||
|
||||
pest()->extend(TestCase::class)->in('Browser');
|
||||
|
||||
// Unit/Autopodbor: часть тестов использует app() и requires Laravel-контейнер
|
||||
// (в т.ч. FakeCompetitorAgentTest — резолвит CompetitorAgent через провайдер).
|
||||
pest()->extend(TestCase::class)->in('Unit/Autopodbor');
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Expectations
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
use App\Services\Autopodbor\Agent\CompetitorAgent;
|
||||
use App\Services\Autopodbor\Agent\Dto\{FindCompetitorsRequest, StudyCompetitorRequest, ResolveByNameRequest};
|
||||
|
||||
it('заглушка резолвится через контейнер и отдаёт данные', function () {
|
||||
$agent = app(CompetitorAgent::class);
|
||||
expect($agent)->toBeInstanceOf(\App\Services\Autopodbor\Agent\FakeCompetitorAgent::class);
|
||||
|
||||
$found = $agent->findCompetitors(new FindCompetitorsRequest(16, ['okna.ru'], [], true, 15));
|
||||
expect(count($found->competitors))->toBeGreaterThan(0)
|
||||
->and($found->competitors[0])->toHaveKeys(['name','relevance_pct','site_url']);
|
||||
|
||||
$study = $agent->studyCompetitor(new StudyCompetitorRequest(['name'=>'Окна Комфорт','site_url'=>'okna-komfort-kzn.ru'], 16));
|
||||
expect($study->sources)->not->toBeEmpty()
|
||||
->and($study->sources[0])->toHaveKeys(['signal_type','identifier','provenance_url']);
|
||||
|
||||
$resolve = $agent->resolveByName(new ResolveByNameRequest('Окна Комфорт', 16));
|
||||
expect($resolve->candidates)->not->toBeEmpty();
|
||||
});
|
||||
Reference in New Issue
Block a user