STABLE: Final schema sync, optimized gitignore
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/services/gamification_service.py
|
||||
import logging
|
||||
import math
|
||||
from decimal import Decimal
|
||||
@@ -5,102 +6,136 @@ 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
|
||||
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):
|
||||
"""Kiolvassa a GAMIFICATION_MASTER_CONFIG-ot a rendszerparaméterekből."""
|
||||
"""
|
||||
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
|
||||
"recovery_rate": 0.5 # Mennyi büntetőpontot dolgoz le 1 XP szerzésekor
|
||||
},
|
||||
"conversion_logic": {"social_to_credit_rate": 100},
|
||||
"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 'Bíró' logika: Ellenőriz, büntet, jutalmaz és szintez."""
|
||||
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. 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", []):
|
||||
# 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. Stats lekérése
|
||||
stats_stmt = select(UserStats).where(UserStats.user_id == user_id)
|
||||
stats = (await db.execute(stats_stmt)).scalar_one_or_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 (Penalty)
|
||||
# 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"PENALTY: {reason}"))
|
||||
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. Dinamikus szorzó alkalmazása
|
||||
multipliers = config["penalty_logic"]["multipliers"]
|
||||
multiplier = multipliers.get(f"level_{stats.restriction_level}", 1.0)
|
||||
|
||||
# 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} activity blocked (Level {stats.restriction_level})")
|
||||
logger.warning(f"User {user_id} tevékenysége blokkolva a magas büntetés miatt.")
|
||||
return stats
|
||||
|
||||
# 5. XP, Ledolgozás és Szintlépés
|
||||
# 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_rate = config["penalty_logic"]["recovery_rate"]
|
||||
stats.penalty_points = max(0, stats.penalty_points - int(final_xp * rec_rate))
|
||||
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_credits(db, user_id, reward, f"Level {new_level} Achievement Bonus")
|
||||
await self._add_earned_credits(db, user_id, reward, f"Szint bónusz: {new_level}")
|
||||
stats.current_level = new_level
|
||||
|
||||
# 6. Social pont és váltás
|
||||
# 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
|
||||
await self._add_credits(db, user_id, new_credits, "Social conversion")
|
||||
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")
|
||||
|
||||
db.add(PointsLedger(user_id=user_id, points=final_xp, reason=reason))
|
||||
# 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_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()
|
||||
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.credit_balance += Decimal(amount)
|
||||
db.add(CreditTransaction(org_id=None, amount=Decimal(amount), description=reason))
|
||||
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()
|
||||
Reference in New Issue
Block a user