Files
service-finder/backend/app/services/gamification_service.py
2026-02-26 08:19:25 +01:00

141 lines
5.9 KiB
Python
Executable File

# /opt/docker/dev/service_finder/backend/app/services/gamification_service.py
import logging
import math
from decimal import Decimal
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from app.models.gamification import UserStats, PointsLedger
from app.models.identity import User, Wallet
from app.models.audit import FinancialLedger
from app.models.system import SystemParameter
logger = logging.getLogger(__name__)
class GamificationService:
@staticmethod
async def get_config(db: AsyncSession):
"""
Dinamikus konfiguráció lekérése.
Ha nincs a DB-ben, ezek az alapértelmezett 'szabályok'.
"""
stmt = select(SystemParameter).where(SystemParameter.key == "GAMIFICATION_MASTER_CONFIG")
res = await db.execute(stmt)
param = res.scalar_one_or_none()
return param.value if param else {
"xp_logic": {"base_xp": 500, "exponent": 1.5},
"penalty_logic": {
"thresholds": {"level_1": 100, "level_2": 500, "level_3": 1000},
"multipliers": {"level_0": 1.0, "level_1": 0.5, "level_2": 0.1, "level_3": 0.0},
"recovery_rate": 0.5 # Mennyi büntetőpontot dolgoz le 1 XP szerzésekor
},
"conversion_logic": {"social_to_credit_rate": 100}, # 100 social pont = 1 credit
"level_rewards": {"credits_per_10_levels": 50},
"blocked_roles": ["superadmin", "service_bot"]
}
async def process_activity(
self,
db: AsyncSession,
user_id: int,
xp_amount: int,
social_amount: int,
reason: str,
is_penalty: bool = False
):
"""
A Rendszer 'Bírája'. Ez a függvény kezeli a teljes folyamatot:
Büntet, jutalmaz, szintet léptet és pénzt vált.
"""
config = await self.get_config(db)
# 1. Felhasználó ellenőrzése
user = (await db.execute(select(User).where(User.id == user_id))).scalar_one_or_none()
if not user or user.is_deleted or user.role in config["blocked_roles"]:
return None
# 2. Statisztikák lekérése (vagy létrehozása)
stats = (await db.execute(select(UserStats).where(UserStats.user_id == user_id))).scalar_one_or_none()
if not stats:
stats = UserStats(user_id=user_id)
db.add(stats)
await db.flush()
# 3. BÜNTETŐ LOGIKA (Ha rosszalkodott a user)
if is_penalty:
stats.penalty_points += xp_amount
th = config["penalty_logic"]["thresholds"]
# Korlátozási szintek beállítása
if stats.penalty_points >= th["level_3"]: stats.restriction_level = 3
elif stats.penalty_points >= th["level_2"]: stats.restriction_level = 2
elif stats.penalty_points >= th["level_1"]: stats.restriction_level = 1
db.add(PointsLedger(user_id=user_id, points=0, penalty_change=xp_amount, reason=f"🔴 BÜNTETÉS: {reason}"))
await db.commit()
return stats
# 4. SZORZÓK ALKALMAZÁSA (Büntetés alatt kevesebb pont jár)
multiplier = config["penalty_logic"]["multipliers"].get(f"level_{stats.restriction_level}", 1.0)
if multiplier <= 0:
logger.warning(f"User {user_id} tevékenysége blokkolva a magas büntetés miatt.")
return stats
# 5. XP SZÁMÍTÁS ÉS SZINTLÉPÉS
final_xp = int(xp_amount * multiplier)
if final_xp > 0:
stats.total_xp += final_xp
# Ledolgozás: Az XP szerzés csökkenti a meglévő büntetőpontokat
if stats.penalty_points > 0:
rec = int(final_xp * config["penalty_logic"]["recovery_rate"])
stats.penalty_points = max(0, stats.penalty_points - rec)
# Szint kiszámítása logaritmikus görbe alapján
xp_cfg = config["xp_logic"]
new_level = int((stats.total_xp / xp_cfg["base_xp"]) ** (1/xp_cfg["exponent"])) + 1
if new_level > stats.current_level:
# Kerek szinteknél jutalom (pl. minden 10. szint)
if new_level % 10 == 0:
reward = config["level_rewards"]["credits_per_10_levels"]
await self._add_earned_credits(db, user_id, reward, f"Szint bónusz: {new_level}")
stats.current_level = new_level
# 6. SOCIAL PONT ÉS VALUTA VÁLTÁS (Kredit generálás)
final_social = int(social_amount * multiplier)
if final_social > 0:
stats.social_points += final_social
rate = config["conversion_logic"]["social_to_credit_rate"]
if stats.social_points >= rate:
new_credits = stats.social_points // rate
stats.social_points %= rate # A maradék megmarad
await self._add_earned_credits(db, user_id, new_credits, "Közösségi aktivitás váltása")
# 7. NAPLÓZÁS (A PointsLedger a forrása a ranglistának)
db.add(PointsLedger(
user_id=user_id,
points=final_xp,
reason=reason
))
await db.commit()
await db.refresh(stats)
return stats
async def _add_earned_credits(self, db: AsyncSession, user_id: int, amount: int, reason: str):
""" Kredit hozzáadása a Wallethez és a Pénzügyi Főkönyvhöz (FinancialLedger). """
wallet = (await db.execute(select(Wallet).where(Wallet.user_id == user_id))).scalar_one_or_none()
if wallet:
wallet.earned_credits += Decimal(str(amount))
# Pénzügyi audit bejegyzés
db.add(FinancialLedger(
user_id=user_id,
amount=float(amount),
currency="HUF",
transaction_type="GAMIFICATION_REWARD",
details={"reason": reason}
))
gamification_service = GamificationService()