Files
service-finder/backend/app/services/gamification_service.py

106 lines
4.8 KiB
Python
Executable File

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