presseportale/app/Models/LegalRequest.php
Kevin Adametz b4b2e013d6 recht: Meldeverfahren v2 Phasen 1-4 — Re-Sync-Sperre, Anonymisierung, Magic-Link-Intake, Self-Service-Korrektur
Phase 1: sync_locked_at auf press_releases (Importer überspringt gesperrte
PMs auch bei --force), LegalRequest um Verifikation/Ausführung erweitert,
verschlüsselte Vorher-Snapshots mit Löschfrist (snapshots:purge, täglich).

Phase 2: AnonymizationService (Unicode-Wortgrenzen-Schwärzung, Pflicht-Diff,
transaktionale Ausführung mit Snapshot, Kontakt-Detach, Bild-Löschung,
Sitemap-Invalidierung, Verlauf ohne Klartext-Altwerte); Admin-Queue kann
DSGVO-Anfragen anonymisieren oder depublizieren, inkl. Snapshot-Restore.

Phase 3: Deflektions-Trichter „PM melden/ändern" (melden/pm) mit Kosten-
Tabelle vorab; Magic-Link geht nur an die hinterlegte Pressekontaktadresse
(neuer purpose press_release_report), atomare Verifikation, Anliegen-Auswahl
erzeugt verifizierte Queue-Anfragen; toter-Kanal-Fallback; Owner-Einstieg.

Phase 4: Panel-Abschnitt „Änderung & Korrektur" — Levenshtein-Gate (5 %,
Mini-Edit-Ausnahme ≤ 2 Zeichen) stuft klein/groß ein; kleine Korrektur
kostenlos sofort live (Snapshot, Sperre, Verlauf, Re-Score), größere als
Change-Anfrage (Interim bis 10-€-Checkout, Phase 7).

Entwicklungsplan mit Entscheidungen E1-E8 und offenen Punkten in
docs/Decision-Update; 41 Pest-Tests (tests/Feature/Legal) grün.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 15:56:34 +00:00

120 lines
3.3 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Models;
use App\Enums\LegalRequestStatus;
use App\Enums\LegalRequestType;
use App\Enums\LegalRequestVerificationMethod;
use Database\Factories\LegalRequestFactory;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
/**
* Rechts-/Compliance-Anfrage (WS-3) – DSGVO, Persönlichkeitsrecht oder Meldung.
* Bewusst OHNE PortalScope: der Admin-Bereich „Recht & Compliance" sieht alle
* Portale; das Portal wird zur Anzeige/Filterung als eigene Spalte gehalten.
*/
class LegalRequest extends Model
{
/** @use HasFactory<LegalRequestFactory> */
use HasFactory;
protected $fillable = [
'press_release_id',
'portal',
'type',
'status',
'requester_email',
'message',
'payload',
'requester_ip',
'verified_at',
'verification_method',
'single_purchase_id',
'resolved_by_user_id',
'admin_note',
'resolved_at',
'executed_at',
];
protected function casts(): array
{
return [
'type' => LegalRequestType::class,
'status' => LegalRequestStatus::class,
'payload' => 'array',
'verified_at' => 'datetime',
'verification_method' => LegalRequestVerificationMethod::class,
'resolved_at' => 'datetime',
'executed_at' => 'datetime',
];
}
/**
* Öffentliche Vorgangsnummer (T10) — analog zur Kündigung ("K-{id}").
* Wird in der Eingangsbestätigung genannt und bei Rückfragen referenziert.
*/
public function reference(): string
{
return 'M-'.$this->id;
}
/**
* Name des Meldenden (Standalone-Formulare, DSA Art. 16) — strukturierte
* Zusatzfelder liegen bewusst im payload (WS-3).
*/
public function requesterName(): ?string
{
return $this->payload['requester_name'] ?? null;
}
/**
* Gemeldete URL, wie vom Meldenden angegeben (Standalone-Formulare).
*/
public function reportedUrl(): ?string
{
return $this->payload['reported_url'] ?? null;
}
public function pressRelease(): BelongsTo
{
return $this->belongsTo(PressRelease::class);
}
public function resolver(): BelongsTo
{
return $this->belongsTo(User::class, 'resolved_by_user_id');
}
/**
* Bezahlvorgang der kostenpflichtigen Spuren (Depublizierung/Änderung).
*/
public function singlePurchase(): BelongsTo
{
return $this->belongsTo(SinglePurchase::class);
}
/**
* Verschlüsselte Vorher-Snapshots ausgeführter Mutationen (Korrektur/
* Anonymisierung) — Grundlage der Rückgängig-Option bis zur Löschfrist.
*/
public function redactionSnapshots(): HasMany
{
return $this->hasMany(PressReleaseRedactionSnapshot::class);
}
/**
* @param Builder<LegalRequest> $query
* @return Builder<LegalRequest>
*/
public function scopeOpen(Builder $query): Builder
{
return $query->whereIn('status', [
LegalRequestStatus::Open->value,
LegalRequestStatus::InProgress->value,
]);
}
}