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.core_logic import CreditTransaction from app.models.system_config import SystemParameter logger = logging.getLogger(__name__) class GamificationService: @staticmethod async def get_config(db: AsyncSession): """Kiolvassa a GAMIFICATION_MASTER_CONFIG-ot a rendszerparaméterekből.""" 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 }, "conversion_logic": {"social_to_credit_rate": 100}, "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 'Bíró' logika: Ellenőriz, büntet, jutalmaz és szintez.""" config = await self.get_config(db) # 1. Jogosultság ellenőrzése user_stmt = select(User).where(User.id == user_id) user = (await db.execute(user_stmt)).scalar_one_or_none() if not user or user.is_deleted or user.role.value in config.get("blocked_roles", []): return None # 2. Stats lekérése stats_stmt = select(UserStats).where(UserStats.user_id == user_id) stats = (await db.execute(stats_stmt)).scalar_one_or_none() if not stats: stats = UserStats(user_id=user_id) db.add(stats) # 3. Büntető logika (Penalty) if is_penalty: stats.penalty_points += xp_amount th = config["penalty_logic"]["thresholds"] 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"PENALTY: {reason}")) await db.commit() return stats # 4. Dinamikus szorzó alkalmazása multipliers = config["penalty_logic"]["multipliers"] multiplier = multipliers.get(f"level_{stats.restriction_level}", 1.0) if multiplier <= 0: logger.warning(f"User {user_id} activity blocked (Level {stats.restriction_level})") return stats # 5. XP, Ledolgozás és Szintlépés final_xp = int(xp_amount * multiplier) if final_xp > 0: stats.total_xp += final_xp if stats.penalty_points > 0: rec_rate = config["penalty_logic"]["recovery_rate"] stats.penalty_points = max(0, stats.penalty_points - int(final_xp * rec_rate)) 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: if new_level % 10 == 0: reward = config["level_rewards"]["credits_per_10_levels"] await self._add_credits(db, user_id, reward, f"Level {new_level} Achievement Bonus") stats.current_level = new_level # 6. Social pont és váltá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 await self._add_credits(db, user_id, new_credits, "Social conversion") db.add(PointsLedger(user_id=user_id, points=final_xp, reason=reason)) await db.commit() return stats async def _add_credits(self, db: AsyncSession, user_id: int, amount: int, reason: str): wallet_stmt = select(Wallet).where(Wallet.user_id == user_id) wallet = (await db.execute(wallet_stmt)).scalar_one_or_none() if wallet: wallet.credit_balance += Decimal(amount) db.add(CreditTransaction(org_id=None, amount=Decimal(amount), description=reason)) gamification_service = GamificationService()