# /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()