companies: Zusammenführungen zum Nachprüfen auflisten
companies:merges --batch= zeigt je Vorgang Überlebenden, Newsroom-URLs auf beiden Portalen, aufgegangene Firmen und deren alte URLs (301). Der scharfe Lauf gibt die ersten 30 Vorgänge jetzt ebenfalls als Tabelle aus. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
1368e2cf07
commit
202cf340f9
4 changed files with 151 additions and 5 deletions
115
app/Console/Commands/ListCompanyMerges.php
Normal file
115
app/Console/Commands/ListCompanyMerges.php
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Enums\CompanyMergeSubject;
|
||||
use App\Enums\Portal;
|
||||
use App\Models\Company;
|
||||
use App\Models\CompanyMerge;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* Zeigt, was eine Zusammenführung (Roadmap 1a.3) getan hat — je Vorgang der
|
||||
* Überlebende, die aufgegangenen Firmen und die URLs zum Nachprüfen: der
|
||||
* Newsroom auf beiden Portalen, die alten Slugs, die per 301 weiterführen.
|
||||
*/
|
||||
class ListCompanyMerges extends Command
|
||||
{
|
||||
protected $signature = 'companies:merges
|
||||
{--batch= : Nur diesen Lauf}
|
||||
{--email= : Nur diese Adresse}
|
||||
{--limit=100 : Höchstens so viele Vorgänge}
|
||||
{--json : Ausgabe als JSON}';
|
||||
|
||||
protected $description = 'Listet Zusammenführungen mit Überlebendem, aufgegangenen Firmen und Prüf-URLs.';
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
$merges = CompanyMerge::query()
|
||||
->when($this->option('batch'), fn (Builder $query, string $batch) => $query->where('batch', $batch))
|
||||
->when($this->option('email'), fn (Builder $query, string $email) => $query->where('email', Company::emailKey($email)))
|
||||
->with([
|
||||
'survivor',
|
||||
'moves' => fn ($query) => $query->where('subject', CompanyMergeSubject::Company->value),
|
||||
])
|
||||
->orderByDesc('id')
|
||||
->limit(max(1, (int) $this->option('limit')))
|
||||
->get();
|
||||
|
||||
if ($merges->isEmpty()) {
|
||||
$this->warn('Keine Vorgänge gefunden.');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
$losers = Company::withTrashed()
|
||||
->withoutGlobalScopes()
|
||||
->whereIn('id', $merges->flatMap(fn (CompanyMerge $merge) => $merge->moves->pluck('subject_id'))->all())
|
||||
->get(['id', 'name', 'slug', 'portal'])
|
||||
->keyBy('id');
|
||||
|
||||
$rows = $merges->map(function (CompanyMerge $merge) use ($losers): array {
|
||||
$survivor = $merge->survivor;
|
||||
$gone = $merge->moves->map(fn ($move) => $losers->get($move->subject_id))->filter();
|
||||
|
||||
return [
|
||||
'id' => $merge->getKey(),
|
||||
'batch' => $merge->batch,
|
||||
'email' => $merge->email,
|
||||
'rueckabgewickelt' => $merge->reverted_at?->toDateTimeString(),
|
||||
'bleibt' => ['id' => $survivor->getKey(), 'name' => $survivor->name, 'slug' => $survivor->slug, 'portal' => $survivor->portal->value],
|
||||
'newsroom' => $this->newsroomUrls($survivor->portal, $survivor->slug),
|
||||
'aufgegangen' => $gone->map(fn (Company $loser): array => [
|
||||
'id' => $loser->getKey(),
|
||||
'name' => $loser->name,
|
||||
'slug' => $loser->slug,
|
||||
'portal' => $loser->portal->value,
|
||||
// Nur bei abweichendem Slug gibt es eine Weiterleitung —
|
||||
// bei gleichem findet die URL den Überlebenden direkt.
|
||||
'alte_url' => $loser->slug !== $survivor->slug ? $this->newsroomUrls($loser->portal, $loser->slug) : [],
|
||||
])->values()->all(),
|
||||
'meldungen' => $merge->moved_press_releases_count,
|
||||
];
|
||||
});
|
||||
|
||||
if ($this->option('json')) {
|
||||
$this->line((string) json_encode($rows->values()->all(), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
$this->table(
|
||||
['#', 'Adresse', 'bleibt', 'Newsroom', 'aufgegangen (alte URL → 301)', 'PM'],
|
||||
$rows->map(fn (array $row): array => [
|
||||
$row['id'].($row['rueckabgewickelt'] ? ' (zurück)' : ''),
|
||||
$row['email'],
|
||||
sprintf('#%d %s', $row['bleibt']['id'], Str::limit($row['bleibt']['name'], 32)),
|
||||
implode("\n", $row['newsroom']),
|
||||
implode("\n", array_map(
|
||||
fn (array $loser): string => sprintf('#%d %s%s', $loser['id'], Str::limit($loser['name'], 28), $loser['alte_url'] ? "\n ".implode("\n ", $loser['alte_url']) : ' (gleicher Slug)'),
|
||||
$row['aufgegangen'],
|
||||
)),
|
||||
$row['meldungen'],
|
||||
])->all(),
|
||||
);
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Newsroom-URL je Portal, auf dem die Firma erreichbar ist.
|
||||
*
|
||||
* @return list<string>
|
||||
*/
|
||||
private function newsroomUrls(Portal $portal, string $slug): array
|
||||
{
|
||||
$portals = $portal === Portal::Both ? ['presseecho', 'businessportal24'] : [$portal->value];
|
||||
|
||||
return array_map(
|
||||
fn (string $key): string => rtrim((string) config("domains.domains.{$key}.url"), '/').'/de/newsroom/'.$slug,
|
||||
$portals,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -69,11 +69,11 @@ class MergeCompanies extends Command
|
|||
$stats['meldungen'] += $result->pressReleases;
|
||||
$stats['both'] += $result->targetPortal->value === 'both' && $result->survivor->portal->value !== 'both' ? 1 : 0;
|
||||
|
||||
if ($dryRun) {
|
||||
if (count($plans) < 30) {
|
||||
$plans[] = $result;
|
||||
}
|
||||
if (count($plans) < 30) {
|
||||
$plans[] = $result;
|
||||
}
|
||||
|
||||
if ($dryRun) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -107,7 +107,9 @@ class MergeCompanies extends Command
|
|||
));
|
||||
|
||||
if (! $dryRun) {
|
||||
$this->line(sprintf(' Batch %s — Rückabwicklung: companies:unmerge --batch=%s', $batch, $batch));
|
||||
$this->line(sprintf(' Batch %s', $batch));
|
||||
$this->line(sprintf(' Prüfen: companies:merges --batch=%s', $batch));
|
||||
$this->line(sprintf(' Rückabwicklung: companies:unmerge --batch=%s', $batch));
|
||||
}
|
||||
|
||||
foreach ($stats['uebersprungen'] as $reason => $count) {
|
||||
|
|
|
|||
|
|
@ -797,6 +797,13 @@ Unique-Index (portal, slug), 0 ms. Und wegfallende Firmen werden per
|
|||
Query-Builder soft-deleted, nicht per `$company->delete()`: Der Observer
|
||||
würde je Firma den Admin-Cache leeren; der Lauf leert einmal am Ende.
|
||||
|
||||
**Prüfen:** `companies:merges --batch=<ID>` listet je Vorgang den
|
||||
Überlebenden mit seinen Newsroom-URLs auf beiden Portalen, die aufgegangenen
|
||||
Firmen und deren alte URLs, die per 301 weiterführen. Nachgerüstet nach dem
|
||||
ersten Live-Lauf mit `--limit=50` — der scharfe Lauf zeigte nur die Batch-ID,
|
||||
und die Frage „welche 50 sind das?" war berechtigt. Der scharfe Lauf zeigt
|
||||
seither auch die ersten 30 Vorgänge als Tabelle.
|
||||
|
||||
**Deploy:** `migrate --force`, dann `companies:merge --dry-run`, dann
|
||||
`companies:merge --limit=50` und die 50 im Admin ansehen, dann ohne Limit.
|
||||
Erledigte Adressen zählen beim zweiten Lauf nicht gegen das Limit. Danach
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ use App\Models\User;
|
|||
use App\Services\Company\CompanyMergePlan;
|
||||
use App\Services\Company\CompanyMergeService;
|
||||
use App\Services\Company\CompanyMergeSkip;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Tests\TestCase;
|
||||
|
||||
|
|
@ -323,3 +324,24 @@ test('the same slug on both portals needs no redirect', function () {
|
|||
$this->get('https://presseecho.test/de/newsroom/muster')->assertOk();
|
||||
$this->get('https://businessportal24.test/de/newsroom/muster')->assertOk();
|
||||
});
|
||||
|
||||
test('the merge list shows survivor, merged companies and the URLs to check', function () {
|
||||
/** @var TestCase $this */
|
||||
mergeGroup('info@muster.de');
|
||||
mergeMember('info@muster.de', Portal::Businessportal24, 2, ['slug' => 'muster-gmbh']);
|
||||
mergeMember('info@muster.de', Portal::Presseecho, 1, ['slug' => 'muster']);
|
||||
|
||||
$this->artisan('companies:merge')->assertSuccessful();
|
||||
$batch = CompanyMerge::query()->value('batch');
|
||||
|
||||
Artisan::call('companies:merges', ['--batch' => $batch, '--json' => true]);
|
||||
$listed = json_decode(Artisan::output(), true)[0];
|
||||
|
||||
expect($listed['newsroom'])->toBe(['https://presseecho.test/de/newsroom/muster-gmbh', 'https://businessportal24.test/de/newsroom/muster-gmbh'])
|
||||
->and($listed['aufgegangen'][0]['alte_url'])->toBe(['https://presseecho.test/de/newsroom/muster'])
|
||||
->and($listed['meldungen'])->toBe(1);
|
||||
|
||||
$this->artisan('companies:merges', ['--email' => 'INFO@muster.de'])
|
||||
->expectsOutputToContain('muster-gmbh')
|
||||
->assertSuccessful();
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue