admin_users_personels
This commit is contained in:
478
plans/logic_spec_admin_user_person_management.md
Normal file
478
plans/logic_spec_admin_user_person_management.md
Normal file
@@ -0,0 +1,478 @@
|
||||
# 🏗️ Logic Spec: Admin User & Person Management
|
||||
|
||||
## 📋 Metadata
|
||||
- **Tervező:** Service Finder Rendszer-Architect
|
||||
- **Státusz:** Draft (jóváhagyásra vár)
|
||||
- **Masterbook 2 illeszkedés:** `docs/masterbook_2.0.1/User_person_kezelés.md`
|
||||
- **Érintett domain:** `identity` (Person ↔ User Dual Entity)
|
||||
|
||||
---
|
||||
|
||||
## 1. 🎯 Célkitűzés
|
||||
|
||||
A feladat az adminisztrációs felület teljes körű felhasználókezelési rendszerének megtervezése és megvalósítása, amely kiterjed mind a **Person** (természetes személy), mind a **User** (bejelentkezési entitás) entitásokra. Jelenleg a frontend admin felületen **egyetlen user/person kezelő oldal sem létezik**, a meglévő backend végpontok részlegesek.
|
||||
|
||||
### Célok:
|
||||
1. Admin felhasználói lista oldal létrehozása kereséssel, szűréssel, rendezéssel
|
||||
2. Felhasználói részletek oldal létrehozása (User + Person adatok egy nézetben)
|
||||
3. Felhasználók szerkesztése (admin által)
|
||||
4. Person entitások kezelése (listázás, részletek, merge)
|
||||
5. Statisztikai adatok megjelenítése (összesítő kártyák, trendek)
|
||||
6. Szervezeti tagságok áttekintése
|
||||
|
||||
---
|
||||
|
||||
## 2. 📊 Meglévő Rendszer Állapota
|
||||
|
||||
### 2.1 Backend API Végpontok Jelenlegi Állapota
|
||||
|
||||
| Végpont | Metódus | Létezik? | Leírás |
|
||||
|---------|---------|----------|--------|
|
||||
| `/admin/users` | GET | ✅ Teljes | Listázás, keresés, szűrés, lapozás. Outerjoin User→Person→Address→GeoPostalCode |
|
||||
| `/admin/users/{user_id}/ban` | POST | ✅ Teljes | Kitiltás audit loggal |
|
||||
| `/admin/users/{user_id}/penalty` | PATCH | ✅ Teljes | Gamification büntetés |
|
||||
| `/admin/users/bulk-action` | POST | ✅ Teljes | Tömeges műveletek (ban/unban/soft_delete/restore/hard_delete) |
|
||||
| `/admin/users/{user_id}` | GET | ❌ Hiányzik | Felhasználó részletes adatai |
|
||||
| `/admin/users/{user_id}` | PATCH | ❌ Hiányzik | Felhasználó szerkesztése admin által |
|
||||
| `/admin/users/stats` | GET | ❌ Hiányzik | Felhasználói statisztikák |
|
||||
| `/admin/persons` | GET | ❌ Hiányzik | Person lista |
|
||||
| `/admin/persons/{person_id}` | GET | ❌ Hiányzik | Person részletek |
|
||||
| `/admin/persons/{person_id}` | PATCH | ❌ Hiányzik | Person szerkesztése |
|
||||
| `/admin/persons/{person_id}/merge` | POST | ❌ Hiányzik | Person merge (duplum kezelés) |
|
||||
| `/admin/users/{user_id}/memberships` | GET | ❌ Hiányzik | Szervezeti tagságok listája |
|
||||
|
||||
### 2.2 Frontend Admin Oldalak Jelenlegi Állapota
|
||||
|
||||
| Oldal | Létezik? | Megjegyzés |
|
||||
|-------|----------|------------|
|
||||
| `/` (Dashboard) | ✅ | Hardcoded statisztikák |
|
||||
| `/login` | ✅ | Bejelentkezés |
|
||||
| `/garages` | ✅ | Garázs lista + részletek |
|
||||
| `/packages` | ✅ | Csomagkezelés |
|
||||
| `/permissions` | ✅ | RBAC jogosultsági mátrix |
|
||||
| `/users` | ❌ | **Hiányzik** |
|
||||
| `/users/[id]` | ❌ | **Hiányzik** |
|
||||
| `/persons` | ❌ | **Hiányzik** |
|
||||
| `/persons/[id]` | ❌ | **Hiányzik** |
|
||||
|
||||
---
|
||||
|
||||
## 3. 🧠 Dual Entity Adatmodell (Person ↔ User)
|
||||
|
||||
### 3.1 Person (természetes személy)
|
||||
|
||||
| Mező | Típus | Leírás |
|
||||
|------|-------|--------|
|
||||
| `id` | int (PK) | Elsődleges kulcs |
|
||||
| `id_uuid` | UUID | Külső azonosító |
|
||||
| `identity_hash` | str | Egyedi hash duplum detektáláshoz |
|
||||
| `last_name` | str | Vezetéknév |
|
||||
| `first_name` | str | Keresztnév |
|
||||
| `phone` | Optional[str] | Telefonszám |
|
||||
| `mothers_last_name` | Optional[str] | Anyja születési vezetékneve |
|
||||
| `mothers_first_name` | Optional[str] | Anyja születési keresztneve |
|
||||
| `birth_place` | Optional[str] | Születési hely |
|
||||
| `birth_date` | Optional[date] | Születési dátum |
|
||||
| `identity_docs` | Optional[JSON] | Személyi igazolvány/útlevél adatok |
|
||||
| `ice_contact` | Optional[JSON] | Emergency contact |
|
||||
| `is_active` | bool | Aktív-e |
|
||||
| `is_ghost` | bool | Részleges adatú személy |
|
||||
| `merged_into_id` | Optional[int] | FK -> persons.id (soft merge) |
|
||||
| `deleted_at` | Optional[datetime] | Soft delete timestamp |
|
||||
|
||||
**Relációk:** `users` (1:N User), `active_user_account` (User), `address` (Address), `memberships` (OrganizationMember), `owned_business_entities` (Organization)
|
||||
|
||||
### 3.2 User (bejelentkezési entitás)
|
||||
|
||||
| Mező | Típus | Leírás |
|
||||
|------|-------|--------|
|
||||
| `id` | int (PK) | Elsődleges kulcs |
|
||||
| `email` | str (unique) | Email cím |
|
||||
| `hashed_password` | str | Jelszó hash |
|
||||
| `role_id` | Optional[int] | FK -> system.roles.id (RBAC) |
|
||||
| `role` | UserRole enum | SUPERADMIN, ADMIN, MODERATOR, SALES_REP, SERVICE_MGR, USER |
|
||||
| `person_id` | Optional[int] | FK -> identity.persons.id |
|
||||
| `subscription_plan` | Optional[str] | Előfizetési csomag |
|
||||
| `subscription_expires_at` | Optional[datetime] | Előfizetés lejárata |
|
||||
| `is_vip` | bool | VIP státusz |
|
||||
| `referral_code` | Optional[str] (unique) | MLM referral kód |
|
||||
| `referred_by_id` | Optional[int] | FK -> identity.users.id (self-ref) |
|
||||
| `is_active` | bool | Aktív-e |
|
||||
| `is_deleted` | bool | Törölt-e |
|
||||
| `deleted_at` | Optional[datetime] | Törlés időpontja |
|
||||
| `preferred_language` | str | Nyelv (default: "hu") |
|
||||
| `region_code` | Optional[str] | Régió kód |
|
||||
| `preferred_currency` | Optional[str] | Pénznem |
|
||||
| `scope_level` | Optional[str] | global/country/region/organization |
|
||||
| `scope_id` | Optional[int] | Scope azonosító |
|
||||
| `custom_permissions` | Optional[JSON] | Egyedi jogosultságok |
|
||||
| `alternative_emails` | Optional[JSON] | Alternatív emailek |
|
||||
| `ui_mode` | str | light/dark (default: "light") |
|
||||
| `visual_settings` | Optional[JSON] | UI beállítások |
|
||||
| `active_organization_id` | Optional[int] | Aktív szervezet |
|
||||
| `max_vehicles` | int | Jármű limit |
|
||||
| `max_garages` | int | Garázs limit |
|
||||
|
||||
**Relációk:** `person` (Person), `system_role` (SystemRole), `referrer` (User self-ref), `wallet` (Wallet), `trust_profile` (UserTrustProfile), `social_accounts` (SocialAccount), `owned_organizations` (Organization), `memberships` (OrganizationMember)
|
||||
|
||||
### 3.3 OrganizationMember (tagsági kapcsolat)
|
||||
|
||||
| Mező | Típus | Leírás |
|
||||
|------|-------|--------|
|
||||
| `organization_id` | int (FK) | -> fleet.organizations.id |
|
||||
| `user_id` | int (FK) | -> identity.users.id |
|
||||
| `person_id` | int (FK) | -> identity.persons.id |
|
||||
| `invited_email` | Optional[str] | Meghívott email |
|
||||
| `expires_at` | Optional[datetime] | Tagság lejárata |
|
||||
| `role` | str (OrgUserRole) | owner/admin/member |
|
||||
| `permissions` | JSONB | Egyedi org jogosultságok |
|
||||
| `status` | str | active/pending/inactive |
|
||||
| `is_permanent` | bool | Állandó tagság |
|
||||
| `is_verified` | bool | Ellenőrzött tagság |
|
||||
|
||||
### 3.4 Kapcsolatok Diagram
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
Person ||--o{ User : "1:N"
|
||||
Person ||--o| Address : "lakcim"
|
||||
Person ||--o{ OrganizationMember : "tagsagok"
|
||||
Person ||--o{ Organization : "tulajdonolt cegek"
|
||||
Person ||--o| Person : "merged_into (soft merge)"
|
||||
|
||||
User ||--o| Person : "tartozik"
|
||||
User ||--o{ OrganizationMember : "tagsagok"
|
||||
User ||--o{ Organization : "tulajdonolt szervezetek"
|
||||
User ||--o| User : "referred_by (MLM)"
|
||||
User ||--o| SystemRole : "RBAC role"
|
||||
User ||--o| Wallet : "penztarca"
|
||||
User ||--o| UserTrustProfile : "trust score"
|
||||
|
||||
OrganizationMember }o--|| Organization : "tagja"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 🔧 Új Backend API Végpontok Specifikációja
|
||||
|
||||
### 4.1 `GET /admin/users/{user_id}` — Felhasználó Részletek
|
||||
|
||||
**Cél:** Egy felhasználó összes adatának lekérése, beleértve Person, Address, Wallet, TrustProfile, OrganizationMembership adatokat.
|
||||
|
||||
**SQL logika:**
|
||||
```sql
|
||||
SELECT u.*, p.*, a.*, w.*, tp.*, om.*
|
||||
FROM identity.users u
|
||||
LEFT JOIN identity.persons p ON p.id = u.person_id
|
||||
LEFT JOIN identity.addresses a ON a.id = p.address_id
|
||||
LEFT JOIN identity.wallets w ON w.user_id = u.id
|
||||
LEFT JOIN identity.user_trust_profiles tp ON tp.user_id = u.id
|
||||
LEFT JOIN marketplace.organization_members om ON om.user_id = u.id
|
||||
WHERE u.id = :user_id
|
||||
```
|
||||
|
||||
**Válasz struktúra:**
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"email": "user@example.com",
|
||||
"role_id": 2,
|
||||
"role": "admin",
|
||||
"is_active": true,
|
||||
"is_deleted": false,
|
||||
"is_vip": false,
|
||||
"preferred_language": "hu",
|
||||
"region_code": "HU",
|
||||
"preferred_currency": "HUF",
|
||||
"subscription_plan": "premium",
|
||||
"subscription_expires_at": "2026-12-31T23:59:59",
|
||||
"max_vehicles": 10,
|
||||
"max_garages": 5,
|
||||
"active_organization_id": 3,
|
||||
"scope_level": "organization",
|
||||
"scope_id": 3,
|
||||
"created_at": "2026-01-15T08:30:00",
|
||||
"updated_at": "2026-06-28T14:22:00",
|
||||
"person": { "id": 1, "last_name": "Nagy", "first_name": "Istvan", ... },
|
||||
"wallet": { "balance": 15000, ... },
|
||||
"trust_profile": { "trust_score": 85, ... },
|
||||
"memberships": [ { "organization_id": 3, "role": "owner", ... } ],
|
||||
"system_capabilities": [...],
|
||||
"org_capabilities": [...]
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 `PATCH /admin/users/{user_id}` — Felhasználó Szerkesztése
|
||||
|
||||
**Admin által szerkeszthető mezők:**
|
||||
- `email` (csak admin/superadmin)
|
||||
- `is_active` (letiltás/feloldás)
|
||||
- `is_vip`
|
||||
- `preferred_language`, `region_code`, `preferred_currency`
|
||||
- `subscription_plan`, `subscription_expires_at`
|
||||
- `max_vehicles`, `max_garages`
|
||||
- `scope_level`, `scope_id`
|
||||
- `role_id` (csak superadmin)
|
||||
- `custom_permissions`
|
||||
|
||||
**Person almzők (opcionális):**
|
||||
- `person.last_name`, `person.first_name`, `person.phone`
|
||||
- `person.mothers_last_name`, `person.mothers_first_name`
|
||||
- `person.birth_place`, `person.birth_date`
|
||||
- `person.identity_docs`
|
||||
|
||||
### 4.3 `GET /admin/users/stats` — Felhasználói Statisztikák
|
||||
|
||||
```json
|
||||
{
|
||||
"total_users": 2847,
|
||||
"active_users": 2100,
|
||||
"deleted_users": 120,
|
||||
"banned_users": 45,
|
||||
"new_users_today": 12,
|
||||
"new_users_this_week": 85,
|
||||
"new_users_this_month": 320,
|
||||
"users_by_role": {
|
||||
"superadmin": 2, "admin": 15, "moderator": 8,
|
||||
"sales_rep": 23, "service_mgr": 45, "user": 2754
|
||||
},
|
||||
"users_by_plan": {
|
||||
"free": 1500, "basic": 800, "premium": 400, "enterprise": 147
|
||||
},
|
||||
"users_by_language": { "hu": 2200, "en": 400, "de": 150, "ro": 97 },
|
||||
"users_with_person": 2600,
|
||||
"users_without_person": 247,
|
||||
"registration_trend": [
|
||||
{ "date": "2026-06-01", "count": 15 },
|
||||
{ "date": "2026-06-02", "count": 12 }
|
||||
],
|
||||
"active_organizations_count": 420,
|
||||
"total_memberships": 1800
|
||||
}
|
||||
```
|
||||
|
||||
### 4.4 `GET /admin/persons` — Person Lista
|
||||
|
||||
**Paraméterek:** `search`, `is_ghost`, `is_active`, `has_user`, `is_merged`, `skip`, `limit`
|
||||
|
||||
**Válasz:**
|
||||
```json
|
||||
{
|
||||
"total": 3000,
|
||||
"items": [
|
||||
{
|
||||
"id": 1, "last_name": "Nagy", "first_name": "Istvan",
|
||||
"phone": "+36201234567", "birth_date": "1985-03-15",
|
||||
"is_ghost": false, "is_active": true, "merged_into_id": null,
|
||||
"users_count": 1,
|
||||
"active_user": { "id": 1, "email": "user@example.com", "role": "user" },
|
||||
"address": { "address_city": "Budapest", "address_zip": "1011" }
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 4.5 `GET /admin/persons/{person_id}` — Person Részletek
|
||||
|
||||
Teljes Person adatlap kapcsolódó User-ekkel, szervezeti tagságokkal, tulajdonolt cégekkel, merge history-val.
|
||||
|
||||
### 4.6 `PATCH /admin/persons/{person_id}` — Person Szerkesztése
|
||||
|
||||
Person adatok admin általi módosítása (last_name, first_name, phone, birth data, address, identity_docs, is_active, is_ghost).
|
||||
|
||||
### 4.7 `POST /admin/persons/{person_id}/merge` — Person Merge
|
||||
|
||||
**Body:**
|
||||
```json
|
||||
{ "source_person_id": 42, "keep_source_user": false }
|
||||
```
|
||||
|
||||
**Logika:**
|
||||
1. Ellenorizze, hogy mindket Person letezik es nincs mar merge-elve
|
||||
2. Forras Person `merged_into_id` -> cel Person ID
|
||||
3. Forras User-ek `person_id` -> cel Person ID
|
||||
4. Forras OrganizationMember `person_id` -> cel Person ID
|
||||
5. Audit log bejegyzes
|
||||
|
||||
### 4.8 `GET /admin/users/{user_id}/memberships` — Szervezeti Tagsagok
|
||||
|
||||
Egy felhasznalo szervezeti tagsagainak listazasa.
|
||||
|
||||
---
|
||||
|
||||
## 5. 🖼️ Frontend Admin UI Specifikacio
|
||||
|
||||
### 5.1 Oldalak Terkepe
|
||||
|
||||
```
|
||||
frontend_admin/pages/
|
||||
users/
|
||||
index.vue -> Felhasznaloi lista (GET /admin/users)
|
||||
[id]/index.vue -> Felhasznalo reszletek (GET /admin/users/{id})
|
||||
persons/
|
||||
index.vue -> Person lista (GET /admin/persons)
|
||||
[id]/index.vue -> Person reszletek (GET /admin/persons/{id})
|
||||
```
|
||||
|
||||
### 5.2 `users/index.vue` — Felhasznaloi Lista Oldal
|
||||
|
||||
**Elrendezes:**
|
||||
- Header: "Felhasznalok" cim
|
||||
- **Statisztikai kartyak sor:** Osszes, Aktiv, Torolt, Kitiltott, Mai regisztraciok
|
||||
- **Keresomezo:** Email, nev, telefonszam
|
||||
- **Szurolok:** Statusz (active/deleted/banned/all), Szerepkor (role), Elofizetes
|
||||
- **Tablazat:** ID, Email, Nev (Person), Szerepkor, Statusz (szines jelolo), Elofizetes, Regisztracio datuma, Nyelv, Muveletek
|
||||
- **Tomeges muveletek checkbox-szal:** Kitiltas, Feloldas, Torles, Visszaallitas
|
||||
|
||||
### 5.3 `users/[id]/index.vue` — Felhasznalo Reszlet Oldal
|
||||
|
||||
**Tab-ok:**
|
||||
1. **Attekintes (Overview)** — Alapadatok, statusz, szerepkor
|
||||
2. **Szemelyes adatok (Person)** — Person adatok, lakcim
|
||||
3. **Penzingyek (Finance)** — Wallet, elofizetes
|
||||
4. **Tagsagok (Memberships)** — Szervezeti tagsagok
|
||||
5. **Bizalom (Trust)** — Trust score, gamification
|
||||
6. **Tevekenyseg (Activity)** — Naplozott tevekenysegek
|
||||
|
||||
### 5.4 `persons/index.vue` — Person Lista Oldal
|
||||
|
||||
- Kereso: Nev, telefonszam, szuletesi adatok
|
||||
- Szurok: Ghost statusz, Aktiv/Inaktiv, Van-e User, Merge statusz
|
||||
- Tablazat: ID, Nev, Telefon, Szuletesi datum, Userek szama, Aktiv email, Statusz
|
||||
|
||||
### 5.5 `persons/[id]/index.vue` — Person Reszlet Oldal
|
||||
|
||||
- Szemely adatok
|
||||
- Kapcsolodo User-ek
|
||||
- Cim adatok
|
||||
- Szervezeti tagsagok
|
||||
- Merge informacio (forras/cel)
|
||||
- Tulajdonolt cegek
|
||||
|
||||
### 5.6 Navigacios Frissites
|
||||
|
||||
Oldalsav uj bejegyzesek:
|
||||
```
|
||||
Felhasznalok -> /users
|
||||
Felhasznalok -> /users/
|
||||
Szemelyek -> /persons/
|
||||
```
|
||||
|
||||
### 5.7 i18n Kovetelmenyek
|
||||
|
||||
Uj fordítási kulcsok (`hu.json` / `en.json`):
|
||||
- `users.title`, `users.search_placeholder`, `users.filter_role`
|
||||
- `users.status.active`, `users.status.deleted`, `users.status.banned`
|
||||
- `users.table.id`, `users.table.email`, `users.table.name`, `users.table.role`, `users.table.status`
|
||||
- `users.bulk.ban`, `users.bulk.unban`, `users.bulk.delete`, `users.bulk.restore`
|
||||
- `persons.title`, `persons.search_placeholder`
|
||||
- `person.merge.title`, `person.merge.confirm`, `person.merge.source`, `person.merge.target`
|
||||
- `stats.total_users`, `stats.active_users`, `stats.new_today`
|
||||
|
||||
### 5.8 Permission Check (RBAC)
|
||||
|
||||
Minden admin user/person oldalnak ellenoriznie kell:
|
||||
- `users:view` — Felhasznalok listazasa
|
||||
- `users:edit` — Felhasznalok szerkesztese
|
||||
- `users:ban` — Kitiltas
|
||||
- `users:delete` — Torles
|
||||
- `persons:view` — Person lista
|
||||
- `persons:edit` — Person szerkesztes
|
||||
- `persons:merge` — Person merge
|
||||
|
||||
Hasznald a meglévo `admin_permissions.py` rendszert es a `RequirePermission()` fuggoseget.
|
||||
|
||||
---
|
||||
|
||||
## 6. 📦 EPIC Bontas (3A Granularitas)
|
||||
|
||||
### 6.1 EPIC-1: Backend — User Management Vegpontok
|
||||
|
||||
| # | Task | Scope | Type | Fuggoseg |
|
||||
|---|------|-------|------|-----------|
|
||||
| 1.1 | `GET /admin/users/{user_id}` endpoint | Backend | Feature | - |
|
||||
| 1.2 | `PATCH /admin/users/{user_id}` endpoint | Backend | Feature | 1.1 |
|
||||
| 1.3 | `GET /admin/users/stats` endpoint | Backend | Feature | - |
|
||||
| 1.4 | `GET /admin/users/{user_id}/memberships` | Backend | Feature | 1.1 |
|
||||
|
||||
### 6.2 EPIC-2: Backend — Person Management Vegpontok
|
||||
|
||||
| # | Task | Scope | Type | Fuggoseg |
|
||||
|---|------|-------|------|-----------|
|
||||
| 2.1 | `GET /admin/persons` endpoint | Backend | Feature | - |
|
||||
| 2.2 | `GET /admin/persons/{person_id}` endpoint | Backend | Feature | 2.1 |
|
||||
| 2.3 | `PATCH /admin/persons/{person_id}` endpoint | Backend | Feature | 2.2 |
|
||||
| 2.4 | `POST /admin/persons/{person_id}/merge` | Backend | Feature | 2.2 |
|
||||
|
||||
### 6.3 EPIC-3: Frontend — User List & Detail Oldalak
|
||||
|
||||
| # | Task | Scope | Type | Fuggoseg |
|
||||
|---|------|-------|------|-----------|
|
||||
| 3.1 | `users/index.vue` — Felhasznaloi lista | Frontend | Feature | 1.x |
|
||||
| 3.2 | `users/[id]/index.vue` — Reszlet oldal | Frontend | Feature | 1.1, 3.1 |
|
||||
| 3.3 | Navigacios menu frissitese | Frontend | Feature | 3.1 |
|
||||
|
||||
### 6.4 EPIC-4: Frontend — Person List & Detail Oldalak
|
||||
|
||||
| # | Task | Scope | Type | Fuggoseg |
|
||||
|---|------|-------|------|-----------|
|
||||
| 4.1 | `persons/index.vue` — Person lista | Frontend | Feature | 2.x |
|
||||
| 4.2 | `persons/[id]/index.vue` — Reszlet oldal | Frontend | Feature | 2.2, 4.1 |
|
||||
|
||||
### 6.5 EPIC-5: Frontend — Statisztikak & Dashboard
|
||||
|
||||
| # | Task | Scope | Type | Fuggoseg |
|
||||
|---|------|-------|------|-----------|
|
||||
| 5.1 | Dashboard statisztika kartyak dinamikus adatokkal | Frontend | Feature | 1.3 |
|
||||
| 5.2 | Felhasznaloi trend diagram komponens | Frontend | Feature | 1.3 |
|
||||
|
||||
### 6.6 EPIC-6: i18n & Permission Integration
|
||||
|
||||
| # | Task | Scope | Type | Fuggoseg |
|
||||
|---|------|-------|------|-----------|
|
||||
| 6.1 | i18n forditasok felvetele (hu/en) | Frontend | Feature | 3.x, 4.x |
|
||||
| 6.2 | RBAC permission check implementacio | Frontend | Feature | 3.x, 4.x |
|
||||
|
||||
---
|
||||
|
||||
## 7. 📐 Megvalosítási Terv (Phasing)
|
||||
|
||||
### Phase 0 (P0) — Alap Felhasznaloi Muv-eletek
|
||||
- **Backend:** EPIC-1 (1.1, 1.2, 1.3) — User reszletek, szerkesztes, statisztikak
|
||||
- **Frontend:** EPIC-3 (3.1) — User lista oldal
|
||||
- **Indoklas:** A legalapvetobb admin funkcio
|
||||
|
||||
### Phase 1 (P1) — Person Management & Tagsagok
|
||||
- **Backend:** EPIC-1 (1.4), EPIC-2 (2.1, 2.2, 2.3) — Person vegpontok
|
||||
- **Frontend:** EPIC-3 (3.2), EPIC-4 (4.1) — User reszlet + Person lista
|
||||
|
||||
### Phase 2 (P2) — Halado Funkciok
|
||||
- **Backend:** EPIC-2 (2.4) — Person merge
|
||||
- **Frontend:** EPIC-4 (4.2), EPIC-5, EPIC-6
|
||||
|
||||
---
|
||||
|
||||
## 8. ✅ Jovahagyasi Pont
|
||||
|
||||
**A terv felulvizsgalatra es jovahagyasra var.**
|
||||
|
||||
Kerdesek:
|
||||
1. **Prioritas:** A Phase 0 -> Phase 1 -> Phase 2 sorrend megfelelo?
|
||||
2. **Hianyzo funkcio:** Van olyan admin funkcio a user/person kezelesben, ami kimaradt?
|
||||
3. **Adatbiztonsag:** A szerkesztheto mezo-k listaja (Section 4.2) megfelelo?
|
||||
4. **Megerosites:** Ha a terv rendben van, kerem a jovahagyast a megvalositas megkezdesehez.
|
||||
|
||||
---
|
||||
|
||||
## 9. 🔗 Kapcsolodo Fajlok
|
||||
|
||||
| Fajl | Leiras |
|
||||
|------|--------|
|
||||
| `backend/app/models/identity/identity.py` | Person, User modellek |
|
||||
| `backend/app/models/marketplace/organization.py` | OrganizationMember modell |
|
||||
| `backend/app/schemas/user.py` | User/Person Pydantic semak |
|
||||
| `backend/app/api/v1/endpoints/admin.py` | Meglevo admin vegpontok (list_users, bulk_action) |
|
||||
| `backend/app/api/v1/endpoints/admin_permissions.py` | RBAC permission management |
|
||||
| `frontend_admin/pages/index.vue` | Dashboard (statisztikak frissitendok) |
|
||||
| `frontend_admin/nuxt.config.ts` | Nuxt konfig (i18n, proxy) |
|
||||
| `docs/masterbook_2.0.1/User_person_kezeles.md` | Masterbook dokumentacio |
|
||||
372
plans/logic_spec_gamification_admin_integration.md
Normal file
372
plans/logic_spec_gamification_admin_integration.md
Normal file
@@ -0,0 +1,372 @@
|
||||
# 🏗️ Gamification Admin Integration — Rendszerszintű Terv
|
||||
|
||||
## 1. Küldetés és Cél
|
||||
|
||||
**Mérföldkő:** Master Book 2.0 — Gamification Pro (MILESTONE_8)
|
||||
**Cél:** A meglévő Gamification backend (modellek, service-ek, API végpontok) teljes admin felületre történő bekötése, egy új, dedikált **"Gamification"** menüpont alatt az admin Nuxt frontendben.
|
||||
|
||||
---
|
||||
|
||||
## 2. Jelenlegi Rendszer Állapota (Audit Summary)
|
||||
|
||||
### 2.1 Adatbázis Modellek (`gamification` séma)
|
||||
|
||||
| Tábla | Státusz | Fejlettség |
|
||||
|-------|---------|-------------|
|
||||
| `point_rules` | ✅ Létezik | Alap (action_key, points, description, is_active) |
|
||||
| `level_configs` | ✅ Létezik | Alap (level_number, min_points, rank_name, is_penalty) |
|
||||
| `points_ledger` | ✅ Létezik | Teljes (user_id, points, penalty_change, reason) |
|
||||
| `user_stats` | ✅ Létezik | Teljes (XP, szint, penalty, restriction, quota, helyek) |
|
||||
| `badges` | ✅ Létezik | Alap (name, description, icon_url) |
|
||||
| `user_badges` | ✅ Létezik | Kapcsolótábla |
|
||||
| `user_contributions` | ✅ Létezik | Fejlett (type, entity, status, fingerprint) |
|
||||
| `seasons` | ✅ Létezik | Alap (name, start, end, is_active) |
|
||||
| `seasonal_competitions` | ✅ Létezik | Részleges (JSONB rules) |
|
||||
| `competitions` | ✅ Létezik | `gamification` sémában |
|
||||
| `user_competition_scores` | ✅ Létezik | Pontszámok |
|
||||
|
||||
**Hiányzó mezők a modellekből:**
|
||||
- `UserStats`-ból hiányzik: `services_submitted`, `total_points` (a gamification.py endpointok használják, de a modellben nincs definiálva!)
|
||||
- `PointsLedger`-ből hiányzik: `xp` mező, `source_type`, `source_id` (a gamification.py endpoint használja, de a modellben nincs!)
|
||||
|
||||
### 2.2 GamificationService
|
||||
|
||||
✅ Teljes, jól működő service:
|
||||
- `process_activity()` — Pontozás + büntetés szűrés + szintszámítás + kifizetés
|
||||
- `_apply_penalty()` — Büntetőpontok és restriction_level kezelése
|
||||
- `_calculate_multiplier()` — Dinamikus szorzó L0-L3 között
|
||||
- `_handle_level_up()` — Kredit jutalom minden 10. szintnél
|
||||
- `_add_earned_credits()` — Wallet + FinancialLedger integráció
|
||||
|
||||
**Figyelem:** A service master configot a `SystemParameter` táblából `GAMIFICATION_MASTER_CONFIG` kulccsal olvassa. Ez már admin által módosítható, de nincs hozzá admin UI!
|
||||
|
||||
### 2.3 API Végpontok
|
||||
|
||||
| Végpont | Metódus | Státusz | Megjegyzés |
|
||||
|---------|---------|---------|------------|
|
||||
| `/gamification/me` | GET | ✅ | UserStatResponse |
|
||||
| `/gamification/my-stats` | GET | ✅ | Saját statok |
|
||||
| `/gamification/leaderboard` | GET | ✅ | Top lista |
|
||||
| `/gamification/seasons` | GET | ✅ | Szezonok |
|
||||
| `/gamification/seasons/active` | GET | ✅ | Aktív szezon |
|
||||
| `/gamification/my-contributions` | GET | ✅ | Hozzájárulások |
|
||||
| `/gamification/season-standings/{id}` | GET | ✅ | Szezon állás |
|
||||
| `/gamification/self-defense-status` | GET | ✅ | Önvédelmi rendszer |
|
||||
| `/gamification/submit-service` | POST | ✅ | Szerviz beküldés |
|
||||
| `/gamification/badges` | GET | ✅ | Összes badge |
|
||||
| `/gamification/my-badges` | GET | ✅ | Saját badge-ek |
|
||||
| `/gamification/badges/award/{id}` | POST | ✅ | Badge adományozás |
|
||||
| `/gamification/achievements` | GET | ✅ | Achievement progress |
|
||||
| `/gamification/quiz/*` | GET/POST | ✅ | Napi kvíz |
|
||||
| **`/admin/users/{id}/penalty`** | **PATCH** | ⚠️ | **HIÁNYOS!** Nem használja a GamificationService-t, direktül módosítja a `level` mezőt! |
|
||||
|
||||
### 2.4 Admin Frontend
|
||||
|
||||
A `layouts/default.vue` menüpontjai:
|
||||
- Központ → Dashboard
|
||||
- Felhasználók & Partnerek → Garázsok, Felhasználók, Személyek
|
||||
- Pénzügyek → Csomagok
|
||||
- Rendszer → Jogosultságok, Rendszernaplók
|
||||
|
||||
**❌ NINCS Gamification menüpont!**
|
||||
|
||||
---
|
||||
|
||||
## 3. Komplex Terv — Gamification Admin Bekötése
|
||||
|
||||
### 3.1 Backend Új Végpontok (Admin API)
|
||||
|
||||
#### Új router: `backend/app/api/v1/endpoints/admin_gamification.py`
|
||||
|
||||
Létrehozandó végpontok:
|
||||
|
||||
```python
|
||||
# ==================== POINT RULES ====================
|
||||
GET /admin/gamification/point-rules # Összes pontszabály listázása
|
||||
POST /admin/gamification/point-rules # Új pontszabály létrehozása
|
||||
PUT /admin/gamification/point-rules/{id} # Pontszabály módosítása
|
||||
DELETE /admin/gamification/point-rules/{id} # Pontszabály törlése (soft)
|
||||
|
||||
# ==================== LEVEL CONFIGS ====================
|
||||
GET /admin/gamification/level-configs # Szintkonfigurációk listázása
|
||||
POST /admin/gamification/level-configs # Új szint létrehozása
|
||||
PUT /admin/gamification/level-configs/{id} # Szint módosítása
|
||||
|
||||
# ==================== BADGES ====================
|
||||
GET /admin/gamification/badges # Összes badge listázása
|
||||
POST /admin/gamification/badges # Új badge létrehozása
|
||||
PUT /admin/gamification/badges/{id} # Badge módosítása
|
||||
DELETE /admin/gamification/badges/{id} # Badge törlése
|
||||
|
||||
# ==================== SEASONS ====================
|
||||
GET /admin/gamification/seasons # Szezonok listázása
|
||||
POST /admin/gamification/seasons # Új szezon létrehozása
|
||||
PUT /admin/gamification/seasons/{id} # Szezon módosítása
|
||||
POST /admin/gamification/seasons/{id}/activate # Szezon aktiválása
|
||||
|
||||
# ==================== COMPETITIONS ====================
|
||||
GET /admin/gamification/competitions # Versenyek listázása
|
||||
POST /admin/gamification/competitions # Új verseny
|
||||
PUT /admin/gamification/competitions/{id} # Verseny módosítása
|
||||
|
||||
# ==================== USER STATS ADMIN ====================
|
||||
GET /admin/gamification/user-stats # Összes felhasználói stat (szűrhető)
|
||||
GET /admin/gamification/user-stats/{user_id} # Egy felhasználó statjai
|
||||
PUT /admin/gamification/user-stats/{user_id} # Statisztika manuális módosítása
|
||||
POST /admin/gamification/user-stats/{user_id}/penalty # Büntetés (GamificationService-en keresztül!)
|
||||
POST /admin/gamification/user-stats/{user_id}/reward # Jutalom (GamificationService-en keresztül!)
|
||||
|
||||
# ==================== MASTER CONFIG ====================
|
||||
GET /admin/gamification/master-config # GAMIFICATION_MASTER_CONFIG lekérése
|
||||
PUT /admin/gamification/master-config # GAMIFICATION_MASTER_CONFIG módosítása
|
||||
|
||||
# ==================== SYSTEM PARAMETERS (gamification scope) ====================
|
||||
GET /admin/gamification/system-params # Gamification kategóriájú rendszerparaméterek
|
||||
PUT /admin/gamification/system-params/{key} # Rendszerparaméter módosítása
|
||||
|
||||
# ==================== AUDIT / HISTORY ====================
|
||||
GET /admin/gamification/points-ledger # Pontnapló (szűrhető user_id, date range)
|
||||
GET /admin/gamification/contributions # Hozzájárulások listája (admin review)
|
||||
PUT /admin/gamification/contributions/{id}/review # Hozzájárulás jóváhagyása/elutasítása
|
||||
```
|
||||
|
||||
### 3.2 Frontend — új Gamification menüpont
|
||||
|
||||
#### 3.2.1 Új oldalak
|
||||
|
||||
```
|
||||
frontend_admin/pages/gamification/
|
||||
├── index.vue # Gamification Dashboard (áttekintő)
|
||||
├── point-rules.vue # Pontszabályok CRUD
|
||||
├── levels.vue # Szint konfigurációk
|
||||
├── badges.vue # Badge-ek kezelése
|
||||
├── seasons.vue # Szezonok kezelése
|
||||
├── competitions.vue # Versenyek kezelése
|
||||
├── users.vue # Felhasználói statisztikák listája
|
||||
├── users/[id].vue # Egy felhasználó részletes gamification adatai
|
||||
├── leaderboard.vue # Ranglista admin nézet
|
||||
├── config.vue # Master config szerkesztése
|
||||
├── parameters.vue # Gamification rendszerparaméterek
|
||||
└── ledger.vue # Pontnapló böngészése
|
||||
```
|
||||
|
||||
#### 3.2.2 Menüpont beszúrása
|
||||
|
||||
A `frontend_admin/layouts/default.vue` `menuGroups` tömbjébe új csoport:
|
||||
|
||||
```javascript
|
||||
{
|
||||
title: 'Gamification',
|
||||
items: [
|
||||
{
|
||||
path: '/gamification',
|
||||
label: 'Gamification HQ',
|
||||
icon: '<svg...trophy icon...>',
|
||||
},
|
||||
{
|
||||
label: 'Játékmenet Beállítások',
|
||||
icon: '<svg...settings icon...>',
|
||||
children: [
|
||||
{ path: '/gamification/point-rules', label: 'Pontszabályok', icon: '...' },
|
||||
{ path: '/gamification/levels', label: 'Szintek', icon: '...' },
|
||||
{ path: '/gamification/badges', label: 'Kitüntetések', icon: '...' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Események & Szezonok',
|
||||
icon: '<svg...calendar icon...>',
|
||||
children: [
|
||||
{ path: '/gamification/seasons', label: 'Szezonok', icon: '...' },
|
||||
{ path: '/gamification/competitions', label: 'Versenyek', icon: '...' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Felhasználói Adatok',
|
||||
icon: '<svg...users icon...>',
|
||||
children: [
|
||||
{ path: '/gamification/users', label: 'Statisztikák', icon: '...' },
|
||||
{ path: '/gamification/leaderboard', label: 'Ranglista', icon: '...' },
|
||||
{ path: '/gamification/ledger', label: 'Pontnapló', icon: '...' },
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/gamification/config',
|
||||
label: 'Rendszer Konfig',
|
||||
icon: '<svg...config icon...>',
|
||||
},
|
||||
{
|
||||
path: '/gamification/parameters',
|
||||
label: 'Rendszerparaméterek',
|
||||
icon: '<svg...params icon...>',
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 Séma Javítások (Adatbázis)
|
||||
|
||||
A `UserStats` modellt ki kell egészíteni:
|
||||
|
||||
```python
|
||||
# HIÁNYZÓ MEZŐK - hozzáadandó:
|
||||
services_submitted: Mapped[int] = mapped_column(Integer, default=0, server_default=text("0"))
|
||||
total_points: Mapped[int] = mapped_column(Integer, default=0, server_default=text("0"))
|
||||
```
|
||||
|
||||
A `PointsLedger` modellt ki kell egészíteni:
|
||||
|
||||
```python
|
||||
# HIÁNYZÓ MEZŐK - hozzáadandó:
|
||||
xp: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)
|
||||
source_type: Mapped[Optional[str]] = mapped_column(String(50), nullable=True)
|
||||
source_id: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)
|
||||
```
|
||||
|
||||
### 3.4 Business Logic Javítások
|
||||
|
||||
1. **A meglévő `PATCH /admin/users/{user_id}/penalty` végpontot** át kell írni, hogy a `GamificationService` `process_activity()` metódusát használja `is_penalty=True` paraméterrel, ne direkt `level` módosítást végezzen.
|
||||
|
||||
2. **A gamification.py endpointok** (`submit-service`, `quiz/answer`, `badges/award`) direktül módosítják a `UserStats` mezőket a GamificationService helyett — ezeket is át kell írni.
|
||||
|
||||
3. **A `GamificationService.process_activity()`** jelenleg csak a `GAMIFICATION_MASTER_CONFIG`-ot használja. Ki kell egészíteni, hogy a `point_rules` táblából is olvasson.
|
||||
|
||||
---
|
||||
|
||||
## 4. UI Komponensek Terve
|
||||
|
||||
### 4.1 Gamification Dashboard (`/gamification/index.vue`)
|
||||
- **Kártyák:** Összes felhasználó (UserStats count), Aktív szezon, Függő contribution-ök, Ma szerzett XP
|
||||
- **Grafikonok:** Napi XP trend (utolsó 30 nap), Szint eloszlás, Pontszabályok aktivitása
|
||||
- **Táblák:** Legutóbbi PointsLedger bejegyzések, Top 5 leaderboard
|
||||
|
||||
### 4.2 Pontszabályok (`/gamification/point-rules.vue`)
|
||||
- **DataTable:** action_key, points, description, is_active
|
||||
- **CRUD:** Inline szerkeszthető sorok, új sor hozzáadása
|
||||
- **Keresés/Szűrés:** action_key alapján
|
||||
|
||||
### 4.3 Szintek (`/gamification/levels.vue`)
|
||||
- **DataTable:** level_number, min_points, rank_name, is_penalty
|
||||
- **Vizuális:** Szintlépés fa diagram (Mermaid vagy Vue komponens)
|
||||
|
||||
### 4.4 Kitüntetések (`/gamification/badges.vue`)
|
||||
- **Grid:** Badge kártyák nézet (ikon, név, leírás)
|
||||
- **CRUD:** Badge létrehozás/szerkesztés, ikon feltöltés
|
||||
- **Felhasználók:** Badge kiosztása felhasználónak
|
||||
|
||||
### 4.5 Szezonok (`/gamification/seasons.vue`)
|
||||
- **Timeline:** Szezonok idővonal nézetben
|
||||
- **CRUD:** Új szezon, dátumok, aktív státusz
|
||||
- **Zárás:** Szezon lezárása automatikus jutalom kiosztással
|
||||
|
||||
### 4.6 Felhasználói Statisztikák (`/gamification/users.vue` + `users/[id].vue`)
|
||||
- **Lista:** UserStats tábla adatai (user, XP, level, penalty)
|
||||
- **Részletek:** Részletes gamification profil, pontnapló, badge-ek, contribution-ök
|
||||
- **Műveletek:** Büntetés, Jutalom, Stat reset (auditáltan)
|
||||
|
||||
### 4.7 Ranglista (`/gamification/leaderboard.vue`)
|
||||
- **Tábla:** Top 100 felhasználó ranglétrája
|
||||
- **Szűrés:** Szezonális / Globális váltás
|
||||
|
||||
### 4.8 Master Config (`/gamification/config.vue`)
|
||||
- **JSON Editor:** `GAMIFICATION_MASTER_CONFIG` szerkesztése validált JSON mezőkkel
|
||||
- `xp_logic` — base_xp, exponent
|
||||
- `penalty_logic` — recovery_rate, thresholds, multipliers
|
||||
- `conversion_logic` — social_to_credit_rate
|
||||
- `level_rewards` — credits_per_10_levels
|
||||
- **Előnézet:** Hatás kalkulátor (sliderekkel)
|
||||
|
||||
---
|
||||
|
||||
## 5. Engedélyezés (RBAC)
|
||||
|
||||
Minden admin gamification végponthoz `gamification:manage` permission kell:
|
||||
|
||||
```python
|
||||
Depends(deps.RequirePermission("gamification:manage"))
|
||||
```
|
||||
|
||||
A meglévő `fix_phantom_permissions.py` script már tartalmazza ezt a permission-t.
|
||||
|
||||
---
|
||||
|
||||
## 6. Adatfolyam Diagram
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[Admin User] -->|Böngésző| B[Nuxt Admin Frontend]
|
||||
B -->|HTTP /admin/gamification/*| C[FastAPI Admin Gateway]
|
||||
|
||||
C --> D[admin_gamification.py Router]
|
||||
D --> E{GamificationService}
|
||||
D --> F[Direct CRUD]
|
||||
|
||||
E --> G[(gamification schema)]
|
||||
F --> G
|
||||
|
||||
G --> H[point_rules]
|
||||
G --> I[level_configs]
|
||||
G --> J[user_stats]
|
||||
G --> K[points_ledger]
|
||||
G --> L[badges]
|
||||
G --> M[seasons]
|
||||
G --> N[seasonal_competitions]
|
||||
G --> O[user_contributions]
|
||||
|
||||
subgraph "Biztonsági Réteg"
|
||||
P[gamification:manage permission]
|
||||
Q[SecurityAuditLog minden művelethez]
|
||||
end
|
||||
|
||||
C --> P
|
||||
D --> Q
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Implementációs Sorrend (Gitea Kártyák)
|
||||
|
||||
| # | Kártya Név | Scope | Függőség | Becsült komplexitás |
|
||||
|---|------------|-------|----------|---------------------|
|
||||
| 1 | **Adatbázis: Gamification modell javítások (UserStats, PointsLedger)** | Database | Nincs | Közepes |
|
||||
| 2 | **Backend: Új admin_gamification.py router létrehozása** | Backend | 1 | Magas |
|
||||
| 3 | **Backend: GamificationService refaktor (point_rules integráció)** | Backend | 1 | Magas |
|
||||
| 4 | **Backend: Meglévő penalty endpoint javítása** | Backend | 2, 3 | Alacsony |
|
||||
| 5 | **Backend: gamification.py endpointok GamificationService-re átállítása** | Backend | 3 | Közepes |
|
||||
| 6 | **Frontend: Gamification menüpont + routing** | Frontend | 2 | Alacsony |
|
||||
| 7 | **Frontend: Gamification Dashboard oldal** | Frontend | 6 | Közepes |
|
||||
| 8 | **Frontend: Pontszabályok + Szintek CRUD** | Frontend | 6 | Közepes |
|
||||
| 9 | **Frontend: Kitüntetések (Badge) kezelő** | Frontend | 6 | Közepes |
|
||||
| 10 | **Frontend: Szezonok + Versenyek kezelő** | Frontend | 6 | Magas |
|
||||
| 11 | **Frontend: Felhasználói statisztikák + büntetés/jutalom** | Frontend | 6 | Magas |
|
||||
| 12 | **Frontend: Ranglista admin nézet** | Frontend | 6 | Alacsony |
|
||||
| 13 | **Frontend: Master Config szerkesztő** | Frontend | 6 | Közepes |
|
||||
| 14 | **Frontend: Pontnapló böngésző** | Frontend | 6 | Közepes |
|
||||
| 15 | **Tesztelés: Gamification admin E2E tesztek** | Backend+Frontend | 2-14 | Magas |
|
||||
|
||||
---
|
||||
|
||||
## 8. Használt Technológiák
|
||||
|
||||
- **Backend:** FastAPI, SQLAlchemy Async, Pydantic v2
|
||||
- **Frontend:** Nuxt 3, Pinia (state), TailwindCSS, Chart.js (dashboard), vue-json-pretty (config editor)
|
||||
- **Adatbázis:** PostgreSQL `gamification` séma
|
||||
- **Auth:** JWT + `gamification:manage` permission
|
||||
|
||||
---
|
||||
|
||||
## 9. Függőségek
|
||||
|
||||
- **Bemenet:** Meglévő `gamification` séma (modellek már készen), `GamificationService`, `SystemParameter` kezelés
|
||||
- **Kimenet:** Az új admin felület nélkülözhetetlen a gamification rendszer operatív kezeléséhez. Enélkül az admin-ok nem tudják:
|
||||
- Módosítani a pontszabályokat
|
||||
- Kezelni a büntetéseket (jelenleg direkt SQL/API szinten)
|
||||
- Létrehozni szezonokat és versenyeket
|
||||
- Felügyelni a felhasználói aktivitást
|
||||
|
||||
---
|
||||
|
||||
## 10. Kockázatok
|
||||
|
||||
1. **Adatbázis migráció:** A modell javításokhoz `sync_engine` futtatása szükséges, ami meglévő adatokat is érinthet.
|
||||
2. **Kompatibilitás:** A meglévő gamification.py endpointok direktül módosítják a `UserStats` mezőket. Az átállásnál óvatos rollover kell.
|
||||
3. **JSONB konfiguráció:** A `GAMIFICATION_MASTER_CONFIG` JSONB validációja kritikus — hibás JSON letiltja a gamification rendszert.
|
||||
Reference in New Issue
Block a user