13 KiB
🔍 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 felület valós adatokat jelenít meg és teljes CRUD műveleteket támogat:
- Frontend:
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— valós adatbázis lekérdezésGET /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 — PointRule osztály:
id,action_key(unique),points,description,is_active
2.3 Service Provider Modell
backend/app/models/identity/social.py — ServiceProvider osztály:
status(pending/approved/rejected/flagged)source(user_submitted/admin_imported/api/external)validation_scoreadded_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 — Dinamikus pontkiosztó függvény:
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 — 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 — 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 — 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 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:
- Implementálni a
find_or_create_provider_by_name()függvényt aprovider_service.py-ban - Bekötni az
expenses.pycreate_expense végpontba - 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-ban NEM hívja meg a _award_provider_points(action_key="RATE_PROVIDER") függvényt.
Szükséges:
- A
social_service.pyvote_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:
# 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 — process_activity() metódus:
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 — új függvény
Logika:
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 — create_expense végpont
# 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 — vote_for_provider() metódus
# 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 — create_service_provider() metódus
# 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
-
Duplikált pont kiosztás: Ha a
social_service.py-ban lévőcreate_service_provider()és aprovider_service.py-ban lévőquick_add_provider()is meghívásra kerül ugyanarra a providerre, dupla pontot kaphat a user. Megoldás: asocial_service.py-t át kell irányítani, hogy aquick_add_provider()-en keresztül hozzon létre providert. -
Végtelen loop: Ha a
find_or_create_provider_by_name()hibát dob, és azexpenses.pyújrapróbálkozik, végtelen loop alakulhat ki. Megoldás: max 1 újrapróbálkozás, utánalogger.error()+ graceful fallback. -
Performance: Minden expense létrehozásnál egy extra SELECT + esetleg INSERT fut le a
service_providerstá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:
- P0 — Kritikus:
find_or_create_provider_by_name()implementálása - P0 — Kritikus: Expense auto-discovery hook bekötése
- P1 — Magas:
social_service.pyhardcoded XP javítása - P1 — Magas: RATE_PROVIDER bekötése
- P1 — Magas: PROVIDER_CONFIRMATION/VERIFIED_USE logika
Ezek a feladatok a Gitea #358 kártyán kerültek rögzítésre.