Compare commits

..

No commits in common. "main" and "v1.74.3" have entirely different histories.

70 changed files with 2345 additions and 680 deletions

View file

@ -16,5 +16,4 @@ ci:
docs:
- changed-files:
- any-glob-to-any-file:
- "projektdokumentation/**"
- "docs/**"

View file

@ -1,7 +1,6 @@
name: Build docs
on:
pull_request:
push:
branches: [main]
@ -12,18 +11,18 @@ jobs:
image: git.kjan.de/actions/runner-latex:latest
env:
# Edit here with the names of your latex file and directory (can use ".")
DIR: projektdokumentation
FILE: Projektdokumentation.tex
DIR: docs
FILE: projektdokumentation.tex
steps:
- name: Checkout
uses: actions/checkout@v4
- name: LaTeX compile
working-directory: ${{ env.DIR }}
run: latexmk -pdf ${{ env.FILE }}
run: latexmk -pdf -xelatex ${{ env.FILE }}
- name: Upload artifacts
uses: https://git.kjan.de/actions/upload-artifact@v3 # Do not upgrade
with:
name: Doku
path: projektdokumentation/Projektdokumentation.pdf
path: docs/projektdokumentation.pdf

File diff suppressed because it is too large Load diff

View file

@ -13,11 +13,28 @@
% Die Option (in den eckigen Klammern) enthält das längste Label oder
% einen Platzhalter der die Breite der linken Spalte bestimmt.
\begin{acronym}[WWWWW]
\acro{API}{Application Programming Interface}
\acro{CI}{Continuous Integration}
\acro{CI/CD}{Continuous Integration/Continuous Deployment}
\acro{CLI}{Command Line Interface}
\acro{CRM}{Customer Relationship Management}
\acro{CRON}{Vorgangsausführung gemäß geplanten Zeitabläufen für UNIX Programme}
\acro{E2E}{End-to-End}
\acro{API}{Application Programming Interface}
\acro{JSON}{JavaScript Object Notation}
\acro{eCommerce}{Electronic Commerce}
\acro{ERM}{Entity-Relationship-Model}
\acro{GUI}{Graphical User Interface}
\acro{HTTP}{Hypertext Transfer Protocol}
\acro{IDE}{Integrated Development Environment}
\acro{IX}{Intex Fusion Pro Omnichannel CRM}
\acro{JSON}{JavaScript Object Notation}
\acro{M2}{\textsc{Magento 2} eCommerce Platform}
\acro{NSD}{\textsc{neusta software development} GmbH}
\acro{NXP}{\textsc{neusta experience} GmbH}
\acro{PHP}{Hypertext Preprocessor}\acused{PHP}
\acro{SQL}{Structured Query Language}
\acro{URL}{Uniform Resource Locator}\acused{URL}
\acro{VM}{Virtual Machine}
\acro{XML}{Extensible Markup Language}
\acro{API}{Application Programming Interface}
\acro{JWT}{JSON Web Token}
\end{acronym}

View file

@ -1,35 +1,78 @@
% !TEX root = Projektdokumentation.tex
\section{Anhang}
\subsection{Detaillierte Zeitplanung}
\label{app:Zeitplanung}
\subsection{Implementierungsbeispiele}
\label{app:CodeSchichten}
\tabelleAnhang{ZeitplanungKomplett}
\subsubsection{Frontend-Schicht: Angular Component}
\label{app:FrontendComponent}
\lstinputlisting[language=C, caption={Angular TypeScript Component - Coinflip Game}]{Listings/CoinflipComponent.ts}
\input{Anhang/AnhangLastenheft.tex}
\clearpage
\subsubsection{Controller-Schicht: Spring Boot REST Controller}
\label{app:ControllerSchicht}
\lstinputlisting[language=java, caption={Spring Boot REST Controller - Coinflip}]{Listings/CoinflipController.java}
\input{Anhang/AnhangRessourcen.tex}
\clearpage
\subsubsection{Service-Schicht: Business Logic}
\label{app:ServiceSchicht}
\lstinputlisting[language=java, caption={Service-Klasse mit Geschäftslogik - Coinflip}]{Listings/CoinflipService.java}
\subsection{Use Case-Diagramm}
\label{app:UseCase}
\begin{figure}[htb]
\centering
\includegraphicsKeepAspectRatio{UseCase.pdf}{1.3}
\caption{Use Case-Diagramm}
\end{figure}
\clearpage
\subsubsection{Persistierung-Schicht: JPA Entity}
\label{app:PersistierungSchicht}
\lstinputlisting[language=java, caption={JPA Entity - Benutzer}]{Listings/UserEntity.java}
\subsection{Amortisation}
\label{app:Amortisation}
Der Zeitpunkt der Amortisation wird als Schnittpunkt der beiden Geraden angegeben.
\begin{figure}[htb]
\centering
\includegraphicsKeepAspectRatio{amortisationgrafik.png}{1}
\caption{Grafische Darstellung der Amortisation}
\end{figure}
\clearpage
\subsubsection{Konfiguration: Application Properties}
\label{app:Konfiguration}
\lstinputlisting[caption={Spring Boot Anwendungskonfiguration}]{Listings/application.properties}
\subsection{composer.json Konfiguration für neusta-m2-intex-client}
\label{app:ComposerJson}
\lstinputlisting[language=json, caption={Konfiguration für neusta-m2-intex-client}]{Listings/composer.json}
\clearpage
\subsection{Deklaration zur Anlage einer SQL Tabelle im Magento 2 Umfeld}
\label{app:InstallData}
\lstinputlisting[language=xml, caption={Deklaration zur Anlage einer SQL Tabelle im Magento 2 Umfeld}]{Listings/InstallData.xml}
\clearpage
\subsection{Klasse: Factory}
\label{app:Factory}
\lstinputlisting[language=php, caption={Klasse: Factory}]{Listings/Factory.php}
\clearpage
\subsection{Klasse: CustomerConnection}
\label{app:CustomerConnection}
\lstinputlisting[language=php, caption={Klasse: CustomerConnection}]{Listings/CustomerConnection.php}
\clearpage
\subsection{Klasse: Connection}
\label{app:Connection}
\lstinputlisting[language=php, caption={Abstrakte Klasse: Connection}]{Listings/Connection.php}
\clearpage
\subsection{Klasse: CustomerDataController}
\label{app:CustomerDataController}
\lstinputlisting[language=php, caption={Klasse: CustomerDataController}]{Listings/CustomerDataController.php}
\clearpage
\subsection{UnitTest: FactoryTest}
\label{app:UnitTest}
\lstinputlisting[language=php, caption={Unit Test der Klasse: Factory}]{Listings/UnitTest.php}
\clearpage

View file

