Warning
|
Hinweis zu unserer GitHub-Nutzung: Wir haben oft zu zweit programmiert. Da dann immer nur eine Person in Github vermerkt ist, haben wir uns geeinigt, die Personen (GitHub-Name), die an einem Commit/Merge beteiligt waren, in die Commit- bzw. Merge- Message zu schreiben. Wir haben festgelegt, dass für jede neue Funktionalität ein neuer Branch ausgecheckt wird, um eine saubere Historie und auf dem master nach Möglichkeit nur laufende Entwicklungsstufen zu haben. Dadurch konnten wir Fehler auf dem master sehr spezifisch Änderungen zuordnen. |
Teilen statt Kaufen! Die Anwendung, ist eine Kampfansage an die Wegwerfgesellschaft. Eine Platform für das private Ausleihen und Verkaufen von Gegenständen:
Im Folgenden wird die Struktur unseres Projektes erläutert.
Das Dockerfile ist ein einfaches Skript, um ein Docker-Image zu erstellen. Dabei wird unsere SharingIsCaring-Anwendung gebaut und die Datenbank gestartet. Wir haben zusätzlich eine Funktion (wait-for-it.sh) eingebaut. Diese ist dafür verantwortlich sicherzustellen, dass zuerst die Datenbank gestartet wird und dann erst die Anwendung darauf zugreifen kann, um Fehlern vorzugreifen.
Mit Docker Compose können innerhalb einer einzigen Datei mehrere Container erstellt werden, die in Beziehung zueinander stehen. In der docker-compose.yml werden die Container und ihre Beziehungen definiert. Das Docker-Image (bei uns nginx) wird definiert und die Ports (bei uns 8080) zugewiesen.
Die docker-compose.yml haben wir für usere SharingIsCaring Application erweitert. Dabei initialisieren wir die DB (Password, name, user) und stellen Angaben zur Application. Außerdem wird hier das ProPay Image gebaut.
Wir haben die build.gradle aus unserem Projekt 4 übernommen und erweitert.
Es mussten einige dependencies erweitert werden, wie spring-security. Die wichtigsten dependencies beziehen sich hier auf spring-boot-starter-webflux aber auch auf unsere Datenbank postgresql und auf unsere Testimplementation. Dabei ist zu beachten, dass wir für unsere Tests eine eigene Datenbank (h2) verwenden. Dazu unter dem Punkt Test mehr.
Für unsere Anwendung setzen wir hier die spring.datasource unserer Datenbank, sowie die applications für hibernate und springframework.web Außerdem haben wir das error-whitelable ausgeschaltet um auf eine eigene error page zu verweisen. Auf dieser sollte eigentlich ein Bild von Jens (von einer der Vorlesungsfolien) auftauchen, dies klappt jedoch nicht unter docker-compose. Startet man die Anwendung lokal über IntelliJ wird das Bild angezeigt. Aus Zeitgründen konnten wir diesen Fehler nicht mehr fixen. Unsere Tests haben eine eigene application.properties, in der der Consolen-Output für unsere TestDatenbank h2 aktiviert wird. Für unsere Entwicklung haben wir eine application.properties.dev verwendet, die unseren localhost anspricht.
Für den Login und die Registrierung unserer Webanwendung benutzen wir Spring Security. Spring Security stellt den Rahmen unserer Anwendung. Hier definieren wir, auf welche Resourcen der Nutzer eingeloggt zugreifen kann. Außerdem definieren wir für den Nutzer eine Role, um zwischen User und Admin zu unterscheiden. Der nichteingeloggte Nutzer kann nur auf wenige Resourcen zugreifen. Da das Login erst die Anwendung ermöglicht haben wir auf die base url "/" eine Startseite gelegt, von der aus man sich bei Spring Security einloggen kann oder aber neu registrieren kann. Alle Resourcen nach dem Login sind nur für eingeloggte Nutzer erreichbar. Außerdem haben wir hier einen statischen Admin angelegt, der beim ersten Aufrufen der Resource angelegt wird.
Hat der Nutzer sich erfolgreich eingeloggt, gelangt er auf seine Homepage. Auf dieser erhält er Informationen zur Anwendung und bekommt persönliche Meldungen, wie zum Beispiel die Nachricht, dass der Nutzer morgen ein Produkt zurückgeben muss. Den Header, die SideNav und den Footer haben wir in ein base.html ausgelagert, da sie auf jeder Ressource verfügbar sein soll. Unsere Navigation führt den Nutzer zu den Hauptanwendungen unseres Portals.
Auf seinem Profile kann der Nutzer seine persönlichen Daten einsehen und bearbeiten. Außerdem sieht er, wie viel Geld auf seinem ProPayAccount liegt und kann dieses aufladen. Er sieht eine Übersicht der aktuell blockierten Kautionen und kann über einen Button zu einer Übersicht aller Transactionen gelangen.
Möchte der Nutzer ein Product erstellen, gelangt er über die Navigation auf add Product. Hier kann er sich entscheiden, ob er ein Produkt zum Verleih oder zum Kauf anbieten möchte und die entsprechenden Angaben zum Produkt eintragen. Anschließend kann er ein entsprechendes Bild hochladen. Standardmäßig wird hier ein Dummybild ausgewählt.
Über View Products gelangt der Nutzer zu unserer Suchfunktion. Standardmäßig werden ihm hier alle Produkte angezeigt. Er kann spezifisch nach einem Produkt oder Eigenschaften eines Produkts suchen oder aber einen unserer Filter verwenden. Dabei kann er sich seine aktuell ausgeliehenen Produkte, seine angebotenen Produkte, alle Produkte die zum Kauf verfügbar sind oder alle Produkte, die zum Leihen verfügbar sind, auswählen.
Über check Availability kann man die Verfügbarkeit eines Produkts prüfen.
Über read more gelangt man zu einer Detailübersicht des Produkts. Als Besitzer hat man hier die Möglichkeit, das Produkt zu bearbeiten oder zu löschen, man kann das Produkt allerdings nur löschen, wenn es nicht verliehen ist.
Neben den Änderungen am Produkt selbst (Name, Adresse, …) ist es durch die Weiterleitung auf eine weitere Seite ebenfalls möglich, das angezeigte Produktbild zu ändern/ zu löschen, wobei letzteres die Ersetzung durch das Dummybild bewirkt. Als Interessent hat man hier die Möglichkeit, das Produkt auszuleihen bzw zu kaufen.
Möchte ein Nutzer ein Produkt leihen, kann er dem Eigentümer eine Nachricht hinterlassen und den gewünschten Ausleihzeitraum angeben. Hier wird geprüft, ob der Leihende genug Guthaben auf seinem ProPayAccount hat und, ob der Zeitraum für das Produkt verfügbar ist.
Die Dokumentation des Ausleihprozesses findet man unter requests. Hier bekommt der Eigentümer die Anfrage und kann diese, ebenfalls mit einer Nachricht akzeptieren oder ablehnen.
Der Leihende kann den Prozess ebenfalls einsehen. Wird seine Anfrage akzeptiert, hat er bis zum geforderten Zeitraum Zeit, die Anfrage zurückzuziehen. Danach hat er die Möglichkeit, das Produkt zurückzugeben. In diesem Fall wird der Tagessatz für das Produkt abgebucht. Der Eigentümer hat nun die Möglichkeit, die Rückgabe zu akzeptieren oder eben abzulehnen. In diesem Fall wird ein Konflikt angemeldet. Hier kommt der Admin ins Spiel. Dieser hat einen Bereich Conflicts, in dem ihm konflikthafte Prozesse angezeigt werden. Der Admin muss nun entscheiden ob er den Konflikt bestätigt oder ablehnt. Wird der Konflikt bestätigt, wird die Kaution automatisch überwiesen.
Ist ein Prozess abgeschlossen, kann er gelöscht werden.
Kaufe ich ein Produkt, wird der Preis von meinem ProPayAccount abgebucht. Meine gekauften und verkauften Produkte finde ich ebenfalls unter requests.
Ein gekauftes Produkt wird nicht mehr in der Produktübersicht angezeigt. Als Eigentümer sehe ich es allerdings immer noch unter meinen angebotenen Produkten mit availability=false. Der Eigentümer hat die Möglichkeit, das verkaufte Produkt zu löschen.
Hat man Fragen zur Benutzung der Anwendung, findet man in den FAQs die oben beschriebenen Prozesse detailiert erläutert vor.
Im Impressum findet der User alle Angaben zum Unternehmen ThreeWeeksASlave AG, sowie die Angaben zu verwendeten Bildern, Statement on privacy und Liability Notes.
Zusammen mit den FAQs und dem Impressum befindet sich unsere Datenschutzerklärung am Ende der Seite. Wir haben diese generieren lassen über https://datenschutz-generator.de/.
Unser System ist eine Springboot Anwendung. Wir haben mit IntelliJ und der Standard-Code-Formattierung gearbeitet.
Wir arbeiten mit Controllern, die HTML-Templates ansprechen. Für unsere Anwendung haben wir acht verschiedene Controller geschrieben:
AuthenticationController:
Im AuthenticationController bearbeiten wir Anfragen zur Startseite unsere Anwendung, Registrierung oder home und prüfen, ob ein User existiert.
ProfileController:
Der ProfileController beantwortet Anfragen, die aus dem Profil heraus gestellt werden, wie das Updaten der userDaten. Auch die Anfrage an die FAQs resource wird hier verarbeitet.
ProductController:
Der ProductController beantwortet die Anfragen für die Suche nach Produkten, das Erstellen eines Produkts und das Bearbeiten eines Produktes.
OrderProcessController:
Der OrderProcessController startet einen orderProcess.
RequestController:
Im RequestController behandeln wir den Großteil unseres Verleihprozesses.
ConflictController:
Im ConflictController behandeln wir die konfliktbehafteten OrderProcesses.
ProPayController:
Im ProPayController bearbeiten wir die Anflage zum Aufladen des Guthabens und die Transactionübersicht.
FileUploadController:
Der FileUploadController ist zuständig für die Produktbilder.
Unsere Datenbankklasse Customer speichert unsere Nutzer und Admins. Für die Produkte haben wir eine Datenbankklasse Product. Die Verleih- und Kaufprozesse speichern wir in der Datenbankklasse OrderProcess. Alle Transaktionen, die während dieser Prozesse passieren, werden in der Transaction Datenbankklasse gespeichert. Für die Benachrichtigungen an den Nutzer haben wir eine Datenbankklasse Notification.
Alle zusätzlichen Datenklassen oder enums liegen im package model.
In unseren Handlern liegt der große Teil der Logik unseres Programms.
Unser NotificationHandler führt alle 20 Sekunden eine Datensynchronisation durch. Dabei durchlaufen wir alle orderProcesses und filtern die Prozesse heraus, die heute oder morgen enden oder aber in der Vergangenheit hätten enden sollen. Für diese Prozesse bekommt der Nutzer eine Meldung auf seiner Home-page.
Im OrderProcessHandler behandeln wir den kompletten Ausleihprozess. Je nach Status des Prozesses werden hier Anfragen an ProPay gestellt, wie das Blocken oder Überweisen von Kautionen.
Der SearchProductHandler verarbeitet die Filter unserer Produktsuche und filtert die entsprechenden Produkte heraus, die dann auf der Website angezeigt werden können.
Der UserHandler verarbeitet Anfragen an ProPay, die der Nutzer stellt ohne Beteiligung anderer, wie das Aufladen seines Guthabens. Außerdem wird hier bei jedem Aufrufen des Profils der ProPayAccount synchronisiert.
ProPay ist unser Zahlungsprogramm. Über Anfragen mit Spring WebClient greifen wir auf ProPay zu. Es kann jedoch passieren, dass ProPay nicht erreichbar ist. Um diesen Fall abzufangen führen wir jede ProPayAnfrage in einem try-catch aus. Zusätzlich haben wir einen timeout und ein retry zu den Anfragen an ProPay hinzugefügt. Erreicht unsere Anwendung ProPay nicht in unserem angegebenen Zeitraum, wird die Anfrage nocheinmal ausgeführt. Schlägt die Anfrage an ProPay fehl, werden die Änderungen zurückgesetzt und der Nutzer bekommt folgende Meldung: Sorry, connection to your ProPayAccount failed. Please try it again later.
Erreichen wir bei der Registrierung ProPay nicht, wird ein default Account angelegt. Bei der nächsten Anfrage an ProPay, die erfolgreich ist, wird ein richtiger ProPayAccount angelegt.
Wir testen in unseren Testklassen unsere Repositories, die Controller, die Handler und unsere Security.
Für die Tests nutzen wir Mockito, um unsere
Test-Umgebungen zu schaffen.
Mit Integrationtests prüfen wir unsere Controller. Dabei werden Anfragen simuliert (beim Anlegen und Ändern einer Person) und die Weiterleitung geprüft. Außerdem prüfen wir, ob wir unsere Templates erreichen.
In den Repository Tests prüfen wir unsere Datenbank. Dabei stellen wir sicher, dass die Repository Methoden für unsere Entities, wie FindById, richtig funktionieren.
Außerdem prüfen wir unsere Logik im Handler und in unserer Security.
Für unsere Tests benutzen wir die relationale Datenbank h2, daher haben wir für die Tests eine separate application.properties geschrieben.
Um auch Produkte einstellen zu können, die verkauft werden können, mussten wir nur wenig an der Anwendung
ändern.
Um die zu verleihenden Produkte von den zu verkaufenden Produkten unterscheiden zu können, haben
wir dem Produkt ein zusätzliches Attribut gegeben.
Wir mussten zum Einen die Templates abändern: mit if-Abfragen prüfen wir an den kritischen Stellen, ob ein Produkt zu Leihen
oder zu Verkaufen ist. Da sich die Produkte abgesehen von z.B. dem Preis und der Kaution bzw. dem Tagessatz nicht groß unterscheiden,
wird nur an wenigen Stellen auf eine if-Abfrage zurückgegriffen. Wir haben uns aus diesem Grund dagegen
entschieden, für jedes verschiedene Produkt ein eigenes Template zu machen bzw. mit Fragmenten zu arbeiten.
In diesem Fall ist die Anwendung unserer Meinung nach verständlicher, wenn man die kurzen Unterscheidungen
in einem Template lässt. Nur an wenigen Stellen (z.B. neues Produkt anlegen) haben wir uns entschieden,
zwei verschiedene Templates zu verwenden, weil dort die Unterschiede zu groß waren.
Auch die Controller-Methoden mussten so angepasst werden, dass z.B. bei einer Anfrage zum Kaufen einmalig Geld abgebucht wird und einige Schritte des Verleihens nicht
ausgeführt werden.
Um mehr Möglichleiten bei der Produktsuche zu haben, mussten der Suche weitere Filter hinzugefügt werden.