43012c982e
Кнопка «Удалить» на карточках «На актуализацию» рядом с Добавить/Заменить. Отклоняет ТОЛЬКО находку (новый сайт/адрес): фирма поля остаётся, находка → архив, её новые ключи запоминаются у фирмы поля (новая колонка dismissed_actualize_keys jsonb). Классификатор вычитает отклонённые ключи → та же находка больше не всплывает; реально другой новый сайт даст новый ключ → покажется. Не глушим по имени, конкурента не убираем. Бэк: миграция + модель-каст + ProposalClassifier (вычитание) + эндпоинт dismissActualize + роут. Фронт: api + store + кнопка «Удалить». Тесты: классификатор 8/8, ветка 185/185, фронт 30/30. Canon-sync schema.sql (v8.62) — follow-up (миграция idempotent-guarded). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
103 lines
4.5 KiB
PHP
103 lines
4.5 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
use App\Services\Autopodbor\AutopodborNormalizer;
|
||
use App\Services\Autopodbor\CompetitorIdentity;
|
||
use App\Services\Autopodbor\ProposalClassifier;
|
||
|
||
function classifier(): ProposalClassifier
|
||
{
|
||
return new ProposalClassifier(new CompetitorIdentity(new AutopodborNormalizer));
|
||
}
|
||
|
||
function grp(array $out, int $findId): array
|
||
{
|
||
foreach ($out as $row) {
|
||
if ($row['find']['id'] === $findId) {
|
||
return $row;
|
||
}
|
||
}
|
||
throw new RuntimeException("find $findId not classified");
|
||
}
|
||
|
||
it('новая — ни с чем не пересеклась', function () {
|
||
$out = classifier()->classify(
|
||
finds: [['id' => 1, 'name' => 'A', 'site_url' => 'a.ru', 'directory_urls' => []]],
|
||
existing: [['id' => 9, 'box' => 'field', 'name' => 'B', 'site_url' => 'b.ru', 'directory_urls' => []]],
|
||
);
|
||
expect(grp($out, 1)['group'])->toBe('new');
|
||
});
|
||
|
||
it('скрытая — полный дубль активной (те же опознавалки)', function () {
|
||
$out = classifier()->classify(
|
||
finds: [['id' => 1, 'name' => 'A новое имя', 'site_url' => 'a.ru', 'directory_urls' => []]],
|
||
existing: [['id' => 9, 'box' => 'field', 'name' => 'A', 'site_url' => 'a.ru', 'directory_urls' => []]],
|
||
);
|
||
expect(grp($out, 1)['group'])->toBe('hidden');
|
||
});
|
||
|
||
it('на актуализацию — совпала карточка, но новый сайт (дельта не пуста)', function () {
|
||
$out = classifier()->classify(
|
||
finds: [['id' => 1, 'name' => 'A', 'site_url' => 'a-new.ru', 'directory_urls' => ['https://2gis.ru/x/firm/111']]],
|
||
existing: [['id' => 9, 'box' => 'field', 'name' => 'A', 'site_url' => 'a.ru', 'directory_urls' => ['https://2gis.ru/x/firm/111']]],
|
||
);
|
||
$r = grp($out, 1);
|
||
expect($r['group'])->toBe('actualize')
|
||
->and($r['matched_id'])->toBe(9)
|
||
->and($r['delta_keys'])->toBe(['s:a-new.ru']);
|
||
});
|
||
|
||
it('ранее удалённая — совпала только с архивом', function () {
|
||
$out = classifier()->classify(
|
||
finds: [['id' => 1, 'name' => 'A', 'site_url' => 'a.ru', 'directory_urls' => []]],
|
||
existing: [['id' => 9, 'box' => 'archived', 'name' => 'A', 'site_url' => 'a.ru', 'directory_urls' => []]],
|
||
);
|
||
$r = grp($out, 1);
|
||
expect($r['group'])->toBe('archived')->and($r['matched_id'])->toBe(9);
|
||
});
|
||
|
||
it('приоритет активного над архивом', function () {
|
||
$out = classifier()->classify(
|
||
finds: [['id' => 1, 'name' => 'A', 'site_url' => 'a.ru', 'directory_urls' => []]],
|
||
existing: [
|
||
['id' => 8, 'box' => 'archived', 'name' => 'A', 'site_url' => 'a.ru', 'directory_urls' => []],
|
||
['id' => 9, 'box' => 'field', 'name' => 'A', 'site_url' => 'a.ru', 'directory_urls' => []],
|
||
],
|
||
);
|
||
$r = grp($out, 1);
|
||
expect($r['group'])->toBe('hidden')->and($r['matched_id'])->toBe(9);
|
||
});
|
||
|
||
it('без опознавалок находка всегда новая', function () {
|
||
$out = classifier()->classify(
|
||
finds: [['id' => 1, 'name' => 'Безымянный', 'site_url' => null, 'directory_urls' => []]],
|
||
existing: [['id' => 9, 'box' => 'field', 'name' => 'Безымянный', 'site_url' => null, 'directory_urls' => []]],
|
||
);
|
||
expect(grp($out, 1)['group'])->toBe('new');
|
||
});
|
||
|
||
it('отклонённый клиентом ключ актуализации → находка уходит в hidden, а не actualize', function () {
|
||
$out = classifier()->classify(
|
||
finds: [['id' => 1, 'name' => 'A', 'site_url' => 'a-new.ru', 'directory_urls' => ['https://2gis.ru/x/firm/111']]],
|
||
existing: [[
|
||
'id' => 9, 'box' => 'field', 'name' => 'A', 'site_url' => 'a.ru',
|
||
'directory_urls' => ['https://2gis.ru/x/firm/111'],
|
||
'dismissed_actualize_keys' => ['s:a-new.ru'], // клиент отклонил этот новый сайт
|
||
]],
|
||
);
|
||
expect(grp($out, 1)['group'])->toBe('hidden');
|
||
});
|
||
|
||
it('другой новый сайт (не отклонённый) → всё ещё actualize', function () {
|
||
$out = classifier()->classify(
|
||
finds: [['id' => 1, 'name' => 'A', 'site_url' => 'a-other.ru', 'directory_urls' => ['https://2gis.ru/x/firm/111']]],
|
||
existing: [[
|
||
'id' => 9, 'box' => 'field', 'name' => 'A', 'site_url' => 'a.ru',
|
||
'directory_urls' => ['https://2gis.ru/x/firm/111'],
|
||
'dismissed_actualize_keys' => ['s:a-new.ru'],
|
||
]],
|
||
);
|
||
expect(grp($out, 1)['group'])->toBe('actualize');
|
||
});
|