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>
This commit is contained in:
@@ -28,6 +28,11 @@ public class EventApplier(LiteDbContext db, byte[] syncKey, HttpClient? http = n
|
||||
EventQueue? versions = null)
|
||||
{
|
||||
private static readonly Dictionary<string, EntityHandler> Handlers = BuildHandlers();
|
||||
private static readonly Dictionary<string, Func<string, IHasAttachments?>> AttachmentDeserializers = new()
|
||||
{
|
||||
[nameof(Documentation)] = json => JsonSerializer.Deserialize<Documentation>(json),
|
||||
[nameof(Lesson)] = json => JsonSerializer.Deserialize<Lesson>(json),
|
||||
};
|
||||
|
||||
public async Task ApplyAsync(SyncEvent evt)
|
||||
{
|
||||
@@ -41,8 +46,9 @@ public class EventApplier(LiteDbContext db, byte[] syncKey, HttpClient? http = n
|
||||
{
|
||||
var json = evt.Payload.Length == 0 ? "" : Decrypt(evt.Payload);
|
||||
handler(db, evt.Operation, evt.EntityId, json);
|
||||
if (evt.EntityType == nameof(Documentation) && evt.Operation != "Delete" && http is not null)
|
||||
await DownloadMissingAttachmentsAsync(json);
|
||||
if (evt.Operation != "Delete" && http is not null &&
|
||||
AttachmentDeserializers.TryGetValue(evt.EntityType, out var deserialize))
|
||||
await DownloadMissingAttachmentsAsync(deserialize(json));
|
||||
// evt.SequenceNr trägt bei einem vom Server empfangenen Ereignis immer dessen
|
||||
// ServerSeq (siehe EventStore.Pull) — Grundlage für BasedOnServerSeq beim nächsten
|
||||
// eigenen Push dieser Entität (optimistische Nebenläufigkeitskontrolle, TODO 10.3.4).
|
||||
@@ -72,13 +78,12 @@ public class EventApplier(LiteDbContext db, byte[] syncKey, HttpClient? http = n
|
||||
}
|
||||
|
||||
// Anhang-Bytes reisen nicht im JSON-Ereignis mit (siehe SyncEventPublisher) - nach dem
|
||||
// Anwenden der Documentation-Metadaten fehlende, lokal noch nicht vorhandene Anhänge einzeln
|
||||
// nachladen. Gegenstück zum Upload in AttachmentSyncer.
|
||||
private async Task DownloadMissingAttachmentsAsync(string json)
|
||||
// Anwenden der Metadaten fehlende, lokal noch nicht vorhandene Anhänge einzeln nachladen.
|
||||
// Gegenstück zum Upload in AttachmentSyncer.
|
||||
private async Task DownloadMissingAttachmentsAsync(IHasAttachments? entity)
|
||||
{
|
||||
var doc = JsonSerializer.Deserialize<Documentation>(json);
|
||||
if (doc is null) return;
|
||||
foreach (var attachment in doc.Attachments)
|
||||
if (entity is null) return;
|
||||
foreach (var attachment in entity.Attachments)
|
||||
{
|
||||
if (db.Attachments.Exists(attachment.StorageId)) continue;
|
||||
var resp = await http!.GetAsync($"/api/sync/attachments/{attachment.StorageId}");
|
||||
|
||||
Reference in New Issue
Block a user