Schulprojekt Modul 335 – Mobile Applikation
Klasse: Modul 335
| Dokument | Beschreibung |
|---|---|
| UML- & Architekturdiagramme | Systemarchitektur, Klassendiagramm, ER-Diagramm, Sequenz- und Statusdiagramme (Mermaid) |
| User Stories – HR | User Stories mit Akzeptanzkriterien für die HR-Rolle |
| User Stories – Schichtleiter | User Stories mit Akzeptanzkriterien für die Schichtleiter-Rolle |
| User Stories – Mitarbeiter | User Stories mit Akzeptanzkriterien für die Mitarbeiter-Rolle |
| User Stories – Admin | User Stories mit Akzeptanzkriterien für die Admin-Rolle |
| Tests & CI-Pipeline | Wie Tests ausgeführt werden, was die CI-Pipeline prüft, was bei Fehlern zu tun ist |
| Testbericht | Ergebnisse des automatischen API-Testlaufs (54/54 Tests bestanden) |
| OWASP Top 10 Testplan | Sicherheits-Testplan mit OWASP-Kategorien, Befunden, Verbesserungen und offenen Punkten |
| Flipper Auth Integration | Übernommene Flipper-, HCE-, ESP32- und Auth-Service-Teile |
- Docker Desktop installiert und gestartet
- Ports 3001–3004, 8000–8009, 3307, 27017, 8080, 8081 sind frei
Im Stammverzeichnis des Projekts (Modul_335_Mobile_Applikation/) ausführen:
Erster Start oder nach einem Reset:
docker compose down -v
docker compose up --build -dFolgestarts (kein Code geändert, Daten sollen erhalten bleiben):
docker compose up -dErster Build: dauert 3–5 Minuten (Maven-Dependencies, npm install).
Folgestarts: gehen in Sekunden, da Images bereits gebaut sind.
Wichtig – Demo-Accounts: Die Benutzer (admin, hr.mueller, sl.huber, emp.meier) werden nicht per SQL-Seed angelegt, sondern beim Start des user-role-service per CommandLineRunner mit korrekt generiertem BCrypt-Hash. MySQL-Healthcheck stellt sicher, dass die Rollen bereits vorhanden sind, bevor der Service startet.
Seeding prüfen:
docker compose logs user-role-service | grep SeedErwartete Ausgabe:
Seed-User angelegt: admin (ADMIN)
Seed-User angelegt: hr.mueller (HR)
Seed-User angelegt: sl.huber (SHIFT_LEAD)
Seed-User angelegt: emp.meier (EMPLOYEE)
| Anwendung | URL | Beschreibung |
|---|---|---|
| Admin-Frontend | http://localhost:3001 | Rollen, Benutzerverwaltung, Aufträge |
| HR-Frontend | http://localhost:3002 | Schichtleiter anlegen, Stunden, Rechnungen, Absenzen |
| Schichtleiter-Frontend | http://localhost:3003 | Arbeitspläne, Schichten, Kalender |
| Flipper Auth Dashboard | http://localhost:3004 | Flipper-Login/Logout-Challenges testen |
| phpMyAdmin | http://localhost:8080 | MySQL-Datenbankadmin |
| Mongo Express | http://localhost:8081 | MongoDB-Admin (kein Login nötig) |
Alle Frontends verwenden denselben Login-Endpunkt über den API-Gateway (localhost:8000). Die Demo-Accounts werden beim Start automatisch angelegt.
| Benutzername | Passwort | Rolle | Frontend |
|---|---|---|---|
admin |
password |
Admin | http://localhost:3001 |
hr.mueller |
password |
HR | http://localhost:3002 |
sl.huber |
password |
Schichtleiter | http://localhost:3003 |
emp.meier |
password |
Mitarbeiter | Flutter Mobile App |
Jedes Frontend prüft nach dem Login die Rolle im JWT.
adminkann sich z.B. nicht im HR-Frontend einloggen — falsche Rolle wird verweigert.
Admin → http://localhost:3001
| Seite | Funktion |
|---|---|
| Rollen | Benutzer aus der DB anzeigen, Rolle ändern, deaktivieren/aktivieren |
| Aufträge | Aufträge über den Order Service erstellen, bearbeiten, zuweisen und Status ändern |
| HR / Mitarbeiter | Übersicht (lokal) |
| Seite | URL | Funktion |
|---|---|---|
| Benutzerverwaltung | /users |
Schichtleiter/Mitarbeiter anlegen, bearbeiten, deaktivieren |
| Stundenübersicht | /time |
Gesamtstunden, Monatsdetail und Pausenverstösse prüfen |
| Stundenfreigabe | /hour-budgets |
Monatliche HR-Stundenkontingente für Schichtleiter festlegen |
| Rechnungen | /invoices |
Rechnungen erstellen, versenden, als bezahlt markieren |
| Lohnauszüge | /payroll |
Monatslohn aus Stunden, Rate, Zuschlägen und Abzügen berechnen |
| Absenzen & Ferien | /absences |
Ferienanträge genehmigen/ablehnen, Absenzen erfassen |
Schichtleiter → http://localhost:3003
| Seite | URL | Funktion |
|---|---|---|
| Arbeitsplanung | /planning |
Arbeitspläne mit HR-Stundenfreigabe erstellen, Schichten hinzufügen, veröffentlichen |
| Aufträge | /orders |
Zugewiesene Aufträge aus dem Order Service einsehen und Status ändern |
| Arbeitszeiten | /time |
Gesamtstunden, Monatsdetails und Pausenverstösse der Mitarbeiter einsehen |
Flutter Mobile App (Login: emp.meier / password)
| Screen | Funktion |
|---|---|
| Check-in/out | POST /api/time/checkin, POST /api/time/checkout; Pausenminuten werden beim Check-out mitgegeben |
| Kalender | GET /api/planning/calendar/{employeeId} – zeigt veröffentlichte Schichten des laufenden Monats |
| Absenzen | POST /api/absences, GET /api/absences/employee/{employeeId} |
| Rapport | POST /api/media/upload mit optionaler Auftrags-ID; Bild wird in MongoDB gespeichert |
Wenn der Docker-Stack läuft, können die API-Tests aus dem tests-Ordner gestartet werden:
cd tests
node api-test.jsDie Tests verwenden den lokalen API-Gateway unter http://localhost:8000.
POST http://localhost:8000/api/auth/login
Content-Type: application/json
{
"username": "hr.mueller",
"password": "password"
}
Antwort:
{
"token": "eyJ...",
"role": "HR",
"username": "hr.mueller",
"userId": 2
}Den token-Wert als Authorization: Bearer <token> Header für alle weiteren API-Anfragen verwenden.
# Status aller Container anzeigen
docker compose ps
# Logs eines bestimmten Services live verfolgen
docker compose logs -f api-gateway
docker compose logs -f user-role-service
docker compose logs -f hr-web
# Alle Container stoppen (Daten bleiben erhalten)
docker compose down
# Einzelnen Service nach Code-Änderung neu bauen und starten
docker compose up --build user-role-service -d
# Kompletter Reset – stoppt alles und löscht alle Datenbankdaten
docker compose down -v && docker compose up --build -d
down -vlöscht die MySQL- und MongoDB-Volumes. Beim nächsten Start legtinit.sqlSchema und Rollen neu an, deruser-role-serviceseeded die Demo-Accounts.
| Problem | Ursache | Lösung |
|---|---|---|
| Login schlägt mit 401 fehl | MySQL-Volume mit falsch geseedeten Usern aus alter Version | docker compose down -v && docker compose up --build -d |
Login leitet sofort zurück auf /login |
401-Response vom Login-Endpoint triggerte früher einen Hard-Redirect | Behoben in api.js aller drei Frontends (Interceptor prüft jetzt ob Request = Login-Endpoint) |
| Admin zeigt 403 und kann Mitarbeiter oder Aufträge nicht laden/speichern | Im Browser ist noch ein abgelaufener oder ungültiger JWT gespeichert | Admin-Web entfernt bei 401/403 den alten Loginzustand und leitet zur erneuten Anmeldung weiter; User- und Order-Service antworten bei ungültigen Tokens korrekt mit 401 |
| Leere Seite ohne Login-Formular | vite.config.js fehlte → JSX wurde nicht verarbeitet |
Behoben, vite.config.js ist vorhanden |
| CORS-Fehler 403 | Gateway hatte kein globalcors, Services gaben doppelte CORS-Header |
Behoben: Gateway verwaltet CORS, Services haben cors.disable() |
| Admin zeigt andere User als HR-Frontend | adminSeed.js enthielt lokale Dummy-User (Amir Suter, Lea Baumann etc.); HR- und Mitarbeiter-Formulare schrieben in localStorage statt in die DB |
Behoben in DashboardPage.jsx: saveHrUser und saveEmployee rufen jetzt POST /api/users auf; Suche und Stats verwenden echte API-Daten |
| Nach Wechsel auf neuen Rechner zeigt Admin noch alte Daten | Browser-localStorage vom alten Rechner enthält veralteten Admin-State |
DevTools → Application → Local Storage → planifywork-admin-state-v1 löschen, Seite neu laden |
- Projektidee
- Technologie-Stack
- Rollen & Zugänge
- Gesamtarchitektur
- Projektstruktur
- Services im Detail
- Frontend-Apps
- Mobile App (Flutter)
- Datenbanken
- Docker & lokale Umgebung
- Port-Übersicht
- Arbeitsweise im Team (Kanban)
- Konventionen & Coding-Standards
Das Workforce Management System ist eine verteilte Applikation zur Verwaltung von Mitarbeitern, Arbeitszeiten, Aufträgen, Schichten und Rechnungen.
Das System besteht aus:
- 3 React-Webapplikationen für Admin, HR und Schichtleiter (Desktop)
- 1 Flutter-Mobile-App für Mitarbeiter (iOS / Android)
- 8 Spring-Boot-Microservices als Backend
- MySQL für strukturierte Daten
- MongoDB für Bild-Uploads und Mediendaten
- Docker Compose zur lokalen Ausführung aller Komponenten
| Bereich | Technologie | Version |
|---|---|---|
| Frontend Web | React + Vite | React 18 |
| Mobile | Flutter | SDK 3.3+ |
| Backend | Spring Boot | 3.3 |
| API Gateway | Spring Cloud Gateway | 2023.0 |
| Authentifizierung | JWT (jjwt) | 0.12.5 |
| Datenbank 1 | MySQL | 8.0 |
| Datenbank 2 | MongoDB | 7.0 |
| Container | Docker + Docker Compose | – |
| DB-Admin | phpMyAdmin + Mongo Express | – |
| HTTP Client | Axios (React) / http (Flutter) | – |
| State (Flutter) | Provider | 6.1 |
| Routing (React) | React Router DOM | v6 |
| Rolle | Zugang | JWT-Rolle | Test-Benutzer | Passwort |
|---|---|---|---|---|
| Admin | Admin Web – http://localhost:3001 | ADMIN |
admin |
password |
| HR | HR Web – http://localhost:3002 | HR |
hr.mueller |
password |
| Schichtleiter | Schichtleiter Web – http://localhost:3003 | SHIFT_LEAD |
sl.huber |
password |
| Mitarbeiter | Flutter Mobile App | EMPLOYEE |
emp.meier |
password |
Jedes Frontend prüft nach dem Login die Rolle im JWT-Token. Stimmt die Rolle nicht überein, wird der Zugang verweigert.
Die Testbenutzer werden beim ersten Start des user-role-service automatisch in der Datenbank angelegt (via CommandLineRunner in UserRoleServiceApplication.java). Voraussetzung: Die Rollen müssen in der roles-Tabelle vorhanden sein (wird via database/mysql/init.sql beim ersten Start von MySQL erledigt).
graph TB
subgraph Clients["Clients"]
AW["React Admin Web\n:3001"]
HW["React HR Web\n:3002"]
SW["React Schichtleiter Web\n:3003"]
MA["Flutter Mobile App\nAndroid / iOS"]
end
subgraph GWLayer["API Gateway"]
GW["Spring Cloud Gateway :8000\nJWT-Prüfung · CORS · Routing"]
end
subgraph SVC["Backend Microservices (Spring Boot 3.3)"]
AS["Auth Service\n:8001"]
URS["User & Role Service\n:8002"]
OS["Order Service\n:8003"]
PS["Planning Service\n:8004"]
TS["Time Service\n:8005"]
AVS["Absence Service\n:8006"]
BS["Billing Service\n:8007"]
RMS["Report/Media Service\n:8008"]
end
subgraph DBLayer["Datenbanken"]
MYSQL[("MySQL :3307\nworkforce DB\nBenutzer · Aufträge · Schichten\nZeiten · Absenzen · Rechnungen")]
MONGO[("MongoDB :27017\nworkforce-media DB\nBilder · Rapport-Metadaten")]
end
AW & HW & SW & MA --> GW
GW --> AS & URS & OS & PS & TS & AVS & BS & RMS
AS & URS & OS & PS & TS & AVS & BS --> MYSQL
RMS --> MONGO
Detaillierte Diagramme (ER, Klassendiagramm, Sequenz-, Statusdiagramme) → docs/diagrams.md
Modul_335_Mobile_Applikation/
│
├── docker-compose.yml # Startet alle Container
├── .gitignore
│
├── backend/ # Spring Boot Microservices
│ ├── api-gateway/ # Zentraler Einstiegspunkt, JWT-Prüfung
│ ├── auth-service/ # Login, Logout, JWT erstellen
│ ├── user-role-service/ # Benutzerverwaltung, Rollen
│ ├── order-service/ # Auftragsmanagement
│ ├── planning-service/ # Arbeitsplanung, Schichten
│ ├── time-service/ # Check-in/out, Arbeitszeitberechnung
│ ├── absence-vacation-service/ # Absenzen und Ferien
│ ├── billing-service/ # Rechnungen
│ └── report-media-service/ # Bild-Upload (MongoDB)
│
├── frontend/ # React Webapplikationen
│ ├── admin-web/ # Admin-Oberfläche
│ ├── hr-web/ # HR-Oberfläche
│ └── shiftlead-web/ # Schichtleiter-Oberfläche
│
├── mobile/ # Flutter Mobile App
│ └── lib/
│ ├── main.dart
│ ├── screens/ # UI-Screens
│ └── services/ # API- und Auth-Logik
│
└── database/
├── mysql/init.sql # MySQL Schema + Seed-Daten
└── mongodb/init.js # MongoDB Collection-Setup
Jeder Spring-Boot-Service hat dieselbe interne Struktur:
<service-name>/
├── Dockerfile
├── pom.xml
└── src/main/
├── java/com/workforce/<name>/
│ ├── <Name>Application.java # Einstiegspunkt
│ ├── controller/ # REST-Endpoints (@RestController)
│ ├── service/ # Geschäftslogik
│ ├── model/ # Entities / Datenmodelle
│ ├── repository/ # Datenbankzugriff (JPA / MongoDB)
│ └── config/ # Security, JWT, CORS etc.
└── resources/
└── application.yml # Port, DB-Verbindung, JWT-Secret
Jede React-App hat dieselbe interne Struktur:
<app-name>/
├── Dockerfile
├── nginx.conf
├── package.json
├── index.html
└── src/
├── main.jsx # React-Einstiegspunkt
├── App.jsx # Router-Setup, Protected Routes
├── pages/ # Seitenkomponenten (LoginPage, Dashboard...)
├── components/ # Wiederverwendbare UI-Komponenten
└── services/
└── api.js # Axios-Instanz mit JWT-Interceptor
Aufgabe: Einziger Einstiegspunkt für alle Frontends. Leitet HTTP-Anfragen anhand des URL-Pfades an den passenden Microservice weiter.
Routing-Tabelle:
| Pfad-Prefix | Ziel-Service |
|---|---|
/api/auth/** |
auth-service :8001 |
/api/users/** |
user-role-service :8002 |
/api/orders/** |
order-service :8003 |
/api/planning/** |
planning-service :8004 |
/api/time/** |
time-service :8005 |
/api/absences/** |
absence-vacation-service :8006 |
/api/billing/** |
billing-service :8007 |
/api/media/** |
report-media-service :8008 |
/api/flipper-auth/** |
flipper-auth-service :8009 |
Hinweis zur Integration:
- Das Gateway leitet alle
/api/**-Requests an die passenden Microservices weiter. - JWT-Header werden durchgereicht; die rollenbasierte Prüfung passiert in den jeweiligen Services per Spring Security.
- CORS ist für Admin, HR, Schichtleiter und Flipper-Web konfiguriert.
- Rate Limiting bleibt optional.
Aufgabe: Authentifizierung. Erstellt JWT-Tokens nach erfolgreichem Login.
Bereits implementiert:
POST /api/auth/login→ gibt JWT-Token + Rolle zurückGET /api/auth/validate→ prüft ob ein Token gültig istUser- undRole-Entity mit JPAUserRepository(findByUsername, findByEmail)JwtUtil(Token erstellen, validieren, Rolle/Username extrahieren)- Spring Security Konfiguration (stateless, BCrypt)
Noch zu implementieren:
POST /api/auth/logout(Token-Blacklist oder Frontend-seitig)- Passwort ändern
- Erster Admin-Benutzer per Seed-Script
Aufgabe: Verwaltung aller Benutzer und ihrer Rollen.
Noch zu implementieren:
GET /api/users– alle Benutzer auflisten (Admin)POST /api/users– neuen Benutzer anlegenPUT /api/users/{id}– Benutzer bearbeitenDELETE /api/users/{id}– Benutzer deaktivierenGET /api/users/{id}– Benutzerdetails- Entities:
User,Role - Repository, Service, Controller
Aufgabe: Auftragsmanagement. Admin erstellt Aufträge, Schichtleiter empfangen sie, Mitarbeiter können Auftragsdaten herunterladen.
Implementiert:
GET /api/orders– Aufträge auflisten, optional mit?shiftLeadId=und?status=filternGET /api/orders/{id}– Auftragsdetail anzeigenPOST /api/orders– Auftrag erstellen (Admin)PUT /api/orders/{id}– Auftrag bearbeiten (Admin)PUT /api/orders/{id}/assign– Schichtleiter/Mitarbeiter zuweisen (Admin)PUT /api/orders/{id}/status– Status ändern (Admin/Schichtleiter)GET /api/orders/{id}/download– Auftragsdaten als JSON abrufen- Entities:
WorkOrder,OrderEmployee - Status-Enum:
OPEN,IN_PROGRESS,DONE
Beispiel: Auftrag erstellen
POST http://localhost:8000/api/orders
Authorization: Bearer <admin-token>
Content-Type: application/json
{
"title": "Umbau Eingang A",
"description": "Material prüfen und Rapportbilder hochladen",
"company": "Demo AG",
"location": "Zürich",
"startDate": "2026-06-01",
"endDate": "2026-06-30",
"requiredRole": "Montage",
"assignedShiftLeadId": 3,
"createdBy": 1,
"status": "OPEN",
"employeeIds": [4]
}Aufgabe: HR gibt monatliche Stundenkontingente pro Schichtleiter frei. Schichtleiter erstellen darauf basierend Arbeitspläne für ihr Team. Mitarbeiter sehen veröffentlichte Schichten im Mobile-Kalender.
Implementiert:
POST /api/planning/hour-budgets– HR-Stundenkontingent pro Schichtleiter und Monat erstellen/aktualisierenGET /api/planning/hour-budgets– HR-Stundenkontingente auflisten, optional mit?shiftLeadId=filternPOST /api/planning/workplans– Arbeitsplan erstellen und HR-Stundenkontingent automatisch übernehmenGET /api/planning/workplans– Arbeitspläne auflisten, optional mit?shiftLeadId=filternGET /api/planning/workplans/{id}– Arbeitsplan inkl. Schichten und Stundenübersicht anzeigenPUT /api/planning/workplans/{id}– Arbeitsplan-Entwurf bearbeitenPOST /api/planning/workplans/{id}/shifts– Schicht hinzufügen, optional mitorderIdPUT /api/planning/workplans/{id}/publish– Arbeitsplan veröffentlichenGET /api/planning/calendar/{employeeId}– veröffentlichte Kalenderschichten eines Mitarbeiters anzeigen- Entities:
HourBudget,WorkPlan,Shift,WorkPlanStatus
Stundenlogik:
approvedHourswird aus der HR-Stundenfreigabe übernommen und nicht mehr vom Schichtleiter eingegeben.plannedHourswird aus allen Schichten eines Arbeitsplans berechnet.remainingHourszeigt die Differenz zwischen freigegebenen und geplanten Stunden.overLimitwirdtrue, wenn mehr als das HR-Kontingent geplant wurde.underPlannedwirdtrue, wenn weniger als 95 % des HR-Kontingents geplant wurden.
Beispiel: HR-Stundenfreigabe erstellen
POST http://localhost:8000/api/planning/hour-budgets
Authorization: Bearer <hr-token>
Content-Type: application/json
{
"shiftLeadId": 3,
"year": 2026,
"month": 6,
"approvedHours": 1000,
"createdBy": 2,
"notes": "Sommermonat Juni"
}Beispiel: Arbeitsplan erstellen
POST http://localhost:8000/api/planning/workplans
Authorization: Bearer <token>
Content-Type: application/json
{
"title": "Monatsplan Juni",
"shiftLeadId": 3,
"startDate": "2026-06-01",
"endDate": "2026-06-30"
}Beispiel: Schicht hinzufügen
POST http://localhost:8000/api/planning/workplans/1/shifts
Authorization: Bearer <token>
Content-Type: application/json
{
"employeeId": 4,
"orderId": null,
"shiftDate": "2026-06-03",
"startTime": "08:00",
"endTime": "17:00",
"notes": "Tagesschicht"
}Aufgabe: Check-in / Check-out erfassen, Arbeitsstunden berechnen, Auswertungen bereitstellen.
Implementiert:
POST /api/time/checkin– Check-in speichernPOST /api/time/checkout– Check-out speichern und Netto-Arbeitszeit berechnenGET /api/time/current/{employeeId}– aktuell offener Check-in eines MitarbeitersGET /api/time/latest/{employeeId}– letzter Zeiteintrag eines MitarbeitersGET /api/time/today/{employeeId}– heutiger Zeiteintrag eines MitarbeitersGET /api/time/month/{employeeId}?month=&year=– Monatsauswertung pro MitarbeiterGET /api/time/total?from=&to=– Gesamtstunden aller Mitarbeiter im ZeitraumGET /api/time/total/{employeeId}?from=&to=– Gesamtstunden eines Mitarbeiters im ZeitraumGET /api/time/break-violations?from=&to=&employeeId=– Pausenverstösse auswerten- Rollen: HR/Admin für Auswertungen, Schichtleiter für Team-Übersicht, Mitarbeiter für eigenen Check-in/out
- Entities:
TimeEntry - Berechnung: Netto-Stunden aus Check-in, Check-out und Pausenzeit
- Pausenregel: mehr als 6 Stunden Brutto-Arbeitszeit → mindestens 30 Minuten Pause; mehr als 9 Stunden → mindestens 45 Minuten Pause
Aufgabe: Ferienanfragen und Absenzen verwalten.
Implementiert:
POST /api/absences– Absenz/Ferienanfrage einreichen (Mitarbeiter, HR, Admin)GET /api/absences/employee/{employeeId}– eigene Absenzen/Ferienanfragen für die Mobile App ladenGET /api/absences– Absenzen nach Mitarbeiter und/oder Typ filternGET /api/absences/pending– offene Anfragen (HR)PUT /api/absences/{id}/approve– genehmigen (HR)PUT /api/absences/{id}/reject– ablehnen (HR)PUT /api/absences/{id}– Abwesenheit bearbeiten (HR/Admin)DELETE /api/absences/{id}– Abwesenheit löschen (HR/Admin)- Entities:
Absence - Type-Enum:
VACATION,SICK,OTHER - Status-Enum:
PENDING,APPROVED,REJECTED
Aufgabe: HR erstellt Rechnungen und monatliche Lohnauszüge basierend auf erfassten Arbeitsstunden.
Implementiert Rechnungen:
POST /api/billing/invoices– Rechnung erstellenGET /api/billing/invoices– alle RechnungenGET /api/billing/invoices/{id}– RechnungsdetailPUT /api/billing/invoices/{id}/send– Rechnung versendenPUT /api/billing/invoices/{id}/pay– Rechnung als bezahlt markieren- Entities:
Invoice,InvoicePosition - Status-Enum:
DRAFT,SENT,PAID
Implementiert Lohnauszüge:
POST /api/billing/payroll-statements– Lohnauszug aus Monatsstunden, Stundenrate, Zuschlägen und Abzügen erstellen oder neu berechnenGET /api/billing/payroll-statements– Lohnauszüge auflisten, optional mit?status=filternGET /api/billing/payroll-statements/{id}– Lohnauszug anzeigenPUT /api/billing/payroll-statements/{id}/approve– Lohnauszug freigebenPUT /api/billing/payroll-statements/{id}/pay– Lohnauszug als bezahlt markieren- Entities:
PayrollStatement,PayrollStatus - Status-Enum:
DRAFT,APPROVED,PAID - Stundendaten kommen aus
time_entriesdes Time Service
Aufgabe: Bild-Uploads aus der Mobile App empfangen und in MongoDB speichern.
Implementiert:
POST /api/media/upload– Bild aus der Mobile App per Multipart hochladenGET /api/media/{id}– Bilddatei aus MongoDB abrufenGET /api/media/order/{orderId}– alle Rapportbilder eines Auftrags auflistenGET /api/media/employee/{employeeId}– alle Rapportbilder eines Mitarbeiters auflisten- MongoDB-Document:
MediaReport(employeeId, orderId, rapportId, filename, contentType, fileSize, storagePath, uploadedAt, metadata, data) - Bilddaten werden direkt in MongoDB gespeichert; maximale Upload-Grösse: 10 MB
Beispiel: Rapportbild hochladen
POST http://localhost:8000/api/media/upload
Authorization: Bearer <employee-token>
Content-Type: multipart/form-data
file=<bild.jpg>
employeeId=4
orderId=1
note=Rapportfoto Eingang A- Vite als Build-Tool
- React Router v6 für clientseitiges Routing
- Axios mit JWT-Interceptor (
src/services/api.js)- Setzt automatisch den
Authorization: Bearer <token>Header - Leitet bei 401 automatisch auf
/loginweiter
- Setzt automatisch den
- Protected Routes – nicht eingeloggte Nutzer werden auf
/loginumgeleitet - Login prüft die JWT-Rolle, falsche Rolle = Zugang verweigert
Implementiert:
- Login mit Rollenprüfung
ADMIN - Dashboard mit vollständiger Navigation (Übersicht, Aufträge, HR, Firmenkonzepte, Lohn und Stunden, Mitarbeiter, Rollen, Berichte, Suche, Audit-Log)
- Rollen-Tab: alle Benutzer aus der DB anzeigen, Rolle ändern, deaktivieren/aktivieren (
GET /api/users,PUT /api/users/:id) - HR-Tab: HR-Benutzer aus DB anzeigen, anlegen, bearbeiten und deaktivieren/aktivieren (
GET /api/users?role=HR,POST /api/users,PUT /api/users/:id) - Mitarbeiter-Tab: Mitarbeiter aus DB anzeigen, anlegen, bearbeiten und deaktivieren/aktivieren (
GET /api/users?role=EMPLOYEE,POST /api/users,PUT /api/users/:id) - Aufträge: werden über den Order Service gespeichert (
GET/POST/PUT /api/orders) - Firmenkonzepte, Stunden-/Lohnregeln, Berichte, Audit-Log: weiterhin lokal im Browser
- Suche und Übersichts-Statistiken verwenden echte DB-Daten für Benutzer/Mitarbeiter/Aufträge
Hinweis: Firmenkonzepte, Stunden- und Lohnregeln werden aktuell noch im
localStoragedes Browsers gespeichert. Aufträge sind jetzt backendgestützt und bleiben in MySQL erhalten.
Hinweis Bearbeiten-Formular: Beim Bearbeiten eines bestehenden HR- oder Mitarbeiter-Benutzers werden Benutzername und Passwort ausgeblendet, da der
PUT /api/users/:idEndpunkt diese Felder nicht akzeptiert (Benutzername ist eindeutig und unveränderlich; Passwortänderung ist nicht implementiert).
Noch zu implementieren:
- Firmendaten-Seite (
/company) - Stundenübersicht aus dem Time Service laden
Implementiert:
- Login mit Rollenprüfung
HR - Benutzerverwaltung (
/users): Schichtleiter/Mitarbeiter anlegen, bearbeiten, deaktivieren - Stundenübersicht (
/time): Gesamtstunden, Monatsdetail und Pausenverstösse prüfen - Stundenfreigabe (
/hour-budgets): Monatskontingente pro Schichtleiter freigeben - Rechnungen (
/invoices): Erstellen, versenden, als bezahlt markieren (DRAFT → SENT → PAID) - Lohnauszüge (
/payroll): Monatslohn aus Stunden, Stundenrate, Zuschlägen und Abzügen berechnen (DRAFT → APPROVED → PAID) - Absenzen & Ferien (
/absences): Ferienanfragen genehmigen/ablehnen, Absenzen erfassen und verwalten
Noch zu implementieren:
- Abwesenheitskalender (
/absences/calendar) — Backend-Endpunkt vorhanden, Frontend-Widget fehlt (US-HR-10)
Implementiert:
- Login mit Rollenprüfung
SHIFT_LEADund Speicherung vonuserId - Dashboard mit Kacheln für Planung, Aufträge und Notizen
- Arbeitsplan-Erstellung (
/planning) mit automatisch übernommener HR-Stundenfreigabe - Schichten hinzufügen inklusive Mitarbeiter-Auswahl, optionaler Auftrag-ID und Notiz
- Stundenübersicht mit HR-Kontingent, geplanten Stunden, Reststunden und Warnungen
- Arbeitszeiten-Seite (
/time) lädt Gesamtstunden, Monatsdetails und Pausenverstösse aus dem Time Service - Arbeitsplan veröffentlichen, damit Mitarbeiter die Schichten im Mobile-Kalender sehen
Implementiert zusätzlich:
- Auftrags-Ansicht (
/orders) lädt zugewiesene Aufträge aus dem Order Service - Schichtleiter kann den Auftragsstatus auf
OPEN,IN_PROGRESSoderDONEsetzen - Arbeitsplanung bietet zugewiesene Aufträge als Auswahl für neue Schichten an
Vorbereitet:
- Notizen-Übersicht (
/notes) als Platzhalter; Schichtnotizen werden bereits im Arbeitsplan gespeichert
Verzeichnis: mobile/
Rolle: Mitarbeiter (emp.meier / password)
Technologie: Flutter SDK 3.3+, Provider 6.1, http, SharedPreferences, image_picker
Die Mobile App verbindet sich mit demselben API Gateway (:8000) und verwendet dasselbe JWT wie die drei Web-Frontends. Sie ist ausschliesslich für die Rolle EMPLOYEE gedacht.
- Flutter SDK ≥ 3.3 (flutter.dev/docs/get-started/install)
- Android Studio mit installiertem Android Emulator (API 33+) oder ein physisches Android-Gerät
- Docker-Stack läuft (
docker compose up -d) – die App spricht gegen den Backend-Stack
Die Basis-URL für alle API-Anfragen steht zentral in einer Datei:
mobile/lib/services/api_config.dart
class ApiConfig {
// Android Emulator: 10.0.2.2 = localhost des Host-Rechners
// Echtes Gerät im selben WLAN: lokale IP des Host-Rechners (z.B. 192.168.1.x)
static const String baseUrl = 'http://10.0.2.2:8000';
static const Duration requestTimeout = Duration(seconds: 8);
}| Situation | Wert für baseUrl |
|---|---|
| Android-Emulator (Standard) | http://10.0.2.2:8000 |
| Echtes Gerät im selben WLAN | http://<lokale-IP-des-Rechners>:8000 |
| iOS-Simulator | http://localhost:8000 |
Die lokale IP des Rechners findet man unter Windows mit
ipconfig(z.B.192.168.1.42), unter macOS/Linux mitifconfigoderip a.
Wichtig: Nach einer URL-Änderung muss die App neu gebaut werden (flutter run).
cd mobile
flutter pub get # Dependencies installieren (einmalig)
flutter run # App im verbundenen Emulator / Gerät startenAlternativer Start direkt im Android Studio:
- Emulator über Device Manager starten
- In der Run-Konfiguration das
mobile/-Verzeichnis auswählen - Run drücken
| Feld | Wert |
|---|---|
| Benutzername | emp.meier |
| Passwort | password |
Die App ruft POST /api/auth/login auf denselben API Gateway auf wie die Web-Frontends. Nach erfolgreichem Login speichert sie token, role, username und userId in den SharedPreferences des Geräts. Das JWT ist 24 Stunden gültig.
Die App prüft die Rolle nicht beim Login – technisch kann jeder Systembenutzer die Mobile App verwenden. Im Normalbetrieb ist sie für die Rolle
EMPLOYEEvorgesehen.
Ermöglicht dem Mitarbeiter, die Arbeitszeit direkt aus der App zu erfassen.
| Aktion | Endpoint | Payload |
|---|---|---|
| Status laden | GET /api/time/current/{userId} |
– |
| Check-in | POST /api/time/checkin |
{ "employeeId": 4 } |
| Check-out | POST /api/time/checkout |
{ "employeeId": 4, "breakMinutes": 30 } |
- Beim Check-out wird die Pausenzeit in Minuten abgezogen
- Die Netto-Arbeitsstunden werden vom Time Service berechnet und zurückgegeben
- Ist der Mitarbeiter bereits eingecheckt, zeigt die App den aktuellen Check-in-Zeitpunkt
Zeigt die veröffentlichten Schichten des Mitarbeiters für den laufenden Monat.
| Aktion | Endpoint |
|---|---|
| Schichten laden | GET /api/planning/calendar/{userId}?from=YYYY-MM-DD&to=YYYY-MM-DD |
Schichten sind nur sichtbar, wenn der Schichtleiter den Arbeitsplan veröffentlicht hat (Status
PUBLISHED). Entwürfe (DRAFT) erscheinen nicht.
Mitarbeiter kann eigene Ferienanträge und Absenzen einreichen und den Status einsehen.
| Aktion | Endpoint | Payload |
|---|---|---|
| Absenzen laden | GET /api/absences/employee/{userId} |
– |
| Absenz einreichen | POST /api/absences |
{ "employeeId": 4, "type": "VACATION", "startDate": "...", "endDate": "...", "reason": "..." } |
Typen: VACATION (Ferien), SICK (Krank), OTHER (Sonstiges)
Status: PENDING → APPROVED / REJECTED (wird von HR gesetzt)
Mitarbeiter fotografiert den Arbeitsort und lädt das Bild mit optionaler Auftrags-ID hoch.
| Aktion | Endpoint |
|---|---|
| Bild hochladen | POST /api/media/upload (Multipart) |
Felder: file (Bilddatei), employeeId, orderId (optional), note (optional)
Das Bild wird in MongoDB (workforce-media.media_reports) gespeichert. Max. 10 MB.
mobile/lib/
├── main.dart # App-Einstieg, Provider-Setup, Login/Home-Weiche
├── services/
│ ├── api_config.dart # Zentrale URL-Konfiguration (hier bei Bedarf anpassen)
│ ├── api_service.dart # GET, POST, Multipart-Upload mit JWT-Header
│ └── auth_service.dart # Login, Logout, JWT/UserId/Rolle (SharedPreferences)
└── screens/
├── login_screen.dart # Login-UI
├── home_screen.dart # Bottom-Navigation (4 Tabs)
├── checkin_screen.dart # Check-in / Check-out → Time Service
├── calendar_screen.dart # Monatskalender → Planning Service
├── absence_screen.dart # Absenzen einreichen und anzeigen → Absence Service
└── report_screen.dart # Kamera → Bild-Upload → Report/Media Service
Alle API-Aufrufe laufen ausschliesslich über ApiService. Kein Screen ruft http direkt auf. Der AuthService verwaltet den Login-Zustand und stellt den JWT-Header für alle Requests bereit.
| Problem | Ursache | Lösung |
|---|---|---|
Backend nicht erreichbar |
Falsche baseUrl in api_config.dart |
URL auf 10.0.2.2:8000 (Emulator) oder lokale IP (echtes Gerät) setzen |
Nicht autorisiert (401) |
Token abgelaufen (24h) oder nie gesetzt | Ausloggen und neu einloggen |
| Keine Schichten im Kalender | Schichtleiter hat Plan noch nicht veröffentlicht | Im Schichtleiter-Web Plan veröffentlichen (Status → PUBLISHED) |
| Kamera öffnet sich nicht | image_picker Berechtigungen fehlen |
Android: Kameraberechtigung in App-Einstellungen erteilen |
| Upload schlägt mit 413 fehl | Bild grösser als 10 MB | Kleinere Auflösung / niedrigere Bildqualität wählen |
- Auftragsdaten herunterladen (
GET /api/orders/{id}/download) - Eigenes Benutzerprofil anzeigen
Wird verwendet für alle strukturierten Daten.
Datenbank: workforce
Benutzer: workforce / workforce
Intern verwenden die Backend-Services weiterhin mysql-db:3306. Auf dem Windows-Host ist MySQL über localhost:3307 erreichbar, damit es keinen Konflikt mit einer lokal installierten Windows-MySQL-Instanz auf Port 3306 gibt.
Tabellen (automatisch angelegt via database/mysql/init.sql):
| Tabelle | Inhalt |
|---|---|
roles |
ADMIN, HR, SHIFT_LEAD, EMPLOYEE |
users |
Alle Benutzer mit Rollenzuweisung |
orders |
Aufträge inklusive Firma, Einsatzort, Zeitraum, Status und Schichtleiter-Zuweisung |
order_employees |
Zuordnung Mitarbeiter ↔ Auftrag |
work_plans |
Arbeitspläne inkl. HR-Stundenkontingent, Status und Veröffentlichungszeitpunkt |
shifts |
Einzelschichten mit Mitarbeiter- und optionalem Auftragsbezug |
time_entries |
Check-in/out Einträge |
absences |
Absenzen und Ferienanfragen |
invoices |
Rechnungen |
invoice_positions |
Rechnungspositionen |
phpMyAdmin: http://localhost:8080
Wird verwendet für Bild-Uploads aus der Mobile App.
Datenbank: workforce-media
Benutzer: workforce / workforce
Collection: media_reports
{
"employee_id": 42,
"order_id": 7,
"rapport_id": "uuid-...",
"filename": "bild_2024_01.jpg",
"content_type": "image/jpeg",
"file_size": 204800,
"storage_path": "mongodb://workforce-media/media_reports/uuid-...",
"uploaded_at": "2024-01-15T14:30:00Z",
"metadata": { "note": "Rapportfoto Eingang A" },
"data": "<binary image data>"
}Mongo Express: http://localhost:8081
- Docker Desktop installiert und gestartet
- Ports 3001–3004, 8000–8009, 3307, 27017, 8080, 8081 sind frei
- Node.js v18+ nur nötig, wenn Frontends im Entwicklungsmodus (ausserhalb Docker) gestartet werden
docker compose up --build -dStartet alle Container auf einmal: Datenbanken, alle 8 Backend-Services, alle 3 Frontends und die DB-Admins.
| Flag | Bedeutung |
|---|---|
--build |
Bilder neu bauen (nötig beim ersten Start und nach Code-Änderungen) |
-d |
Hintergrundmodus (Terminal bleibt frei) |
Folgestarts ohne Code-Änderungen:
docker compose up -d(kein--build, startet in Sekunden).
docker compose psmysql-db muss den Status healthy haben, bevor die Backend-Services bereit sind. Wenn ein Service unhealthy zeigt, Logs prüfen.
# Live-Log eines Services
docker compose logs -f api-gateway
docker compose logs -f user-role-service
docker compose logs -f absence-vacation-service
# Letzten 50 Zeilen ohne Live-Follow
docker compose logs --tail=50 hr-web# Ein Service
docker compose up --build user-role-service -d
# Mehrere Services gleichzeitig
docker compose up --build auth-service user-role-service absence-vacation-service -ddocker compose up mysql-db mongo-db phpmyadmin mongo-express -dSpring-Boot-Services können dann direkt aus IntelliJ gestartet werden (SPRING_PROFILES_ACTIVE=local).
# Stoppen – Daten bleiben erhalten
docker compose down
# Stoppen + alle Volumes löschen (DB-Reset)
docker compose down -vNach
docker compose down -v: beim nächstendocker compose up --build -dwerden Schema, Rollen und alle Demo-Accounts automatisch neu angelegt.
| Anwendung | URL | Login |
|---|---|---|
| Admin-Frontend | http://localhost:3001 | admin / password |
| HR-Frontend | http://localhost:3002 | hr.mueller / password |
| Schichtleiter-Frontend | http://localhost:3003 | sl.huber / password |
| Flipper Auth Dashboard | http://localhost:3004 | (kein Login) |
| phpMyAdmin | http://localhost:8080 | workforce / workforce |
| Mongo Express | http://localhost:8081 | (kein Login) |
| API Gateway | http://localhost:8000 | (direkte API-Calls) |
| Komponente | Port |
|---|---|
| API Gateway | 8000 |
| Auth Service | 8001 |
| User & Role Service | 8002 |
| Order Service | 8003 |
| Planning Service | 8004 |
| Time Service | 8005 |
| Absence/Vacation Svc. | 8006 |
| Billing Service | 8007 |
| Report/Media Service | 8008 |
| Flipper Auth Service | 8009 |
| Admin Web | 3001 |
| HR Web | 3002 |
| Schichtleiter Web | 3003 |
| Flipper Auth Web | 3004 |
| MySQL | 3307 Host → 3306 Container |
| MongoDB | 27017 |
| phpMyAdmin | 8080 |
| Mongo Express | 8081 |
Das Kanban-Board findet ihr direkt hier im GitHub-Repository unter dem Tab Projects.
main ← stabiler Stand
└── feature/<name> ← Feature-Branch für eine Aufgabe
Beispiele:
feature/time-service-checkinfeature/hr-web-invoicesfeature/flutter-calendarfeature/owasp-testplan
- Karte im Kanban-Board von To Do → In Progress verschieben
- Feature-Branch erstellen:
git checkout -b feature/<name>
- Implementieren, committen
- Branch pushen
- Pull Request auf
mainerstellen oder nach Absprache direkt inmainmergen - Kurze Gegenkontrolle durch eine andere Person (Code Review)
- Merge → Karte auf Done verschieben
<typ>(<bereich>): <kurze Beschreibung>
Beispiele:
feat(time-service): add check-in endpoint
fix(auth-service): correct JWT expiration
feat(flutter): connect calendar to planning API
feat(hr-web): implement invoice creation page
Typen: feat, fix, refactor, docs, style, test
- Packagestruktur:
com.workforce.<servicename>.<schicht> - Schichten:
controller→service→repository→model - REST-Endpoints geben immer
ResponseEntity<T>zurück - Fehlerbehandlung:
@ControllerAdvicemit sinnvollen HTTP-Status-Codes - JWT-Secret nie in den Code schreiben – nur über
application.yml/ Umgebungsvariable - Lombok (
@Data,@RequiredArgsConstructor) für Boilerplate
- Komponenten: PascalCase (
UserList.jsx) - Hooks/Funktionen: camelCase
- API-Aufrufe immer über
src/services/api.js(nicht direktaxios.get(...)) - Kein Token-Handling direkt in Komponenten – nur in
api.jsundlocalStorage
- Screens in
lib/screens/, Services inlib/services/, Modelle inlib/models/ - API-Aufrufe nur über
ApiService, nie direkthttp.get()in Widgets - State-Management mit Provider (
AuthService extends ChangeNotifier) - Keine hardcodierten URLs – Konstante in
ApiService._baseUrl
Viel Erfolg beim Ausarbeiten! Bei Fragen → Issue erstellen oder direkt im Kanban kommentieren.