Merge branch 'main' of https://git.kocoder.xyz/Diplomarbeit-Absolventenverein/pm
This commit is contained in:
155
Diplomarbeitsbuch-individueller-teil-Adam-Gaiswinkler.md
Normal file
155
Diplomarbeitsbuch-individueller-teil-Adam-Gaiswinkler.md
Normal file
@@ -0,0 +1,155 @@
|
||||
# Diplomarbeitsbuch
|
||||
---
|
||||
## 1. Einleitung
|
||||
### Ausgangssituation
|
||||
### Motivation
|
||||
### Kurzbeschreibung des Projekts
|
||||
### Persönlicher Aufgabenbereich
|
||||
### Abgrenzung der Arbeit
|
||||
Die Datenbankinfrastruktur war zum Projektstart bereits vorhanden und wurde nicht im Rahmen dieser Arbeit konzipiert. Die Diplomarbeit beschränkt sich auf die Entwicklung der Anwendungsschicht, bestehend aus CMS-Konfiguration, Modul- und Theme-Entwicklung.
|
||||
### Individuelle Themenstellung
|
||||
---
|
||||
## 3. Projektumfeld & Rahmenbedingungen
|
||||
### Einsatz eines bestehenden CMS (Oqtane)
|
||||
### Teamarbeit
|
||||
Das Projektteam wurde im Verlauf des Projekts von sechs auf drei Personen reduziert.
|
||||
### Schulisches Umfeld (HTL)
|
||||
### Zeitliche Rahmenbedingungen
|
||||
---
|
||||
## 4. Technologischer Überblick
|
||||
### 4.1 Web-Entwicklung mit ASP.NET & C#
|
||||
#### Backend-Logik
|
||||
#### API-basierte Kommunikation
|
||||
#### Zusammenspiel mit Oqtane
|
||||
### 4.2 Oqtane – Überblick
|
||||
#### Grundidee & Architektur
|
||||
#### Modul- & Theme-Konzept
|
||||
#### Vorteile für das Projekt
|
||||
### 4.3 CMS-Grundkonfiguration
|
||||
#### Initiale Einrichtung
|
||||
#### Modul-Integration
|
||||
#### Rollen & Berechtigungen
|
||||
#### Grundlegende Systemeinstellungen
|
||||
---
|
||||
## 5. System- und Lösungsarchitektur
|
||||
### Gesamtarchitektur des Systems
|
||||
### Einordnung von CMS, Modulen und Theme
|
||||
### Datenfluss
|
||||
### Architekturentscheidungen
|
||||
---
|
||||
## 6. Entwicklung des Oqtane Themes
|
||||
### Ziel des Themes
|
||||
### Technische Umsetzung
|
||||
### Herausforderungen
|
||||
---
|
||||
## 7. Umsetzung der Module
|
||||
### 7.1 Anmeldetool
|
||||
#### Ziel des Moduls
|
||||
#### Frontend (Eingabemaske)
|
||||
#### Backend-Logik
|
||||
#### API-Schnittstelle
|
||||
#### Datenauswertung
|
||||
#### UX-Überlegungen
|
||||
---
|
||||
### 7.2 Hall of Fame
|
||||
Das Hall-of-Fame-Modul ist ein Oqtane-Modul, das ehemalige Absolventinnen und Absolventen der Schule auf der Vereinswebseite präsentiert. Es wurde als eigenständiges, wiederverwendbares Modul innerhalb des Oqtane-Frameworks entwickelt und ermöglicht registrierten Benutzerinnen und Benutzern, sich selbst mit einem persönlichen Profil einzutragen. Die Einträge werden erst nach bewusster Veröffentlichung durch die jeweilige Person auf der öffentlichen Seite angezeigt, können als PDF exportiert werden und unterliegen einem Meldesystem zur Qualitätssicherung.
|
||||
#### Datenmodell
|
||||
Das Modul verwendet zwei Entitäten, die in der Datenbank als Tabellen abgebildet werden.
|
||||
**Entität HallOfFame**
|
||||
Die zentrale Entität repräsentiert einen einzelnen Absolventeneintrag und wird in der Datenbanktabelle `SZUAbsolventenvereinHallOfFame` gespeichert.
|
||||
| Spalte | Datentyp | Beschreibung |
|
||||
|--------|----------|--------------|
|
||||
| `HallOfFameId` | `int` (PK, Auto-Inkrement) | Primärschlüssel |
|
||||
| `ModuleId` | `int` (FK → `Module`) | Fremdschlüssel zur Oqtane-Modulinstanz |
|
||||
| `Name` | `string` | Name der Person |
|
||||
| `Year` | `int` | Abschlussjahrgang |
|
||||
| `Description` | `string` | Beschreibung bzw. Werdegang |
|
||||
| `Image` | `string` | Relativer Pfad zum hochgeladenen Foto |
|
||||
| `Link` | `string` | Optionaler externer Link |
|
||||
| `Status` | `string` (max. 50) | Veröffentlichungsstatus: „Draft" oder „Published" |
|
||||
| `UserId` | `int` | ID der Benutzerin bzw. des Benutzers, der den Eintrag erstellt hat |
|
||||
| `IsReported` | `bool` | Kennzeichnung, ob der Eintrag gemeldet wurde |
|
||||
| `ReportReason` | `string` | (Legacy) Ursprüngliches Feld für Meldegrund, abgelöst durch die Report-Tabelle |
|
||||
| `CreatedBy` | `string` | Erstellt von (Audit) |
|
||||
| `CreatedOn` | `DateTime` | Erstellzeitpunkt (Audit) |
|
||||
| `ModifiedBy` | `string` | Zuletzt geändert von (Audit) |
|
||||
| `ModifiedOn` | `DateTime` | Zeitpunkt der letzten Änderung (Audit) |
|
||||
Die Entität implementiert das Oqtane-Interface `IAuditable`, wodurch die Audit-Felder automatisch vom Framework befüllt werden. Der Fremdschlüssel `ModuleId` verknüpft jeden Eintrag mit einer bestimmten Modulinstanz und ermöglicht so den Multi-Tenant-Betrieb: Mehrere Hall-of-Fame-Module auf verschiedenen Seiten der Website verwalten jeweils unabhängige Datensätze.
|
||||
**Entität HallOfFameReport**
|
||||
Die zweite Entität bildet einzelne Meldungen zu einem Eintrag ab und wird in der Tabelle `SZUAbsolventenvereinHallOfFameReport` gespeichert.
|
||||
| Spalte | Datentyp | Beschreibung |
|
||||
|--------|----------|--------------|
|
||||
| `HallOfFameReportId` | `int` (PK, Auto-Inkrement) | Primärschlüssel |
|
||||
| `HallOfFameId` | `int` (FK → `SZUAbsolventenvereinHallOfFame`) | Zugehöriger Eintrag |
|
||||
| `Reason` | `string` | Meldegrund |
|
||||
| `CreatedBy` | `string` | Erstellt von (Audit) |
|
||||
| `CreatedOn` | `DateTime` | Erstellzeitpunkt (Audit) |
|
||||
| `ModifiedBy` | `string` | Zuletzt geändert von (Audit) |
|
||||
| `ModifiedOn` | `DateTime` | Zeitpunkt der letzten Änderung (Audit) |
|
||||
Der Fremdschlüssel zu `SZUAbsolventenvereinHallOfFame` ist mit kaskadierendem Löschen konfiguriert, sodass beim Löschen eines Eintrags automatisch alle zugehörigen Meldungen entfernt werden. Zwischen den beiden Entitäten besteht somit eine 1:n-Beziehung: Ein Eintrag kann beliebig viele Meldungen besitzen.
|
||||
#### Datenbankmigrationen
|
||||
Die Datenbankstruktur wird über Entity Framework Core Migrationen versioniert verwaltet. Oqtane verwendet ein eigenes Migrationssystem, das auf der Klasse `MultiDatabaseMigration` basiert und die Kompatibilität mit verschiedenen Datenbankanbietern sicherstellt.
|
||||
| Migration | Versionsnummer | Inhalt |
|
||||
|-----------|---------------|--------|
|
||||
| `InitializeModule` | `01.00.00.00` | Erstellt die Haupttabelle mit allen Grundspalten (Name, Year, Description, Image, Link, Status, UserId) sowie den Audit-Spalten |
|
||||
| `AddReportingColumns` | `01.00.00.02` | Erweitert die Haupttabelle um die Spalten `IsReported` und `ReportReason` |
|
||||
| `AddReportTable` | `01.00.00.03` | Erstellt die eigenständige Report-Tabelle mit Fremdschlüssel zur Haupttabelle |
|
||||
Jede Migration definiert sowohl eine Aufwärts- als auch eine Abwärtsmethode, sodass ein Rollback möglich ist.
|
||||
#### Benutzeroberfläche (Razor-Komponenten)
|
||||
Das Modul umfasst vier Blazor-Razor-Komponenten.
|
||||
**Index.razor – Übersichtsseite**
|
||||
Die Übersichtsseite zeigt alle veröffentlichten Einträge in einem responsiven Kartenlayout mit drei Spalten auf Desktop-Breite. Die Funktionalität umfasst eine Echtzeit-Textsuche über Name und Beschreibung, eine umschaltbare Sortierung nach Datum, Name oder Jahrgang sowie eine Kartenanzeige mit Bild, Name, Jahrgang und gekürzter Beschreibung. Administratorinnen und Administratoren sehen zusätzlich Warnmeldungen bei gemeldeten Einträgen und einen Lösch-Button. Angemeldete Benutzerinnen und Benutzer können über einen eigenen Button ihren Eintrag erstellen oder bearbeiten.
|
||||
**Edit.razor – Erstellungs- und Bearbeitungsseite**
|
||||
Die Edit-Komponente dient sowohl zum Erstellen als auch zum Bearbeiten eines Eintrags. Sie bietet Formularfelder für Name, Jahrgang, Beschreibung (mit Live-Zeichenzähler), Foto-Upload und Link. Über zwei Speicheroptionen kann zwischen Entwurf und Veröffentlichung gewählt werden. Eine Eigentümerprüfung verhindert das Bearbeiten fremder Einträge, und eine Duplikatprüfung verhindert das Erstellen mehrerer Einträge pro Person.
|
||||
**Details.razor – Detailseite**
|
||||
Die Details-Komponente zeigt einen einzelnen Eintrag in einem zweispaltigen Layout mit unscharfem Bildhintergrund. Administratorinnen und Administratoren sehen bei gemeldeten Einträgen eine Liste aller Meldungen mit Lösch-Button. Ein modaler Dialog ermöglicht die PDF-Vorschau sowie den Download. Die Meldefunktion wird über die zentrale `IReportUI`-Komponente eingebunden.
|
||||
**Settings.razor – Moduleinstellungen**
|
||||
Die Settings-Komponente bietet eine einfache Oberfläche für Moduleinstellungen über Oqtanes Setting-Service.
|
||||
#### Gemeinsame Melde-Komponente (IReportUI)
|
||||
Die Meldefunktion in der Detailseite ist nicht direkt im Hall-of-Fame-Modul implementiert, sondern wird über eine zentrale Schnittstelle aus dem Interfaces-Paket eingebunden. Das Hall-of-Fame-Modell implementiert das Interface `IReportable`, das eine Entität als meldbar kennzeichnet. In der Detailseite wird per Dependency Injection eine `IReportUI`-Implementierung injiziert – die konkrete `ReportComponent` stammt dabei aus dem Admin-Modul und stellt den Melden-Button samt modalem Dialog bereit. Die Komponente wird über Blazors `DynamicComponent` dynamisch gerendert. Ist keine Implementierung im Container registriert, wird die Meldefunktion schlicht nicht angezeigt. Dieses Konzept ermöglicht es, die Melde-Oberfläche zentral zu pflegen und in beliebig vielen weiteren Modulen wiederzuverwenden, ohne dass die einzelnen Module die Melde-Logik selbst implementieren müssen.
|
||||
#### PDF-Export mit QuestPDF
|
||||
Für die Generierung der PDF-Dokumente wird die Open-Source-Bibliothek QuestPDF in der Community-Edition eingesetzt. Im Server-Projekt wurde ein benutzerdefiniertes MSBuild-Target erstellt, das nach jedem Build automatisch die QuestPDF-DLL sowie die plattformspezifischen nativen Bibliotheken in das bin-Verzeichnis des Oqtane-Servers kopiert. Dies ist notwendig, weil Oqtane Module zur Laufzeit dynamisch lädt und QuestPDF native Abhängigkeiten (unter anderem die SkiaSharp-Rendering-Engine) benötigt.
|
||||
Das PDF-Design folgt einem Glasmorphismus-Ansatz. Jede Seite hat das Format DIN A4 ohne Ränder. Über die Layers-API von QuestPDF wird das Hintergrundbild von der Inhaltsebene getrennt. Der Name wird in 36 Punkt ExtraBold mit Großbuchstaben und Zeichenabstand dargestellt, der Jahrgang in 15 Punkt mit erhöhtem Zeichenabstand und die Beschreibung in 11 Punkt mit 1,5-fachem Zeilenabstand. Der Glaseffekt wird durch halbtransparente dunkle Hintergründe mit mehrschichtigen Rahmen unterschiedlicher Transparenz erzeugt.
|
||||
#### Implementierungsdetails und Problemlösungen
|
||||
Während der Entwicklung traten mehrere technische Herausforderungen auf, die im Folgenden zusammen mit ihren Lösungen beschrieben werden.
|
||||
**Bild-Upload-System**
|
||||
In der ursprünglichen Version des Moduls mussten Benutzerinnen und Benutzer eine Bild-URL manuell in ein Textfeld eingeben. Das implementierte Bild-Upload-System ersetzt dies durch eine Blazor-InputFile-Komponente mit Live-Vorschau, Fortschrittsanzeige und Lösch-Button. Die Upload-Methode prüft die Dateigröße (maximal 5 MB), öffnet die Datei als Stream und übermittelt sie als MultipartFormDataContent an den Server, der den Dateityp serverseitig validiert, einen UUID-Dateinamen generiert und die Datei speichert. Das System unterstützt beide Blazor-Rendering-Modi.
|
||||
**Concurrency Exception beim Löschen**
|
||||
Beim Löschen von Einträgen mit vorhandenen Meldungen trat eine `DbUpdateConcurrencyException` auf, verursacht durch Konflikte im Entity Framework Change Tracker. Die Lösung bestand in der Aufteilung der Löschoperation in zwei separate Transaktionen mit jeweils eigenem DbContext: zuerst werden alle zugehörigen Meldungen gelöscht, danach der Haupteintrag.
|
||||
**Kartendesign-Optimierung**
|
||||
Karten hatten ursprünglich unterschiedliche Höhen durch variierende Beschreibungslängen. Die Lösung kombiniert CSS-Flexbox auf dem Kartenelement mit einer Maximalhöhe von 150 Pixeln und `overflow: hidden` auf dem Beschreibungscontainer, sodass alle Karten einer Zeile dieselbe Höhe aufweisen.
|
||||
**Sortier-Toggle**
|
||||
Die ursprünglich fest codierten Sortierrichtungen wurden durch einen Toggle-Button neben dem Sortier-Dropdown ersetzt, der mit einem dynamischen Pfeil-Icon zwischen aufsteigender und absteigender Sortierung umschaltet. Die Sortierlogik ist in einer berechneten Eigenschaft gekapselt, die Suche und Sortierung kombiniert.
|
||||
---
|
||||
## 8. Projektorganisation & Teamarbeit
|
||||
### 8.1 Planung & Meilensteine
|
||||
#### Meilensteine
|
||||
#### Soll-/Ist-Vergleich
|
||||
#### Zeitverzug
|
||||
### 8.2 Teamverkleinerung
|
||||
#### Downsizing von 6 auf 3 Personen
|
||||
#### Neue Aufgabenverteilung
|
||||
#### Auswirkungen auf Theme-Entwicklung, Module und Zeitplanung
|
||||
---
|
||||
## 9. Übergangslösung, Probleme & Learnings
|
||||
### 9.1 Übergangslösung (Sommer 2025)
|
||||
#### Gründe & Technische Umsetzung
|
||||
#### Differenzen zur finalen Lösung
|
||||
### 9.2 Probleme
|
||||
#### Technische Probleme
|
||||
#### Organisatorische Probleme
|
||||
### 9.3 Learnings
|
||||
#### Technisch
|
||||
#### Methodisch
|
||||
#### Persönlich
|
||||
---
|
||||
## 10. Testen & Qualitätssicherung
|
||||
### Funktionstests der Module
|
||||
### Theme-Tests
|
||||
### Usability-Tests
|
||||
### Bekannte Einschränkungen
|
||||
---
|
||||
## 11. Fazit & Ausblick
|
||||
### Zielerreichung & Zusammenfassung
|
||||
### Persönliche Reflexion
|
||||
### Erweiterungsmöglichkeiten
|
||||
Reference in New Issue
Block a user