Zum Inhalt

Globales Event-Notify-System

Das Event-Notify-System ist die zentrale Infrastruktur fuer Echtzeit-Benachrichtigungen in der xynap Platform. Es verbindet Backend-Events mit dem Frontend und unterstuetzt mehrere Kanaele (In-App, E-Mail, Telegram, Browser-Push, Sprachausgabe).

Architektur

┌──────────────┐     emit_event()     ┌──────────────┐
│   Backend    │ ──────────────────→   │  Event Bus   │
│  (Service)   │                       │  (Pub/Sub)   │
└──────────────┘                       └──────┬───────┘
                         ┌────────────────────┼─────────────────────┐
                         ▼                    ▼                     ▼
                   ┌───────────┐      ┌──────────────┐    ┌──────────────┐
                   │ Workflow   │      │  WebSocket   │    │  Dispatcher  │
                   │  Engine    │      │  Broadcast   │    │ (E-Mail/TG)  │
                   └───────────┘      └──────┬───────┘    └──────────────┘
                                      ┌──────┴───────┐
                                      │   Frontend   │
                                      │ EventStream  │
                                      └──────┬───────┘
                        ┌────────────────────┼────────────────────┐
                        ▼                    ▼                    ▼
                  ┌───────────┐     ┌──────────────┐    ┌──────────────┐
                  │ Bell +    │     │ Call Notifier │    │   Browser    │
                  │ Badge +   │     │ (Modal +     │    │ Notification │
                  │ Dropdown  │     │  Sprache)    │    │   (Desktop)  │
                  └───────────┘     └──────────────┘    └──────────────┘

Kanaele

Kanal Beschreibung Trigger
app In-App Notification (DB + UI) create_notification()
email E-Mail via SMTP Dispatcher
telegram Telegram Bot-API Dispatcher
browser Desktop-Notification (Web Push) Frontend bei notification.created
speech Sprachausgabe (Web Speech API) Frontend bei call.incoming

Event-Typen

System-Events

Event Payload Beschreibung
notification.created uuid, title, body, category, severity, ... Neue In-App Notification
call.incoming caller_id, did_number, customer, ... Eingehender Anruf (SIP)
mail.new_message folder, message Neue E-Mail
mail.folder_update folder Mail-Ordner geaendert
support.session.created session_code, channel Neue Support-Anfrage
file.uploaded path, name Datei hochgeladen

Notification-Kategorien

Kategorie Beispiele
system Systemstatus, Updates, Wartung
payment Zahlungseingang, Zahlungserinnerung
dunning Mahneskalation
crm Kunden-Events, Aufgaben
security Login, 2FA, verdaechtige Aktivitaeten
mail Neue E-Mails (optional)

Severity-Level

Level Farbe Verwendung
info Blau Allgemeine Information
success Gruen Erfolgreiche Aktion
warning Gelb Warnung, Aufmerksamkeit noetig
error Rot Fehler, sofortiges Handeln

Backend: Notification erstellen

from app.core.notifications.service import create_notification

# Einfache Notification
notif = await create_notification(
    db, user_id=42,
    title="Neuer Kunde angelegt",
    body="Kunde 'Mustermann GmbH' wurde erfolgreich erstellt.",
    category="crm",
    severity="success",
    icon="building",
    entity_type="customer",
    entity_id="uuid-...",
    action_url="/crm/customers/uuid-...",
)
# → Speichert in DB
# → Emittiert notification.created via WebSocket an User 42
# → Frontend zeigt sofort Badge + Animation + Sound

Tenant-Admins benachrichtigen

from app.core.notifications.service import notify_tenant_admins

await notify_tenant_admins(
    db, tenant_id=1,
    title="Rechnung ueberfaellig",
    body="Rechnung INV-2026-001 ist seit 14 Tagen ueberfaellig.",
    category="dunning",
    severity="warning",
)

Eigenes Event emittieren

from app.core.events import emit_event

await emit_event("order.completed", {
    "user_id": user.id,
    "order_id": order.id,
    "total": 299.00,
}, source="shop", db=db)
# → Wird via Wildcard-Handler an WebSocket weitergeleitet
# → Workflow-Engine kann darauf reagieren

Frontend: Events empfangen

Notification Center (Composable)

import { useNotificationCenter } from '@/shell/composables/useNotificationCenter'

const {
  notifications,     // ref<AppNotification[]>
  unreadCount,       // ref<number>
  hasUnread,         // computed<boolean>
  bellAnimating,     // ref<boolean> — true waehrend Shake-Animation
  incomingCalls,     // ref<IncomingCall[]>
  showCallNotifier,  // ref<boolean>
  markRead,          // (notif) => Promise
  markAllRead,       // () => Promise
  deleteNotification,// (notif) => Promise
  dismissCall,       // (callUuid) => void
} = useNotificationCenter()

Eigene Event-Handler registrieren

import { useEventStream } from '@/shell/composables/useEventStream'

const events = useEventStream()

// Exakter Match
events.on('order.completed', (event) => {
  console.log('Bestellung abgeschlossen:', event.data)
})

// Wildcard (alle order.* Events)
events.on('order.*', (event) => {
  console.log('Order-Event:', event.type, event.data)
})

REST API

Notifications abrufen

GET /api/v1/notifications?limit=20&unread_only=true&category=payment

Zusammenfassung

GET /api/v1/notifications/summary
→ { "total": 42, "unread": 3 }

Als gelesen markieren

PUT /api/v1/notifications/{uuid}/read
POST /api/v1/notifications/read-all

Loeschen

DELETE /api/v1/notifications/{uuid}

Call Notifier

Bei eingehenden Anrufen (call.incoming Event):

  1. Modal erscheint mit Treffer-Liste (aehnlich Spotlight-Suche)
  2. Klingelton (425 Hz, deutsches Freizeichen-Muster)
  3. Sprachausgabe via Web Speech API: "Eingehender Anruf von Kunde Mustermann GmbH, Nummer K-10042"
  4. Kunden-Link: Direkter Sprung zum Kunden-Detail
  5. Auto-Dismiss: Nach 30 Sekunden verschwindet der Klingelton

Caller-Lookup

Das Backend fuehrt automatisch einen Kunden-Lookup anhand der CallerID durch:

  • Vergleicht letzte 10 Ziffern mit gespeicherten Telefonnummern
  • Bei Treffer: Kundenname, Kundennummer, Locale im Event-Payload
  • Frontend zeigt Treffer-Badge (gruen: "Kunde") oder (grau: "Unbekannt")

Datenmodell

notifications (Tabelle)

Feld Typ Beschreibung
id BigInt Primary Key
uuid String(36) Unique Identifier
user_id BigInt FK → users
tenant_id BigInt Optional
type String(50) Event-Typ
channel String(20) app/email/telegram
category String(30) system/payment/dunning/...
severity String(20) info/warning/error/success
icon String(50) Lucide Icon-Name
title String(255) Titel
body Text Nachricht
data JSON Zusaetzliche Daten
entity_type String(30) z.B. "invoice", "customer"
entity_id String(36) UUID der verknuepften Entitaet
action_url String(500) Navigation-Ziel
read_at DateTime Gelesen-Zeitpunkt
created_at DateTime Erstellungszeitpunkt

notification_preferences (Tabelle)

Feld Typ Beschreibung
user_id BigInt FK → users
event_type String(100) z.B. "payment.*"
channel String(20) Kanal
enabled Boolean Aktiviert