admin_szolgáltatók_

This commit is contained in:
Roo
2026-07-01 02:27:38 +00:00
parent 189cbfd7ca
commit 6e627d0ebe
491 changed files with 20965 additions and 9703 deletions

View 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.