# 🔍 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.