df83fa836c
Шаг 1 «Конкурентное поле» — чекпоинт: - Финальный проход «Найти и объединить дубли» на поле и предложениях; клиент решает по каждой группе, ничего не склеиваем молча. - Тихая склейка только по сайту и коду справочника; телефон и людный номер у более чем 4 фирм — на решение клиента. - Колонка phones jsonb; телефон-дубли видны уже на «Предложениях». - Sonar даёт только имя и тип, сайт всегда через EXA; вскрываем все карточки без гейта «есть сайт». - Яндекс-карточки через локальный Playwright параллельно; рубрика из заголовка идёт в описание. - Фикс рекламных номеров: 2ГИС-парсер разбирает телефоны пообъектно и выкидывает рекламные кнопки с platforms/caption чужих фирм. Тесты: автоподбор 224/224 unit, 105/105 feature; фронт-спеки зелёные. Времянки app/scripts/diag-*/probe-* в гит не входят. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
106 lines
5.9 KiB
PHP
106 lines
5.9 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Services\Autopodbor\Agent\CompetitorCardEnricher;
|
|
use App\Services\Autopodbor\Agent\Extract\DirectoryParser;
|
|
use App\Services\Autopodbor\Agent\Fetch\PageFetcher;
|
|
|
|
// «Вскрыть всё»: обогатитель открывает карточки справочника (2ГИС/Яндекс) у кандидата и достаёт
|
|
// сайт + телефоны + описание, дописывая их на карточку конкурента. Фетчер фейковый (без сети).
|
|
|
|
function enricherPages(array $map): PageFetcher
|
|
{
|
|
return new class($map) implements PageFetcher
|
|
{
|
|
/** @var list<string> */
|
|
public array $requested = [];
|
|
|
|
public function __construct(private array $map) {}
|
|
|
|
public function html(string $url): string
|
|
{
|
|
$this->requested[] = $url;
|
|
foreach ($this->map as $needle => $html) {
|
|
if (str_contains($url, $needle)) {
|
|
return $html;
|
|
}
|
|
}
|
|
|
|
return '';
|
|
}
|
|
};
|
|
}
|
|
|
|
$YA_CARD = <<<'HTML'
|
|
<title>Автодом24, микрофинансовая организация, Красноярск — Яндекс Карты</title>
|
|
<div class="orgpage-phones-view__phone-number" dir="ltr">+7 (913) 539-65-46</div>
|
|
<div class="business-urls-view__url"><a class="business-urls-view__link" href="https://automoney-krsk.ru/"></a></div>
|
|
HTML;
|
|
|
|
$GIS_CARD = '<title>AutoMoney, Красноярск — 2ГИС</title>'
|
|
.'<meta property="og:description" content="Займы под залог ПТС/авто от 2,5% в месяц">'
|
|
.'{"url":"http://automoney-krsk.ru","text":"automoney-krsk.ru","code":"1","type":"website","value":"http://link.2gis.ru/x"}'
|
|
.'{"contact_text":"+7 913 539-65-46","type":"phone","value":"+79135396546"}';
|
|
|
|
it('вскрывает яндекс-карточку кандидата без сайта → появляется сайт и телефон', function () use ($YA_CARD) {
|
|
$pages = enricherPages(['yandex.ru/maps/org/avtodom24' => $YA_CARD]);
|
|
$enricher = new CompetitorCardEnricher($pages, new DirectoryParser);
|
|
|
|
$out = $enricher->enrich([
|
|
['name' => 'Автодом24', 'site_url' => null, 'description' => null, 'phones' => [],
|
|
'directory_urls' => ['https://yandex.ru/maps/org/avtodom24/1'], 'is_federal' => false],
|
|
]);
|
|
|
|
expect($out[0]['site_url'])->toContain('automoney-krsk.ru');
|
|
$digits = implode('', array_map(fn ($p) => preg_replace('/\D/', '', (string) $p), $out[0]['phones']));
|
|
expect($digits)->toContain('9135396546');
|
|
});
|
|
|
|
it('вскрывает 2ГИС-карточку → сайт из поля контакта, телефон, описание', function () use ($GIS_CARD) {
|
|
$pages = enricherPages(['2gis.ru/krasnoyarsk/firm/111' => $GIS_CARD]);
|
|
$enricher = new CompetitorCardEnricher($pages, new DirectoryParser);
|
|
|
|
$out = $enricher->enrich([
|
|
['name' => 'AutoMoney', 'site_url' => null, 'description' => null, 'phones' => [],
|
|
'directory_urls' => ['https://2gis.ru/krasnoyarsk/firm/111'], 'is_federal' => false],
|
|
]);
|
|
|
|
expect($out[0]['site_url'])->toContain('automoney-krsk.ru');
|
|
expect($out[0]['site_url'])->not->toContain('link.2gis.ru');
|
|
expect($out[0]['description'])->toContain('Займы под залог');
|
|
$digits = implode('', array_map(fn ($p) => preg_replace('/\D/', '', (string) $p), $out[0]['phones']));
|
|
expect($digits)->toContain('79135396546');
|
|
});
|
|
|
|
it('вскрывает карточку ДАЖЕ если сайт уже есть — свой сайт сохраняем, телефоны/описание дописываем', function () use ($GIS_CARD) {
|
|
// Максимум качества: карточку открываем ВСЕГДА (гейта «сайт есть → пропустить» больше нет).
|
|
// Сайт из листинга — надёжный, его НЕ затираем; из карточки берём только телефоны и описание.
|
|
$pages = enricherPages(['2gis.ru/krasnoyarsk/firm/111' => $GIS_CARD]);
|
|
$enricher = new CompetitorCardEnricher($pages, new DirectoryParser);
|
|
|
|
$out = $enricher->enrich([
|
|
['name' => 'AutoMoney', 'site_url' => 'known-good.ru', 'description' => null, 'phones' => [],
|
|
'directory_urls' => ['https://2gis.ru/krasnoyarsk/firm/111'], 'is_federal' => false],
|
|
]);
|
|
|
|
expect($pages->requested)->not->toBe([]); // карточку ОТКРЫЛИ, хотя сайт был
|
|
expect($out[0]['site_url'])->toBe('known-good.ru'); // свой сайт из листинга НЕ затёрли
|
|
expect($out[0]['description'])->toContain('Займы под залог'); // описание дописали
|
|
$digits = implode('', array_map(fn ($p) => preg_replace('/\D/', '', (string) $p), $out[0]['phones']));
|
|
expect($digits)->toContain('79135396546'); // телефон дописали
|
|
});
|
|
|
|
it('без лимита и без гейта: открывает карточки у ВСЕХ кандидатов (и с сайтом, и без)', function () use ($YA_CARD) {
|
|
$pages = enricherPages(['yandex.ru' => $YA_CARD]);
|
|
$enricher = new CompetitorCardEnricher($pages, new DirectoryParser);
|
|
|
|
$enricher->enrich([
|
|
['name' => 'A', 'site_url' => null, 'description' => null, 'phones' => [], 'directory_urls' => ['https://yandex.ru/maps/org/a/1'], 'is_federal' => false],
|
|
['name' => 'B', 'site_url' => 'b.ru', 'description' => null, 'phones' => [], 'directory_urls' => ['https://yandex.ru/maps/org/b/2'], 'is_federal' => false],
|
|
['name' => 'C', 'site_url' => null, 'description' => null, 'phones' => [], 'directory_urls' => ['https://yandex.ru/maps/org/c/3'], 'is_federal' => false],
|
|
]);
|
|
|
|
expect(count($pages->requested))->toBe(3); // все три, включая B с сайтом — гейта нет
|
|
});
|