admin_szolgáltatók_
This commit is contained in:
292
docs/gamification_provider_connection_analysis.md
Normal file
292
docs/gamification_provider_connection_analysis.md
Normal file
@@ -0,0 +1,292 @@
|
||||
# 🔍 Gamification Pontszabályok és Service Provider Kapcsolat Elemzés
|
||||
|
||||
**Dátum:** 2026-06-30
|
||||
**Létrehozva:** Gitea #358
|
||||
**Scope:** Backend, Gamification, Service Provider
|
||||
**Típus:** Audit + Feature Analysis
|
||||
|
||||
---
|
||||
|
||||
## 1. ⚡ Executive Summary
|
||||
|
||||
A vizsgálat célja annak megállapítása volt, hogy a gamification pontszabályok (`gamification.point_rules`) össze vannak-e kötve a szolgáltatók (Service Provider) létrehozásával és validálásával.
|
||||
|
||||
### Főbb Megállapítások
|
||||
|
||||
| Pontszabály | Pont | Státusz | Megjegyzés |
|
||||
|------------|------|---------|------------|
|
||||
| `ADD_NEW_PROVIDER` | 500 | ✅ Bekötve | `quick_add_provider()`, `approve_provider()` |
|
||||
| `UPDATE_PROVIDER` | 100 | ✅ Bekötve | `update_provider()` |
|
||||
| `PROVIDER_DISCOVERY` | 300 | ❌ NINCS bekötve | Soha nem kerül kiosztásra |
|
||||
| `PROVIDER_CONFIRMATION` | 150 | ❌ NINCS bekötve | Soha nem kerül kiosztásra |
|
||||
| `PROVIDER_VERIFIED_USE` | 100 | ❌ NINCS bekötve | Soha nem kerül kiosztásra |
|
||||
| `USE_UNVERIFIED_PROVIDER` | 200 | ❌ NINCS bekötve | Soha nem kerül kiosztásra |
|
||||
| `RATE_PROVIDER` | 250 | ❌ NINCS bekötve | Soha nem kerül kiosztásra |
|
||||
|
||||
Összesen **5 pontszabály** van, amely definiálva van az adatbázisban és a seed scriptben, de **egyetlen kódrészlet sem hívja meg őket** — teljesen használatlanok.
|
||||
|
||||
---
|
||||
|
||||
## 2. 🏗️ Rendszerarchitektúra
|
||||
|
||||
### 2.1 Admin Gamification UI — ÉLES ÉS MŰKÖDŐ
|
||||
|
||||
A [`https://admin.servicefinder.hu/gamification/point-rules`](https://admin.servicefinder.hu/gamification/point-rules) felület **valós adatokat** jelenít meg és **teljes CRUD** műveleteket támogat:
|
||||
|
||||
- **Frontend:** [`frontend_admin/pages/gamification/point-rules.vue`](frontend_admin/pages/gamification/point-rules.vue) — `$fetch('/api/v1/admin/gamification/point-rules')` hívás
|
||||
- **Backend:** [`backend/app/api/v1/endpoints/admin_gamification.py`](backend/app/api/v1/endpoints/admin_gamification.py:214) — valós adatbázis lekérdezés
|
||||
- `GET /point-rules` (214. sor)
|
||||
- `POST /point-rules` (236. sor)
|
||||
- `PUT /point-rules/{rule_id}` (270. sor)
|
||||
- `DELETE /point-rules/{rule_id}` (299. sor)
|
||||
|
||||
**Következtetés:** Nincs szükség új admin felület készítésére. A pontszabályok a meglévő admin UI-n keresztül közvetlenül szerkeszthetők.
|
||||
|
||||
### 2.2 Pontszabály Modell
|
||||
|
||||
[`backend/app/models/gamification/gamification.py`](backend/app/models/gamification/gamification.py:13) — `PointRule` osztály:
|
||||
- `id`, `action_key` (unique), `points`, `description`, `is_active`
|
||||
|
||||
### 2.3 Service Provider Modell
|
||||
|
||||
[`backend/app/models/identity/social.py`](backend/app/models/identity/social.py:23) — `ServiceProvider` osztály:
|
||||
- `status` (pending/approved/rejected/flagged)
|
||||
- `source` (user_submitted/admin_imported/api/external)
|
||||
- `validation_score`
|
||||
- `added_by_user_id`
|
||||
|
||||
---
|
||||
|
||||
## 3. 🔗 Jelenlegi Bekötések (Működő)
|
||||
|
||||
### 3.1 `_award_provider_points()` — Központi függvény
|
||||
|
||||
[`backend/app/services/provider_service.py`](backend/app/services/provider_service.py:59) — Dinamikus pontkiosztó függvény:
|
||||
|
||||
```python
|
||||
async def _award_provider_points(db, user_id, action_key) -> int:
|
||||
# 1. Pontszabály lekérése adatbázisból
|
||||
rule_stmt = select(PointRule).where(
|
||||
PointRule.action_key == action_key, PointRule.is_active == True
|
||||
)
|
||||
rule = (await db.execute(rule_stmt)).scalar_one_or_none()
|
||||
if not rule:
|
||||
return 0
|
||||
# 2. Pont kiosztása GamificationService-en keresztül
|
||||
await gamification_service.award_points(
|
||||
db=db, user_id=user_id, amount=rule.points, ...
|
||||
)
|
||||
# 3. UserStats frissítése
|
||||
stats.providers_added_count += 1
|
||||
return points_to_award
|
||||
```
|
||||
|
||||
### 3.2 `quick_add_provider()` → `ADD_NEW_PROVIDER` (500 XP)
|
||||
|
||||
[`backend/app/services/provider_service.py`](backend/app/services/provider_service.py:611) — Gyors provider hozzáadás API végpont:
|
||||
- Automatikusan meghívja a `_award_provider_points(action_key="ADD_NEW_PROVIDER")` függvényt
|
||||
- 500 XP kerül kiosztásra a user-nek
|
||||
|
||||
### 3.3 `update_provider()` → `UPDATE_PROVIDER` (100 XP)
|
||||
|
||||
[`backend/app/services/provider_service.py`](backend/app/services/provider_service.py:1122) — Provider adatainak frissítése:
|
||||
- Meghívja a `_award_provider_points(action_key="UPDATE_PROVIDER")` függvényt
|
||||
- 100 XP kerül kiosztásra
|
||||
|
||||
### 3.4 Admin Approve → `ADD_NEW_PROVIDER` (500 XP)
|
||||
|
||||
[`backend/app/api/v1/endpoints/admin_providers.py`](backend/app/api/v1/endpoints/admin_providers.py:361) — Admin provider jóváhagyása:
|
||||
- `POST /admin/providers/{id}/approve`
|
||||
- Gamification XP kiosztás a provider beküldőjének
|
||||
- ProviderValidation rekord létrehozása
|
||||
- UserContribution státusz frissítése
|
||||
|
||||
---
|
||||
|
||||
## 4. ❌ HIÁNYZÓ BEKÖTÉSEK (Implementálandó)
|
||||
|
||||
### 4.1 `PROVIDER_DISCOVERY` (300 XP) — Soha nem kerül kiosztásra
|
||||
|
||||
**Tervezett működés:** Amikor egy user költséget rögzít, és az `external_vendor_name` alapján új provider kerül felfedezésre (még nem létezik a `marketplace.service_providers` táblában), a felfedező user 300 XP-t kap.
|
||||
|
||||
**Hiányzó kód:** A [`plans/logic_spec_service_provider_discovery_admin.md`](plans/logic_spec_service_provider_discovery_admin.md:116) specifikáció leírja a `find_or_create_provider_by_name()` függvényt, de az **SOHA nem lett implementálva**.
|
||||
|
||||
**Szükséges lépések:**
|
||||
1. Implementálni a [`find_or_create_provider_by_name()`](plans/logic_spec_service_provider_discovery_admin.md:116) függvényt a `provider_service.py`-ban
|
||||
2. Bekötni az [`expenses.py`](backend/app/api/v1/endpoints/expenses.py:566) create_expense végpontba
|
||||
3. A függvény hívja meg a `_award_provider_points(action_key="PROVIDER_DISCOVERY")`-t
|
||||
|
||||
### 4.2 `PROVIDER_CONFIRMATION` (150 XP) — Soha nem kerül kiosztásra
|
||||
|
||||
**Tervezett működés:** Amikor egy második user használ egy meglévő, de még nem 100%-ban megerősített providert (pl. ugyanaz a `external_vendor_name` egy másik költségben), a confirmation pont jár.
|
||||
|
||||
**Hiányzó logika:** A `find_or_create_provider_by_name()` függvény része kell legyen — ha a provider már létezik, de `validation_score < 100`, akkor `PROVIDER_CONFIRMATION` jár.
|
||||
|
||||
### 4.3 `PROVIDER_VERIFIED_USE` (100 XP) — Soha nem kerül kiosztásra
|
||||
|
||||
**Tervezett működés:** Ha a provider már `approved` státuszú, és valaki használja költség rögzítésénél, a `PROVIDER_VERIFIED_USE` pont jár.
|
||||
|
||||
**Szükséges:** A `find_or_create_provider_by_name()` függvény része — ha `provider.status == "approved"`, akkor `PROVIDER_VERIFIED_USE` jár.
|
||||
|
||||
### 4.4 `USE_UNVERIFIED_PROVIDER` (200 XP) — Soha nem kerül kiosztásra
|
||||
|
||||
**Tervezett működés:** Amikor egy user olyan providert használ, amely még nincs admin által jóváhagyva (pl. `pending` státuszú).
|
||||
|
||||
**Szükséges:** Szintén a `find_or_create_provider_by_name()` függvény része kell legyen.
|
||||
|
||||
### 4.5 `RATE_PROVIDER` (250 XP) — Soha nem kerül kiosztásra
|
||||
|
||||
**Tervezett működés:** Amikor egy user értékeli a providert (csillag/Vote/VoteValue).
|
||||
|
||||
**Hiányzó kód:** A `vote_for_provider()` függvény a [`social_service.py`](backend/app/services/social_service.py:31)-ban NEM hívja meg a `_award_provider_points(action_key="RATE_PROVIDER")` függvényt.
|
||||
|
||||
**Szükséges:**
|
||||
1. A [`social_service.py`](backend/app/services/social_service.py:31) `vote_for_provider()` metódusában meghívni a pontkiosztást
|
||||
|
||||
### 4.6 `social_service.py` — Hardcoded XP `create_service_provider()`-ben
|
||||
|
||||
[`backend/app/services/social_service.py`](backend/app/services/social_service.py:26):
|
||||
```python
|
||||
# HIBA: Hardcoded 50 XP a dinamikus pontszabály helyett
|
||||
await gamification_service.process_activity(db, user_id, 50, 10, f"New Provider: {new_provider.name}")
|
||||
```
|
||||
|
||||
**Javítás:** Ki kell cserélni a `_award_provider_points(action_key="ADD_NEW_PROVIDER")` hívásra, hogy dinamikusan az adatbázisból olvassa a pontértéket.
|
||||
|
||||
---
|
||||
|
||||
## 5. 📊 GamificationService Folyamat
|
||||
|
||||
[`backend/app/services/gamification_service.py`](backend/app/services/gamification_service.py:52) — `process_activity()` metódus:
|
||||
|
||||
```python
|
||||
async def process_activity(self, db, user_id, xp_amount, social_amount, reason,
|
||||
is_penalty=False, commit=True, action_key=None,
|
||||
source_type=None, source_id=None):
|
||||
# 1. Master config betöltése
|
||||
# 2. Ha action_key van, pontszabály lekérése (adatbázisból)
|
||||
if action_key:
|
||||
rule = await self._get_point_rule(db, action_key)
|
||||
if rule:
|
||||
xp_amount = rule["points"]
|
||||
# 3. Büntetés szűrés
|
||||
# 4. Szorzók alkalmazása
|
||||
# 5. Szintszámítás: Level = (XP/500)^(1/1.5) + 1
|
||||
# 6. Kredit átváltás
|
||||
# 7. Naplózás PointsLedger-be
|
||||
```
|
||||
|
||||
**Megjegyzés:** Maga a `process_activity()` és az `_award_provider_points()` működik helyesen — a probléma az, hogy **senki sem hívja meg** ezeket a megfelelő `action_key`-kel.
|
||||
|
||||
---
|
||||
|
||||
## 6. 🗺️ Implementációs Terv
|
||||
|
||||
### 6.1 `find_or_create_provider_by_name()` Implementálása
|
||||
|
||||
**Helye:** [`backend/app/services/provider_service.py`](backend/app/services/provider_service.py) — új függvény
|
||||
|
||||
**Logika:**
|
||||
|
||||
```python
|
||||
async def find_or_create_provider_by_name(
|
||||
db: AsyncSession,
|
||||
external_vendor_name: str,
|
||||
user_id: int
|
||||
) -> tuple[ServiceProvider | None, str]:
|
||||
"""
|
||||
Keres vagy létrehoz egy providert external_vendor_name alapján.
|
||||
|
||||
Visszatérési érték: (provider, action_key)
|
||||
- action_key: "PROVIDER_DISCOVERY" | "PROVIDER_CONFIRMATION" | "PROVIDER_VERIFIED_USE"
|
||||
"""
|
||||
# 1. Pontos match keresése (kisbetűsen, space-sztrippelten)
|
||||
provider = await db.execute(
|
||||
select(ServiceProvider).where(
|
||||
func.lower(ServiceProvider.name) == func.lower(external_vendor_name.strip())
|
||||
)
|
||||
)
|
||||
provider = provider.scalar_one_or_none()
|
||||
|
||||
if not provider:
|
||||
# 2. Ha nem létezik → létrehozás + PROVIDER_DISCOVERY
|
||||
new_provider = ServiceProvider(
|
||||
name=external_vendor_name.strip(),
|
||||
status=ModerationStatus.PENDING,
|
||||
source=SourceType.USER_SUBMITTED,
|
||||
added_by_user_id=user_id,
|
||||
validation_score=10 # Kezdeti alacsony score
|
||||
)
|
||||
db.add(new_provider)
|
||||
await db.flush()
|
||||
return new_provider, "PROVIDER_DISCOVERY"
|
||||
|
||||
# 3. Ha létezik, státusz alapján döntés
|
||||
if provider.status == ModerationStatus.APPROVED:
|
||||
return provider, "PROVIDER_VERIFIED_USE"
|
||||
else:
|
||||
return provider, "PROVIDER_CONFIRMATION"
|
||||
```
|
||||
|
||||
### 6.2 Expense Létrehozás Hook
|
||||
|
||||
**Helye:** [`backend/app/api/v1/endpoints/expenses.py`](backend/app/api/v1/endpoints/expenses.py:566) — `create_expense` végpont
|
||||
|
||||
```python
|
||||
# A provider field feldolgozása után, de a költség létrehozása előtt:
|
||||
if expense_data.service_provider_id:
|
||||
provider = await db.get(ServiceProvider, expense_data.service_provider_id)
|
||||
if provider and provider.added_by_user_id != current_user.id:
|
||||
# Megerősítés más user által
|
||||
await _award_provider_points(db, current_user.id, "PROVIDER_CONFIRMATION")
|
||||
elif expense_data.external_vendor_name:
|
||||
provider, action_key = await find_or_create_provider_by_name(
|
||||
db, expense_data.external_vendor_name, current_user.id
|
||||
)
|
||||
await _award_provider_points(db, current_user.id, action_key)
|
||||
```
|
||||
|
||||
### 6.3 RATE_PROVIDER Bekötése
|
||||
|
||||
**Helye:** [`backend/app/services/social_service.py`](backend/app/services/social_service.py:31) — `vote_for_provider()` metódus
|
||||
|
||||
```python
|
||||
# A Vote létrehozása után:
|
||||
await _award_provider_points(db, voter_id, "RATE_PROVIDER")
|
||||
```
|
||||
|
||||
### 6.4 `social_service.py` Hardcoded XP Javítása
|
||||
|
||||
**Helye:** [`backend/app/services/social_service.py`](backend/app/services/social_service.py:26) — `create_service_provider()` metódus
|
||||
|
||||
```python
|
||||
# Eredeti (hibás):
|
||||
await gamification_service.process_activity(db, user_id, 50, 10, ...)
|
||||
|
||||
# Javítás:
|
||||
await _award_provider_points(db, user_id, "ADD_NEW_PROVIDER")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. ⚠️ Kockázatok
|
||||
|
||||
1. **Duplikált pont kiosztás:** Ha a `social_service.py`-ban lévő `create_service_provider()` és a `provider_service.py`-ban lévő `quick_add_provider()` is meghívásra kerül ugyanarra a providerre, dupla pontot kaphat a user. Megoldás: a `social_service.py`-t át kell irányítani, hogy a `quick_add_provider()`-en keresztül hozzon létre providert.
|
||||
|
||||
2. **Végtelen loop:** Ha a `find_or_create_provider_by_name()` hibát dob, és az `expenses.py` újrapróbálkozik, végtelen loop alakulhat ki. Megoldás: max 1 újrapróbálkozás, utána `logger.error()` + graceful fallback.
|
||||
|
||||
3. **Performance:** Minden expense létrehozásnál egy extra SELECT + esetleg INSERT fut le a `service_providers` táblán. Ez normál terhelés mellett elhanyagolható, de batch importoknál figyelni kell rá.
|
||||
|
||||
---
|
||||
|
||||
## 8. ✅ Jóváhagyási Pont
|
||||
|
||||
A fenti elemzés alapján az alábbi feladatokra van szükség:
|
||||
|
||||
1. **P0 — Kritikus:** `find_or_create_provider_by_name()` implementálása
|
||||
2. **P0 — Kritikus:** Expense auto-discovery hook bekötése
|
||||
3. **P1 — Magas:** `social_service.py` hardcoded XP javítása
|
||||
4. **P1 — Magas:** RATE_PROVIDER bekötése
|
||||
5. **P1 — Magas:** PROVIDER_CONFIRMATION/VERIFIED_USE logika
|
||||
|
||||
Ezek a feladatok a Gitea #358 kártyán kerültek rögzítésre.
|
||||
Reference in New Issue
Block a user