e8c53f80e7
1. Разворот по телефонам запускается ТОЛЬКО при наличии своего сайта (seedDomain). Без сайта имя размыто (напр. «Южный берег» → автосервисы/гостиницы), сеедные телефоны грязные, а разворот их лишь умножает. Доказано вживую: ДСК/СМ-сити (сайт есть) — чисто; «Южный берег» (нет) — 70% мусора. 2. Поиск дублей научился кросс-ящик поле↔предложения (scope=cross): предложение- дубль карточки поля теперь всплывает (merge кросс-ящик уже умеет). В выдаче виден box каждой фирмы. Без scope — прежнее поведение (один ящик). 3. На кнопке «Найти и объединить дубли» показывается число найденных групп-дублей (считается на загрузке, обновляется после слияния/«это разные»). Юнит 267/267, feature 153/153, фронт 15/15. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
191 lines
7.9 KiB
PHP
191 lines
7.9 KiB
PHP
<?php
|
|
|
|
use App\Services\Autopodbor\Agent\Fetch\FetchedSite;
|
|
use App\Services\Autopodbor\Agent\Fetch\Fetcher;
|
|
use App\Services\Autopodbor\Agent\Fetch\XfetchClient;
|
|
use App\Services\Autopodbor\Agent\Study\DeepStudyCollector;
|
|
use App\Services\Autopodbor\Agent\Study\Fingerprint;
|
|
use App\Services\Autopodbor\Agent\Study\PhoneExpansionResult;
|
|
use App\Services\Autopodbor\Agent\Study\PhoneReverse;
|
|
use App\Services\Autopodbor\Agent\Study\SiteOpener;
|
|
use Illuminate\Support\Facades\Http;
|
|
|
|
it('за флагом phone_reverse вплетает находки разворота: сайт проекта и его телефон', function () {
|
|
config()->set('autopodbor.phone_reverse', true);
|
|
config()->set('services.exa', ['key' => 't', 'base_url' => 'https://api.exa.ai']);
|
|
Http::fake([
|
|
'api.exa.ai/*' => Http::response(['results' => [['url' => 'https://gk-dsk.ru/']]]),
|
|
'*' => Http::response(['response_body_base64' => '']),
|
|
]);
|
|
|
|
$siteFetcher = new class implements Fetcher
|
|
{
|
|
public function site(string $url): FetchedSite
|
|
{
|
|
return new FetchedSite($url, '<a href="tel:+73912273531">gk-dsk.ru</a>');
|
|
}
|
|
|
|
public function directory(string $url): array
|
|
{
|
|
return [];
|
|
}
|
|
};
|
|
|
|
$reverse = new class implements PhoneReverse
|
|
{
|
|
public function expand(string $gisSlug, array $seedPhones, array $seedDomains, string $yandexSlug = ''): PhoneExpansionResult
|
|
{
|
|
return new PhoneExpansionResult(
|
|
sites: [['domain' => 'zvezda-dsk.ru', 'site_url' => 'https://zvezda-dsk.ru', 'card_url' => 'https://2gis.ru/krasnoyarsk/firm/111']],
|
|
phones: [['digits' => '73912002288', 'url' => 'https://2gis.ru/krasnoyarsk/firm/111', 'source' => '2ГИС']],
|
|
);
|
|
}
|
|
};
|
|
|
|
$collector = new DeepStudyCollector(
|
|
xfetch: new XfetchClient(null),
|
|
opener: new SiteOpener($siteFetcher),
|
|
reverse: $reverse,
|
|
);
|
|
$fp = new Fingerprint('ДСК', 'gk-dsk.ru', 'Красноярск', '391', 'krasnoyarsk', false);
|
|
|
|
$rows = $collector->collect($fp);
|
|
$siteDomains = array_map(fn ($r) => $r['identifier'], array_filter($rows, fn ($r) => $r['signal_type'] === 'site'));
|
|
$callDigits = array_map(fn ($r) => $r['identifier'], array_filter($rows, fn ($r) => $r['signal_type'] === 'call'));
|
|
|
|
expect($siteDomains)->toContain('zvezda-dsk.ru')
|
|
->and($callDigits)->toContain('73912002288');
|
|
});
|
|
|
|
it('без флага phone_reverse разворот не зовётся (находок разворота нет)', function () {
|
|
config()->set('autopodbor.phone_reverse', false);
|
|
config()->set('services.exa', ['key' => 't', 'base_url' => 'https://api.exa.ai']);
|
|
Http::fake([
|
|
'api.exa.ai/*' => Http::response(['results' => []]),
|
|
'*' => Http::response(['response_body_base64' => '']),
|
|
]);
|
|
|
|
$siteFetcher = new class implements Fetcher
|
|
{
|
|
public function site(string $url): FetchedSite
|
|
{
|
|
return new FetchedSite($url, '');
|
|
}
|
|
|
|
public function directory(string $url): array
|
|
{
|
|
return [];
|
|
}
|
|
};
|
|
$reverse = new class implements PhoneReverse
|
|
{
|
|
public function expand(string $gisSlug, array $seedPhones, array $seedDomains, string $yandexSlug = ''): PhoneExpansionResult
|
|
{
|
|
return new PhoneExpansionResult(
|
|
sites: [['domain' => 'nemojet.ru', 'site_url' => 'https://nemojet.ru', 'card_url' => 'https://2gis.ru/x/firm/9']],
|
|
);
|
|
}
|
|
};
|
|
|
|
$collector = new DeepStudyCollector(
|
|
xfetch: new XfetchClient(null),
|
|
opener: new SiteOpener($siteFetcher),
|
|
reverse: $reverse,
|
|
);
|
|
$fp = new Fingerprint('ДСК', 'gk-dsk.ru', 'Красноярск', '391', 'krasnoyarsk', false);
|
|
|
|
$rows = $collector->collect($fp);
|
|
$siteDomains = array_map(fn ($r) => $r['identifier'], array_filter($rows, fn ($r) => $r['signal_type'] === 'site'));
|
|
|
|
expect($siteDomains)->not->toContain('nemojet.ru');
|
|
});
|
|
|
|
it('без сайта-якоря (seedDomain пуст) разворот НЕ зовётся — защита от мусора', function () {
|
|
config()->set('autopodbor.phone_reverse', true);
|
|
config()->set('services.exa', ['key' => 't', 'base_url' => 'https://api.exa.ai']);
|
|
Http::fake([
|
|
'api.exa.ai/*' => Http::response(['results' => []]),
|
|
'*' => Http::response(['response_body_base64' => '']),
|
|
]);
|
|
$siteFetcher = new class implements Fetcher
|
|
{
|
|
public function site(string $url): FetchedSite
|
|
{
|
|
return new FetchedSite($url, '');
|
|
}
|
|
|
|
public function directory(string $url): array
|
|
{
|
|
return [];
|
|
}
|
|
};
|
|
$called = (object) ['n' => 0];
|
|
$reverse = new class($called) implements PhoneReverse
|
|
{
|
|
public function __construct(private object $c) {}
|
|
|
|
public function expand(string $gisSlug, array $seedPhones, array $seedDomains, string $yandexSlug = ''): PhoneExpansionResult
|
|
{
|
|
$this->c->n++;
|
|
|
|
return new PhoneExpansionResult(
|
|
sites: [['domain' => 'junk.ru', 'site_url' => 'https://junk.ru', 'card_url' => 'x']],
|
|
);
|
|
}
|
|
};
|
|
$collector = new DeepStudyCollector(
|
|
xfetch: new XfetchClient(null),
|
|
opener: new SiteOpener($siteFetcher),
|
|
reverse: $reverse,
|
|
);
|
|
// seedDomain пуст (у элемента нет своего сайта — размытый конкурент как «Южный берег»)
|
|
$fp = new Fingerprint('Южный берег', '', 'Красноярск', '391', 'krasnoyarsk', false, '62/krasnoyarsk');
|
|
|
|
$rows = $collector->collect($fp);
|
|
$siteDomains = array_map(fn ($r) => $r['identifier'], array_filter($rows, fn ($r) => $r['signal_type'] === 'site'));
|
|
|
|
expect($called->n)->toBe(0) // разворот не звался
|
|
->and($siteDomains)->not->toContain('junk.ru'); // мусор не приполз
|
|
});
|
|
|
|
it('в разворот передаётся yandexSlug из отпечатка (для Яндекс-разворота)', function () {
|
|
config()->set('autopodbor.phone_reverse', true);
|
|
config()->set('services.exa', ['key' => 't', 'base_url' => 'https://api.exa.ai']);
|
|
\Illuminate\Support\Facades\Http::fake([
|
|
'api.exa.ai/*' => \Illuminate\Support\Facades\Http::response(['results' => [['url' => 'https://gk-dsk.ru/']]]),
|
|
'*' => \Illuminate\Support\Facades\Http::response(['response_body_base64' => '']),
|
|
]);
|
|
$siteFetcher = new class implements \App\Services\Autopodbor\Agent\Fetch\Fetcher
|
|
{
|
|
public function site(string $url): \App\Services\Autopodbor\Agent\Fetch\FetchedSite
|
|
{
|
|
return new \App\Services\Autopodbor\Agent\Fetch\FetchedSite($url, '<a href="tel:+73912273531">x</a>');
|
|
}
|
|
|
|
public function directory(string $url): array
|
|
{
|
|
return [];
|
|
}
|
|
};
|
|
$captured = (object) ['yandexSlug' => null];
|
|
$reverse = new class($captured) implements \App\Services\Autopodbor\Agent\Study\PhoneReverse
|
|
{
|
|
public function __construct(private object $cap) {}
|
|
|
|
public function expand(string $gisSlug, array $seedPhones, array $seedDomains, string $yandexSlug = ''): \App\Services\Autopodbor\Agent\Study\PhoneExpansionResult
|
|
{
|
|
$this->cap->yandexSlug = $yandexSlug;
|
|
|
|
return new \App\Services\Autopodbor\Agent\Study\PhoneExpansionResult;
|
|
}
|
|
};
|
|
$collector = new \App\Services\Autopodbor\Agent\Study\DeepStudyCollector(
|
|
xfetch: new \App\Services\Autopodbor\Agent\Fetch\XfetchClient(null),
|
|
opener: new \App\Services\Autopodbor\Agent\Study\SiteOpener($siteFetcher),
|
|
reverse: $reverse,
|
|
);
|
|
$fp = new \App\Services\Autopodbor\Agent\Study\Fingerprint('ДСК', 'gk-dsk.ru', 'Красноярск', '391', 'krasnoyarsk', false, '62/krasnoyarsk');
|
|
$collector->collect($fp);
|
|
expect($captured->yandexSlug)->toBe('62/krasnoyarsk');
|
|
});
|