29-05-2026 Optimierungen Fixes am Code

This commit is contained in:
Kevin Adametz 2026-05-29 12:42:05 +00:00
parent e8c47b7553
commit 4bb9094207
31 changed files with 5141 additions and 76 deletions

View file

@ -0,0 +1,305 @@
# User-Admin: Zusammenhänge und relevante Daten
Stand: 2026-05-21 (aktualisiert nach Phase 7)
Diese Notiz beschreibt den User als fachlichen Mittelpunkt für die weitere Konzeption des Admin-User-Bereichs. Grundlage sind die aktuellen Models und Migrationen im Laravel-Projekt.
> **Was sich seit dem ursprünglichen Stand (2026-05-05) geändert hat**:
>
> - Pressemitteilungen haben zusätzliche Felder: `subtitle`, `scheduled_at`,
> `embargo_at`, `boilerplate_override`, `no_export` (Phase 7).
> - Neue Tabelle `press_release_attachments` (Modell `PressReleaseAttachment`).
> UI ist temporär deaktiviert (Security-Review), Tabelle und Service
> bleiben aber erhalten.
> - Neuer Service-Layer für PMs: `PressReleaseService` (Status-Übergänge,
> Scheduling-Auflösung), `PressReleaseHtmlSanitizer` (HTMLPurifier-Wrapper),
> `PressReleaseAttachmentStorage`.
> - Neuer Console-Command `php artisan press-releases:publish-scheduled`
> (Scheduler-Intervall 5 Min) veröffentlicht geplante PMs automatisch.
> - Pivot `press_release_contact` bleibt n:m, der Customer-Flow speichert
> aber nur einen Kontakt pro PM und das Feld ist nullable.
## Zentraler Ausgangspunkt
Ein `User` ist nicht nur ein Login-Konto, sondern bündelt im System mehrere fachliche Bereiche:
- Zugang und Status: Name, E-Mail, Verifikation, Passwort, 2FA, Aktiv/Inaktiv, Super-Admin-Flag, Rollen/Berechtigungen.
- Portal- und Legacy-Zuordnung: `portal`, `registration_type`, `language`, `legacy_portal`, `legacy_id`.
- CRM-Zuordnung: Firmen, Firmenrollen, Kontakte.
- Content-Zuordnung: Pressemitteilungen, Bilder, Statusverlauf.
- Abrechnung und Verträge: Rechnungsadresse, Zahlungsoptionen, Zahlungen, Rechnungen, Legacy-Rechnungen.
- API und Sicherheit: Sanctum-Tokens, API-Nutzung, Magic-Links.
- Kommunikation und Arbeitskomfort: Newsletter-Abos, Filter-Presets.
## Datenmodell im Überblick
```mermaid
erDiagram
users ||--o| profiles : "hat"
users ||--o| billing_addresses : "hat"
users ||--o{ companies : "owner_user_id"
users }o--o{ companies : "company_user.role"
users }o--o{ contacts : "contact_user"
companies ||--o{ contacts : "hat"
users ||--o{ press_releases : "autor"
companies ||--o{ press_releases : "zugeordnet"
contacts }o--o{ press_releases : "press_release_contact"
press_releases ||--o{ press_release_images : "hat"
press_releases ||--o{ press_release_status_logs : "hat"
users ||--o{ press_release_status_logs : "changed_by_user_id"
users ||--o{ user_payment_options : "hat"
user_payment_options }o--o{ companies : "user_payment_option_company"
user_payment_options ||--o{ user_payments : "hat"
user_payments ||--o{ invoices : "erzeugt"
users ||--o{ invoices : "hat"
users ||--o{ legacy_invoices : "hat"
users ||--o{ newsletter_subscriptions : "hat"
users ||--o{ magic_links : "hat"
users ||--o{ user_filter_presets : "hat"
users ||--o{ api_usage_logs : "hat"
```
## Direkte Relationen am User
`profile`
- Kardinalität: 1:0..1.
- Tabelle: `profiles`, Foreign Key `user_id` ist eindeutig.
- Inhalt: persönliche Profildaten, Anrede, Titel, Vor-/Nachname, Telefon, Adresse, Land, Geburtsdatum, Backlink, Statistik-/Footer-Code-Flags, Validierungs-/Vertragsdaten, Steuerdaten.
- Relevanz im Admin: Datenqualität, Legacy-Profil, persönliche Stammdaten, Vertrag/Validierung.
`billingAddress`
- Kardinalität: 1:0..1.
- Tabelle: `billing_addresses`, Foreign Key `user_id` ist eindeutig.
- Inhalt: Rechnungsname, Adresse, PLZ, Ort, Land.
- Relevanz im Admin: Rechnungsfähigkeit, fehlende Abrechnungsdaten, Kundendatenpflege.
`ownedCompanies`
- Kardinalität: 1:n.
- Tabelle: `companies`, Foreign Key `owner_user_id`.
- Bedeutung: Der User ist fachlicher Eigentümer einer Firma, auch ohne Pivot-Eintrag in `company_user`.
- Relevanz im Admin: wichtig für Rechte im Customer-Profil und für klare Verantwortlichkeit.
`companies`
- Kardinalität: n:m.
- Pivot: `company_user` mit `company_id`, `user_id`, `role`.
- Rollen: `member`, `responsible`, `owner`.
- Bedeutung: Ein User kann mehrere Firmen haben; eine Firma kann mehreren Usern zugeordnet sein.
- Relevanz im Admin: Hauptstruktur für Kundenkonto, Berechtigungen in der Firmenpflege und spätere User-Detailansicht.
`contacts`
- Kardinalität: n:m.
- Pivot: `contact_user` mit `contact_id`, `user_id`.
- Zusätzlich gehört jeder Kontakt zwingend zu genau einer Firma über `contacts.company_id`.
- Bedeutung: Kontakte können direkt Usern zugeordnet sein, fachlich sind sie aber an Firmen aufgehängt.
- Relevanz im Admin: Ansprechpartner und Zuordnungsqualität.
`pressReleases`
- Kardinalität: 1:n.
- Tabelle: `press_releases`, Foreign Key `user_id`.
- Zusätzlich: optionale Firmenzuordnung über `company_id` und Pflichtkategorie über `category_id`.
- Wichtig: Datenbankseitig ist `company_id` nullable, fachlich sollte eine Customer-PM einer Firma zugeordnet sein. Der Customer-Flow erzwingt aktuell eine eigene Firma und leitet daraus das Portal ab.
- Relevanz im Admin: Content-Historie, Status, Veröffentlichungen, Qualität, Freigabeprozess.
`newsletterSubscriptions`
- Kardinalität: 1:n.
- Tabelle: `newsletter_subscriptions`, Foreign Key `user_id`.
- Inhalt: Portal, Name, E-Mail, IP, Bestätigung, Subscribe/Unsubscribe, Legacy-Zuordnung.
- Relevanz im Admin: Kommunikationsstatus und Opt-in/Opt-out-Historie.
`billing / payments`
- `userPaymentOptions`: 1:n über `user_payment_options.user_id`.
- `invoices`: 1:n über `invoices.user_id`.
- `legacyInvoices`: 1:n über `legacy_invoices.user_id`.
- Indirekt: `user_payment_options` hat n:m zu Firmen über `user_payment_option_company`; `user_payments` hängt an `user_payment_options`; `invoices` können zusätzlich an `user_payments` und `invoice_billing_addresses` hängen.
- Relevanz im Admin: Vertrags-/Abo-Status, aktive API-Freischaltung, Rechnungen, Legacy-Archiv.
`tokens`
- Kommt über Laravel Sanctum `HasApiTokens`.
- Tabelle: `personal_access_tokens` mit polymorphem `tokenable_type` / `tokenable_id`.
- Im Customer-Bereich werden Tokens für API-Zugriffe verwaltet.
- Relevanz im Admin: API-Zugang, genutzte Berechtigungen, letzter Zugriff.
`magicLinks`
- Kardinalität: 1:n.
- Tabelle: `magic_links`, Foreign Key `user_id`.
- Inhalt: Token-Hash, Zweck, Payload, Ablauf, Verbrauch, IPs.
- Relevanz im Admin: Auth-/Support-Kontext, Magic-Login-Historie.
`filterPresets`
- Kardinalität: 1:n.
- Tabelle: `user_filter_presets`, Foreign Key `user_id`.
- Inhalt: Seite, Name, Default-Flag, letzte Nutzung, Filter-JSON.
- Relevanz im Admin: eher Arbeitskomfort/Personalisierung, nicht Kern-Kundendaten.
`apiUsageLogs`
- Kardinalität: faktisch 1:n über `api_usage_logs.user_id`, aber aktuell keine Relation im `User`-Model.
- Inhalt: Token, Methode, Pfad, Route, Status, IP, User-Agent, Dauer, Zeitpunkt.
- Relevanz im Admin: API-Aktivität, Support und Missbrauchsanalyse.
`roles` und `permissions`
- Kommen über Spatie `HasRoles`.
- Tabellen u. a. `roles`, `permissions`, `model_has_roles`, `model_has_permissions`, `role_has_permissions`.
- Relevanz im Admin: Trennung zwischen Admin, Editor, Customer, Super-Admin und feineren Berechtigungen wie `press-releases:publish` oder `users:manage`.
## Firmen, Kontakte und Pressemitteilungen
Die fachliche CRM-/Content-Struktur läuft im Kern so:
- User hat mehrere Firmen über `company_user`.
- User kann zusätzlich direkter Eigentümer einer Firma über `companies.owner_user_id` sein.
- Firma hat mehrere Kontakte über `contacts.company_id`.
- Firma hat mehrere Pressemitteilungen über `press_releases.company_id`.
- Pressemitteilung hat genau einen Autor/User über `press_releases.user_id`.
- Pressemitteilung kann mehrere Kontakte über `press_release_contact` referenzieren.
- Kontakt kann mehreren Usern direkt zugeordnet sein über `contact_user`, bleibt aber immer an genau eine Firma gebunden.
Daraus ergibt sich für den User-Admin eine wichtige Sicht:
- User-Zentrum: Wer ist der Account?
- Firmen-Zentrum: Welche Firmen gehören dazu, in welcher Rolle?
- Kontakt-Zentrum: Welche Ansprechpartner hängen an diesen Firmen und/oder am User?
- Content-Zentrum: Welche Pressemitteilungen wurden vom User erstellt und welcher Firma sind sie zugeordnet?
- Abrechnungs-Zentrum: Welche Zahloptionen, Rechnungen und Legacy-Rechnungen hängen am User und ggf. an Firmen?
## Pressemitteilungen im Detail
Eine Pressemitteilung (`press_releases`) enthält:
- Identifikation: `id`, `uuid`, `slug`, `legacy_portal`, `legacy_id`.
- Zuordnung: `portal`, `language`, `user_id`, `company_id`, `category_id`.
- Inhalt: `title`, `subtitle`, `text`, `backlink_url`, `keywords`, `boilerplate_override`.
- Status/Publikation: `status`, `published_at`, `scheduled_at`, `embargo_at`, `no_export`, `hits`, `teaser_begin`, `teaser_end`.
- Medien: Bilder über `press_release_images`, Anhänge über `press_release_attachments` (UI deaktiviert).
- Kontakte: Pivot `press_release_contact`.
- Verlauf: Statuslogs über `press_release_status_logs`, inklusive `changed_by_user_id`, Statuswechsel, Grund und Quelle.
Service-Layer rund um PMs:
- `App\Services\PressRelease\PressReleaseService` — kapselt die Übergänge `submitForReview`, `publish`, `reject`, `archive`, `deleteFromAdmin` sowie das Auflösen von `scheduled_at`/`embargo_at` für die echte Veröffentlichung.
- `App\Services\PressRelease\PressReleaseHtmlSanitizer` — Wrapper um `mews/purifier` mit definierter Tag-/Attribut-Whitelist.
- `App\Services\PressRelease\PressReleaseAttachmentStorage` — kapselt Datei-Operationen auf dem `public`-Disk (UI aktuell deaktiviert).
- `App\Console\Commands\PublishScheduledPressReleases` — wird per Scheduler (alle 5 Min) ausgeführt und veröffentlicht freigegebene PMs zum geplanten Zeitpunkt, respektiert Embargo.
Fachliche Konsequenz:
- Für Kunden sollte `company_id` praktisch Pflicht sein, obwohl die DB es nullable erlaubt.
- Für Admins kann `company_id = null` als Migrations-/Altfall auftauchen und sollte im User-Admin sichtbar markiert werden.
- `portal` sollte mit der Firma konsistent sein. Im Customer-Flow wird das Portal aus der Firma abgeleitet; im Admin-Bereich sollte eine bewusste Korrektur möglich sein.
## User-relevante Tabellen
Kernkonto:
- `users`
- `profiles`
- `billing_addresses`
- `sessions`
- `password_reset_tokens`
- `personal_access_tokens`
- `magic_links`
- Spatie Permission-Tabellen
CRM:
- `companies`
- `company_user`
- `contacts`
- `contact_user`
Content:
- `press_releases`
- `press_release_images`
- `press_release_attachments` (UI temporaer deaktiviert)
- `press_release_contact`
- `press_release_status_logs`
- `categories`
- `category_translations`
Billing:
- `billing_addresses`
- `user_payment_options`
- `user_payment_option_company`
- `user_payments`
- `payment_options`
- `payment_option_translations`
- `invoices`
- `invoice_billing_addresses`
- `legacy_invoices`
Kommunikation/API/Arbeitskomfort:
- `newsletter_subscriptions`
- `api_usage_logs`
- `user_filter_presets`
## Relevante Inhalte für den Admin-User-Bereich
Für eine optimierte Admin-User-Ansicht sollten diese Blöcke geplant werden:
- Account: Status, Portal, Sprache, Registrierungstyp, Rollen, Berechtigungen, Super-Admin, letzte Aktivität, letzte IP, E-Mail-Verifikation, 2FA-Status.
- Datenqualität: Profil vorhanden, Rechnungsadresse vorhanden, Firma vorhanden, Kontakte vorhanden, veröffentlichte PMs vorhanden, Legacy-Zuordnung vorhanden.
- Firmen: Firmenliste mit Rolle, Eigentümerstatus, Portal, Aktivstatus, Logo, Footer-Code-Flag, Kontakt-/PM-Anzahl.
- Kontakte: Kontakte je Firma plus direkte User-Kontakte aus `contact_user`.
- Pressemitteilungen: Gesamtzahl, Statusverteilung, letzte PMs, veröffentlichte PMs, PMs ohne Firma, PMs mit Portalabweichung.
- Abrechnung: Rechnungsadresse, aktive Zahlungsoptionen, verknüpfte Firmen zu Zahlungsoptionen, Zahlungen, Rechnungen, Legacy-Rechnungen.
- API: Token-Anzahl, Berechtigungen, letzte Nutzung, API-Usage-Logs.
- Support/Admin-Aktionen: User bearbeiten, Rollen/Berechtigungen ändern, Impersonation, Magic-Link-Kontext, ggf. Account deaktivieren.
## Offene Punkte für die weitere Konzeption
- `User` hat keine direkte `apiUsageLogs()`-Relation, obwohl `api_usage_logs.user_id` existiert. Für Admin-Detailansichten wäre eine Relation sinnvoll.
- `press_releases.company_id` ist nullable. Fachlich sollte geklärt werden, ob das nur Legacy-/Admin-Fälle erlaubt oder langfristig verboten werden soll.
- `contacts` gehören immer zu einer Firma, können aber zusätzlich direkt Usern zugeordnet werden. Für die Oberfläche muss klar sein, ob "User-Kontakte" nur direkte Pivot-Kontakte meint oder alle Kontakte seiner Firmen.
- Eine Firma kann über `owner_user_id` und zusätzlich über `company_user.role = owner` eine Eigentümerinformation haben. Das sollte im Admin eindeutig dargestellt und ggf. harmonisiert werden.
- Billing hängt primär am User, Zahlungsoptionen können aber über Pivot auch Firmen betreffen. Für den Admin sollte klar werden, ob Rechnungen immer User-Rechnungen sind oder später firmenscharf betrachtet werden sollen.
- Portal-Scope ist auf Firmen, Kontakte, Pressemitteilungen und Newsletter aktiv. Admin-Auswertungen nutzen teils `withoutGlobalScopes()`. In der User-Admin-Konzeption muss entschieden werden, wann portalübergreifend gesucht und angezeigt wird.
## Quellen im Code
Modelle:
- `app/Models/User.php`
- `app/Models/Company.php`
- `app/Models/Contact.php`
- `app/Models/PressRelease.php`
- `app/Models/PressReleaseImage.php`
- `app/Models/PressReleaseAttachment.php`
- `app/Models/PressReleaseStatusLog.php`
- `app/Models/Profile.php`
- `app/Models/BillingAddress.php`
- `app/Models/Invoice.php`
- `app/Models/LegacyInvoice.php`
- `app/Models/UserPaymentOption.php`
- `app/Models/UserPayment.php`
- `app/Models/NewsletterSubscription.php`
- `app/Models/MagicLink.php`
- `app/Models/UserFilterPreset.php`
- `app/Models/ApiUsageLog.php`
Services / Commands:
- `app/Services/PressRelease/PressReleaseService.php`
- `app/Services/PressRelease/PressReleaseHtmlSanitizer.php`
- `app/Services/PressRelease/PressReleaseAttachmentStorage.php`
- `app/Services/Image/ImageService.php`
- `app/Console/Commands/PublishScheduledPressReleases.php`
- `routes/console.php` (Scheduler-Eintraege)
Migrationen:
- `database/migrations/*`