Files
LehrerApp/LehrerApp.Sync/SyncEventPublisher.cs
adminandClaude Sonnet 5 038337997f feat: Anhänge je Stunde, Klausur-Sitzplan mischen, Gefährdungsbeurteilungs-Assistent
Lesson bekommt dieselbe Anhang-Infrastruktur wie Documentation (Material,
Arbeitsblätter, Experimentunterlagen), samt Fix einer Sync-Lücke, die Anhang-
Dateibytes bisher nur für Documentation statt generisch übertragen hat
(IHasAttachments). Sitzplan-Tab bekommt einen "Plätze mischen"-Button für
Klausursitzpläne. Neu: mehrschrittiger Gefährdungsbeurteilungs-Assistent mit
optionalem KI-Entwurf (ai-backend/gbu.php) und PDF-Export, Format bewusst als
JSON-Anhang statt eigener Datenbank-Entität. Details und Architekturentscheidungen
in TODO.md (4.2, 7.1.5, 10.1.8).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-20 17:13:14 +02:00

33 lines
1.7 KiB
C#

using LehrerApp.Core.Models;
using LehrerApp.Core.Services;
using LehrerApp.Data;
using LehrerApp.Sync.Crypto;
using LehrerApp.Sync.Models;
namespace LehrerApp.Sync;
/// <summary>
/// Wandelt <see cref="LiteDbContext.OnChange"/>-Aufrufe in ausgehende Sync-Ereignisse um. Wird in
/// AppBootstrapper an <see cref="LiteDbContext.OnChange"/> gehängt, wenn Sync konfiguriert ist —
/// dort entsteht aus den ~27 Repository-Aufrufen genau ein verschlüsseltes Ereignis pro Aufruf.
/// </summary>
public class SyncEventPublisher(EventQueue queue, string deviceId, byte[] syncKey, AppLogger? logger = null)
{
public void Publish(string entityType, string entityId, string operation, object? payload)
{
var encrypted = payload is null ? "" : SyncCrypto.EncryptObject(payload, syncKey);
var evt = queue.Enqueue(deviceId, DeviceType.Desktop, entityType, entityId, operation, encrypted);
// Erfolgs-Protokollierung (nicht nur Fehler, siehe SyncEngine/EventApplier) — sonst lässt
// sich ohne zwei parallele Testgeräte nie nachvollziehen, ob eine lokale Änderung
// überhaupt in die Outbox gelangt ist, bevor Push/Pull überhaupt ins Spiel kommen.
logger?.Info($"Sync: Ereignis eingereiht (SequenceNr={evt.SequenceNr}) - {entityType} {operation} " +
$"EntityId={entityId}");
// Anhänge reisen nicht im JSON-Ereignis mit (würde den Kanal für Fotos/Scans aufblähen),
// sondern als eigener Binärtransfer über AttachmentSyncer — hier nur zur Warteliste hinzufügen.
if (payload is IHasAttachments withAttachments)
foreach (var attachment in withAttachments.Attachments)
queue.QueueAttachmentUpload(attachment.StorageId);
}
}