892ca06093
Клиент может собрать источники по конкуренту ещё раз. Новые → в предложения,
ранее удалённые (найдены снова) → помечаются для возврата, уже известные не
задваиваются и не трогаются.
- удаление источника стало мягким (box=archived) — помним, чтобы показать «ранее удалён»
- снят жёсткий стоп «уже изучали» в startStudy (повтор разрешён; от нахлёста — assertNoInFlight)
- колонка result (jsonb) на прогоне + cast + RunResource
- джоба изучения классифицирует источники и пишет итог {new_sources, refound_deleted, ids}
- тесты: мягкое удаление, повтор, классификация
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
99 lines
4.8 KiB
PHP
99 lines
4.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Jobs\Autopodbor\RunAutopodborStudyJob;
|
|
use App\Models\AutopodborCompetitor;
|
|
use App\Models\AutopodborRun;
|
|
use App\Models\AutopodborSource;
|
|
use App\Models\SystemSetting;
|
|
use App\Models\Tenant;
|
|
use App\Services\Autopodbor\Agent\CompetitorAgent;
|
|
use App\Services\Autopodbor\Agent\Dto\FindCompetitorsRequest;
|
|
use App\Services\Autopodbor\Agent\Dto\FindCompetitorsResult;
|
|
use App\Services\Autopodbor\Agent\Dto\ResolveByNameRequest;
|
|
use App\Services\Autopodbor\Agent\Dto\ResolveByNameResult;
|
|
use App\Services\Autopodbor\Agent\Dto\StudyCompetitorRequest;
|
|
use App\Services\Autopodbor\Agent\Dto\StudyCompetitorResult;
|
|
use App\Services\Autopodbor\AutopodborNormalizer;
|
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Tests\Concerns\SharesSupplierPdo;
|
|
|
|
uses(DatabaseTransactions::class, SharesSupplierPdo::class);
|
|
|
|
/** Агент отдаёт при изучении фиксированные сайты (site-источники). */
|
|
function bindStudyAgentReturning(array $siteIdentifiers): void
|
|
{
|
|
app()->bind(CompetitorAgent::class, fn () => new class($siteIdentifiers) implements CompetitorAgent
|
|
{
|
|
public function __construct(private array $sites) {}
|
|
|
|
public function findCompetitors(FindCompetitorsRequest $r): FindCompetitorsResult
|
|
{
|
|
return new FindCompetitorsResult([]);
|
|
}
|
|
|
|
public function studyCompetitor(StudyCompetitorRequest $r): StudyCompetitorResult
|
|
{
|
|
return new StudyCompetitorResult(array_map(fn (string $id): array => [
|
|
'signal_type' => 'site', 'identifier' => $id, 'phone_kind' => null, 'phone_type' => null,
|
|
'provenance_url' => null, 'provenance_label' => 'тест',
|
|
], $this->sites));
|
|
}
|
|
|
|
public function resolveByName(ResolveByNameRequest $r): ResolveByNameResult
|
|
{
|
|
return new ResolveByNameResult([]);
|
|
}
|
|
});
|
|
}
|
|
|
|
it('повторный сбор: новые в предложения, ранее удалённые помечаются «вернуть», известные не тронуты', function () {
|
|
$tenant = Tenant::factory()->create(['balance_rub' => '100000.00']);
|
|
DB::statement('SET app.current_tenant_id = '.$tenant->id);
|
|
SystemSetting::updateOrCreate(['key' => 'autopodbor_price_study_rub'], ['value' => '0', 'type' => 'decimal']);
|
|
$norm = app(AutopodborNormalizer::class);
|
|
|
|
$oldRun = AutopodborRun::create(['tenant_id' => $tenant->id, 'kind' => 'study', 'status' => 'done', 'region_code' => 16, 'params' => []]);
|
|
$comp = AutopodborCompetitor::create([
|
|
'tenant_id' => $tenant->id, 'name' => 'Окна', 'dedup_key' => 'okna', 'box' => 'field',
|
|
'studied_at' => now(), 'study_run_id' => $oldRun->id,
|
|
]);
|
|
// Уже известный источник, взятый в работу (box=field) — трогать нельзя.
|
|
$known = AutopodborSource::create([
|
|
'tenant_id' => $tenant->id, 'competitor_id' => $comp->id, 'study_run_id' => $oldRun->id, 'signal_type' => 'site',
|
|
'identifier' => 'known.ru', 'dedup_key' => $norm->sourceKey('site', 'known.ru'), 'box' => 'field',
|
|
]);
|
|
// Ранее удалённый источник (box=archived) — помним.
|
|
$deleted = AutopodborSource::create([
|
|
'tenant_id' => $tenant->id, 'competitor_id' => $comp->id, 'study_run_id' => $oldRun->id, 'signal_type' => 'site',
|
|
'identifier' => 'deleted.ru', 'dedup_key' => $norm->sourceKey('site', 'deleted.ru'), 'box' => 'archived',
|
|
]);
|
|
|
|
// Движок при повторе находит: известный + ранее удалённый + СОВСЕМ новый.
|
|
bindStudyAgentReturning(['known.ru', 'deleted.ru', 'new.ru']);
|
|
|
|
$run = AutopodborRun::create([
|
|
'tenant_id' => $tenant->id, 'kind' => 'study', 'status' => 'queued',
|
|
'region_code' => 16, 'params' => [], 'competitor_id' => $comp->id,
|
|
]);
|
|
app()->call([new RunAutopodborStudyJob($run->id), 'handle']);
|
|
|
|
// Новый → в предложения.
|
|
$new = AutopodborSource::where('competitor_id', $comp->id)->where('identifier', 'new.ru')->first();
|
|
expect($new)->not->toBeNull()->and($new->box)->toBe('proposal');
|
|
// Ранее удалённый остаётся в архиве, но помечен последним прогоном (для блока «вернуть»).
|
|
$deleted->refresh();
|
|
expect($deleted->box)->toBe('archived')->and($deleted->study_run_id)->toBe($run->id);
|
|
// Известный (в работе) не тронут.
|
|
$known->refresh();
|
|
expect($known->box)->toBe('field');
|
|
// Итог прогона: 1 новый, 1 ранее удалённый (с его id).
|
|
expect($run->fresh()->result)->toEqual([
|
|
'new_sources' => 1,
|
|
'refound_deleted' => 1,
|
|
'refound_deleted_ids' => [$deleted->id],
|
|
]);
|
|
});
|