@ -0,0 +1,68 @@
%PDF-1.5
%µí®û
3 0 obj
<< /Length 4 0 R
/Filter /FlateDecode
>>
stream
xœm“MŽ1 …÷9EÖ,L;Çਤ™Y àþÏŽSÝ ¨¥ê|ñOÞsª~¦’í÷ë=ý^òûïT•®Ü˜™‰kË­ÐÔ‰<YÔÊ¢6jkæ KäèðgG„Jáœî¥]V6$<08>y­¨
¸»{>Úì:×}}ÉXw„ûôuÑü¬ÿÊî§ö¬<C3B6>f1GÝêæ¶ûð‰ÆA<>=HÌMløïÝnéÁ,4ÍÁ.UmÏú¦M×Ι´ç<e=Ñ«Œf)M«Q™ùÕØÿ¬¾¥º\…LÒfÞÑ Œ\ulÒh|4aÂ8­5\A„Ù%ŸÒ ï{ÔBºÆ©ÔŠAËÃûÆë{Â[Ò)}Öë–^ ¼}I8kà<6B>XTðBº%=ôÁfA®Ö8ÅF bCS«6{cŒYÌ;áX©ÔÙ7«v GPj7ŠƒÑC_ 'ôôÚ]ÆVw¢L #HlCŸã[ˆ(&45Nàå•÷ñ Š«yF<79><46>t£6ÆoÛA1ôŠ.Â|ß#üøûp%ßÒ<>Ò'
endstream
endobj
4 0 obj
414
endobj
2 0 obj
<<
/ExtGState <<
/a0 << /CA 1 /ca 1 >>
>>
>>
endobj
5 0 obj
<< /Type /Page
/Parent 1 0 R
/MediaBox [ 0 0 51.200001 51.200001 ]
/Contents 3 0 R
/Group <<
/Type /Group
/S /Transparency
/CS /DeviceRGB
>>
/Resources 2 0 R
>>
endobj
1 0 obj
<< /Type /Pages
/Kids [ 5 0 R ]
/Count 1
>>
endobj
6 0 obj
<< /Creator (cairo 1.10.2 (http://cairographics.org))
/Producer (cairo 1.10.2 (http://cairographics.org))
>>
endobj
7 0 obj
<< /Type /Catalog
/Pages 1 0 R
>>
endobj
xref
0 8
0000000000 65535 f
0000000812 00000 n
0000000528 00000 n
0000000015 00000 n
0000000506 00000 n
0000000600 00000 n
0000000877 00000 n
0000001004 00000 n
trailer
<< /Size 8
/Root 7 0 R
/Info 6 0 R
>>
startxref
1056
%%EOF

Binary file not shown.

View file

@ -0,0 +1,68 @@
%PDF-1.5
%µí®û
3 0 obj
<< /Length 4 0 R
/Filter /FlateDecode
>>
stream
xœmRIŽÜ0 ¼ëz#nZž'fæ09$ù?<3F>"¥v÷<04>[%eY¿J«ñü~¯ß~´úþ§ˆÑ²U<C2B2>IêÏÊL,^½Ñ´‰<]ämá N¾f½pDŽ<44>|wD¨5®áÞä +ʆf^ëTœù ÙuÙËaÌóHǺ#ÜgžÕ×þ¯úz¤Wë4[(Šl—<6C>}óð#zþqÚ¤¡æ\ä‡÷í<C3B7>îÖ
nyÁ[6ºvÎ<ÁxÏGÙO΄àjÃ#Åue7¦³~ö?©o…<6F>lAÆ8,Ö)ÙÕÁŸ• ŒB^X„°í;,<2C>ÌÛ|¹˜fU´(S4¢Fb@=NM,ÖÓ‰a“Àvls‡Ÿ;YA*ó<>Á³ÂeçhøsxÅ¢©÷htö*8.7o hºBŒÏžá4át /jÚ! ü!•i ˜+ ÛŒÉ8µpµC鈪ÿóSSp´76Ó5ázævñß5otK8%ŽÈÊ~×!âƒÒåËÇuô&‰¦%¡è#V2ÙÆ¾èù?“þRª˜ã“¸a¬3°,Ll»¤h§æ«~5Më_½•ïå/ÅÏÕ
endstream
endobj
4 0 obj
447
endobj
2 0 obj
<<
/ExtGState <<
/a0 << /CA 1 /ca 1 >>
>>
>>
endobj
5 0 obj
<< /Type /Page
/Parent 1 0 R
/MediaBox [ 0 0 51.200001 51.200001 ]
/Contents 3 0 R
/Group <<
/Type /Group
/S /Transparency
/CS /DeviceRGB
>>
/Resources 2 0 R
>>
endobj
1 0 obj
<< /Type /Pages
/Kids [ 5 0 R ]
/Count 1
>>
endobj
6 0 obj
<< /Creator (cairo 1.10.2 (http://cairographics.org))
/Producer (cairo 1.10.2 (http://cairographics.org))
>>
endobj
7 0 obj
<< /Type /Catalog
/Pages 1 0 R
>>
endobj
xref
0 8
0000000000 65535 f
0000000845 00000 n
0000000561 00000 n
0000000015 00000 n
0000000539 00000 n
0000000633 00000 n
0000000910 00000 n
0000001037 00000 n
trailer
<< /Size 8
/Root 7 0 R
/Info 6 0 R
>>
startxref
1089
%%EOF

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 261 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 347 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 952 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -1,6 +1,5 @@
% !TEX root = Projektdokumentation.tex
\input{Inhalt/Einleitung}
\input{Inhalt/Vorbereitung.tex}
\input{Inhalt/Projektarchitektur}
\input{Inhalt/CI}
\input{Inhalt/Auth.tex}
@ -11,6 +10,3 @@
\input{Inhalt/Lootboxes.tex}
\input{Inhalt/Deployment.tex}
\input{Inhalt/Wirtschaftlichebetrachtung.tex}
\input{Inhalt/Abschluss.tex}

View file

@ -0,0 +1,11 @@
% !TEX root = ../Projektdokumentation.tex
\section{Abnahmephase}
\label{sec:Abnahmephase}
Die Anwendung wurde gemäß der in Abschnitt~\ref{sec:Entwicklungsprozess}: \nameref{sec:Entwicklungsprozess} agilen Entwicklung stetig von Mitarbeitern der \ac{NSD}
per Code Review abgenommen. Die Code Review erfolgte über das webbasierte Development Operations Lebenszyklus Tool GitLab \footnote{GitLab - \url{https://gitlab.com}}, in dessen Repository die Anwendung per git \footnote{git - \url{https://git-scm.com/}}
versioniert wurde. Sofern ein neues Feature vom Autor fertiggestellt wurde, erstellte der Autor einen sogenannten Merge Request in GitLab. Dieser Merge Request hat zur Folge,
dass ein zugeteilter Mitarbeiter der \ac{NSD} den Code freigeben muss und dieser dann erst in den Quellcode der Anwendung übernommen werden kann.
Teil der Abnahmebedingungen war, dass der zu implementierende Code sinnvoll mit Unit Tests abgedeckt ist. Exemplarisch hierfür steht der \Anhang{app:UnitTest}, welcher
einen beispielhaften Unit Test für die Komponente neusta-m2-intex-client-config zeigt.

View file

@ -1,12 +0,0 @@
% !TEX root = ../Projektdokumentation.tex
\section{Abschluss}
\subsection{Projektziel}
Im Rahmen der Soll-Analyse konnten wesentliche Bestandteile des Projekts erfolgreich umgesetzt werden. Aufgrund zeitlicher Einschränkungen sowie unvorhergesehener technischer Herausforderungen war es jedoch nicht möglich, sämtliche ursprünglich geplanten Funktionen vollständig zu realisieren. Während der Entwicklungsphase wurde ein besonderer Schwerpunkt auf die technischen Grundlagen gelegt, insbesondere auf die sichere Authentifizierung, die serverseitige Logik sowie die Bereitstellung einer stabilen Infrastruktur. Monetarisierungsstrategien und benutzerorientierte Aspekte wurden in dieser Phase hingegen nur am Rande berücksichtigt.
Im Verlauf der Ausarbeitung wurde zudem deutlich, dass die Umsetzung einzelner Spiele deutlich komplexer ist als zunächst angenommen. Aus diesem Grund wurde die Entwicklung auf den Kern des Spielkonzepts reduziert, wohingegen begleitende Elemente wie soziale Funktionen oder ein vollständig responsives Design vorerst nicht weiter ausgearbeitet wurden. Im weiteren Projektverlauf wurde deutlich, dass die Implementierung von Poker und Plinko nicht mehr im geplanten Zeitrahmen realisiert werden konnte. Insbesondere die Entwicklung der Authentifizierungsmechanismen sowie das Deployment der Anwendung erforderten einen höheren Aufwand als erwartet.
\subsection{Fazit}
Die Zusammenarbeit im Team kann als grundsätzlich positiv bewertet werden. Es sei besonders hervorgehoben, dass eine offene und funktionierende Kommunikation existierte, die es ermöglichte, Herausforderungen gemeinsam zu bewältigen und Entscheidungen transparent zu treffen. Gleichzeitig wurde jedoch Optimierungspotenzial in der Organisation und Strukturierung der Arbeitsprozesse aufgezeigt. Das Projektverhalten zeigte phasenweise Unkoordiniertheit und chaotische Züge, was die Effizienz beeinträchtigte. Das Ticket-Handling hätte klarer und konsequenter erfolgen müssen, um Aufgaben besser zu priorisieren und den Überblick über den Projektfortschritt zu wahren. Trotz der zuvor genannten strukturellen Schwächen war eine gute inhaltliche Zusammenarbeit innerhalb des Teams zu verzeichnen, in deren Folge bedeutende Meilensteine erreicht werden konnten.
Trotzdem war das Projekt von erheblicher Frustration geprägt, deren Hauptursache die als unzureichend empfundene Technologie, insbesondere Java, war. Gepaart mit Mängeln im Projektmanagement, wie zu spät begonnener Dokumentation und ineffektiven Retrospektiven, führte der massive Zeitdruck zu einem unbefriedigenden Ergebnis. Das Gesamterlebnis gipfelt in dem Gefühl, letztlich „garnichts“ erreicht zu haben, und dem nachdrücklichen Wunsch, Java zukünftig zu meiden

View file

@ -0,0 +1,105 @@
% !TEX root = ../Projektdokumentation.tex
\section{Analysephase}
\label{sec:Analysephase}
\subsection{Ist-Analyse}
\label{sec:IstAnalyse}
Ein Direktimport über Schnittstellen wird für \ac{M2} nicht zur Verfügung gestellt.
Derzeit werden solche Importer per Modul für jeden Webshop individuell selbst entwickelt und angepasst.
Als Modul bezeichnet man im \ac{M2} Umfeld eine Erweiterung des Frameworks durch neue Funktionalitäten.
Die hierfür neu entwickelnden Module stellen \acs{API}-Abfragen an \ac{IX} und importieren die Daten ohne temporäres Zwischenspeichern in \ac{M2}.
Vorhandene Fehlerquellen sind hier die benötigte Verbindung zur \ac{IX}-\acs{API} und die unbedingte Richtigkeit der übertragenen Daten.
Kommen Fehler beim Import aufgrund von fehlender oder unvollständiger Datensätze zustande, bricht der komplette Import ab.
Gleiches gilt für die Export Funktionen zum Exportieren von Bestellungen von \ac{M2} zu \ac{IX}.
\subsection{Wirtschaftlichkeitsanalyse}
\label{sec:Wirtschaftlichkeitsanalyse}
Aufgrund des in den Abschnitten~\ref{sec:Projektbegruendung}: \nameref{sec:Projektbegruendung} und~\ref{sec:IstAnalyse}: \nameref{sec:IstAnalyse}
geschilderten und erläuterten derzeitigen Zustandes von Import/Export-Modulen in Abhängigkeit zum \ac{CRM} \ac{IX} für \ac{M2}, ist die Umsetzung
des Projektes unbedingt erforderlich. Eine gerechtfertigte Realisierung aus wirtschaftlichen Gesichtspunkten wird in den folgenden Abschnitten analysiert.
\subsubsection{\gqq{Make or Buy}-Entscheidung}
\label{sec:MakeOrBuyEntscheidung}
Bei dem Projekt handelt es sich um die Erfüllung von kundenspezifischen Anforderungen, sofern \ac{IX} als \ac{CRM} genutzt wird.
Hierfür lässt sich auf dem Markt keine Lösung finden, die eine Integration der Warenwirtschaft in ein \ac{M2} ermöglicht.
Daher soll das Projekt in Eigenentwicklung durchgeführt und durch Nutzung von Open Source Anwendungen erweitert werden.
\subsubsection{Projektkosten}
\label{sec:Projektkosten}
Im folgenden sollen die Kosten kalkuliert werden, die während der Entwicklung des Projektes anfallen. Neben Personalkosten, die durch die Umsetzung
des Projektes verursacht werden, mussten auch Aufwendungen für die in Abschnitt~\ref{sec:Ressourcenplanung}: \nameref{sec:Ressourcenplanung} genannten Ressourcen
berücksichtigt werden. Da Personalkosten im Original nicht herausgegeben werden dürfen, erfolgte die Kalkulation anhand von unterstellten Werten.
Die Durchführungszeit des Projekts beträgt 70 Stunden.
Unterstellt wurde ein Stundensatz in Höhe von \eur{10} für Auszubildende sowie \eur{25} für Mitarbeiter.
Für die o.g. Ressourcennutzung wurde ein pauschaler Satz von \eur{15} angenommen.
Eine Aufstellung der Kosten lassen sich der Tabelle~\ref{tab:Kostenaufstellung} entnehmen und betragen insgesamt \eur{2910}.
\tabelle{Kostenaufstellung}{tab:Kostenaufstellung}{Kostenaufstellung.tex}
\subsubsection{Amortisationsdauer}
\label{sec:Amortisationsdauer}
Der folgende Abschnitt dient zur Ermittlung des Zeitpunktes, ab welchem sich die Entwicklung der Anwendung amortisiert hat.
Anhand dieses Wertes kann beurteilt werden, ob die Umsetzung des Projektes aus wirtschaftlicher Sicht sinnvoll ist und sich hieraus
Kostenvorteile ergeben.
Nachfolgend soll nun die Amortisationsdauer berechnet werden. Diese gibt an, ab welchem Zeitpunkt die Anschaffungs- und Entwicklungskosten durch die
Zeitersparnis beglichen werden kann.
\tabelle{Zeitersparnis}{tab:Zeitersparnis}{Zeitersparnis} \\
\textbf{Berechnung der Amortisationsdauer:}
\begin{align}
415\ \frac{min}{Monat} \cdot 12\ \frac{Monate}{Jahr} = 4980\ \frac{min}{Jahr} = 83\ \frac{h}{Jahr}
\end{align}
\begin{align}
83\ \frac{h}{Jahr} \cdot (\eur{25} + \eur{15}) = 3320\ \frac{\eur{}}{Jahr}
\end{align}
\begin{align}
\frac{\eur{3320}}{2910 \frac{\eur{}}{Jahr}} = 1,14\ Jahre \approx 1\ Jahr\ \&\ 1,5\ Monate
\end{align}
Zusätzlich wurde die Amortisation grafisch dargestellt. Das Diagramm enthält die variablen Kosten (pro Jahr) der neuen Lösung sowie die Kosten,
die für die Entwicklung der neuen Umsetzung angefallen sind. Die Grafik findet sich im~\Anhang{app:Amortisation}.
Anhand der Amortisationsrechnung ergibt sich für das Projekt eine Amortisationsdauer von einem Jahr und ca. eineinhalb Monaten. Dies definiert den Zeitraum, über den
die neue Anwendung mindestens eingesetzt werden muss, um Anschaffungskosten und Kosteneinsparung auszugleichen. Da das Projekt von der \ac{NSD} langfristig eingesetzt
werden soll, kann das Projekt unter wirtschaftlichen Gesichtspunkten als sinnvoll eingestuft werden.
\subsection{Nutzwertanalyse}
\label{sec:Nutzwertanalyse}
Durch die in den Abschnitten~\ref{sec:Projektkosten}: \nameref{sec:Projektkosten} und~\ref{sec:Amortisationsdauer}: \nameref{sec:Amortisationsdauer} aufgeführten Ergebnisse wird die Realisierung
des Projektes bereits ausreichend gerechtfertigt. Hierdurch soll an dieser Stelle auf eine detaillierte Analyse nicht-monetärer Vorteile verzichtet werden.
Nicht-monetäre Vorteile der Anwendung wären aber \zB die angenehmere Wartbarkeit durch bessere Übersicht der neu implementierten Modulstruktur sowie die Implementierung von aussagekräftigeren Fehlermeldungen
und der Möglichkeit, die Anwendung schnell für individuelle Wünsche anzupassen.
\subsection{Anwendungsfälle}
\label{sec:Anwendungsfaelle}
Eine grobe Übersicht über die von der Anwendung abzudeckenden Anwendungsfälle wurde im Laufe der Analysephase per Use-Case-Diagramm dargestellt.
Dieses Diagramm befindet sich im \Anhang{app:UseCase} und bildet alle Funktionen ab, welche aus Anwendersicht benötigt werden.
Dem Entwickler stehen der Vollimport sowie die Teilimporte der Daten für Nutzer- und Produktdaten zur Verfügung. Die Teilimporte sind erforderlich,
um mögliche Fehlimporte im Kleinen neu anstoßen zu können und so einen erneuten Vollimport zu verhindern.
Beim Auslösen des Import-Vorgangs werden die Daten von der \ac{IX}-\acs{API} angefordert und zwischengespeichert, um abermalige Requests zu verhindern.
Die zwischengespeicherten Daten werden dann zu \ac{M2} importiert und dem Webshop zur Verfügung gestellt.
Der Vorgang des Vollimports wird außerdem auch über einen \acs{CRON}-Job in geplanten Zeitabständen zur Verfügung gestellt, um ein abermaliges, manuelles
Anstoßen zu umgehen.
\subsection{Qualitätsanforderungen}
\label{sec:Qualitaetsanforderungen}
Die Anwendung soll unter Green Development \footnote{Sustainable Web Design - \url{https://sustainablewebdesign.org/}} Grundsätzen entwickelt werden. Dies bedeutet, dass auf effiziente Datenabfragen sowie auf Minderung von Komplexität während
der Entwicklung geachtet wird. Weitere Punkte der Qualitätsanforderungen sind die Möglichkeit nachvollziehbare Logs einzusehen, falls Fehler während
eines Importes entstehen sowie die Wartbarkeit der Anwendung durch modulare und entkoppelte Entwicklung. Außerdem soll die Anwendung mit Unit-Tests abgedeckt sein,
um die Funktionalität des Codes zu gewährleisten.
\subsection{Lastenheft/Fachkonzept}
\label{sec:Lastenheft}
Am Ende der Analysephase wurde mit den Mitarbeitern der \ac{NSD} sowie der \ac{NXP} ein Lastenheft erstellt. Dieses umfasst alle Anforderungen,
welche an die neue Anwendung gestellt werden. Ein Auszug aus dem erstellten Lastenheft befindet sich im \Anhang{app:Lastenheft}.

View file

@ -2,22 +2,8 @@
\label{sec:Authentifizierung}
Die Authentifizierung gegenüber der \acs{API} erfolgt über einen \acs{JWT}-Token, der dem Frontend nach Erfolgreicher Authentifizierung übergeben wird.
Authentifizierung läuft über zwei verschiedene Wege ab:
\subsection{Registrierung mit username/password} Der Nutzer füllt ein Registrierungs-Formular aus, welches die Anmeldedaten an die \acs{API} sendet. Diese validitert die Anmeldedaten und legt bei Erfolg einen neuen Nutzer an. Anschließend wird eine E-Mail-Verifizierungs-Mail gesendet. Bis der Link in der Verifizierungs-Mail nicht angeklickt wurde, ist der Nutzer nicht aktiv und kann sich nicht anmelden. Nach dem Klick auf den Link wird der Nutzer aktiviert und kann sich anmelden.
\subsection{Login mit username/password} Der Nutzer füllt ein Anmelde-Formular, welches die Anmeldedaten an die \acs{API} sendet. Diese prüft die Anmeldedaten und gibt bei Erfolg einen \acs{JWT}-Token zurück. Falls kein Nutzer mit den Anmeldedaten existiert, wird der Nutzer aufgefordert einen Account zu erstellen.
\begin{figure}
\centering
\includegraphics[width=0.3\textwidth]{login.png}
\caption{Login-Formular der Anwendung}
\label{fig:login}
\end{figure}
\subsection{Login über Oauth (Open Authorization)} Der Nutzer meldet sich mit einem Oauth-Provider an, in unserem Fall Google oder Github. Das Backend leitet den Nutzer zum Oauth-Provider weiter, der die Anmeldedaten prüft und bei Erfolg den Nutzer auf die Applikation weiterleitet und einen Authorization-Code zurück gibt. Mit diesem Code holt sich die \acs{API} einen \acs{JWT} vom jeweiligen Provider und holt sich Nutzer-Informationen. Mit diesen wird dann ein existierender Nutzer eingeloggt, oder registriert falls der Nutzer noch kein Konto hatte. Anschließend wird von der \acs{API} ein \acs{JWT} generiert und an das Frontend weitergegeben.
\begin{figure}
\centering
\includegraphics[width=0.45\textwidth]{oauth.png}
\caption{OAuth-Authentifizierungsablauf}
\label{fig:oauth}
\end{figure}
\begin{itemize}
\item \textbf{Registrierung mit username/password:} Der Nutzer füllt ein Registrierungs-Formular aus, welches die Anmeldedaten an die \acs{API} sendet. Diese validitert die Anmeldedaten und legt bei Erfolg einen neuen Nutzer an. Anschließend wird eine E-Mail-Verifizierungs-Mail gesendet. Bis der Link in der Verifizierungs-Mail nicht angeklickt wurde, ist der Nutzer nicht aktiv und kann sich nicht anmelden. Nach dem Klick auf den Link wird der Nutzer aktiviert und kann sich anmelden.
\item \textbf{Login mit username/password:} Der Nutzer füllt ein Anmelde-Formular, welches die Anmeldedaten an die \acs{API} sendet. Diese prüft die Anmeldedaten und gibt bei Erfolg einen \acs{JWT}-Token zurück. Falls kein Nutzer mit den Anmeldedaten existiert, wird der Nutzer aufgefordert einen Account zu erstellen.
\item \textbf{Login über Oauth (Open Authorization):} Der Nutzer meldet sich mit einem Oauth-Provider an, in unserem Fall Google oder Github. Das Backend leitet den Nutzer zum Oauth-Provider weiter, der die Anmeldedaten prüft und bei Erfolg den Nutzer auf die Applikation weiterleitet und einen Authorization-Code zurück gibt. Mit diesem Code holt sich die \acs{API} einen \acs{JWT} vom jeweiligen Provider und holt sich Nutzer-Informationen. Mit diesen wird dann ein existierender Nutzer eingeloggt, oder registriert falls der Nutzer noch kein Konto hatte. Anschließend wird von der \acs{API} ein \acs{JWT} generiert und an das Frontend weitergegeben.
\end{itemize}

View file

@ -1,3 +1,4 @@
\clearpage
\section{Coinflip}
\subsection{Was ist Coinflip?}
@ -35,7 +36,4 @@ Das Spielergebnis wird strukturiert an das Frontend übermittelt und enthält:
\item Gewinnstatus (gewonnen/verloren)
\item Auszahlungsbetrag (bei Gewinn: 2x Einsatz)
\item Geworfene Münzseite
\end{itemize}
\subsubsection{Implementierungsdetails}
Die vollständige Implementierung der Coinflip-Funktionalität umfasst verschiedene Architekturschichten: Das Angular Frontend-Component (siehe \ref{app:FrontendComponent}), den Spring Boot REST Controller (siehe \ref{app:ControllerSchicht}) und die Service-Schicht mit der Geschäftslogik (siehe \ref{app:ServiceSchicht}). Zusätzlich wird die Benutzer-Entity (siehe \ref{app:PersistierungSchicht}) für die Guthaben-Verwaltung verwendet.
\end{itemize}

View file

@ -1,5 +1,7 @@
\section{Deployment}
\label{sec:Deployment}
Es gibt zwei Server auf denen Instanzen der Applikation laufen.
\subsection{\href{https://casino.simonis.lol/}{Entwicklungsserver}} Auf dem Entwicklungsserver läuft eine Instanz der Applikation, die für die Entwicklung und das Testen von neuen Features genutzt wird. Diese Instanz ist Lokal bei Constantin gehostet und wird durch einen Cloudflare-Tunnel öffentlich zugänglich gemacht.
\subsection{\href{https://trustworthy.casino/}{Produktionsserver}} Auf dem Produktionsserver läuft die finale Version der Applikation, die für die Nutzer zugänglich ist. Diese Instanz ist öffentlich zugänglich und wird von den Nutzern genutzt. Diese Instanz ist auf einem gemieteten Server gehostet. Die Applikation wird durch eine Nginx Reverse-Proxy bereitgestellt, die Anfragen an die \acs{API} und das Frontend weiterleitet und SSL-Zertifikate verwaltet. Die Konfiguration der Anwendung erfolgt über Umgebungsvariablen und Properties-Dateien (siehe \ref{app:Konfiguration}).
\begin{itemize}
\item \textbf{\href{https://casino.simonis.lol/}{Entwicklungsserver}:} Auf dem Entwicklungsserver läuft eine Instanz der Applikation, die für die Entwicklung und das Testen von neuen Features genutzt wird. Diese Instanz ist Lokal bei Constantin gehostet und wird durch einen Cloudflare-Tunnel öffentlich zugänglich gemacht.
\item \textbf{\href{https://trustworthy.casino/}{Produktionsserver}:} Auf dem Produktionsserver läuft die finale Version der Applikation, die für die Nutzer zugänglich ist. Diese Instanz ist öffentlich zugänglich und wird von den Nutzern genutzt. Diese Instanz ist auf einem gemieteten Server gehostet. Die Applikation wird durch eine Nginx Reverse-Proxy bereitgestellt, die Anfragen an die \acs{API} und das Frontend weiterleitet und SSL-Zertifikate verwaltet.
\end{itemize}

View file

@ -1,3 +1,4 @@
\clearpage
\section{Dice}
\subsection{Was ist Dice?}

View file

@ -0,0 +1,7 @@
% !TEX root = ../Projektdokumentation.tex
\section{Dokumentation}
\label{sec:Dokumentation}
Die Dokumentation der Anwendung besteht aus zwei Bestandteilen: der Projektdokumentation, in welcher der Autor die einzelnen
Phasen, die während der Projektumsetzung durchlaufen wurden, beschreibt sowie der Entwicklerdokumentation, bei der es sich um eine
detaillierte Beschreibung der Klassen, die von der Anwendung genutzt werden, handelt. Außerdem werden auch deren Attribute und Methoden sowie die
Abhängigkeiten der jeweiligen Klassen untereinander erläutert.

View file

@ -0,0 +1,12 @@
% !TEX root = ../Projektdokumentation.tex
\section{Einführungsphase}
\label{sec:Einfuehrungsphase}
Durch die in Abschnitt~\ref{sec:ImplementierungGeschaeftslogik}: \nameref{sec:ImplementierungGeschaeftslogik} vorgenommene Implementierung
der Anwendung als Composer basierte Packages, ist das zur Verfügung stellen der Anwendung an den \ac{NSD} internen Satis \footnote{Composer Satis - \url{https://github.com/composer/satis}} Server angebunden.
Der interne Satis dient als Composer Repository und stellt so eigen entwickelte Module für die \ac{NSD} für alle Kundenprojekte zur Verfügung.
Die Komponenten der Anwendung werden dort aufgeführt und können in die realen Kundenprojekte nach Fertigstellung der ausgelassenen
Funktionen per Composer implementiert werden. Hier kommt nun auch der größte Vorteil zu tragen, da eine Änderung der Module lediglich
in der Grundversion, die dem Satis zur Verfügung steht, vorgenommen muss und somit automatisch per Composer bei Deployments für Live Umgebungen übernommen werden.
So müssen Änderungen nicht code-seitig an den Projekten vorgenommen werden.

View file

@ -1,57 +1,55 @@
% !TEX root = ../Projektdokumentation.tex
\section{Einleitung}
\label{sec:Einleitung}
Die folgende Projektdokumentation beschreibt die Entwicklung einer innovativen Online-Casino-Plattform für die HiTec GmbH,
ein mittelgroßes IT-Systemhaus mit Sitz in Bremen, das seit über 15 Jahren als IT-Dienstleister tätig und seit einigen Jahren ISO/IEC 27001 zertifiziert ist. Das Kerngeschäft der HiTec GmbH umfasst Entwicklung eigener Softwareprodukte, Consulting, IT-Systembereich sowie Support und Wartung und beschäftigt spezialisierte Mitarbeiter in verschiedenen Abteilungen.
Die folgende Projektdokumentation behandelt den Ablauf des IHK-Abschlussprojekts, welches der Autor im Rahmen seiner Ausbildung
zum Fachinformatiker mit der Fachrichtung Anwendungsentwicklung durchgeführt hat. Ausbildungsbetrieb ist die \ac{NSD},
ein Tochterunternehmen der team neusta Unternehmensgruppe mit Sitz in Bremen. Das Kerngeschäft der \ac{NSD} umfasst die Beratung,
Entwicklung und Umsetzung von komplexen Software- und \ac{eCommerce}-Lösungen und beschäftigt zur Zeit ca. 1000 Mitarbeiter.
\subsection{Projektumfeld}
\label{sec:Projektumfeld}
Auftraggeber des Projektes ist Herr Hofmann, der Inhaber der HiTec GmbH.
Das Unternehmen möchte sein Produkt-Portfolio in den nächsten zwei Jahren wesentlich erweitern, um weiterhin am Markt zu bestehen und noch stärker zu wachsen. Dazu soll der Bereich der Webanwendungen mit einem neuartigen Produkt beitragen, das über einzigartige Features verfügt.
Auftraggeber des Projektes ist die \ac{NXP}, ebenfalls eine Tochtergesellschaft der team neusta Unternehmensgruppe.
Zu den Leistungen der \ac{NXP} gehören die Beratung und Umsetzung der Bereiche Konzeption, Design und Usability sowie die Leitung von Web- und \ac{eCommerce}-Projekten.
Das Ziel ist es, das fachliche Know-How des Entwicklungsteams stärker zu nutzen, um als innovatives IT-Unternehmen langfristig am Markt wahrgenommen zu werden und schnell Erträge zu erzielen.
Die technische Umsetzung von Web- und \ac{eCommerce}-Projekten für Kunden der \ac{NXP} wird in Zusammenarbeit mit Mitarbeitern der \ac{NSD} vorgenommen.
Die technische Umsetzung erfolgt mit den in der HiTec GmbH etablierten Technologien und Projektmanagement-Methoden unter Verwendung agiler Entwicklungsmethoden.
Diese Form der Zusammenarbeit erfordert intensive, regelmäßige Kommunikation und Rücksprache zwischen beiden Parteien.
\subsection{Projektziel}
\label{sec:Projektziel}
Ziel des Projektes ist die Entwicklung einer innovativen Online-Casino-Plattform als neuartiges Produkt für das Portfolio der HiTec GmbH.
Ziel des Projektes ist die Entwicklung einer Schnittstelle zum Importieren und Exportieren von Produkt- und Nutzerdaten
zwischen dem \ac{CRM} \ac{IX} und einem \ac{M2} Webshop.
Die Plattform soll verschiedene Casino-Spiele wie Blackjack, Coinflip, Würfelspiele und Spielautomaten anbieten und durch moderne Features wie Lootboxes und ein umfassendes Benutzermanagement erweitert werden.
Das System soll eine vollständige Transaktionsabwicklung mit Einzahlungsfunktionen und eine sichere Benutzerauthentifizierung über OAuth2-Integration bieten.
Die \ac{NSD} entwickelt für Kunden der \ac{NXP} spezifische Webshops und bindet diese an das vom Kunden vorgesehene \ac{CRM} an.
Für \ac{IX} erfordert dies eine individuelle Entwicklung pro Kunden, welche mit hohem Aufwand und Kosten verbunden ist.
Dieses Projekt soll einen modularen Rahmen für Importe/Exporte zwischen \ac{M2}, einer Open-Source \ac{eCommerce} Platform
auf \ac{PHP} Basis und \ac{IX} abbilden, welcher in verschiedene Kundenprojekte implementiert werden kann und so die
abermalige Neuentwicklung eines Import/Export Modules ablöst.
Durch den Einstieg in den Entertainment-Bereich soll das bisherige IT-Systemhaus-Portfolio um ein profitables Gaming-Produkt erweitert werden.
Die Fokussierung auf Social Casino Games ermöglicht den Zugang zum wachsenden Markt der Casual-Gamer, während gleichzeitig die strengen Glücksspiellizenzen und regulatorischen Anforderungen umgangen werden können.
Die Monetarisierung erfolgt primär über den Verkauf virtueller Währungen und Premium-Features, was bei vergleichsweise niedrigen Entwicklungskosten eine hohe Gewinnmarge verspricht.
Zusätzliche Einnahmequellen werden durch In-App-Käufe und spezielle Lootbox-Angebote generiert.
Diese Webanwendung soll als hochproduktive und kostenarme Implementierung realisiert werden, um schnell Marktreife zu erlangen und Erträge zu generieren.
\subsection{Projektbegründung}
\label{sec:Projektbegruendung}
Der Online-Gaming- und Casino-Markt zeigt kontinuierliches Wachstum und bietet erhebliches Potenzial für innovative Lösungen.
Durch die Entwicklung einer modernen Casino-Plattform kann die HiTec GmbH eine neue Kundengruppe erschließen und eine Nische im Gaming-Bereich besetzen.
Die \ac{NSD} entwickelt derzeit für jeden Kunden, der \ac{IX} als \ac{CRM} nutzt, individuell ein Modul zur Erweiterung
des relevanten \ac{M2}.
Diese Module handhaben den Kunden- sowie Produktdaten-Import von \ac{IX} zu \ac{M2}. Die Neuentwicklungen für jeden
Kunden beinhalten Schwierigkeiten in der Wartung und Fehleranalyse, da jeder Entwickler sich in das individuell entwickelte Modul
einarbeiten muss. Gleichzeitig liegt hier kein Standard vor, wie mit Fehlermeldungen, die von der \ac{IX}-\acs{API} empfangen werden, umgegangen wird.
Dies resultiert in längerer und umständlicher Wartung und schlägt dem Kunden durch Mehraufwand zu Buche, der vermieden werden kann.
Die Verwendung bewährter Technologien (Java mit Spring-Boot, Angular, Keycloak) minimiert die Entwicklungsrisiken und ermöglicht eine schnelle Markteinführung.
Gleichzeitig demonstriert das Projekt die technische Kompetenz des Unternehmens in der Entwicklung komplexer, interaktiver Webanwendungen.
Ein modularer Rahmen mit einfachen Individualisierungsmöglichkeiten für einen \ac{IX}-Importer als Modul für \ac{M2} sorgt somit für
eine Minderung von Neu-Entwicklungskosten und -aufwand. Eine höhere Wartbarkeit mit geringerer Einarbeitungszeit pro Kundenprojekt wird hierdurch
ebenfalls garantiert.
Ein modularer Aufbau mit verschiedenen Spielen und Features ermöglicht eine schrittweise Erweiterung und Anpassung an Marktanforderungen, was langfristige Wartbarkeit und Skalierbarkeit gewährleistet.
\subsection{Projektschnittstellen}
\label{sec:Projektschnittstellen}
Die Anwendung wird als Full-Stack-Webanwendung mit getrenntem Backend und Frontend entwickelt.
Das Backend basiert auf Java mit Spring-Boot und stellt RESTful APIs für die Spiellogik, Benutzerverwaltung und Transaktionsabwicklung bereit.
Damit die Daten, die zu \ac{M2} importiert werden sollen, vollständig ermittelt und validiert werden können, muss die Anwendung mit
der \ac{IX}-\acs{API} interagieren können. Diese Anforderung soll mit Hilfe von cURL \footnote{cURL - Client URL \url{https://github.com/curl/curl}} im Umfeld der \ac{PHP} Entwicklung umgesetzt werden.
Für die Benutzerauthentifizierung wird Keycloak als Identity Provider eingesetzt, der OAuth2-Integration mit externen Anbietern wie Google und GitHub ermöglicht.
Die Frontend-Anwendung wird mit Angular entwickelt und kommuniziert über HTTP-APIs mit dem Backend.
Zur Containerisierung und für das Deployment wird Docker verwendet, was eine konsistente Entwicklungs- und Produktionsumgebung gewährleistet.
Die Projektverwaltung erfolgt mit Jira unter Verwendung der Scrum-Methodik, die in der HiTec GmbH als Standard etabliert ist.
Um den eigentlichen Import-Vorgang der bereits geholten Daten von \ac{IX} starten zu können wird zudem eine Verbindung zur Import-\acs{API} von \ac{M2} benötigt.
Aus diesem Grund wird die Anwendung als \ac{M2}-Modul implementiert. So wird garantiert, dass Fehler im Import-Vorgang minimiert werden, weil der Fokus darauf gelegt
wird, die \ac{M2} eigenen Import-Funktionen zu nutzen.
\subsection{Projektabgrenzung}
\label{sec:Projektabgrenzung}
Da der Projektumfang beschränkt ist, konzentriert sich die Entwicklung auf die Kernfunktionalitäten der Casino-Plattform.
Erweiterte Features wie Live-Dealer-Spiele, komplexe Turniere oder Integration mit externen Zahlungsdienstleistern sind nicht Bestandteil dieses Mittelstufenprojekts.
Die Implementierung beschränkt sich auf eine begrenzte Anzahl von Casino-Spielen und grundlegende Benutzerfunktionen, um eine funktionsfähige Proof-of-Concept-Anwendung zu erstellen.
Da der Projektumfang beschränkt ist, soll die Entwickung der Import- und Export-Funktionen von Bestellungen und Warenkörben nicht Bestandteil dieses Abschlussprojektes sein.

View file

@ -0,0 +1,73 @@
% !TEX root = ../Projektdokumentation.tex
\section{Entwurfsphase}
\label{sec:Entwurfsphase}
\subsection{Zielplattform}
\label{sec:Zielplattform}
Das Abschlussprojekt soll, wie in Abschnitt~\ref{sec:Projektziel}: \nameref{sec:Projektziel} beschrieben, aufgrund des Aspektes der Fehlerminimierung als \ac{M2}-Modul
umgesetzt werden. Zur temporären Zwischenspeicherung der importierten Datensätze von \ac{IX} wird eine Tabelle in das
bestehende Datenbank-System der \ac{M2} Instanz implementiert.
Die Auswahl der Programmiersprache, mit der das Projekt realisiert werden soll, wurde anhand der Anforderungen von \ac{M2}
als \ac{eCommerce} Platform durchgeführt. Um eine Fehler minimierende Einbindung in bestehende Shops zu garantieren, wurde beschlossen,
auf eine Nutzwertanalyse zu verzichten und den genannten Anforderungen von \ac{M2} Folge zu leisten.
Daher ist die Programmiersprache \ac{PHP}, unter Berücksichtigung der genannten Gesichtspunkte, am besten geeignet und soll für die Umsetzung des Projektes verwendet werden.
\subsection{Architekturdesign}
\label{sec:Architekturdesign}
Die Implementierung der einzelnen Import-Funktionen für Benutzer- und Produktdaten sollen auf Basis des Factory Design Pattern \footnote{PHP the Right Way - Factory Pattern - \url{https://phptherightway.com/pages/Design-Patterns.html}}
umgesetzt werden. Demnach wird jede Import-Komponente durch eine zugehörige Factory per Singleton Prinzip instanziiert.
So wird sichergestellt, dass jeweils nur eine Instanz des Imports vorhanden ist und Rechenlast während der Nutzung der Anwendung
minimiert wird.
Zusätzlich wird jede Funktionalität modular als Composer \footnote{Composer - \url{https://getcomposer.org}} Package eingebunden, sodass die einzelnen Komponenten nur eine Basis-Komponente zum
Funktionieren benötigen. Die Basis-Komponente bindet alle gewünschten Komponenten ein und kann sie anhand des o.g. Factory Patterns instanziieren.
Diese Modularität bildet eine übersichtliche Anwendungsgestaltung, die nachträglich - ohne viel Aufwand - um mehr Funktionalität erweitert werden kann.
\subsection{Entwurf der Benutzeroberfläche}
\label{sec:Benutzeroberflaeche}
Der Autor hat sich auf Grund des festgelegten Projektumfangs sowie des festgelegten Nutzerkreises, der aus Mitarbeitern der \ac{NSD} besteht,
für eine Umsetzung der Benutzeroberfläche als \ac{CLI}, zur Ausführung der in
Abschnitt~\ref{sec:Anwendungsfaelle}: \nameref{sec:Anwendungsfaelle} beschriebenen Anwendungsfälle, entschieden.
Die Einbindung der Konsolenbefehle soll über die standardisierte \ac{M2} Implementierungsmöglichkeit für \ac{CLI} Befehle erfolgen.
\subsection{Datenmodell}
\label{sec:Datenmodell}
Um eine Zwischenspeicherung der Daten, die von der \ac{IX}-\ac{API} bereitgestellt werden, zu realisieren, wird eine Tabelle zur
Datenstruktur vom betroffenen \ac{M2} Webshop hinzugefügt. Diese Tabelle soll lediglich Informationen über die importierten Daten und den
jeweiligen Zeitstempel zur Nachvollziehbarkeit enthalten.
Da keine komplexen Entitäten hierfür benötigt werden, wurde vom Autor darauf verzichtet ein \ac{ERM} sowie eine Datenbankstruktur zu
erstellen.
\subsection{Geschäftslogik}
\label{sec:Geschaeftslogik}
Wie in Abschnitt~\ref{sec:Architekturdesign}: \nameref{sec:Architekturdesign} beschrieben, wird die Anwendung in mehrere Komponenten aufgeteilt.
Die Aufteilung der Komponenten erfolgt per Registrierung als Composer Package. Composer als \ac{PHP} Package Manager bietet die Möglichkeit,
Abhängigkeiten der genutzten Software zu versionieren und in einem kontrollierten Umfeld zu nutzen. Durch die Aufteilung der Anwendung in solche
Packages, erlangt der Autor eine entkoppelte Anwendung, die ohne großen Aufwand ebenfalls erweitert werden kann.
Es wird eine Basis-Komponente geben von der jede Subkompenente ableitet. Diese Basis-Komponente beinhaltet die Grundabhängigkeiten,
damit die Anwendung im jeweiligen \ac{M2} Umfeld lauffähig ist. Diese Grundabhängigkeiten beinhalten die Version der Programmiersprache
\ac{PHP}, die Version des genutzten \ac{M2} sowie die genauen Abhängigkeiten der genutzten \ac{M2} Kern-Komponenten. Zusätzlich wird eine Version
der Anwendung selbst definiert, sodass in der weiteren Entwicklung eigene Versionen für verschiedene \ac{M2} Versionen (\zB ältere) zur Verfügung gestellt
werden könnten. Die Basis Komponente wird den Namen neusta-m2-intex-base tragen.
neusta-m2-intex-base wird erweitert um folgende Subkomponenten:
\begin{itemize}
\item neusta-m2-intex-client: Subkomponente zur Verbindung zur \ac{IX} \ac{API}
\item neusta-m2-intex-client-config: Subkomponente zur Konfiguration des Key-Value Mappings für Daten von \ac{IX}
\item neusta-m2-intex-cli: Subkomponente zur Implementierung der Benutzeroberfläche
\item neusta-m2-intex-customer: Subkomponente zur Implementierung der Import Logik für Nutzerdaten
\item neusta-m2-intex-catalog: Subkomponente zur Implementierung der Import Logik für Produktdaten
\end{itemize}
Durch die Namenskonvention, die vom Autor gewählt wurde, ist für aussenstehende Entwickler schneller ersichtig, in welcher Komponente
welche Logik eingebaut wurde. So wird weiterhin die Wartungsfähigkeit der Anwendung in den Vordergrund gestellt.

View file

@ -0,0 +1,38 @@
% !TEX root = ../Projektdokumentation.tex
\section{Fazit}
\label{sec:Fazit}
\subsection{Soll-/Ist-Vergleich}
\label{sec:SollIstVergleich}
Bei der rückblickenden Betrachtung des Projektes, wird vom Autor festgehalten, dass die zuvor festgelegten Anforderungen erfüllt wurden.
Die Entwicklung des Projektes hat einen Grundstein für ein modulares Import/Export System von \ac{M2} zu \ac{IX} gelegt, welches allerdings zur vollständigen
Ersetzung der alten Module noch erweitert werden muss. Der Autor hat dies allerdings berücksichtigt und gemäß Abschnitt~\ref{sec:Projektabgrenzung}: \nameref{sec:Projektabgrenzung}
aufgrund des begrenzten Projektumfangs eingeplant.
Wie der Tabelle~\ref{tab:Vergleich} zu entnehmen ist, konnte ebenfalls die Zeitplanung bis auf wenige Ausnahmen eingehalten werden.
\tabelle{Soll-/Ist-Vergleich}{tab:Vergleich}{Zeitnachher.tex}
\subsection{Lessons Learned}
\label{sec:LessonsLearned}
Der Autor konnte während des Projektes wertvolle Erfahrungen \bzgl der Planung und Durchführung von Software-Projekten
sammeln. Hierbei wurde besonders deutlich, welch große Bedeutung stetige Rücksprache und Kommunikation mit den Projektbeteiligten
für den Projekterfolg hat. Zusätzlich konnte der Autor neue Kompetenzen und Sicherheiten im Umgang mit \ac{M2} erlangen, welche
für die zukünftige, angestrebte Tätigkeit im \ac{eCommerce} hilfreich sind.
Abschließend lässt sich sagen, dass die Realisierung des Projektes eine Erleichterung zur alltäglichen Arbeit mit \ac{M2}
Webshops, die eine \ac{IX} Schnittstelle nutzen, darstellt und auch für den Autor während der Entwicklung eine Bereicherung war.
\subsection{Ausblick}
\label{sec:Ausblick}
Die im Lastenheft definierten Anforderungen wurden zwar realisiert, dennoch können in Zukunft noch weitere Anforderungen \bzw
Erweiterungsmöglichkeiten entwickelt werden. Es kann \zB die Erweiterung der Benutzeroberfläche in das bestehende \ac{M2} Administrator
Backend als \ac{GUI} vorgenommen werden. Gleichzeitig können die Funktionen um weitere Datentypen erweitert werden, wie \zB
Mediadaten und Bestellungen. Eine Möglichkeit zum Export von allen importierten Daten zu \ac{IX} selbst, sind ebenfalls Aussichten für die Zukunft.
Aufgrund des im Abschnitt~\ref{sec:Architekturdesign}: \nameref{sec:Architekturdesign} beschriebenen modularen Aufbaus, können die Anpassungen
\bzw Erweiterungen einfach vorgenommen werden. Die Modularität der Anwendung ermöglicht somit eine gute Wart- und Erweiterbarkeit.

View file

@ -0,0 +1,82 @@
% !TEX root = ../Projektdokumentation.tex
\section{Implementierungsphase}
\label{sec:Implementierungsphase}
\subsection{Implementierung der Datenstrukturen}
\label{sec:ImplementierungDatenstrukturen}
Wie in Abschnitt~\ref{sec:Datenmodell}: \nameref{sec:Datenmodell} beschrieben, ermöglicht \ac{M2} eine Einbindung von individuell benötigten \acs{SQL}
Tabellen in die vorhandene Datenstruktur.
Dem \Anhang{app:InstallData} kann ein \acs{XML} entnommen werden, welches der Komponente neusta-m2-intex-base hinzugefügt wurde.
Dieses deklarative Initialisieren von Datenstrukturen wird ebenfalls vom Framework Symfony genutzt. Es ermöglicht dem Entwickler anzugeben,
wie die Datenstruktur einer Tabelle aussehen soll. \ac{M2} bestimmt dann den Unterschied der derzeitigen Tabellenstruktur und der Struktur, die durch
vorhandene \acs{XML} Definitionen angegeben wurde. Diese Unterschiede werden dann durch atomare \acs{SQL} Operationen wiedergespiegelt.
Zusätzlich priorisiert \ac{M2} deklarative Schemen und führt diese generell vor allen älteren Schema \ac{PHP} Scripten aus.
Die bestehende Datenbank wurde durch o.g. vorgehen um eine Basis-Tabelle erweitert, welche zur Nachvollziehbarkeit der Import-Vorgänge beitragen soll.
\subsection{Implementierung der Benutzeroberfläche}
\label{sec:ImplementierungBenutzeroberflaeche}
Die Implementierung der Benutzeroberfläche erfolgte, wie bereits im Abschnitt~\ref{sec:Benutzeroberflaeche}: \nameref{sec:Benutzeroberflaeche} geplant,
mit Hilfe der \ac{M2} eigenen Möglichkeiten zur Einbindung von \ac{CLI}-Befehlen.
Durch die Entscheidung des Autors, die Anwendung als \ac{M2} Modul umzusetzen, ergibt sich hier der Vorteil, dass die gewünschten und benötigten
\ac{CLI} Befehle per Dependency Injection direkt in die \ac{M2} Befehlsliste aufgenommen werden können. Somit können Entwickler bei der Nutzung der Anwendung
auf das Standard Interface von \ac{M2} zurückgreifen und separate Zugangsdaten oder Aufrufe werden vermieden.
Um der \ac{M2} Instanz die gewünschten Befehle zuzufügen, wurde die Klasse CliCommand der Komponente neusta-m2-intex-cli hinzugefügt. Diese Klasse
leitet von der abstrakten \ac{M2} Klasse Command ab und bindet in \ac{M2} inkludierte Symfony-Komponenten zur Verarbeitung von Ein- und Ausgabe ein.
\subsection{Implementierung der Geschäftslogik}
\label{sec:ImplementierungGeschaeftslogik}
Da die Implementierung der Geschäftslogik den Kernbestandteil des gesamten Projektes darstellt, soll diese im folgenden Abschnitt
genauer erläutert werden. Um die Implementierung möglichst komfortabel durchzuführen, wurde die Entwicklungsumgebung JetBrains PHPStorm 2019 hierfür
vom Autor gewählt.
Zu Beginn der Implementierung wurde das Grundgerüst der Komponenten für die Anwendung erstellt. Hierzu wurde für jede in
Abschnitt~\ref{sec:Geschaeftslogik}: \nameref{sec:Geschaeftslogik} erwähnte Komponente und Subkomponente die Grundlage zur Einbindung als
Composer Package gelegt. Jede Komponente beinhaltet eine \acs{JSON} Datei, die Angaben zum Namen des Packages, der Version, einer Package-Typ Definition
(hier: magento2-module), Abhängigkeiten zu anderen Packages sowie Informationen zum Autor beinhaltet (Beispiel: \Anhang{app:ComposerJson}). Somit konnten die Komponenten
im einzelnen als separates Projekt angesehen werden.
Nachdem die Grundlagen zur Aufteilung der Anwendung in modulare Komponenten gelegt wurde, befasste sich der Autor damit, die lokale Entwicklungsumgebung aufzusetzen.
Hierzu wurde ein für \ac{M2} bereits vorhandenes Docker \footnote{Docker - \url{https://www.docker.com}} Setup der \ac{NSD} genutzt. Docker ist ein \ac{VM} Container System, welches im genutzten Setup Container für
jeweils den Quellcode der \ac{M2} Instanz, der Datenbank sowie dem benötigten NGINX \footnote{NGINX - \url{https://www.nginx.com/}} Server bereit stellt. Dem Autor wurde hiermit ein natives Aufsetzen einer \ac{M2} Instanz
erspart, was zur Qualität der Entwicklung erheblich beitrug und gleichzeitig garantiert, dass die Anwendung auf der tatsächlichen Serverumgebung lauffähig ist.
Nach der Fertigstellung des Grundgerüstes und der lokalen Umgebung konnte mit der eigentlichen Implementierung fortgesetzt werden.
Wie in Abschnitt~\ref{sec:Entwicklungsprozess}: \nameref{sec:Entwicklungsprozess} beschrieben, entschied der Autor sich für eine agile Umsetzung in einer Mischung
aus Kanban und Scrum. Jede einzelne Komponente ergab somit eine Story im Kanban Board, welche nach Scrum Prinzip zusammen mit den Mitarbeitern der \ac{NSD} priorisiert wurden.
Die Implementierung des Basis Moduls neusta-m2-intex-base wurde zügig Vollzogen, da die Komponente lediglich als Wrapper für alle Funktionen dienen soll.
Die o.g. Priorisierung gemäß Scrum sah nun vor, dass die Implementierung der Verbindungslogik zu \ac{IX} am wichtigsten ist, da alle weiteren Logik-Komponenten hierrauf aufbauen müssen.
Deswegen soll an dieser Stelle die Implementierung der Komponente neusta-m2-intex-client als Beispiel herangezogen werden.
Zunächst wurde in der \acs{JSON} Datei für Composer definiert, dass neusta-m2-intex-base die Grundabhängigkeit für das Modul darstellt. So wurden duplizierte
Einträge in der Konfiguration vermieden, da diese bereits in der Komponente neusta-m2-intex-base inkludiert sind. Zusätzlich besteht eine Abhängigkeit zur Komponente
neusta-m2-intex-client-config, da in der Komponente die Konfigurationen für die \ac{IX}-\acs{API} Zugriffe vorgenommen werden sollen. Die Konfigurationen beinhalten neben
den Key-Value Paaren zum Mapping der Produkt- und Nutzerattribute auf den \ac{M2} Standard auch die grundlegenden Informationen wie Passwort und Nutzername zur Verbindung
mit \ac{IX}.
Die Aufgabe des neusta-m2-intex-client in der Anwendung soll sein, jegliche Datenverbindung von der entwickelnden Anwendung mit der \ac{IX}-\acs{API}
herzustellen und gleichzeitig ein nachvollziehbares Logging dieser Verbindungen und eventuell auftretender Fehler zur Verfügung zu stellen.
Wie in Abschnitt~\ref{sec:Architekturdesign}: \nameref{sec:Architekturdesign} beschrieben, sah der Entwurf vor, das Factory Design Pattern zu nutzen und so
mehrere Instanzen der Verbindungen zu vermeiden. Hierzu wurde in der Komponente neusta-m2-intex-client die Klasse Factory implementiert, welche die Instanziierung
der Klassen abbildete, die neusta-m2-intex-client der Anwendung zur Verfügung stellen soll. Der Grundgedanke ist hierbei, dass die Komponente zum Import der Nutzerdaten
(hier: neusta-m2-intex-customer) die Factory des neusta-m2-intex-client nutzen und so seine Verbindung zu \ac{IX} herstellen lassen kann. Der Quellcode der Factory kann
dem \Anhang{app:Factory} entnommen werden.
Um Requests zur \ac{IX}-\acs{API} zu verarbeiten, entschied der Autor, den Open Source \ac{HTTP} Client GuzzlePhp \footnote{GuzzlePhp - \url{https://github.com/guzzle/guzzle}} zu nutzen. Hierdurch erlangte der Autor die Möglichkeit,
asynchrone Requests an die \acs{API} von \ac{IX} zu stellen, ohne einen eigenen \ac{PHP} \acs{HTTP} Client entwickeln zu müssen.
Die Factory instanziiert nun die beim Aufruf angeforderte Klasse. Hier als Beispiel CustomerConnection (siehe \Anhang{app:CustomerConnection} und \Anhang{app:Connection}), welche im
CustomerDataController in der Komponente neusta-m2-intex-customer instanziiert wird (siehe \Anhang{app:CustomerDataController}).
Durch den Fokus auf das Fertigstellen der neusta-m2-intex-client Komponente, waren alle folgenden Subkomponenten trivialer zu implementieren, da bei einem Datenimport
die Abfrage der Daten der Hauptbestandteil einer solchen Anwendung darstellt. Dem folgend wurde vom Autor in Reihenfolge die Logik zur Verarbeitung der erhaltenen Daten
implementiert (neusta-m2-intex-customer \& neusta-m2-intex-catalog) sowie die Möglichkeit die Funktionalität über einen \acs{CRON}-Job anstoßen zu können, welche
im \ac{M2} Standard per \acs{XML} Datei inkludiert werden kann.

View file

@ -27,7 +27,7 @@ Services übernehmen die Kommunikation mit dem Backend und kapseln die Geschäft
\subsubsection{Backend-Architektur}
Das Backend implementiert eine klassische mehrschichtige Architektur, die eine klare Trennung der Verantwortlichkeiten gewährleistet. Die Controller-Schicht stellt die REST-\acs{API}-Endpunkte bereit und behandelt \acs{HTTP}-Anfragen. Die Service-Schicht enthält die Geschäftslogik und orchestriert verschiedene Use Cases.
Die Repository-Schicht abstrahiert den Datenzugriff und verwendet Spring Data JPA für die Kommunikation mit der Datenbank. Entity-Klassen repräsentieren die Domain-Modelle und bilden die Datenbankstrukturen ab. Eine detaillierte Darstellung der verschiedenen Architekturschichten mit konkreten Code-Beispielen findet sich im Anhang (siehe \ref{app:CodeSchichten}).
Die Repository-Schicht abstrahiert den Datenzugriff und verwendet Spring Data JPA für die Kommunikation mit der Datenbank. Entity-Klassen repräsentieren die Domain-Modelle und bilden die Datenbankstrukturen ab.
\subsection{Datenarchitektur}
Die Datenbank folgt einem relationalen Design mit klar definierten Entitätsbeziehungen. Das Schema gliedert sich in mehrere Hauptbereiche: Der User Management Bereich verwaltet Benutzerkonten und Benutzerprofile. Spielbezogene Daten wie Spielstände, Wetten und Ergebnisse werden in separaten Tabellen gespeichert, um die Integrität der Spiellogik zu gewährleisten.

View file

@ -0,0 +1,48 @@
% !TEX root = ../Projektdokumentation.tex
\section{Projektplanung}
\label{sec:Projektplanung}
\subsection{Projektphasen}
\label{sec:Projektphasen}
Für die Umsetzung des Projektes standen dem Autor 70 Stunden zur Verfügung. Zu Projektbeginn wurden diese auf, wie in der
Softwareentwicklung üblich, verschiedene Phasen verteilt, die während der Entwicklung durchlaufen werden.
Eine grobe Zeitplanung sowie die Hauptphasen lassen sich der Tabelle~\ref{tab:Zeitplanung} entnehmen.
Eine Pufferzeit wurde bereits in der Planung der Implementierung inkludiert.
Die Hauptphasen werden außerdem noch in kleinere Unterpunkte zerlegt. Eine detaillierte Übersicht der Phasen befindet sich im \Anhang{app:Zeitplanung}.
\tabelle{Zeitplanung}{tab:Zeitplanung}{ZeitplanungKurz}\\
\subsection{Ressourcenplanung}
\label{sec:Ressourcenplanung}
Eine genaue Auflistung der verwendeten Ressourcen, welche für das Projekt eingesetzt wurden, kann der Übersicht im
\Anhang{app:Ressourcen} entnommen werden. Hiermit sind sowohl Hard- und Softwareressourcen als auch Personal vom Autor berücksichtigt worden.
Bei der Auswahl der verwendeten Software wurde darauf geachtet, dass diese kostenfrei (\zB als Open Source) zur Verfügung
steht oder die \ac{NSD} bereits Lizenzen für diese besitzt. Hierdurch sollen anfallende Projektkosten so gering wie möglich
gehalten werden.
\subsection{Entwicklungsprozess}
\label{sec:Entwicklungsprozess}
Bevor mit der Realisierung des Projektes begonnen werden konnte, musste sich der Autor für eine geeignete Vorgehensweise zur Umsetzung entscheiden.
Der Autor hat sich auf einen agilen Entwicklungsprozess festgelegt. Hierbei wird in Anlehnung
an das Vorgehensmodell Kanban \footnote{Wikipedia Kanban Definition - \url{https://en.wikipedia.org/wiki/Kanban}} gearbeitet, welches durch Elemente des Vorgehensmodells Scrum \footnote{Scrum.org - \url{https://www.scrum.org}} erweitert werden soll.
Die für Scrum typischen Iterationen der Projektphasen sowie die damit verbundene, stetige Rücksprache
mit den Stakeholdern sind einige Merkmale, die diesen agilen Entwicklungsprozess auszeichnen und in diesem Abschlussprojekt Anwendung finden.
Eine flexible Umsetzung der Anforderung wird durch die relativ kurzen Entwicklungszyklen ermöglicht. Hierdurch können den Mitarbeitern der \ac{NSD}
zeitnah Resultate präsentiert werden. Aufgrund dieser Tatsache wurde bei der Projektplanung in Abschnitt~\ref{sec:Projektphasen}: \nameref{sec:Projektphasen} für die Entwurfsphase
weniger Zeit eingeplant, da sich Teile dieser Phase erst im Laufe der Entwicklung der Anwendung ergeben.
Das stetige Feedback zur Anwendung, resultierend aus der engen Kommunikation mit den Mitarbeitern, gewährleistet eine bessere und flexiblere Reaktionsfähigkeit
auf Änderungswünsche. So können die Kollegen des Autors während der Entwicklung mit dem System vertraut gemacht werden, welches zur Folge den Vorteil bringt,
dass bei Projektabnahme und -einführung Zeit eingespart werden kann. Dies wurde vom Autor in der Zeitkalkulation bereits berücksichtigt.
Außerdem soll der agile Entwicklungsprozess durch die Praktik der testabgedeckten Entwicklung erweitert werden. Hierbei werden Softwaretests nach der Implementierung
des Quellcodes erstellt. Dies hat zum Vorteil, dass nachträgliche Änderungen immer das gleiche Endresultat in der Datenausgabe erwarten und erhöht somit die Wartbarkeit
der gesamten Anwendung.

View file

@ -1,27 +0,0 @@
\section{Vorbereitung}
\label{sec:Vorbereitung}
\subsection{Ist-Analyse}
Im Rahmen der durchgeführten Marktanalyse konnten mehrere relevante Marktlücken identifiziert werden, die das geplante Produkt gezielt adressieren kann. Ein zentrales Defizit betrifft die technologische Basis vieler bestehender Casino-Plattformen. Zahlreiche Anbieter setzen noch auf veraltete Technologien, was sich nachteilig auf Performance, Wartbarkeit und Erweiterbarkeit auswirkt. Moderne Webtechnologien und Frameworks könnten in diesem Zusammenhang klare Vorteile schaffen, etwa durch schnellere Ladezeiten und verbesserte Skalierbarkeit.
Im Bereich der Benutzerfreundlichkeit bestehen Defizite. Viele Plattformen bieten eine unzureichende mobile Nutzererfahrung oder verlangen komplexe, zeitaufwändige Registrierungsprozesse oft ohne Social-Login oder einfache Authentifizierung. Ein Beispiel hierfür ist der Zwang zu langwierigen Formularen, die Nutzer gerade auf mobilen Geräten häufig frustrieren und zum Abbruch des Vorgangs führen.
Ein weiteres Problem liegt bei den Sicherheitsstandards. In zahlreichen Anwendungen sind moderne Authentifizierungsverfahren wie OAuth2 oder Zwei-Faktor-Authentifizierung noch nicht implementiert. Dies hat nicht nur zur Folge, dass das Vertrauen der Nutzer geschwächt wird, sondern öffnet auch potenzielle Sicherheitslücken.
Schließlich bestehen Defizite in der Integration zeitgemäßer Zahlungsprozesse. Oft sind Einzahlungen umständlich und erfordern manuelle Schritte, da moderne Payment-APIs wie Stripe, Apple Pay oder Google Pay nicht eingebunden sind. Ein reibungsloser, mobiltauglicher Zahlungsfluss könnte sich in diesem Zusammenhang als entscheidender Wettbewerbsvorteil erweisen.
\subsection{Soll-Analyse}
Für das geplante Online-Casino wurde eine umfassende Soll-Analyse durchgeführt, die die zentralen funktionalen, technischen und sozialen Anforderungen beschreibt.
Im Bereich des Spieleportfolios ist eine ausgewogene Mischung aus klassischen und unterhaltsamen Spielen vorgesehen. Zu den angebotenen Spielen zählen Blackjack mit erweiterten Spieloptionen wie Split und Double Down sowie verschiedene Slotmaschinen, deren RTP (Return to Player) flexibel konfigurierbar sein soll. Das Angebot wird durch einfache Zufallsspiele wie Dice, Coinflip sowie unterhaltungsorientierte Features wie Lootboxen, Plinko und Poker ergänzt, um unterschiedliche Spieltypen anzusprechen und die Nutzer langfristig zu binden.
Die Benutzerverwaltung basiert auf einem modernen, sicheren Authentifizierungssystem. Die Anmeldung erfolgt über eine Multi-Provider-OAuth2-Lösung, beispielsweise mit GitHub oder Google-Konten. Die Sitzungsverwaltung erfolgt über JWT-basierte Tokens, ergänzt durch zusätzliche Sicherheitsmechanismen wie E-Mail-Verifizierung und die Möglichkeit zum Passwort-Reset, um Datenschutz und Benutzerkomfort gleichermaßen zu gewährleisten.
Das Finanzsystem ist auf Einzahlungen in Echtzeit ausgerichtet. Zu diesem Zweck wird eine Stripe-Integration implementiert, die einen reibungslosen und sicheren Zahlungsprozess ermöglicht. Intern erfolgt die Verwaltung der Guthaben über eine virtuelle Währung, deren Genauigkeit mithilfe von BigDecimal präzise nachvollziehbar bleibt. Eine lückenlos nachvollziehbare Transaktionshistorie mit Status-Tracking gewährleistet maximale Transparenz und Nachvollziehbarkeit aller Zahlungsvorgänge.
Gemäß den technischen Anforderungen ist ein responsives Design mit Fokus auf Mobile-First-Nutzung erforderlich, um eine hohe Benutzerfreundlichkeit auf verschiedenen Plattformen zu gewährleisten. Echtzeitaktualisierungen der Inhalte sollen ohne erneuten Seitenaufbau erfolgen, um eine nahtlose und dynamische Spielerfahrung zu gewährleisten. Es ist essenziell, dass sämtliche API-Kommunikation über gesicherte Schnittstellen abgewickelt wird, um die Integrität der Daten und den Schutz sensibler Informationen sicherzustellen.
Schließlich spielen auch die sozialen Funktionen eine wichtige Rolle. Ranglisten, eine motivierende In-Game-Währung sowie ein Fokus auf Transparenz im Umgang mit Daten, Spielverläufen und Zahlungsflüssen sollen die Community stärken, die Nutzerbindung fördern und das Vertrauen in das Produkt nachhaltig unterstützen. Das Produkt wird als moderne Webanwendung direkt über den Browser bereitgestellt und ist somit ohne Download oder Installation für den Nutzer zugänglich. Um das Online-Casino nutzen zu können, sind einige technische Voraussetzungen erforderlich. Für die Anwendung wird ein aktueller Webbrowser wie Google Chrome, Mozilla Firefox, Safari oder Microsoft Edge vorausgesetzt. Zudem ist JavaScript aktiviert sein, da dieses für die Darstellung der Spielmechanik und der Benutzeroberfläche essenziell ist. Für eine reibungslose Kommunikation mit dem Server und ein flüssiges Spielerlebnis ist eine stabile Internetverbindung erforderlich. Zusätzlich ist die Erlaubnis von Cookies erforderlich, um Sitzungsdaten zu speichern und personalisierte Inhalte oder Nutzertracking zu ermöglichen.
Die genannten technischen Voraussetzungen bilden die Grundlage für einen stabilen und benutzerfreundlichen Zugriff auf die Anwendung. Abgesehen von den Basiskomponenten ergeben sich jedoch weiterführende Anforderungen an die Systemarchitektur, insbesondere im Hinblick auf die Sicherheit und die Fairness der Spielergebnisse.
Eine zentrale Herausforderung bei der technischen Umsetzung des Online-Casinos liegt in der sicheren und manipulationssicheren Spielauswertung. Um die Integrität des Spiels zu gewährleisten und Manipulationen durch Nutzer im Frontend zu verhindern, müssen sämtliche Spielmechaniken serverseitig ausgewertet werden. Dies bedeutet, dass Zufallsberechnungen, Spielergebnisse und Gewinnermittlungen ausschließlich auf dem Server erfolgen, bevor sie an das Frontend übermittelt werden. Diese Architektur gewährleistet, dass die Spielchancen stets fair und unveränderbar bleiben. Gleichzeitig erfordert sie eine besonders zuverlässige Server-Infrastruktur, da sie sowohl in Echtzeit als auch mit hoher Verfügbarkeit arbeiten muss. Diese Anforderung stellt eine bedeutsame technische Einschränkung dar, die bei der Systemarchitektur, der Performanceoptimierung sowie der Skalierung von Beginn an zu berücksichtigen ist.

View file

@ -1,67 +0,0 @@
\section{Wirtschaftliche Betrachtung}
\subsection{Marktuntersuchung}
Das Online-Casino fokussiert sich in erster Linie auf eine junge, technikaffine Zielgruppe im Alter von 18 bis 35 Jahren. Diese Nutzergruppe verfügt über Erfahrungen mit digitalen Anwendungen, zeigt eine hohe Bereitschaft für Internetaktivitäteten und ist für neue, spielerische Unterhaltungsformate insbesondere im Bereich des Web-Gamings aufgeschlossen. Sie sucht nach kurzweiligen, optisch ansprechenden Angeboten, die jederzeit und ohne große Einstiegshürde zugänglich sind.
Als sekundäre Zielgruppe werden Gelegenheitsspieler angesprochen, die besonderen Wert auf eine einfache, benutzerfreundliche Bedienung legen. Für diese Nutzer steht nicht die Technik, sondern das unkomplizierte Spielvergnügen im Vordergrund. Die Nutzerinnen und Nutzer erwarten eine klare Navigation, intuitive Spielmechaniken und möglichst wenig Hürden bei Registrierung, Einzahlung und Nutzung. Durch die Fokussierung auf zwei Zielgruppen soll eine breite Nutzerbasis aufgebaut werden, die sowohl regelmäßige als auch spontane Spieler umfasst.
Die Erwartungshaltung dieser Zielgruppe umfasst ein unterhaltsames, fesselndes und optisch ansprechendes Spielerlebnis, das eine authentische Casino-Atmosphäre vermittelt. Dabei legen die Nutzer großen Wert auf eine intuitive Benutzeroberfläche, schnelle Ladezeiten sowie abwechslungsreiche Spiele wie Slots, Blackjack oder CoinFlip. Gleichzeitig legen sie großen Wert auf einen reibungslosen und sicheren Zugang zum Spiel. Aus diesem Grund erwarten sie selbstverständlich moderne Mechanismen für eine zuverlässige und geschützte Anmeldung und Nutzung.
Die Erwartungen der Zielgruppe geben wichtige Hinweise darauf, welche Funktionen und Eigenschaften ein erfolgreiches Produkt aufweisen muss. Um die gestellten Erwartungen jedoch auch unter wirtschaftlichen Gesichtspunkten bedienen zu können, ist eine Analyse des Marktvolumens und -potenzials unerlässlich.
Der Markt für Social Casinos und Online-Casinos mit Auszahlungsverweigerung ist weltweit wachsend. Laut Branchenanalysen (z.B. Statista, Business Research Insights) liegt das Volumen des globalen Social-Casino-Markts bei mehreren Milliarden USD pro Jahr, mit jährlichen Wachstumsraten im hohen einstelligen Prozentbereich. In Zielmärkten wie den USA, Europa und Teilen Asiens ist das Potenzial besonders hoch, insbesondere bei Nutzern, die Glücksspiel aus rechtlichen oder moralischen Gründen meiden, aber den Spielspaß schätzen.
Das vorhandene Absatzpotential ist jedoch stark eingeschränkt auf eine klar umrissene Zielgruppe. Es richtet sich an Nutzer, die Glücksspiele primär als Entertainment betrachten und sich der Tatsache bewusst sind, dass keine Auszahlung möglich ist. In diesem Segment könnte eine emotionale Markenbindung ähnlich wie bei Gaming- oder Streaming-Abonnements aufgebaut werden. Nichtsdestotrotz ist das Marktvolumen im Vergleich zu klassischen Casino-Plattformen begrenzt, da der Reiz vieler Online-Casinos im potenziellen Gewinn liegt.
Die Wettbewerbssituation für das geplante Online-Casino ohne Auszahlungsoption ist als intensiv und dynamisch zu bezeichnen, da sich das Produkt in einem stark umkämpften digitalen Unterhaltungsmarkt bewegt. Das Wettbewerbsumfeld wird dabei von zwei Hauptarten von Konkurrenzprodukten geprägt. Einerseits klassische Online-Casinos mit Echtgeld-Auszahlung, die aufgrund der Aussicht auf reale Geldgewinne für viele Nutzer besonders attraktiv sind. Es sei darauf hingewiesen, dass renommierte Anbieter wie 888casino, LeoVegas, Stake oder Tipico Casino in der Regel staatlich lizenziert sind, ein umfangreiches Spielangebot offerieren und von hoher Markenbekanntheit sowie intensiven Marketingaktivitäten profitieren. Andererseits existieren Social Casinos ohne Auszahlungsfunktion, die in direkter Konkurrenz zum geplanten Produkt stehen. Plattformen wie Slotomania, House of Fun, Big Fish Casino oder DoubleDown Casino ermöglichen den Einsatz von Echtgeld, bieten jedoch keine reale Auszahlung. Stattdessen liegt der Fokus auf Unterhaltung, Gamification, täglichen Belohnungen, virtuellen Währungen und exklusiven VIP-Features. Diese Angebote sind in der Regel als Apps verfügbar, verfügen über eine große Nutzerbasis und arbeiten mit durchdachten Mechaniken zur langfristigen Nutzerbindung.
Die enorme Verbreitung und Beliebtheit dieser Social-Casino-Plattformen wirft die Frage auf, welche konkreten Stärken und Schwächen diese bestehenden Angebote aufweisen. Ein näherer Blick auf die Vor- und Nachteile der Konkurrenzprodukte liefert wichtige Erkenntnisse für die strategische Ausrichtung.
So ist liegt ein zentraler Vorteil bestehender Social-Casino-Produkte in der transparenten Erwartungshaltung der Nutzer. Da keine Auszahlungen von Echtgeld vorgesehen sind, besteht kein Missverständnis über mögliche Gewinne. Zudem unterliegen diese Angebote häufig geringeren regulatorischen Anforderungen, da sie rechtlich nicht als klassisches Glücksspiel eingestuft werden und somit oft keine spezielle Lizenz benötigen. Der gezielte Einsatz von Gamification-Elementen und sozialen Funktionen wie Ranglisten oder täglichen Herausforderungen führt auf vielen dieser Plattformen zu einer hohen Nutzerbindung.
Gleichzeitig sind bei diesen Modellen gewisse Nachteile zu berücksichtigen. Da reale Gewinne ausgeschlossen sind, ist die Zahlungsbereitschaft der Nutzer in der Regel geringer. Wird das Belohnungssystem als wenig attraktiv oder zu repetitiv wahrgenommen, sinkt die Verweildauer auf der Plattform schnell. Des Weiteren bedienen sich zahlreiche dieser Produkte sogenannter Pay-to-Win-Mechaniken, welche den zahlenden Nutzern Vorteile gegenüber anderen Teilnehmern verschaffen. Dieser Umstand resultiert bei der Spielerschaft häufig in Frustration und Unzufriedenheit.
Um sich in dieser Wettbewerbslandschaft zu behaupten, muss sich das neue Produkt durch klare Kommunikation, hohe Benutzerfreundlichkeit, attraktive Preisgestaltung sowie ein überzeugendes Unterhaltungs- und Belohnungssystem von der Masse abzuheben, um sich erfolgreich zu behaupten. Die Transparenz über den Verzicht auf Auszahlungen sowie eine hochwertige, faire Spielerfahrung sind von entscheidender Bedeutung, um Vertrauen aufzubauen und langfristige Nutzerbindung zu erreichen.
Obwohl das geplante Geschäftsmodell einige Vorteile und einen innovativen Ansatz bietet, sollten auch dessen potenzielle Risiken nicht außer Acht gelassen werden. Rechtlich besteht die Möglichkeit, dass das Angebot in bestimmten Ländern dennoch dem Glücksspielrecht unterliegt, auch wenn keine Auszahlungen von Echtgeld erfolgen. Dies kann zu regulatorischen Hürden oder Einschränkungen bei der Vermarktung führen. Es empfiehlt sich, auch auf reputativer Ebene Vorsicht walten zu lassen. Das Modell könnte von Teilen der Öffentlichkeit oder den Medien als irreführend oder potenziell ausbeuterisch wahrgenommen werden, insbesondere wenn hohe In-App-Ausgaben ohne reale Gegenleistung entstehen.
Ein weiteres Risiko betrifft die Marktakzeptanz. Da keine echten Gewinne erzielt werden können, besteht die Gefahr, dass potenzielle Nutzer dem Angebot mit geringem Interesse begegnen oder es schnell wieder verlassen. Die Kaufschwelle ist in diesem Zusammenhang von großer Bedeutung. Ohne die Aussicht auf Rückgewinn oder reale Belohnung wäre die Bereitschaft, für virtuelle Inhalte Geld auszugeben, deutlich geringer als bei klassischen Echtgeld-Casinos.
Aus den genannten Chancen und Risiken ergeben sich klare Anforderungen für das Projekt. Es ist von besonderer Wichtigkeit, dass die Spielbedingungen stets transparent dargestellt werden und die Nutzer darüber informiert werden, dass keine Auszahlungen von Echtgeld erfolgen. Um rechtliche Schwierigkeiten zu vermeiden, ist zudem eine umfassende rechtliche Prüfung in allen relevanten Zielmärkten erforderlich. Auf echte Gewinnchancen muss verzichtet werden, dennoch ist das Nutzererlebnis attraktiv zu gestalten. Dies kann beispielsweise durch die Implementierung von Gamification-Elementen, ansprechendem visuellen Feedback und sozialen Features, welche die Unterhaltung fördern, realisiert werden. Gleichzeitig ist ein ethisches Branding erforderlich, das das Produkt deutlich von traditionellen Glücksspielanbietern abgrenzt. Schließlich sollten innovative Monetarisierungsmodelle zum Einsatz kommen, wie Abonnements, Mikrotransaktionen oder auch Spendenmechanismen, um wirtschaftlich erfolgreich zu sein.
\subsection{Marketing Mix}
\subsubsection{Preispolitik}
Bei der Markteinführung ist eine Positionierung auf einem niedrigen bis mittleren Preisniveau vorgesehen. Das Ziel besteht darin, die Einstiegshürde möglichst gering zu halten und dadurch eine breite Zielgruppe zum Ausprobieren zu motivieren. Nutzer können bereits mit geringen Beträgen ab etwa 1 bis 5 Euro spielen. Zur Steigerung der Attraktivität tragen Willkommensboni, Freispiele oder die Verdopplung der ersten Einzahlung bei. Diese Maßnahmen positionieren das Produkt klar als unterhaltungsorientiertes, risikoarmes Angebot, das sich bewusst von klassischen Echtgeld-Casinos mit hoher finanzieller Schwelle abgrenzt.
Im Laufe des Produktlebenszyklus ist eine flexible Anpassung des Preismodells vorgesehen. In der Einführungsphase werden Niedrigpreise und Rabatte eingesetzt, um Nutzer zu gewinnen. In der Wachstumsphase besteht die Möglichkeit, größere Pakete einzuführen, wie beispielsweise Monatsabonnements oder Coin-Bundles. Während der Reifephase rücken Maßnahmen wie das Up-Selling in den Fokus, etwa durch den Verkauf von kosmetischen Items, Sonderfunktionen oder VIP-Zugängen. In der Sättigungsphase dienen zeitlich begrenzte Rabatte, Sonderaktionen und Events der Reaktivierung inaktiver Nutzer. Preisänderungen sollen stets mit neuen Inhalten, Features oder Events kombiniert werden, um die Zahlungsbereitschaft zu steigern und zu legitimieren.
Als zentrale Preisstrategie kommt eine Kombination aus Promotions- und Bonusstrategien zum Einsatz. Hierzu zählen insbesondere Treueprämien und VIP-Stufenprogramme sowie zeitlich befristete Sonderangebote, wie etwa Freispiele am Wochenende. Die vorliegende Strategie zielt darauf ab, kurzfristig eine große Anzahl an Nutzern zu gewinnen, diese emotional an das Produkt zu binden und sie zur wiederholten Einzahlung zu motivieren, obwohl eine Auszahlung in Echtgeld nicht möglich ist.
Darüber hinaus sind Preisdifferenzierungen ein zentraler Bestandteil der Monetarisierung. Es ist eine segmentbasierte Differenzierung geplant, bei der Gelegenheitsspieler kleinere Pakete erhalten, während Vielspieler Zugang zu größeren, exklusiveren Angeboten haben. Darüber hinaus wäre auch eine zeitliche Preisdifferenzierung, wie etwa Rabatte zu bestimmten Tageszeiten oder saisonalen Events, vorstellbar. Ergänzend werden psychologische Preisstrategien eingesetzt, wie etwa die Preisgestaltung mit Schwellenwerten (z. B. 4,99 € statt 5,00 €), um die Zahlungsbereitschaft der Kunden auf subtilere Weise zu fördern.
Die Preisgestaltung verfolgt insgesamt eine wachstumsorientierte Strategie, die Nutzer sowohl in der frühen als auch in der langfristigen Nutzung des Produkts optimal anspricht und gleichzeitig wirtschaftlich tragfähig bleibt.
\subsubsection{Kommunikationspolitik}
Das Produkt soll mittels einer strategisch ausgerichteten Werbestrategie, die verschiedene Kommunikationskanäle einbezieht, bekannt gemacht werden. Im Mittelpunkt stehen dabei Social-Media-Plattformen wie Meta (Facebook), TikTok und Instagram. Dort besteht die Möglichkeit, visuell ansprechende Kurzvideos zu veröffentlichen, die Spielspaß, schnelle Action und Belohnungssysteme in den Vordergrund stellen. Des Weiteren empfiehlt sich die Durchführung von Influencer-Marketing, um eine authentische Reichweite und ein Vertrauenverhältnis aufzubauen. Darüber hinaus erweisen sich Mobile Ads, also In-App-Werbung in anderen Spielen oder Anwendungen, als besonders effektiv, da sie spielaffine und potenziell zahlungsbereite Nutzer direkt im Nutzungskontext erreichen.
Ein weiterer wichtiger Kanal ist das Suchmaschinenmarketing, insbesondere über Google Ads. In diesem Kontext ist die Implementierung zielgerichteter Kampagnen mit spezifischen Keywords wie "kostenlos spielen", "Online Slots", "Casino ohne Auszahlung" oder "Spielspaß mit Echtgeld" ratsam. Dabei ist es essenziell, den Nutzer stets einen eindeutigen Hinweis zu präsentieren, dass die Erzielung von Echtgeldgewinnen nicht möglich ist. Darüber hinaus besteht die Möglichkeit, Streaming-Plattformen wie Twitch und YouTube zu nutzen, indem mit Streamern kooperiert wird, die unterhaltsames Gameplay zeigen und dabei nicht den Gewinn, sondern das Spielerlebnis betonen.
In allen Kanälen ist eine klare und transparente Kommunikation von essentieller Bedeutung, in welcher darauf hingewiesen wird, dass keine Auszahlungsfunktion zur Verfügung steht. Die Werbung muss das Produkt eindeutig als Entertainment-Angebot und nicht als Glücksspiel mit Gewinnerwartung positionieren, um sowohl die Nutzererwartung korrekt zu steuern als auch rechtlichen Risiken vorzubeugen.
Die Werbemaßnahmen sollen darauf abzielen, zentrale Produkteigenschaften und Mehrwerte zu kommunizieren, die das geplante Unterhaltungs-Casino von der Konkurrenz abheben. Im Mittelpunkt steht dabei die technische und visuelle Qualität des Angebots, wie schnelle Ladezeiten, eine intuitive Bedienoberfläche sowie hochwertige Grafiken und flüssige Animationen sollen ein modernes und ansprechendes Nutzererlebnis vermitteln. Gleichzeitig wird großer Wert auf Transparenz gelegt, insbesondere durch klare Hinweise auf den Unterhaltungscharakter des Spiels, etwa durch Formulierungen wie "Kein echtes Glücksspiel reines Entertainment".
Darüber hinaus ist es von entscheidender Bedeutung, die niedrigen Einstiegshürden deutlich zu kommunizieren. Durch die Bereitstellung von Willkommensangeboten, geringen Einstiegskosten und einer einfachen Einzahlung über moderne Zahlungsschnittstellen wird die Hemmschwelle für neue Nutzer gesenkt. Im Bereich der Sicherheit wird durch die Implementierung von OAuth2 auf aktuelle Standards gesetzt, was ebenfalls als vertrauensbildendes Element in der Werbung hervorgehoben werden kann. Die Intention der kommunizierten Informationen ist es, das Produkt als leicht zugänglich, sicher, unterhaltsam und fair zu positionieren.
\subsubsection{Distributionspolitik}
Das Produkt wird als moderne Webanwendung direkt über den Browser bereitgestellt und ist somit ohne Download oder Installation für den Nutzer zugänglich. Um das Online-Casino nutzen zu können, sind einige technische Voraussetzungen erforderlich. Für die Anwendung wird ein aktueller Webbrowser wie Google Chrome, Mozilla Firefox, Safari oder Microsoft Edge vorausgesetzt. Zudem ist JavaScript aktiviert sein, da dieses für die Darstellung der Spielmechanik und der Benutzeroberfläche essenziell ist. Für eine reibungslose Kommunikation mit dem Server und ein flüssiges Spielerlebnis ist eine stabile Internetverbindung erforderlich. Zusätzlich ist die Erlaubnis von Cookies erforderlich, um Sitzungsdaten zu speichern und personalisierte Inhalte oder Nutzertracking zu ermöglichen.
Um ein optimales Spielerlebnis zu gewährleisten, wird die Nutzung auf einem Desktop-Computer oder Tablet mit ausreichend großem Bildschirm empfohlen, da dies die Übersichtlichkeit und Bedienbarkeit deutlich verbessert. Die Nutzung ist insbesondere dann stabil und sicher, wenn das Betriebssystem aktualisiert ist. Als aktuelle Versionen kommen in diesem Fall Windows 10 oder höher, macOS ab Version 11 sowie die jeweils aktuellen Versionen von Android oder iOS in Betracht. Die Webanwendung zeichnet sich insgesamt durch ihre Flexibilität und Plattformunabhängigkeit aus und ist mit gängigen Geräten und Standards problemlos kompatibel.
\subsection{Wirtschaftlichkeitsbetrachtung}
Die Wirtschaftlichkeit des geplanten Produkts ist grundsätzlich als sehr hoch einzuschätzen, sofern zentrale Rahmenbedingungen erfüllt sind. Ein entscheidender Vorteil besteht darin, dass sämtliche Einzahlungen als Einnahmen verbucht werden können, da es keine Auszahlungsverpflichtungen gibt. Dies ist ein klarer Unterschied zu klassischen Online-Casinos. Gleichzeitig fallen geringe variable Kosten pro Nutzer an, da weder physische Produkte hergestellt noch verschickt werden müssen und auch keine echten Geldgewinne ausbezahlt werden. Das Geschäftsmodell ist darüber hinaus hochgradig skalierbar. Ist die Plattform erst einmal technisch stabil aufgebaut, kann sie mit nur geringem zusätzlichem Aufwand von tausenden oder sogar Millionen Nutzern gleichzeitig genutzt werden.
Die möglichen Einnahmequellen sind vielfältig. Neben den klassischen Echtgeld-Einzahlungen bieten sich zusätzliche Monetarisierungswege an, etwa durch In-App-Verkäufe, wie kosmetische Upgrades oder VIP-Status, durch Werbung innerhalb der Plattform oder über Affiliate-Kooperationen mit passenden Partnern. Diese Faktoren tragen dazu bei, dass das Modell aus wirtschaftlicher Sicht äußerst attraktiv erscheint.
Die Rentabilität ist jedoch von mehreren kritischen Erfolgsfaktoren abhängig. Zu den erforderlichen Maßnahmen zählen die rechtliche Absicherung in den Zielmärkten, eine ausreichend hohe Nutzerakzeptanz trotz fehlender Auszahlungsfunktion und insbesondere die Fähigkeit, eine langfristige Kundenbindung durch ein unterhaltsames, faires und transparentes Spielerlebnis sicherzustellen.
Dennoch sind bei derartigen Unternehmungen stets die bestehenden Risiken zu berücksichtigen. Auf rechtlicher Ebene besteht die Gefahr, dass das Angebot mit echtem Glücksspiel verwechselt wird. Sollte der Eindruck entstehen, dass Nutzer reale Gewinne erwarten dürfen, so könnte dies rechtlich als Täuschung gewertet werden. Dies könnte mit strafrechtlichen Konsequenzen verbunden sein. Zudem existieren international unterschiedliche Regulierungen im Glücksspielrecht, die auch sogenannte Social Casinos betreffen können, etwa durch Lizenzpflichten oder strenge Anforderungen an die AGB und die Nutzeraufklärung.
Auch auf Ebene der Markenreputation können Risiken entstehen. Sollten Nutzer erst im Nachhinein erkennen, dass keine Auszahlung möglich ist, könnten sie sich getäuscht fühlen. Dies könnte sich in Form von negativen Bewertungen, Beschwerden oder sogar Shitstorms auf Social Media und Plattformen wie Trustpilot äußern.
Zudem ist der Markt von hoher Wettbewerbsintensität geprägt, in dem sowohl etablierte Online-Casinos als auch renommierte Anbieter von Social Games bereits eine starke Marktposition innehaben. Die Monetarisierung eines Produkts kann nur dann erfolgreich sein, wenn es gelingt, die Nutzer trotz fehlender Gewinnaussicht emotional an das Produkt zu binden und sie zum freiwilligen Erwerb virtueller Inhalte zu motivieren. An dieser Stelle wird sich letztlich entscheiden, ob das vorhandene wirtschaftliche Potenzial auch realisiert werden kann.

View file

@ -1,247 +0,0 @@
import { NgClass, NgIf, CurrencyPipe, CommonModule } from '@angular/common';
import { HttpClient } from '@angular/common/http';
import { FormsModule } from '@angular/forms';
import {
ChangeDetectionStrategy,
Component,
ElementRef,
inject,
OnInit,
signal,
ViewChild,
} from '@angular/core';
import { AnimatedNumberComponent } from '@blackjack/components/animated-number/animated-number.component';
import { catchError, finalize } from 'rxjs/operators';
import { of } from 'rxjs';
import { AuthService } from '@service/auth.service';
import { AudioService } from '@shared/services/audio.service';
import { CoinflipGame, CoinflipRequest } from './models/coinflip.model';
@Component({
selector: 'app-coinflip',
standalone: true,
imports: [AnimatedNumberComponent, CurrencyPipe, FormsModule, CommonModule, NgIf, NgClass],
templateUrl: './coinflip.component.html',
styleUrl: './coinflip.component.css',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export default class CoinflipComponent implements OnInit {
currentBet = signal(10);
balance = signal(0);
gameInProgress = signal(false);
isActionInProgress = signal(false);
gameResult = signal<CoinflipGame | null>(null);
betInputValue = signal(10);
errorMessage = signal('');
isInvalidBet = signal(false);
@ViewChild('coinElement') coinElement?: ElementRef;
audioService = inject(AudioService);
authService = inject(AuthService);
private http = inject(HttpClient);
private coinflipSound?: HTMLAudioElement;
ngOnInit(): void {
// Abonniere Benutzerupdates fuer Echtzeitaktualisierungen des Guthabens
this.authService.userSubject.subscribe((user) => {
if (user) {
this.balance.set(user.balance);
}
});
// Initialisiere Muenzwurf-Sound
this.coinflipSound = new Audio('/sounds/coinflip.mp3');
}
setBetAmount(percentage: number) {
const newBet = Math.floor(this.balance() * percentage);
this.betInputValue.set(newBet > 0 ? newBet : 1);
this.currentBet.set(this.betInputValue());
}
updateBet(event: Event) {
const inputElement = event.target as HTMLInputElement;
let value = Number(inputElement.value);
// Setze ungueltigen Einsatz-Status zurueck
this.isInvalidBet.set(false);
// Erzwinge Mindesteinsatz von 1
if (value <= 0) {
value = 1;
}
// Begrenze Einsatz auf verfuegbares Guthaben und zeige Feedback
if (value > this.balance()) {
value = this.balance();
// Visuelles Feedback anzeigen
this.isInvalidBet.set(true);
// Zeige den Fehler kurz an
setTimeout(() => this.isInvalidBet.set(false), 800);
// Aktualisiere das Eingabefeld direkt, um dem Benutzer den maximalen Wert anzuzeigen
inputElement.value = String(value);
}
// Aktualisiere Signale
this.betInputValue.set(value);
this.currentBet.set(value);
}
betHeads() {
this.placeBet('HEAD');
}
betTails() {
this.placeBet('TAILS');
}
private placeBet(side: 'HEAD' | 'TAILS') {
if (this.gameInProgress() || this.isActionInProgress()) return;
// Setze vorheriges Ergebnis zurueck
this.gameResult.set(null);
this.errorMessage.set('');
// Setze Spielstatus
this.gameInProgress.set(true);
this.isActionInProgress.set(true);
// Spiele Einsatz-Sound
this.audioService.playBetSound();
// Erstelle Einsatz-Anfrage
const request: CoinflipRequest = {
betAmount: this.currentBet(),
coinSide: side,
};
// API aufrufen
this.http
.post<CoinflipGame>('/backend/coinflip', request)
.pipe(
catchError((error) => {
console.error('Fehler beim Spielen von Coinflip:', error);
if (error.status === 400 && error.error.message.includes('insufficient')) {
this.errorMessage.set('Unzureichendes Guthaben');
} else {
this.errorMessage.set('Ein Fehler ist aufgetreten. Bitte versuche es erneut.');
}
this.gameInProgress.set(false);
return of(null);
}),
finalize(() => {
this.isActionInProgress.set(false);
})
)
.subscribe((result) => {
if (!result) return;
console.log('API-Antwort:', result);
// Behebe moegliche Inkonsistenzen bei der Eigenschaftenbenennung vom Backend
const fixedResult: CoinflipGame = {
isWin: result.isWin ?? result.win,
payout: result.payout,
coinSide: result.coinSide,
};
console.log('Korrigiertes Ergebnis:', fixedResult);
// Spiele Muenzwurf-Animation und -Sound
this.playCoinFlipAnimation(fixedResult.coinSide);
// Setze Ergebnis nach Abschluss der Animation
setTimeout(() => {
this.gameResult.set(fixedResult);
// Aktualisiere Guthaben mit neuem Wert vom Auth-Service
this.authService.loadCurrentUser();
// Spiele Gewinn-Sound, wenn der Spieler gewonnen hat
if (fixedResult.isWin) {
this.audioService.playWinSound();
}
// Setze Spielstatus nach Anzeigen des Ergebnisses zurueck
setTimeout(() => {
this.gameInProgress.set(false);
}, 1500);
}, 1100); // Kurz nach Ende der Animation
});
}
private playCoinFlipAnimation(result: 'HEAD' | 'TAILS') {
if (!this.coinElement) return;
const coinEl = this.coinElement.nativeElement;
// Setze bestehende Animationen zurueck
coinEl.classList.remove('animate-to-heads', 'animate-to-tails');
// Setze alle Inline-Styles von vorherigen Animationen zurueck
coinEl.style.transform = '';
// Erzwinge Reflow, um Animation neu zu starten
void coinEl.offsetWidth;
// Spiele Muenzwurf-Sound
if (this.coinflipSound) {
this.coinflipSound.currentTime = 0;
this.coinflipSound
.play()
.catch((err) => console.error('Fehler beim Abspielen des Sounds:', err));
}
// Fuege passende Animationsklasse basierend auf dem Ergebnis hinzu
if (result === 'HEAD') {
coinEl.classList.add('animate-to-heads');
} else {
coinEl.classList.add('animate-to-tails');
}
console.log(`Animation angewendet fuer Ergebnis: ${result}`);
}
/**
* Validiert Eingabe waehrend der Benutzer tippt, um ungueltige Werte zu verhindern
*/
validateBetInput(event: KeyboardEvent) {
// Erlaube Navigationstasten (Pfeile, Entf, Ruecktaste, Tab)
const navigationKeys = ['ArrowLeft', 'ArrowRight', 'Delete', 'Backspace', 'Tab'];
if (navigationKeys.includes(event.key)) {
return;
}
// Erlaube nur Zahlen
if (!/^\d$/.test(event.key)) {
event.preventDefault();
return;
}
// Ermittle den Wert, der nach dem Tastendruck entstehen wuerde
const input = event.target as HTMLInputElement;
const currentValue = input.value;
const cursorPosition = input.selectionStart || 0;
const newValue =
currentValue.substring(0, cursorPosition) +
event.key +
currentValue.substring(input.selectionEnd || cursorPosition);
const numValue = Number(newValue);
// Verhindere Werte, die groesser als das Guthaben sind
if (numValue > this.balance()) {
event.preventDefault();
}
}
getResultClass() {
if (!this.gameResult()) return '';
const result = this.gameResult();
const isWinner = result?.isWin || result?.win;
return isWinner ? 'text-emerald-500' : 'text-accent-red';
}
}

View file

@ -1,38 +0,0 @@
package de.szut.casino.coinflip;
import de.szut.casino.exceptionHandling.exceptions.InsufficientFundsException;
import de.szut.casino.exceptionHandling.exceptions.UserNotFoundException;
import de.szut.casino.shared.service.BalanceService;
import de.szut.casino.user.UserEntity;
import de.szut.casino.user.UserService;
import jakarta.validation.Valid;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.Optional;
@RestController
public class CoinflipController {
private final UserService userService;
private final BalanceService balanceService;
private final CoinflipService coinflipService;
public CoinflipController(UserService userService, BalanceService balanceService, CoinflipService coinflipService) {
this.userService = userService;
this.balanceService = balanceService;
this.coinflipService = coinflipService;
}
@PostMapping("/coinflip")
public ResponseEntity<Object> coinFlip(@RequestBody @Valid CoinflipDto coinflipDto) {
UserEntity user = userService.getCurrentUser();
if (!this.balanceService.hasFunds(user, coinflipDto)) {
throw new InsufficientFundsException();
}
return ResponseEntity.ok(coinflipService.play(user, coinflipDto));
}
}

View file

@ -1,35 +0,0 @@
package de.szut.casino.coinflip;
import de.szut.casino.shared.service.BalanceService;
import de.szut.casino.user.UserEntity;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.Random;
@Service
public class CoinflipService {
private final Random random;
private final BalanceService balanceService;
public CoinflipService(BalanceService balanceService, Random random) {
this.balanceService = balanceService;
this.random = random;
}
public CoinflipResult play(UserEntity user, CoinflipDto coinflipDto) {
this.balanceService.subtractFunds(user, coinflipDto.getBetAmount());
CoinSide coinSide = this.random.nextBoolean() ? CoinSide.HEAD : CoinSide.TAILS;
CoinflipResult coinflipResult = new CoinflipResult(false, BigDecimal.ZERO, coinSide);
if (coinSide == coinflipDto.getCoinSide()) {
coinflipResult.setWin(true);
BigDecimal payout = coinflipDto.getBetAmount().multiply(BigDecimal.TWO);
this.balanceService.addFunds(user, payout);
coinflipResult.setPayout(payout);
}
return coinflipResult;
}
}

View file

@ -0,0 +1,139 @@
<?php declare(strict_types=1);
namespace Neusta\IntexClient\Connection\Model;
use GuzzleHttp\Exception\RequestException;
use Psr\Http\Message\ResponseInterface;
use Psr\Log\LoggerInterface;
use Neusta\IntexClientConfig\Model\Config\Config;
use Neusta\IntexClientConfig\Provider\JsonConfigProvider;
use \GuzzleHttp\Client as GuzzleHttpClient;
use \GuzzleHttp\Event\BeforeEvent;
use \GuzzleHttp\Event\CompleteEvent;
use function sprintf;
abstract class Connection
{
protected const HTTP_STATUS_CODE_OK = 200;
/**
* @var GuzzleHttpClient
*/
protected $httpClient;
/**
* @var Config
*/
protected $serverConfig;
/**
* @var LoggerInterface
*/
protected $logger;
public function __construct(JsonConfigProvider $configProvider, LoggerInterface $logger)
{
$this->logger = $logger;
$this->serverConfig = $configProvider->getConfig('server');
$this->httpClient = new GuzzleHttpClient(
$this->getHttpClientOptions()
);
$this->initHttpEventEmitter();
}
protected function authenticate(): bool
{
$result = false;
$response = $this->createPost(
$this->serverConfig->getAuthUri()
);
$statusCode = $response->getStatusCode();
if ($statusCode === self::HTTP_STATUS_CODE_OK) {
$this->logger->info('authentication successful');
$result = true;
} else {
$this->logger->info(
sprintf('authentication failed with Code: %s', $statusCode)
);
}
return $result;
}
protected function createPost(string $uri, array $options = []): ?ResponseInterface
{
$result = null;
$request = $this->httpClient->postAsync($uri, $options);
$request->then(
static function (ResponseInterface $response) {
$result = $response;
},
function (RequestException $exception) {
$this->logger->info(
sprintf('Crucial Error: %s \n Trace: %u', $exception->getMessage(), $exception->getTraceAsString())
);
}
);
return $result;
}
protected function createGet(string $uri, array $options = []): ?ResponseInterface
{
$result = null;
$request = $this->httpClient->getAsync($uri, $options);
$request->then(
static function (ResponseInterface $response) {
$result = $response;
},
function (RequestException $exception) {
$this->logger->info(
sprintf('Crucial Error: %s \n Trace: %u', $exception->getMessage(), $exception->getTraceAsString())
);
}
);
return $result;
}
protected function initHttpEventEmitter(): void
{
// initialize the EventEmitters to get a good Logging whether the Request was send
// or failed
$this->httpClient->getEmitter()->on('before', static function (BeforeEvent $event) {
$this->logger->info(
sprintf('About to send Request: %s', $event->getRequest())
);
});
$this->httpClient->getEmitter()->on('complete', static function (CompleteEvent $event) {
$this->logger->info(
sprintf('Request %s finished', $event->getRequest())
);
});
$this->httpClient->getEmitter()->on('error', static function (ErrorEvent $event) {
$this->logger->info(
sprintf('Request %s failed', $event->getRequest())
);
});
}
protected function getHttpClientOptions(): array
{
// Configure Base URL for all ongoing Requests and set Cookies to true,
// so we can handle the auth cookie over and over again without using authenticate() multiple times
return [
'base_url' => $this->serverConfig->getBaseUrl(),
'cookies' => true
];
}
}

View file

@ -0,0 +1,33 @@
<?php declare(strict_types=1);
namespace Neusta\IntexClient\Connection\Model;
use Neusta\IntexClientConfig\Model\Config\Config;
use Neusta\IntexClientConfig\Provider\JsonConfigProvider;
use Psr\Log\LoggerInterface;
use function json_decode;
class CustomerConnection extends Connection
{
/**
* @var Config
*/
private $customerConfig;
public function __construct(JsonConfigProvider $configProvider, LoggerInterface $logger)
{
parent::__construct($configProvider, $logger);
$this->customerConfig = $configProvider->getConfig('customer');
}
public function loadCustomerData(): array
{
$response = $this->createGet(
$this->customerConfig->getCustomerUri()
);
return json_decode($response->getBody(), true);
}
}

View file

@ -0,0 +1,28 @@
<?php declare(strict_types=1);
namespace Neusta\IntexCustomer\Controller;
use Neusta\IntexClient\Connection\Model\CustomerConnection;
class CustomerDataController
{
/**
* @var CustomerConnection
*/
private $connection;
public function __construct(\Neusta\IntexClient\Connection\Factory $connectionFactory, \Neusta\IntexCustomer\Handler\DataHandler $dataHandler)
{
$this->connection = $connectionFactory->create('customer');
$this->customerDataHandler = $dataHandler;
}
public function getCustomerData(): void
{
$customerData = $this->connection->loadCustomerData();
$this->customerDataHandler->saveCustomerData($customerData);
}
}

View file

@ -0,0 +1,27 @@
<?php declare(strict_types=1);
namespace Neusta\IntexClient\Connection;
use Neusta\IntexClient\Connection\Model\Connection;
use function class_exists;
use function sprintf;
use function ucwords;
use function strtolower;
class Factory
{
public static function create(string $connectionType): Connection
{
$connectionPath = sprintf(
'\Neusta\IntexClient\Model\Connection\%sConnection',
ucwords(strtolower($connectionType))
);
if (class_exists($connectionPath)) {
return new $connectionPath();
}
throw new \RuntimeException('Invalid Connection Type given');
}
}

View file

@ -0,0 +1,28 @@
<?xml version="1.0" ?>
<schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd">
<table name="neusta_intex_import" resource="default" engine="innodb"
comment="Neusta IntexImport Logging Table">
<column xsi:type="int" name="value_id" padding="11" unsigned="false" nullable="false" identity="true" comment="Value ID"/>
<column xsi:type="smallint" name="attribute_id" padding="5" unsigned="true" nullable="false" identity="false" default="0" comment="Attribute ID"/>
<column xsi:type="smallint" name="store_id" padding="5" unsigned="true" nullable="false" identity="false" default="0" comment="Store ID"/>
<column xsi:type="int" name="entity_id" padding="10" unsigned="true" nullable="false" identity="false" default="0" comment="Entity ID"/>
<column xsi:type="datetime" name="value" on_update="false" nullable="true" comment="Value"/>
<constraint xsi:type="primary" referenceId="PRIMARY">
<column name="value_id"/>
</constraint>
<constraint xsi:type="foreign" referenceId="CAT_PRD_ENTT_DTIME_ATTR_ID_EAV_ATTR_ATTR_ID" table="catalog_product_entity_datetime" column="attribute_id" referenceTable="eav_attribute" referenceColumn="attribute_id" onDelete="CASCADE"/>
<constraint xsi:type="foreign" referenceId="CAT_PRD_ENTT_DTIME_ENTT_ID_CAT_PRD_ENTT_ENTT_ID" table="catalog_product_entity_datetime" column="entity_id" referenceTable="catalog_product_entity" referenceColumn="entity_id" onDelete="CASCADE"/>
<constraint xsi:type="foreign" referenceId="CATALOG_PRODUCT_ENTITY_DATETIME_STORE_ID_STORE_STORE_ID" table="catalog_product_entity_datetime" column="store_id" referenceTable="store" referenceColumn="store_id" onDelete="CASCADE"/>
<constraint xsi:type="unique" referenceId="CATALOG_PRODUCT_ENTITY_DATETIME_ENTITY_ID_ATTRIBUTE_ID_STORE_ID">
<column name="entity_id"/>
<column name="attribute_id"/>
<column name="store_id"/>
</constraint>
<index referenceId="CATALOG_PRODUCT_ENTITY_DATETIME_ATTRIBUTE_ID" indexType="btree">
<column name="attribute_id"/>
</index>
<index referenceId="CATALOG_PRODUCT_ENTITY_DATETIME_STORE_ID" indexType="btree">
<column name="store_id"/>
</index>
</table>
</schema>

View file

@ -0,0 +1,39 @@
<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;
use Neusta\IntexClient\Connection\Factory;
use Neusta\IntexClient\Connection\Model\CustomerConnection;
class FactoryTest extends TestCase
{
/**
* @var Factory
*/
private $subject;
public function setUp()
{
$this->subject = new Factory();
}
/**
* @test
*/
public function createMustReturnInstanceOfCustomerConnectionWithValidIdentifierGiven()
{
$expectedInstance = $this->subject->create('customer');
self::assertInstanceOf(CustomerConnection::class, $expectedInstance);
}
/**
* @test
* @throws RuntimeException
*/
public function createMustThrowRuntimeExceptionIfInvalidIdentifierIsGiven()
{
$unexpectedInstance = $this->subject->create('fooBar');
self::expectException(RuntimeException::class);
}
}

View file

@ -1,92 +0,0 @@
package de.szut.casino.user;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.math.BigDecimal;
@Setter
@Getter
@Entity
@NoArgsConstructor
public class UserEntity {
@Id
@GeneratedValue
private Long id;
@Version
private Long version;
@Column(unique = true)
private String email;
@Column(unique = true)
private String username;
private String password;
@Column(precision = 19, scale = 2)
private BigDecimal balance;
private Boolean emailVerified = false;
private String verificationToken;
private String passwordResetToken;
@Enumerated(EnumType.STRING)
private AuthProvider provider = AuthProvider.LOCAL;
private String providerId;
public UserEntity(String email, String username, String password, BigDecimal balance, String verificationToken) {
this.email = email;
this.username = username;
this.password = password;
this.balance = balance;
this.verificationToken = verificationToken;
}
public UserEntity(String email, String username, AuthProvider provider, String providerId, BigDecimal balance) {
this.email = email;
this.username = username;
this.provider = provider;
this.providerId = providerId;
this.balance = balance;
this.emailVerified = true; // OAuth providers verify emails
}
public void addBalance(BigDecimal amountToAdd) {
if (amountToAdd == null || amountToAdd.compareTo(BigDecimal.ZERO) <= 0) {
return;
}
if (this.balance == null) {
this.balance = BigDecimal.ZERO;
}
this.balance = this.balance.add(amountToAdd);
}
public void subtractBalance(BigDecimal amountToSubtract) {
if (amountToSubtract == null || amountToSubtract.compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("Amount to subtract must be positive.");
}
if (this.balance == null) {
this.balance = BigDecimal.ZERO;
}
if (this.balance.compareTo(amountToSubtract) < 0) {
throw new IllegalStateException("Insufficient funds to subtract " + amountToSubtract);
}
this.balance = this.balance.subtract(amountToSubtract);
}
public String getEmailAddress() {
return "${name} <${email}>".replace("${name}", this.username).replace("${email}", this.email);
}
}

View file

@ -1,53 +0,0 @@
spring.datasource.url=jdbc:postgresql://${DB_HOST:localhost}:${DB_PORT:5432}/${DB_NAME:postgresdb}
spring.datasource.username=${DB_USER:postgres_user}
spring.datasource.password=${DB_PASS:postgres_pass}
server.port=${HTTP_PORT:8080}
spring.jpa.hibernate.ddl-auto=update
stripe.secret.key=${STRIPE_SECRET_KEY:sk_test_51QrePYIvCfqz7ANgqam8rEwWcMeKiLOof3j6SCMgu2sl4sESP45DJxca16mWcYo1sQaiBv32CMR6Z4AAAGQPCJo300ubuZKO8I}
stripe.webhook.secret=${STRIPE_WEBHOOK_SECRET:whsec_746b6a488665f6057118bdb4a2b32f4916f16c277109eeaed5e8f8e8b81b8c15}
app.frontend-host=${FE_URL:http://localhost:4200}
app.mail.authentication=${MAIL_AUTHENTICATION:false}
app.mail.host=${MAIL_HOST:localhost}
app.mail.port=${MAIL_PORT:1025}
app.mail.username=${MAIL_USER:null}
app.mail.password=${MAIL_PASS:null}
app.mail.from-address=${MAIL_FROM:casino@localhost}
app.mail.protocol=${MAIL_PROTOCOL:smtp}
spring.application.name=casino
# JWT Configuration
jwt.secret=${JWT_SECRET:5367566B59703373367639792F423F4528482B4D6251655468576D5A71347437}
jwt.expiration.ms=${JWT_EXPIRATION_MS:86400000}
# Logging
logging.level.org.springframework.security=DEBUG
# Swagger
springdoc.swagger-ui.path=swagger
springdoc.swagger-ui.try-it-out-enabled=true
# GitHub OAuth2 Configuration
spring.security.oauth2.client.registration.github.client-id=${GITHUB_CLIENT_ID:Ov23lingzZsPn1wwACoK}
spring.security.oauth2.client.registration.github.client-secret=${GITHUB_CLIENT_SECRET:4b327fb3b1ab67584a03bcb9d53fa6439fbccad7}
spring.security.oauth2.client.registration.github.redirect-uri=${app.frontend-host}/oauth2/callback/github
spring.security.oauth2.client.registration.github.scope=user:email,read:user
spring.security.oauth2.client.provider.github.authorization-uri=https://github.com/login/oauth/authorize
spring.security.oauth2.client.provider.github.token-uri=https://github.com/login/oauth/access_token
spring.security.oauth2.client.provider.github.user-info-uri=https://api.github.com/user
spring.security.oauth2.client.provider.github.user-name-attribute=login
# OAuth Success and Failure URLs
app.oauth2.authorizedRedirectUris=${app.frontend-host}/auth/oauth2/callback
# Google OAuth2 Configuration
spring.security.oauth2.client.registration.google.client-id=${GOOGLE_CLIENT_ID:350791038883-c1r7v4o793itq8a0rh7dut7itm7uneam.apps.googleusercontent.com}
spring.security.oauth2.client.registration.google.client-secret=${GOOGLE_CLIENT_SECRET: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}
spring.security.oauth2.client.registration.google.redirect-uri=${app.frontend-host}/oauth2/callback/google
spring.security.oauth2.client.registration.google.scope=email,profile
spring.security.oauth2.client.provider.google.authorization-uri=https://accounts.google.com/o/oauth2/v2/auth
spring.security.oauth2.client.provider.google.token-uri=https://oauth2.googleapis.com/token
spring.security.oauth2.client.provider.google.user-info-uri=https://www.googleapis.com/oauth2/v3/userinfo
spring.security.oauth2.client.provider.google.user-name-attribute=sub

View file

@ -0,0 +1,21 @@
{
"name": "neusta/m2-intex-client",
"license": "proprietary",
"description": "Client for Magento2 Intex Import/Export",
"type": "magento2-module",
"version": "0.0.1",
"require": {
"php": "7.4.33",
"magento/framework": "~101.0",
"neusta/m2-intex-base": "0.0.1",
"guzzlehttp/guzzle": "6.5.*"
},
"autoload": {
"files": [
"registration.php"
],
"psr-4": {
"Neusta\\IntexClient\\": "src"
}
}
}

View file

@ -0,0 +1,33 @@
<?php
include(dirname(__FILE__).'/../bootstrap/Propel.php');
$t = new lime_test(13);
$t->comment('Empty Information');
$emptyComparedInformation = new ComparedNaturalModuleInformation(array());
$t->is($emptyComparedInformation->getCatalogSign(), ComparedNaturalModuleInformation::EMPTY_SIGN, 'Has no catalog sign');
$t->is($emptyComparedInformation->getSourceSign(), ComparedNaturalModuleInformation::SIGN_CREATE, 'Source has to be created');
$t->comment('Perfect Module');
$criteria = new Criteria();
$criteria->add(NaturalmodulenamePeer::NAME, 'SMTAB');
$moduleName = NaturalmodulenamePeer::doSelectOne($criteria);
$t->is($moduleName->getName(), 'SMTAB', 'Right modulename selected');
$comparedInformation = $moduleName->loadNaturalModuleInformation();
$t->is($comparedInformation->getSourceSign(), ComparedNaturalModuleInformation::SIGN_OK, 'Source sign shines global');
$t->is($comparedInformation->getCatalogSign(), ComparedNaturalModuleInformation::SIGN_OK, 'Catalog sign shines global');
$infos = $comparedInformation->getNaturalModuleInformations();
foreach($infos as $info)
{
$env = $info->getEnvironmentName();
$t->is($info->getSourceSign(), ComparedNaturalModuleInformation::SIGN_OK, 'Source sign shines at ' . $env);
if($env != 'SVNENTW')
{
$t->is($info->getCatalogSign(), ComparedNaturalModuleInformation::SIGN_OK, 'Catalog sign shines at ' . $info->getEnvironmentName());
}
else
{
$t->is($info->getCatalogSign(), ComparedNaturalModuleInformation::EMPTY_SIGN, 'Catalog sign is empty at ' . $info->getEnvironmentName());
}
}
?>

View file

@ -53,6 +53,12 @@
\pagenumbering{arabic}
\input{Inhalt.tex}
% Literatur ------------------------------------------------------------------
\clearpage
\renewcommand{\refname}{Literaturverzeichnis}
\bibliography{Bibliographie}
\bibliographystyle{Allgemein/natdin} % DIN-Stil des Literaturverzeichnisses
% Anhang ---------------------------------------------------------------------
\clearpage
\appendix

View file

@ -1,21 +0,0 @@
This is pdfTeX, Version 3.141592653-2.6-1.40.22 (TeX Live 2022/dev/Debian) (preloaded format=pdflatex 2025.6.5) 12 JUN 2025 23:26
entering extended mode
restricted \write18 enabled.
%&-line parsing enabled.
**Projektdokumentation.tex
! Emergency stop.
<*> Projektdokumentation.tex
End of file on the terminal!
Here is how much of TeX's memory you used:
3 strings out of 478287
131 string characters out of 5849290
289007 words of memory out of 5000000
18302 multiletter control sequences out of 15000+600000
469259 words of font info for 28 fonts, out of 8000000 for 9000
1141 hyphenation exceptions out of 8191
0i,0n,0p,1b,6s stack positions out of 5000i,500n,10000p,200000b,80000s
! ==> Fatal error occurred, no output PDF file produced!