Files
service-finder/docs/gamification_provider_connection_analysis.md
2026-07-01 02:27:38 +00:00

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:

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.pyPointRule osztály:

  • id, action_key (unique), points, description, is_active

2.3 Service Provider Modell

backend/app/models/identity/social.pyServiceProvider 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 — 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:

  1. Implementálni a find_or_create_provider_by_name() függvényt a provider_service.py-ban
  2. Bekötni az expenses.py 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-ban NEM hívja meg a _award_provider_points(action_key="RATE_PROVIDER") függvényt.

Szükséges:

  1. A social_service.py 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:

# 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.pyprocess_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.pycreate_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.pyvote_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.pycreate_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

  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.