298 lines
16 KiB
Markdown
298 lines
16 KiB
Markdown
|
|
# Workforce Planner – Zielarchitektur
|
|||
|
|
|
|||
|
|
## 1. Architekturdiagramm
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|||
|
|
│ CLIENTS │
|
|||
|
|
│ │
|
|||
|
|
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │
|
|||
|
|
│ │ Desktop App │ │ Web App │ │ MedWork Plugin │ │
|
|||
|
|
│ │ (tkinter) │ │(React/Vue/…) │ │ (Integration) │ │
|
|||
|
|
│ └──────┬───────┘ └──────┬───────┘ └────────┬─────────┘ │
|
|||
|
|
│ │ │ │ │
|
|||
|
|
│ └──────────────────┼─────────────────────┘ │
|
|||
|
|
│ │ │
|
|||
|
|
│ HTTPS / REST API │
|
|||
|
|
└────────────────────────────┼────────────────────────────────────┘
|
|||
|
|
│
|
|||
|
|
┌────────────────────────────┼────────────────────────────────────┐
|
|||
|
|
│ API GATEWAY │
|
|||
|
|
│ │
|
|||
|
|
│ ┌─────────────────────────┴────────────────────────────────┐ │
|
|||
|
|
│ │ FastAPI Server │ │
|
|||
|
|
│ │ │ │
|
|||
|
|
│ │ ┌─────────┐ ┌────────────┐ ┌──────────┐ ┌────────┐ │ │
|
|||
|
|
│ │ │ Auth │ │ CORS │ │ Audit │ │ Rate │ │ │
|
|||
|
|
│ │ │Middleware│ │ Middleware │ │Middleware │ │Limiter │ │ │
|
|||
|
|
│ │ └─────────┘ └────────────┘ └──────────┘ └────────┘ │ │
|
|||
|
|
│ │ │ │
|
|||
|
|
│ │ ┌────────────────────────────────────────────────────┐ │ │
|
|||
|
|
│ │ │ API Routes │ │ │
|
|||
|
|
│ │ │ /employees /absences /balance /practices │ │ │
|
|||
|
|
│ │ │ /auth /reports /health /audit │ │ │
|
|||
|
|
│ │ └────────────────────────┬───────────────────────────┘ │ │
|
|||
|
|
│ └───────────────────────────┼───────────────────────────────┘ │
|
|||
|
|
└──────────────────────────────┼──────────────────────────────────┘
|
|||
|
|
│
|
|||
|
|
┌──────────────────────────────┼──────────────────────────────────┐
|
|||
|
|
│ SERVICE LAYER │
|
|||
|
|
│ (Businesslogik) │
|
|||
|
|
│ │
|
|||
|
|
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
|
|||
|
|
│ │ Employee │ │ Absence │ │ Approval │ │
|
|||
|
|
│ │ Service │ │ Service │ │ Workflow │ │
|
|||
|
|
│ └──────┬───────┘ └──────┬───────┘ └──────────┬───────────┘ │
|
|||
|
|
│ │ │ │ │
|
|||
|
|
│ │ ┌────────────┴──────────┐ │ │
|
|||
|
|
│ │ │ Business Rules │ │ │
|
|||
|
|
│ │ │ • Überschneidung │ │ │
|
|||
|
|
│ │ │ • Kontingentprüfung │ │ │
|
|||
|
|
│ │ │ • Mindestbesetzung │ │ │
|
|||
|
|
│ │ └───────────────────────┘ │ │
|
|||
|
|
│ │ │ │
|
|||
|
|
└─────────┼────────────────────────────────────────┼──────────────┘
|
|||
|
|
│ │
|
|||
|
|
┌─────────┼────────────────────────────────────────┼──────────────┐
|
|||
|
|
│ │ REPOSITORY LAYER │ │
|
|||
|
|
│ │ (Datenzugriff) │ │
|
|||
|
|
│ │
|
|||
|
|
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
|
|||
|
|
│ │ Employee │ │ Absence │ │ Balance │ │
|
|||
|
|
│ │ Repository │ │ Repository │ │ Repository │ │
|
|||
|
|
│ └──────┬───────┘ └──────┬───────┘ └──────────┬───────────┘ │
|
|||
|
|
└─────────┼─────────────────┼─────────────────────┼──────────────┘
|
|||
|
|
│ │ │
|
|||
|
|
└─────────────────┼──────────────────────┘
|
|||
|
|
│
|
|||
|
|
┌──────┴──────┐
|
|||
|
|
│ SQLAlchemy │
|
|||
|
|
│ ORM │
|
|||
|
|
└──────┬──────┘
|
|||
|
|
│
|
|||
|
|
┌────────────┴────────────┐
|
|||
|
|
│ │
|
|||
|
|
┌──────┴──────┐ ┌───────┴──────┐
|
|||
|
|
│ SQLite │ │ PostgreSQL │
|
|||
|
|
│ (Dev) │ │ (Produktion)│
|
|||
|
|
└─────────────┘ └──────────────┘
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 2. Komponentenbeschreibung
|
|||
|
|
|
|||
|
|
### Client Layer
|
|||
|
|
| Komponente | Beschreibung |
|
|||
|
|
|---|---|
|
|||
|
|
| **Desktop App** | Bestehende tkinter-App. Wird umgebaut: statt lokaler JSON-Dateien ruft sie die REST API auf. Gleiche Benutzeranmeldung wie Web. |
|
|||
|
|
| **Web App** | Modernes Web-Frontend (React/Vue/Svelte). Nutzt dieselbe API. Responsive für Tablet/Desktop. |
|
|||
|
|
| **MedWork Plugin** | Adapter der MedWork-Daten (Patienten, Termine) mit dem Arbeitsplan verknüpft. Eigener API-Endpunkt. |
|
|||
|
|
|
|||
|
|
### API Layer
|
|||
|
|
| Komponente | Beschreibung |
|
|||
|
|
|---|---|
|
|||
|
|
| **FastAPI Server** | Zentraler Einstiegspunkt. Stellt REST-Endpunkte bereit. Auto-generierte OpenAPI-Doku. |
|
|||
|
|
| **Auth Middleware** | JWT-basierte Authentifizierung. Gleiche Tokens für Desktop + Web. |
|
|||
|
|
| **Audit Middleware** | Protokolliert jede schreibende Aktion (wer, was, wann, alte/neue Werte). |
|
|||
|
|
| **CORS Middleware** | Erlaubt Cross-Origin-Zugriffe für Web-Client. |
|
|||
|
|
|
|||
|
|
### Service Layer (Businesslogik)
|
|||
|
|
| Komponente | Beschreibung |
|
|||
|
|
|---|---|
|
|||
|
|
| **EmployeeService** | Mitarbeiter anlegen/bearbeiten/deaktivieren. E-Mail-Duplikat-Check. |
|
|||
|
|
| **AbsenceService** | Abwesenheiten erstellen mit Regelprüfung. Kontingent berechnen. |
|
|||
|
|
| **Business Rules** | `check_no_overlap` – keine überlappenden Einträge. `check_balance` – genug Ferientage? `check_min_staffing` – Mindestbesetzung gewährleistet? |
|
|||
|
|
| **Approval Workflow** | Pending → Approved/Rejected Workflow mit Genehmiger-Zuweisung. |
|
|||
|
|
|
|||
|
|
### Repository Layer
|
|||
|
|
| Komponente | Beschreibung |
|
|||
|
|
|---|---|
|
|||
|
|
| **EmployeeRepository** | CRUD für Mitarbeiter. Einziger Code der direkt SQL ausführt. |
|
|||
|
|
| **AbsenceRepository** | CRUD + Spezialabfragen (Überschneidungen, Abwesende pro Tag). |
|
|||
|
|
| **BalanceRepository** | Ferientage-Kontingent pro Mitarbeiter/Jahr. |
|
|||
|
|
|
|||
|
|
### Data Layer
|
|||
|
|
| Komponente | Beschreibung |
|
|||
|
|
|---|---|
|
|||
|
|
| **SQLAlchemy ORM** | Abstrahiert Datenbank. Identischer Code für SQLite und PostgreSQL. |
|
|||
|
|
| **Practice (Mandant)** | Multi-Tenant: jede Praxis hat eigene Mitarbeiter/Abwesenheiten. |
|
|||
|
|
| **AuditLog** | Unveränderliches Änderungsprotokoll mit JSON-Diff. |
|
|||
|
|
|
|||
|
|
## 3. Technologie-Stack
|
|||
|
|
|
|||
|
|
| Schicht | Technologie | Begründung |
|
|||
|
|
|---|---|---|
|
|||
|
|
| **Backend** | Python 3.11+ | Bestehende Codebasis, grosses Ökosystem |
|
|||
|
|
| **API Framework** | FastAPI | Async, Auto-Docs, Pydantic-Integration, modern |
|
|||
|
|
| **ORM** | SQLAlchemy 2.0 | Industriestandard, DB-agnostisch |
|
|||
|
|
| **Validierung** | Pydantic v2 | Schemas für API + Clients, schnell |
|
|||
|
|
| **Auth** | JWT (python-jose) | Stateless, Desktop + Web kompatibel |
|
|||
|
|
| **Passwörter** | passlib + bcrypt | Industriestandard |
|
|||
|
|
| **DB (Dev)** | SQLite | Kein Setup nötig, lokales Testen |
|
|||
|
|
| **DB (Prod)** | PostgreSQL 16 | ACID, JSON-Support, skalierbar |
|
|||
|
|
| **Desktop** | tkinter (→ später Qt) | Bestehend, HTTP-Client wird ergänzt |
|
|||
|
|
| **Web** | React oder Vue.js | SPA, nutzt dieselbe API |
|
|||
|
|
| **Deployment** | Docker + Docker Compose | Einfach, reproduzierbar |
|
|||
|
|
| **Reverse Proxy** | nginx / Caddy | HTTPS, Load Balancing |
|
|||
|
|
|
|||
|
|
## 4. Datenfluss
|
|||
|
|
|
|||
|
|
### Abwesenheit eintragen (von jedem Client)
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Client (Desktop/Web)
|
|||
|
|
│
|
|||
|
|
▼
|
|||
|
|
POST /api/v1/absences
|
|||
|
|
{ employee_id, category, start_date, end_date, reason }
|
|||
|
|
│
|
|||
|
|
▼
|
|||
|
|
Auth Middleware ──── JWT prüfen ──── 401 wenn ungültig
|
|||
|
|
│
|
|||
|
|
▼
|
|||
|
|
AbsenceService.create_absence()
|
|||
|
|
│
|
|||
|
|
├─→ EmployeeRepository.get_by_id() → Mitarbeiter existiert?
|
|||
|
|
├─→ rules.check_no_overlap() → Überschneidung?
|
|||
|
|
├─→ rules.check_balance() → Genug Ferientage?
|
|||
|
|
├─→ rules.check_min_staffing() → Mindestbesetzung OK?
|
|||
|
|
│
|
|||
|
|
├─→ AbsenceRepository.create() → DB INSERT
|
|||
|
|
├─→ AuditLog.log_action() → Protokollierung
|
|||
|
|
├─→ db.commit()
|
|||
|
|
│
|
|||
|
|
▼
|
|||
|
|
Response: 201 Created
|
|||
|
|
{ id, employee_id, category, status, business_days, ... }
|
|||
|
|
│
|
|||
|
|
▼
|
|||
|
|
Client aktualisiert Kalenderansicht
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Login-Flow (Desktop + Web identisch)
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Client
|
|||
|
|
│
|
|||
|
|
▼
|
|||
|
|
POST /api/v1/auth/login
|
|||
|
|
{ email, password }
|
|||
|
|
│
|
|||
|
|
▼
|
|||
|
|
verify_password(plain, hashed)
|
|||
|
|
│
|
|||
|
|
├─→ Falsch: 401 Unauthorized
|
|||
|
|
├─→ Richtig: create_access_token(employee_id, role)
|
|||
|
|
│
|
|||
|
|
▼
|
|||
|
|
Response: { access_token, token_type, employee }
|
|||
|
|
│
|
|||
|
|
▼
|
|||
|
|
Client speichert Token → sendet bei jedem Request als
|
|||
|
|
Authorization: Bearer <token>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Multi-Tenant Datenfluss
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Praxis A (ID: praxis_a) Praxis B (ID: praxis_b)
|
|||
|
|
│ │
|
|||
|
|
▼ ▼
|
|||
|
|
Mitarbeiter mit Mitarbeiter mit
|
|||
|
|
practice_id = praxis_a practice_id = praxis_b
|
|||
|
|
│ │
|
|||
|
|
▼ ▼
|
|||
|
|
Abwesenheiten nur Abwesenheiten nur
|
|||
|
|
für Praxis A sichtbar für Praxis B sichtbar
|
|||
|
|
|
|||
|
|
Admin-Benutzer mit role=ADMIN kann praxisübergreifend zugreifen.
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 5. Deployment-Konzept
|
|||
|
|
|
|||
|
|
### Entwicklung (lokal)
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌─────────────────────────────────────────┐
|
|||
|
|
│ Entwickler-PC │
|
|||
|
|
│ │
|
|||
|
|
│ uvicorn workforce_planner.api.app:app │
|
|||
|
|
│ │ │
|
|||
|
|
│ └──→ SQLite (workforce_planner.db)│
|
|||
|
|
│ │
|
|||
|
|
│ Desktop App (tkinter) → localhost:8000 │
|
|||
|
|
└─────────────────────────────────────────┘
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Produktion (Docker)
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌──────────────────────────────────────────────────────┐
|
|||
|
|
│ Server / VPS / Cloud │
|
|||
|
|
│ │
|
|||
|
|
│ ┌────────────┐ │
|
|||
|
|
│ │ nginx │ ◄── HTTPS (Let's Encrypt) │
|
|||
|
|
│ │ :443/:80 │ │
|
|||
|
|
│ └─────┬──────┘ │
|
|||
|
|
│ │ │
|
|||
|
|
│ ┌─────┴──────┐ ┌──────────────────────────────┐ │
|
|||
|
|
│ │ FastAPI │ │ PostgreSQL │ │
|
|||
|
|
│ │ :8000 ├──►│ :5432 │ │
|
|||
|
|
│ │ (2 Worker) │ │ Volume: /data/postgres │ │
|
|||
|
|
│ └────────────┘ └──────────────────────────────┘ │
|
|||
|
|
│ │
|
|||
|
|
│ docker-compose.yml orchestriert alles │
|
|||
|
|
└──────────────────────────────────────────────────────┘
|
|||
|
|
|
|||
|
|
Desktop Clients ──────► server.praxis.ch:443/api/v1/
|
|||
|
|
Web Clients ──────► server.praxis.ch:443/
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### docker-compose.yml (Ziel)
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
services:
|
|||
|
|
api:
|
|||
|
|
build: .
|
|||
|
|
ports: ["8000:8000"]
|
|||
|
|
environment:
|
|||
|
|
WP_DATABASE_URL: postgresql://wp:secret@db:5432/workforce
|
|||
|
|
WP_SECRET_KEY: ${SECRET_KEY}
|
|||
|
|
depends_on: [db]
|
|||
|
|
|
|||
|
|
db:
|
|||
|
|
image: postgres:16-alpine
|
|||
|
|
volumes: [pgdata:/var/lib/postgresql/data]
|
|||
|
|
environment:
|
|||
|
|
POSTGRES_DB: workforce
|
|||
|
|
POSTGRES_USER: wp
|
|||
|
|
POSTGRES_PASSWORD: secret
|
|||
|
|
|
|||
|
|
nginx:
|
|||
|
|
image: nginx:alpine
|
|||
|
|
ports: ["443:443", "80:80"]
|
|||
|
|
volumes: [./nginx.conf:/etc/nginx/conf.d/default.conf]
|
|||
|
|
depends_on: [api]
|
|||
|
|
|
|||
|
|
volumes:
|
|||
|
|
pgdata:
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Aktueller Stand
|
|||
|
|
|
|||
|
|
| Komponente | Status |
|
|||
|
|
|---|---|
|
|||
|
|
| Datenmodelle (Employee, Absence, BalanceAccount, Practice, AuditLog) | ✅ Fertig |
|
|||
|
|
| Enums (AbsenceCategory, EmployeeRole, AbsenceStatus) | ✅ Fertig |
|
|||
|
|
| Pydantic Schemas (Create/Update/Read) | ✅ Fertig |
|
|||
|
|
| Database Setup (SQLite/PostgreSQL) | ✅ Fertig |
|
|||
|
|
| Repository Layer (Employee, Absence, Balance) | ✅ Fertig |
|
|||
|
|
| Service Layer + Business Rules | ✅ Fertig |
|
|||
|
|
| FastAPI Routen (Employees, Absences, Balance) | ✅ Fertig |
|
|||
|
|
| JWT Auth + Rollen | ✅ Fertig |
|
|||
|
|
| Audit Logging | ✅ Fertig |
|
|||
|
|
| Multi-Tenant (Practice) | ✅ Modell fertig |
|
|||
|
|
| Desktop-Client Umstellung auf API | ⬜ Nächster Schritt |
|
|||
|
|
| Web-Frontend | ⬜ Geplant |
|
|||
|
|
| MedWork-Integration | ⬜ Geplant |
|
|||
|
|
| Docker Deployment | ⬜ Geplant |
|