564 lines
12 KiB
Markdown
564 lines
12 KiB
Markdown
# Info-Board Neu - Datenmodell
|
|
|
|
## Ziel
|
|
|
|
Das Datenmodell trennt klar zwischen:
|
|
|
|
- Mandanten und Benutzern
|
|
- Bildschirmen und ihrer technischen Laufzeitinformation
|
|
- Medien und Playlists
|
|
- Steuerbefehlen und Rueckmeldungen
|
|
|
|
Das Modell ist so ausgelegt, dass:
|
|
|
|
- jede Firma nur ihren eigenen Monitor bzw. Kanal pflegt
|
|
- die Administration alle Monitore zentral sehen und steuern kann
|
|
- Offline-Betrieb der Player moeglich bleibt
|
|
- Zeitsteuerung mit `valid_from` und `valid_until` sauber abbildbar ist
|
|
|
|
## Kernentitaeten
|
|
|
|
### `tenant`
|
|
|
|
Repraesentiert eine Firma bzw. einen abgeschotteten Inhaltsbereich.
|
|
|
|
Felder:
|
|
|
|
- `id`
|
|
- `slug`
|
|
- `name`
|
|
- `active`
|
|
- `created_at`
|
|
- `updated_at`
|
|
|
|
Hinweise:
|
|
|
|
- ein Tenant kann spaeter mehrere Screens haben, auch wenn zunaechst meist 1:1 gearbeitet wird
|
|
- alle Medien und Playlists sind einem Tenant zugeordnet
|
|
|
|
### `user`
|
|
|
|
Repraesentiert einen Benutzer der Firmen- oder Admin-Oberflaeche.
|
|
|
|
Felder:
|
|
|
|
- `id`
|
|
- `tenant_id` nullable fuer globale Admins
|
|
- `username`
|
|
- `email`
|
|
- `password_hash`
|
|
- `role` (`admin`, `tenant_user`)
|
|
- `active`
|
|
- `last_login_at`
|
|
- `created_at`
|
|
- `updated_at`
|
|
|
|
Regeln:
|
|
|
|
- `tenant_user` darf nur Daten des eigenen Tenants sehen und bearbeiten
|
|
- `admin` darf alle Tenants und alle Screens verwalten
|
|
|
|
### `screen`
|
|
|
|
Repraesentiert einen physischen Monitor bzw. einen Player-Client.
|
|
|
|
Felder:
|
|
|
|
- `id`
|
|
- `tenant_id`
|
|
- `slug`
|
|
- `name`
|
|
- `description`
|
|
- `location`
|
|
- `hardware_name`
|
|
- `enabled`
|
|
- `rotation` (`0`, `90`, `180`, `270`)
|
|
- `resolution_width`
|
|
- `resolution_height`
|
|
- `fallback_dir`
|
|
- `snapshot_interval_seconds`
|
|
- `offline_overlay_enabled`
|
|
- `created_at`
|
|
- `updated_at`
|
|
|
|
Hinweise:
|
|
|
|
- der `slug` dient als technische Kennung fuer Konfiguration, API und MQTT
|
|
- `fallback_dir` beschreibt die lokale oder synchronisierte Fallback-Quelle auf dem Client
|
|
|
|
### `screen_registration`
|
|
|
|
Optionale Trennung zwischen fachlichem Screen und technischer Registrierung.
|
|
|
|
Felder:
|
|
|
|
- `id`
|
|
- `screen_id`
|
|
- `device_uuid`
|
|
- `hostname`
|
|
- `api_token_hash`
|
|
- `mqtt_client_id`
|
|
- `last_seen_at`
|
|
- `last_ip`
|
|
- `player_version`
|
|
- `os_version`
|
|
- `created_at`
|
|
- `updated_at`
|
|
|
|
Zweck:
|
|
|
|
- Wiedererkennung eines konkreten Geraets
|
|
- sichere Kommunikation mit API und Broker
|
|
- technische Inventarisierung
|
|
|
|
### `provisioning_job`
|
|
|
|
Repraesentiert einen Provisionierungslauf fuer einen neuen oder neu aufzubauenden Screen.
|
|
|
|
Felder:
|
|
|
|
- `id`
|
|
- `screen_id`
|
|
- `requested_by_user_id`
|
|
- `target_ip`
|
|
- `target_port`
|
|
- `remote_user`
|
|
- `auth_mode` (`password`, `key`)
|
|
- `provided_secret_ref`
|
|
- `ssh_key_fingerprint` nullable
|
|
- `status` (`queued`, `running`, `succeeded`, `failed`, `cancelled`)
|
|
- `stage` (`connect`, `bootstrap`, `key_install`, `package_install`, `deploy`, `verify`, `done`)
|
|
- `log_excerpt` nullable
|
|
- `error_message` nullable
|
|
- `started_at` nullable
|
|
- `finished_at` nullable
|
|
- `created_at`
|
|
- `updated_at`
|
|
|
|
Regeln:
|
|
|
|
- das eigentliche Passwort wird nicht im Klartext in der Datenbank gespeichert
|
|
- `provided_secret_ref` verweist auf einen kurzlebigen oder extern geschuetzten Secret-Eintrag
|
|
- pro Job soll klar erkennbar sein, in welchem Schritt ein Fehler aufgetreten ist
|
|
|
|
### `screen_group`
|
|
|
|
Optionale logische Gruppierung von Screens.
|
|
|
|
Felder:
|
|
|
|
- `id`
|
|
- `slug`
|
|
- `name`
|
|
- `description`
|
|
- `created_at`
|
|
- `updated_at`
|
|
|
|
### `screen_group_member`
|
|
|
|
Zuordnung eines Screens zu einer Gruppe.
|
|
|
|
Felder:
|
|
|
|
- `id`
|
|
- `screen_group_id`
|
|
- `screen_id`
|
|
- `created_at`
|
|
|
|
Zweck:
|
|
|
|
- praktische Zielauswahl fuer Kampagnen und Provisionierungsaktionen
|
|
|
|
### `media_asset`
|
|
|
|
Repraesentiert ein verwaltetes Medium.
|
|
|
|
Felder:
|
|
|
|
- `id`
|
|
- `tenant_id`
|
|
- `screen_id` nullable
|
|
- `title`
|
|
- `description`
|
|
- `type` (`image`, `video`, `pdf`, `web`)
|
|
- `source_kind` (`upload`, `remote_url`)
|
|
- `storage_path` nullable bei reinen Web-URLs
|
|
- `original_url` nullable
|
|
- `mime_type`
|
|
- `checksum`
|
|
- `size_bytes`
|
|
- `enabled`
|
|
- `created_by_user_id`
|
|
- `created_at`
|
|
- `updated_at`
|
|
|
|
Regeln:
|
|
|
|
- `upload` bedeutet: Datei liegt im Medien-Storage
|
|
- `remote_url` bedeutet: Quelle ist extern und wird vom Player oder Server gecacht
|
|
- `screen_id` kann optional gesetzt werden, wenn ein Medium nur fuer einen Monitor gedacht ist
|
|
|
|
### `playlist`
|
|
|
|
Repraesentiert eine logische Playlist eines Screens.
|
|
|
|
Felder:
|
|
|
|
- `id`
|
|
- `tenant_id`
|
|
- `screen_id`
|
|
- `name`
|
|
- `is_active`
|
|
- `default_duration_seconds`
|
|
- `fallback_enabled`
|
|
- `fallback_dir`
|
|
- `shuffle_enabled`
|
|
- `created_at`
|
|
- `updated_at`
|
|
|
|
Regeln:
|
|
|
|
- pro Screen gibt es in v1 genau eine aktive Playlist
|
|
- spaeter koennen mehrere Playlists mit Umschaltung moeglich werden
|
|
|
|
### `display_template`
|
|
|
|
Repraesentiert ein globales Admin-Template zur monitoruebergreifenden Orchestrierung.
|
|
|
|
Beispiele:
|
|
|
|
- Schriftzug ueber mehrere Monitore
|
|
- saisonales Motiv auf allen Monitoren
|
|
- Veranstaltungslayout fuer einen befristeten Zeitraum
|
|
|
|
Felder:
|
|
|
|
- `id`
|
|
- `slug`
|
|
- `name`
|
|
- `description`
|
|
- `template_type` (`message_wall`, `full_screen_media`, `screen_specific_scene`)
|
|
- `enabled`
|
|
- `created_by_user_id`
|
|
- `created_at`
|
|
- `updated_at`
|
|
|
|
### `template_scene`
|
|
|
|
Repraesentiert die konkrete Auspraegung eines Templates fuer einen oder mehrere Screens.
|
|
|
|
Felder:
|
|
|
|
- `id`
|
|
- `display_template_id`
|
|
- `screen_id` nullable
|
|
- `screen_slot` nullable
|
|
- `type` (`image`, `video`, `pdf`, `web`, `html`)
|
|
- `src`
|
|
- `duration_seconds`
|
|
- `load_timeout_seconds`
|
|
- `cache_policy`
|
|
- `on_error`
|
|
- `layout_json`
|
|
- `created_at`
|
|
- `updated_at`
|
|
|
|
Hinweise:
|
|
|
|
- `screen_id` erlaubt die direkte Zuweisung zu einem konkreten Monitor
|
|
- `screen_slot` erlaubt spaeter abstrakte Wand-Slots wie `row1-col2`
|
|
- `layout_json` kann z. B. Textpositionen, Farben oder Teilbereiche des Gesamtmotivs beschreiben
|
|
|
|
### `campaign`
|
|
|
|
Repraesentiert eine aktivierbare globale Kampagne auf Basis eines Templates.
|
|
|
|
Felder:
|
|
|
|
- `id`
|
|
- `display_template_id`
|
|
- `name`
|
|
- `description`
|
|
- `priority`
|
|
- `active`
|
|
- `valid_from` nullable
|
|
- `valid_until` nullable
|
|
- `override_mode` (`replace_tenant_content`, `replace_all`, `merge_reserved_slots`)
|
|
- `created_by_user_id`
|
|
- `created_at`
|
|
- `updated_at`
|
|
|
|
Regeln:
|
|
|
|
- Kampagnen ueberschreiben in v1 standardmaessig den Firmencontent
|
|
- nach Ende oder Deaktivierung greift automatisch wieder der tenantbezogene Normalbetrieb
|
|
|
|
### `template_assignment`
|
|
|
|
Repraesentiert die Zuordnung einer Kampagne oder eines Templates zu einem oder mehreren Screens.
|
|
|
|
Felder:
|
|
|
|
- `id`
|
|
- `campaign_id`
|
|
- `screen_id`
|
|
- `enabled`
|
|
- `created_at`
|
|
- `updated_at`
|
|
|
|
Zweck:
|
|
|
|
- Auswahl einzelner Monitore, Gruppen oder aller Monitore
|
|
|
|
### `playlist_item`
|
|
|
|
Repraesentiert einen einzelnen Eintrag in einer Playlist.
|
|
|
|
Felder:
|
|
|
|
- `id`
|
|
- `playlist_id`
|
|
- `screen_id`
|
|
- `order_index`
|
|
- `media_asset_id` nullable
|
|
- `type` (`image`, `video`, `pdf`, `web`, `dir`)
|
|
- `src`
|
|
- `title`
|
|
- `duration_seconds`
|
|
- `load_timeout_seconds`
|
|
- `cache_policy` (`required`, `prefer_cache`, `no_cache`)
|
|
- `on_error` (`skip`, `retry`, `fallback`)
|
|
- `retry_count`
|
|
- `valid_from` nullable
|
|
- `valid_until` nullable
|
|
- `enabled`
|
|
- `created_at`
|
|
- `updated_at`
|
|
|
|
Regeln:
|
|
|
|
- `media_asset_id` wird bei verwalteten Medien gesetzt
|
|
- `src` enthaelt den effektiven Pfad oder die URL, damit der Player auch ohne Join-Struktur arbeiten kann
|
|
- `type=dir` erlaubt definierte Verzeichnisreferenzen innerhalb der Playlist
|
|
|
|
### `playlist_item_dir_rule`
|
|
|
|
Optionale Zusatzregel fuer `type=dir`.
|
|
|
|
Felder:
|
|
|
|
- `id`
|
|
- `playlist_item_id`
|
|
- `directory_path`
|
|
- `sort_mode` (`name_asc`, `name_desc`, `mtime_asc`, `mtime_desc`, `shuffle`)
|
|
- `per_item_duration_seconds`
|
|
- `recursive`
|
|
- `file_filter` nullable
|
|
|
|
Zweck:
|
|
|
|
- sauberere Modellierung fuer Verzeichnisbasierte Fallbacks oder Einschuebe
|
|
|
|
### `screen_status`
|
|
|
|
Repraesentiert den letzten bekannten Laufzeitstatus eines Players.
|
|
|
|
Felder:
|
|
|
|
- `screen_id`
|
|
- `online`
|
|
- `server_connected`
|
|
- `mqtt_connected`
|
|
- `last_heartbeat_at`
|
|
- `last_sync_at`
|
|
- `current_playlist_id`
|
|
- `current_playlist_item_id` nullable
|
|
- `current_content_source` (`campaign`, `tenant_playlist`, `fallback`)
|
|
- `current_campaign_id` nullable
|
|
- `current_item_type` nullable
|
|
- `current_item_label` nullable
|
|
- `current_item_started_at` nullable
|
|
- `current_item_duration_seconds` nullable
|
|
- `cache_state` (`ok`, `stale`, `missing`, `error`)
|
|
- `overlay_state` (`online`, `degraded`, `offline`)
|
|
- `error_code` nullable
|
|
- `error_message` nullable
|
|
- `player_version`
|
|
- `uptime_seconds`
|
|
- `free_disk_bytes` nullable
|
|
- `temperature_celsius` nullable
|
|
- `updated_at`
|
|
|
|
Hinweise:
|
|
|
|
- `screen_status` ist eine verdichtete Sicht fuer Dashboard und Ueberwachung
|
|
- historische Verlaeufe koennen spaeter separat gespeichert werden
|
|
|
|
### `screen_snapshot`
|
|
|
|
Repraesentiert eine Vorschauaufnahme des aktuellen Bildschirminhalts.
|
|
|
|
Felder:
|
|
|
|
- `id`
|
|
- `screen_id`
|
|
- `captured_at`
|
|
- `storage_path`
|
|
- `width`
|
|
- `height`
|
|
- `mime_type`
|
|
- `source` (`scheduled`, `item_change`, `manual`)
|
|
|
|
Regeln:
|
|
|
|
- in der UI wird typischerweise nur der letzte Snapshot angezeigt
|
|
- aeltere Snapshots koennen nach Zeit oder Anzahl aufgeraeumt werden
|
|
|
|
### `device_command`
|
|
|
|
Repraesentiert einen an einen Screen gesendeten Befehl.
|
|
|
|
Felder:
|
|
|
|
- `id`
|
|
- `screen_id`
|
|
- `command_type` (`reload`, `restart_player`, `reboot`, `display_on`, `display_off`, `refresh_snapshot`, `clear_cache`)
|
|
- `payload_json`
|
|
- `requested_by_user_id`
|
|
- `requested_at`
|
|
- `delivery_state` (`queued`, `sent`, `acknowledged`, `failed`, `expired`)
|
|
- `delivered_at` nullable
|
|
- `acknowledged_at` nullable
|
|
- `result_code` nullable
|
|
- `result_message` nullable
|
|
|
|
Zweck:
|
|
|
|
- nachvollziehbare Fernsteuerung mit Audit-Trail
|
|
|
|
### `sync_state`
|
|
|
|
Repraesentiert den Synchronisationsstand eines Screens.
|
|
|
|
Felder:
|
|
|
|
- `screen_id`
|
|
- `config_revision`
|
|
- `playlist_revision`
|
|
- `media_revision`
|
|
- `last_successful_sync_at`
|
|
- `last_failed_sync_at` nullable
|
|
- `last_error_message` nullable
|
|
|
|
Zweck:
|
|
|
|
- Erkennung, ob ein Player auf dem aktuellen Stand ist
|
|
|
|
## Beziehungen
|
|
|
|
- ein `tenant` hat viele `users`
|
|
- ein `tenant` hat viele `screens`
|
|
- ein `tenant` hat viele `media_assets`
|
|
- ein `screen` hat genau eine aktive `playlist` in v1
|
|
- eine `playlist` hat viele `playlist_items`
|
|
- ein `display_template` hat viele `template_scenes`
|
|
- ein `display_template` hat viele `campaigns`
|
|
- eine `campaign` hat viele `template_assignments`
|
|
- ein `screen` hat viele `provisioning_jobs`
|
|
- eine `screen_group` hat viele `screen_group_members`
|
|
- ein `screen` hat genau einen aktuellen `screen_status`
|
|
- ein `screen` hat viele `screen_snapshots`
|
|
- ein `screen` hat viele `device_commands`
|
|
- ein `screen` hat genau einen aktuellen `sync_state`
|
|
|
|
## Zugriffsregeln
|
|
|
|
### Tenant-User
|
|
|
|
Ein `tenant_user` darf:
|
|
|
|
- nur den eigenen `tenant` sehen
|
|
- nur den eigenen `screen` bzw. die dem Tenant zugeordneten Screens sehen
|
|
- nur eigene Medien hochladen und pflegen
|
|
- nur eigene Playlists bearbeiten
|
|
- nur die Vorschau des eigenen Screens sehen
|
|
|
|
Ein `tenant_user` darf nicht:
|
|
|
|
- andere Tenants sehen
|
|
- globale Steuerkommandos senden
|
|
- andere Screens administrieren
|
|
|
|
### Admin
|
|
|
|
Ein `admin` darf:
|
|
|
|
- alle Tenants, Screens, Medien und Playlists sehen
|
|
- globale Steuerbefehle senden
|
|
- Vorschau und Status aller Screens sehen
|
|
- neue Screens und Benutzer anlegen
|
|
|
|
## Revisions- und Caching-Modell
|
|
|
|
Damit Offline-Betrieb einfach bleibt, arbeitet der Player nicht gegen beliebige Einzelobjekte, sondern gegen Revisionen.
|
|
|
|
Sinnvoll sind mindestens:
|
|
|
|
- `playlist_revision`
|
|
- `media_revision`
|
|
- `config_revision`
|
|
- `campaign_revision`
|
|
|
|
Der Player kann damit erkennen:
|
|
|
|
- ob neue Konfiguration vorliegt
|
|
- ob Medien nachgeladen werden muessen
|
|
- ob die lokale Kopie noch gueltig ist
|
|
- ob sich globale Uebersteuerungen geaendert haben
|
|
|
|
## Prioritaetsmodell der Inhaltsauswahl
|
|
|
|
Der Player wertet Inhalte in dieser Reihenfolge aus:
|
|
|
|
1. aktive Kampagne mit gueltigem Zeitfenster und Assignment fuer den Screen
|
|
2. aktive tenantbezogene Playlist des Screens
|
|
3. Fallback-Verzeichnis
|
|
|
|
Eine Kampagne ist aktiv, wenn:
|
|
|
|
- `active = true`
|
|
- `valid_from` leer oder erreicht ist
|
|
- `valid_until` leer oder noch nicht abgelaufen ist
|
|
- eine `template_assignment` fuer den Screen aktiv ist
|
|
|
|
Dadurch ist die globale Uebersteuerung ein Kernbestandteil und kein nachtraeglicher Sonderfall.
|
|
|
|
## Beispiel fuer aktive Playlist-Auswertung
|
|
|
|
Ein `playlist_item` ist aktiv, wenn:
|
|
|
|
- `enabled = true`
|
|
- `valid_from` leer oder in der Vergangenheit liegt
|
|
- `valid_until` leer oder in der Zukunft liegt
|
|
|
|
Wenn kein aktives Item existiert:
|
|
|
|
- greift die globale Fallback-Regel des Screens oder der Playlist
|
|
|
|
## Beispiel fuer Admin-Steuerung
|
|
|
|
Wenn ein Admin `reload` ausloest:
|
|
|
|
1. Ein `device_command` wird gespeichert
|
|
2. der Befehl wird per MQTT an den Screen signalisiert
|
|
3. der Player fuehrt den Reload aus
|
|
4. der Player sendet ein ACK
|
|
5. `device_command.delivery_state` wird aktualisiert
|
|
|
|
## Offene spaetere Erweiterungen
|
|
|
|
- mehrere Screens pro Tenant in der Firmenoberflaeche
|
|
- Vorlagen und globale Playlists
|
|
- Historie der Statusdaten als Zeitreihe
|
|
- Geplante Kampagnen mit Prioritaeten
|
|
- serverseitige Konvertierung oder Transkodierung von Medien
|