468 lines
18 KiB
Markdown
468 lines
18 KiB
Markdown
# 🏗️ Architektura Systemu Ustawień Dyscyplin
|
|
|
|
## 📐 Diagram Przepływu
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ UŻYTKOWNIK │
|
|
│ (Admin lub Gracz) │
|
|
└──────────────────────────┬──────────────────────────────────────┘
|
|
│
|
|
┌────────────┴────────────┐
|
|
│ │
|
|
▼ ▼
|
|
┌──────────────────┐ ┌──────────────────┐
|
|
│ PANEL ADMINA │ │ GRA KLIENCKA │
|
|
│ (UI do edycji) │ │ (Startup gry) │
|
|
└────────┬─────────┘ └────────┬─────────┘
|
|
│ │
|
|
│ │
|
|
GET/POST │ │ GET
|
|
/admin.. │ │ /api/..
|
|
│ │
|
|
└────────────┬────────────┘
|
|
│
|
|
▼
|
|
┌──────────────────────────┐
|
|
│ KONTROLER (index.php) │
|
|
│ - Routing GET/POST │
|
|
│ - Auth check (admin) │
|
|
│ - JSON handling │
|
|
└────────────┬─────────────┘
|
|
│
|
|
▼
|
|
┌──────────────────────────┐
|
|
│ SERVICE (Biznesowa logika)
|
|
│ - Walidacja │
|
|
│ - Transformacja │
|
|
│ - Versioning │
|
|
│ - Snapshot │
|
|
└────────────┬─────────────┘
|
|
│
|
|
▼
|
|
┌──────────────────────────┐
|
|
│ MODEL (DB Access) │
|
|
│ - CRUD │
|
|
│ - Walidacja pierwotna │
|
|
│ - Transakcje │
|
|
└────────────┬─────────────┘
|
|
│
|
|
▼
|
|
┌──────────────────────────┐
|
|
│ BAZA DANYCH (MySQL) │
|
|
│ settings_disciplines │
|
|
└──────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## 🔄 Scenariusz 1: Admin zmienia ustawienia
|
|
|
|
```
|
|
1. Admin otwiera panel
|
|
GET /administration/disciplines/ping-pong/
|
|
|
|
2. Service pobiera bieżące ustawienia
|
|
$service->getSettingsForAPI('ping-pong')
|
|
|
|
3. UI wyświetla form z obecnymi wartościami
|
|
|
|
4. Admin zmienia pointsToWin (11 → 21) i zapisuje
|
|
POST /administration/disciplines/ping-pong/settings
|
|
|
|
5. Kontroler odbiera JSON:
|
|
{
|
|
"rules": { "pointsToWin": 21, ... },
|
|
"customization": { ... }
|
|
}
|
|
|
|
6. Service waliduje
|
|
✅ pointsToWin: 21 (OK, od 1 do 100)
|
|
✅ settingsVersion zwiększ (1 → 2)
|
|
✅ Nieparzyste (OK)
|
|
|
|
7. Model UPDATE w BD
|
|
UPDATE settings_disciplines SET
|
|
pointsToWin = 21,
|
|
settingsVersion = 2,
|
|
updated_at = NOW(),
|
|
updated_by = admin_id
|
|
WHERE discipline = 'ping-pong'
|
|
|
|
8. Model zwraca: { settingsVersion: 2, ... }
|
|
|
|
9. Service formatuje odpowiedź
|
|
|
|
10. Kontroler zwraca JSON z status 200
|
|
{
|
|
"success": true,
|
|
"message": "Settings updated",
|
|
"data": { settingsVersion: 2, ... }
|
|
}
|
|
|
|
11. UI odświeża panel - pokazuje wersję 2
|
|
```
|
|
|
|
---
|
|
|
|
## 🎮 Scenariusz 2: Gra pobiera ustawienia
|
|
|
|
```
|
|
1. Gra startuje w przeglądarce
|
|
|
|
2. JavaScript: loadGameSettings()
|
|
|
|
3. Pobiera snapshot
|
|
GET /api/discipline-settings.php?
|
|
discipline=ping-pong&snapshot=true
|
|
|
|
4. Kontroler (discipline-settings.php):
|
|
- Nie sprawdza auth (publiczny endpoint)
|
|
- Query: discipline=ping-pong
|
|
- Model: getSnapshot('ping-pong')
|
|
|
|
5. Model zwraca snapshot
|
|
{
|
|
"discipline": "ping-pong",
|
|
"settingsVersion": 2,
|
|
"rules": {
|
|
"pointsToWin": 21,
|
|
...
|
|
},
|
|
"snapshotTimestamp": "2026-01-28 12:35:10"
|
|
}
|
|
|
|
6. Kontroler zwraca JSON
|
|
{
|
|
"success": true,
|
|
"snapshot": { ... }
|
|
}
|
|
|
|
7. JavaScript inicjalizuje grę z snapshot'em
|
|
new PingPongGame({
|
|
pointsToWin: 21,
|
|
setsToWin: 3,
|
|
settingsVersion: 2
|
|
})
|
|
|
|
8. Gracz gra i uzyskuje wynik
|
|
|
|
9. JavaScript wysyła wynik na backend
|
|
POST /api/matches_sync.php
|
|
{
|
|
"team1_score": 3,
|
|
"team2_score": 2,
|
|
"settingsVersion": 2,
|
|
"snapshotTimestamp": "2026-01-28 12:35:10"
|
|
}
|
|
|
|
10. Backend zapisuje mecz z settingsVersion
|
|
Dzięki temu wiadomo, jakie reguły obowiązywały
|
|
```
|
|
|
|
---
|
|
|
|
## 📊 Tabela Ustawień vs Mechanika Gry
|
|
|
|
```
|
|
Tabela: settings_disciplines
|
|
┌─────────────────┬──────────────────────────────────┐
|
|
│ Kolumna │ Wpływ │
|
|
├─────────────────┼──────────────────────────────────┤
|
|
│ pointsToWin │ ✅ Logika gry │
|
|
│ setsToWin │ ✅ Logika gry │
|
|
│ serveRotation │ ✅ Logika gry │
|
|
│ specialRules │ ✅ Logika gry (informacyjnie) │
|
|
│ customization │ ❌ Tylko UI (bez wpływu) │
|
|
│ settingsVersion │ ⚠️ Dla tracking'u │
|
|
└─────────────────┴──────────────────────────────────┘
|
|
|
|
✅ = Wpływa na wynik meczu
|
|
❌ = Tylko wizualne
|
|
⚠️ = Metadata
|
|
```
|
|
|
|
---
|
|
|
|
## 🔒 Warstwy Bezpieczeństwa
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────┐
|
|
│ ENDPOINT ADMINISTRACYJNY │
|
|
│ /administration/disciplines/{disc}/settings │
|
|
├─────────────────────────────────────────────────┤
|
|
│ 1. Session check │
|
|
│ ✓ Czy zalogowany? │
|
|
├─────────────────────────────────────────────────┤
|
|
│ 2. Role check │
|
|
│ ✓ Czy admin? │
|
|
├─────────────────────────────────────────────────┤
|
|
│ 3. Input validation (Service) │
|
|
│ ✓ Typy, zakresy, wymagane pola │
|
|
├─────────────────────────────────────────────────┤
|
|
│ 4. Business logic validation (Service) │
|
|
│ ✓ Nieparzyste liczby │
|
|
│ ✓ Logika biznesowa │
|
|
├─────────────────────────────────────────────────┤
|
|
│ 5. Database operations (Model) │
|
|
│ ✓ Prepared statements (SQL injection) │
|
|
│ ✓ Transakcje (ACID) │
|
|
└─────────────────────────────────────────────────┘
|
|
|
|
┌─────────────────────────────────────────────────┐
|
|
│ ENDPOINT PUBLICZNY (API) │
|
|
│ /api/discipline-settings.php │
|
|
├─────────────────────────────────────────────────┤
|
|
│ 1. Method check │
|
|
│ ✓ Tylko GET (bez POST) │
|
|
├─────────────────────────────────────────────────┤
|
|
│ 2. Discipline validation │
|
|
│ ✓ Tylko znane dyscypliny │
|
|
├─────────────────────────────────────────────────┤
|
|
│ 3. Read-only (brak możliwości zmian) │
|
|
│ ✓ Model tylko SELECT │
|
|
├─────────────────────────────────────────────────┤
|
|
│ 4. Database queries │
|
|
│ ✓ Prepared statements │
|
|
└─────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## 🗂️ Warianty Danych
|
|
|
|
```
|
|
┌──────────────────────┬──────────┬──────────┐
|
|
│ Variant │ Gdzie │ Kiedy │
|
|
├──────────────────────┼──────────┼──────────┤
|
|
│ Defaults (hardcoded) │ Code │ Zawsze │
|
|
│ DB Snapshot 1 │ Database │ Przy DB │
|
|
│ DB Snapshot 2 │ Database │ Po zmian │
|
|
│ Match Snapshot │ Match │ Start │
|
|
└──────────────────────┴──────────┴──────────┘
|
|
|
|
Defaults → Database (first run)
|
|
↓
|
|
Current Version (v1)
|
|
↓
|
|
Admin zmienia
|
|
↓
|
|
New Version (v2)
|
|
↓
|
|
Każdy nowy mecz → Snapshot v2
|
|
Stare mecze (v1) → Zachowują v1
|
|
```
|
|
|
|
---
|
|
|
|
## 🔁 Cykl Życia Ustawienia
|
|
|
|
```
|
|
CREATE (v1)
|
|
│
|
|
├─ Domyślne wartości z kodu
|
|
├─ Zapisane w BD
|
|
│
|
|
MODIFY (v1 → v2)
|
|
│
|
|
├─ Admin zmienia pointsToWin
|
|
├─ Service: walidacja
|
|
├─ Model: settingsVersion++
|
|
├─ UPDATE w BD
|
|
│
|
|
SNAPSHOT
|
|
│
|
|
├─ Gra: pobiera snapshot (v2)
|
|
├─ Match: zapisany z version = 2
|
|
│
|
|
MODIFY (v2 → v3)
|
|
│
|
|
├─ Admin zmienia kolory
|
|
├─ Service: walidacja
|
|
├─ Model: settingsVersion++
|
|
│
|
|
SNAPSHOT
|
|
│
|
|
├─ Nowe gry: version 3
|
|
├─ Stare gry: version 1 i 2 nie zmienią się
|
|
│
|
|
ANALYTICS
|
|
│
|
|
├─ Porównanie: średnia czasu meczu v1 vs v2 vs v3
|
|
├─ Wnioski: która wersja miała najlepsze wyniki
|
|
```
|
|
|
|
---
|
|
|
|
## 📱 Komponenty
|
|
|
|
### 1. Model (DisciplineSettingsModel)
|
|
```php
|
|
- getSettings($discipline)
|
|
- getSettingsByVersion($discipline, $version)
|
|
- updateSettings($discipline, $settings, $userId)
|
|
- getSnapshot($discipline, $version)
|
|
- ensureTableExists()
|
|
- getDefaults($discipline)
|
|
```
|
|
|
|
### 2. Service (DisciplineSettingsService)
|
|
```php
|
|
- getSettingsForAPI($discipline)
|
|
- validateAndUpdate($discipline, $input, $userId)
|
|
- getMatchSnapshot($discipline, $version)
|
|
- resetToDefaults($discipline, $userId)
|
|
- compareVersions($old, $new)
|
|
```
|
|
|
|
### 3. Controller (index.php)
|
|
```php
|
|
handleGetSettings($service, $discipline)
|
|
handlePostSettings($service, $discipline)
|
|
- Routing
|
|
- Auth
|
|
- JSON parsing
|
|
- Error handling
|
|
```
|
|
|
|
### 4. Public API (discipline-settings.php)
|
|
```php
|
|
- GET only
|
|
- No auth required
|
|
- Zwraca snapshot
|
|
- Dla gry kliencka
|
|
```
|
|
|
|
---
|
|
|
|
## 🧮 Walidacja - Etapy
|
|
|
|
```
|
|
┌─────────────────────────────────────────┐
|
|
│ Input: POST JSON │
|
|
└────────────┬────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────┐
|
|
│ JSON Parse (Controller) │
|
|
│ Czy to poprawny JSON? │
|
|
└────────────┬────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────┐
|
|
│ Type Check (Service) │
|
|
│ Czy rules to array? Czy customization │
|
|
│ to object? │
|
|
└────────────┬────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────┐
|
|
│ Range Check (Service) │
|
|
│ pointsToWin: 1-100? │
|
|
│ setsToWin: 1-100? │
|
|
│ serveRotation: 1-50? │
|
|
└────────────┬────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────┐
|
|
│ Business Logic (Service) │
|
|
│ Nieparzyste liczby? │
|
|
│ Consistency check? │
|
|
└────────────┬────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────┐
|
|
│ DB Level (Model) │
|
|
│ Constraints (unique, NOT NULL) │
|
|
│ Prepared statements │
|
|
└────────────┬────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────┐
|
|
│ ✅ Sukces lub ❌ Błąd │
|
|
│ Z powrotem do controllera │
|
|
└─────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## 🚦 HTTP Status Codes
|
|
|
|
```
|
|
┌─────┬─────────────────────────────────────────┐
|
|
│ 200 │ OK - Operacja udana │
|
|
├─────┼─────────────────────────────────────────┤
|
|
│ 400 │ Bad Request - Błąd walidacji │
|
|
├─────┼─────────────────────────────────────────┤
|
|
│ 401 │ Unauthorized - Nie zalogowany │
|
|
├─────┼─────────────────────────────────────────┤
|
|
│ 403 │ Forbidden - Brak roli admin │
|
|
├─────┼─────────────────────────────────────────┤
|
|
│ 405 │ Method Not Allowed - Typ request │
|
|
├─────┼─────────────────────────────────────────┤
|
|
│ 500 │ Server Error - Błąd bazy / inne │
|
|
└─────┴─────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## 🔄 Versioning Strategy
|
|
|
|
```
|
|
v1 (Initial)
|
|
├─ pointsToWin: 11
|
|
├─ setsToWin: 3
|
|
└─ serveRotation: 2
|
|
|
|
v2 (After change)
|
|
├─ pointsToWin: 21 ← ZMIANA
|
|
├─ setsToWin: 3
|
|
└─ serveRotation: 2
|
|
|
|
v3 (After another change)
|
|
├─ pointsToWin: 21
|
|
├─ setsToWin: 3
|
|
└─ serveRotation: 3 ← ZMIANA
|
|
|
|
Każda zmiana = nowa wersja
|
|
Snapshot zawsze ma konkretną wersję
|
|
Stare gry zawsze "widzą" swoją wersję
|
|
```
|
|
|
|
---
|
|
|
|
## 📈 Możliwości Rozszerzenia
|
|
|
|
```
|
|
Teraz:
|
|
settings_disciplines (bieżące)
|
|
|
|
Przyszłość:
|
|
settings_disciplines_history
|
|
├─ Pełna historia zmian
|
|
├─ Kto zmienił i kiedy
|
|
├─ Co się zmieniło
|
|
└─ Rollback
|
|
|
|
settings_scheduled_changes
|
|
├─ Zmiana planowana na czasę
|
|
├─ Notyfikacja dla użytkowników
|
|
|
|
settings_ab_tests
|
|
├─ Grupa A: wersja 1
|
|
├─ Grupa B: wersja 2
|
|
└─ Analytics
|
|
```
|
|
|
|
---
|
|
|
|
## 🎯 Kluczowe Założenia
|
|
|
|
1. **Immutability** - snapshot po starcie nie zmienia się
|
|
2. **Versioning** - każda zmiana = nowa wersja
|
|
3. **Transparency** - każdy mecz wie jakie reguły obowiązywały
|
|
4. **Backward Compatibility** - stare gry nie wpływane zmianami
|
|
5. **Auditability** - każda zmiana jest zalogowana
|
|
6. **Extensibility** - łatwo dodać nową dyscyplinę
|
|
|