admin frontend elkezdése, járművek tisztázása, frontend fejleszts
This commit is contained in:
162
backend/app/services/subscription_service.py
Normal file
162
backend/app/services/subscription_service.py
Normal file
@@ -0,0 +1,162 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/services/subscription_service.py
|
||||
"""
|
||||
Subscription & Entitlement Service — Premium költségkategória láthatóság.
|
||||
|
||||
A szolgáltatás a felhasználó előfizetési szintje (tier) alapján szabályozza,
|
||||
hogy mely költségkategóriák (CostCategory) érhetők el. A tier-ek hierarchikusak:
|
||||
free < premium < enterprise.
|
||||
|
||||
Hierarchia:
|
||||
- free: alap kategóriák (üzemanyag, szerviz, gumik, biztosítás, adók)
|
||||
- premium: free + útdíj/parkolás, ápolás, egyéb
|
||||
- enterprise: premium + minden (nincs korlátozás)
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import logging
|
||||
from typing import Optional, List
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select
|
||||
from app.models.vehicle.vehicle import CostCategory
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# ── Subscription Feature Matrix ──────────────────────────────────────────────
|
||||
# Minden funkció/kategória minimális tier szintje.
|
||||
# A tier-ek: free < premium < enterprise
|
||||
TIER_HIERARCHY = {
|
||||
"free": 0,
|
||||
"premium": 1,
|
||||
"enterprise": 2,
|
||||
}
|
||||
|
||||
SUBSCRIPTION_FEATURES: dict[str, str] = {
|
||||
# Feature -> minimum required tier
|
||||
"cost_category:fuel": "free",
|
||||
"cost_category:service": "free",
|
||||
"cost_category:tires": "free",
|
||||
"cost_category:insurance": "free",
|
||||
"cost_category:taxes": "free",
|
||||
"cost_category:toll_parking": "premium",
|
||||
"cost_category:cleaning": "premium",
|
||||
"cost_category:other": "premium",
|
||||
"cost_category:all": "enterprise",
|
||||
# Feature flags
|
||||
"export_csv": "free",
|
||||
"analytics_tco": "premium",
|
||||
"analytics_detailed": "enterprise",
|
||||
"api_access": "free",
|
||||
"multi_vehicle": "free",
|
||||
"unlimited_vehicles": "premium",
|
||||
}
|
||||
|
||||
|
||||
class SubscriptionService:
|
||||
"""
|
||||
Entitlement Mátrix — előfizetési szintek kezelése.
|
||||
|
||||
Metódusok:
|
||||
get_user_tier(db, user_id) -> str
|
||||
can_access_feature(tier, feature_key) -> bool
|
||||
get_visible_categories(db, tier) -> list[CostCategory]
|
||||
require_tier(tier, required_feature_or_min_tier) -> bool
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def _tier_to_int(tier: str) -> int:
|
||||
"""Tier string konvertálása numerikus értékre a hierarchiában."""
|
||||
return TIER_HIERARCHY.get(tier.lower(), 0)
|
||||
|
||||
@staticmethod
|
||||
async def get_user_tier(db: AsyncSession, user_id: int) -> str:
|
||||
"""
|
||||
Visszaadja a felhasználó aktuális előfizetési szintjét.
|
||||
|
||||
Jelenlegi implementáció: minden user 'free' tier-t kap.
|
||||
A jövőben ez a subscription/org táblákból lesz lekérdezve.
|
||||
"""
|
||||
# TODO: Éles implementáció — subscription tábla lekérdezése
|
||||
# TODO: Szervezeti tier felülírás (pl. enterprise org)
|
||||
return "free"
|
||||
|
||||
@staticmethod
|
||||
def can_access_feature(user_tier: str, feature_key: str) -> bool:
|
||||
"""
|
||||
Ellenőrzi, hogy a felhasználó hozzáfér-e egy adott funkcióhoz.
|
||||
|
||||
Args:
|
||||
user_tier: A felhasználó tier szintje (free/premium/enterprise)
|
||||
feature_key: A funkció kulcsa (pl. 'cost_category:fuel', 'analytics_tco')
|
||||
|
||||
Returns:
|
||||
bool: True ha hozzáfér, False ha nem
|
||||
"""
|
||||
required_tier = SUBSCRIPTION_FEATURES.get(feature_key)
|
||||
if required_tier is None:
|
||||
# Ismeretlen feature — alapértelmezetten tiltva
|
||||
logger.warning(f"Unknown feature key: {feature_key}")
|
||||
return False
|
||||
|
||||
user_level = SubscriptionService._tier_to_int(user_tier)
|
||||
required_level = SubscriptionService._tier_to_int(required_tier)
|
||||
return user_level >= required_level
|
||||
|
||||
@staticmethod
|
||||
async def get_visible_categories(
|
||||
db: AsyncSession,
|
||||
user_tier: str
|
||||
) -> List[CostCategory]:
|
||||
"""
|
||||
Visszaadja azokat a CostCategory-ket, amelyek a user tier-jének megfelelnek.
|
||||
|
||||
Szűrési logika:
|
||||
- A kategória min_tier mezője alapján szűrünk.
|
||||
- Ha a kategória min_tier értéke kisebb vagy egyenlő, mint a user tier,
|
||||
akkor látható.
|
||||
- Ha a min_tier nincs beállítva (None), akkor 'free'-ként kezeljük.
|
||||
|
||||
Args:
|
||||
db: Adatbázis session
|
||||
user_tier: A felhasználó tier szintje
|
||||
|
||||
Returns:
|
||||
List[CostCategory]: A látható kategóriák listája
|
||||
"""
|
||||
user_level = SubscriptionService._tier_to_int(user_tier)
|
||||
|
||||
# Lekérjük az összes kategóriát
|
||||
stmt = select(CostCategory).order_by(CostCategory.id)
|
||||
result = await db.execute(stmt)
|
||||
all_categories = result.scalars().all()
|
||||
|
||||
# Szűrés a min_tier alapján
|
||||
visible = []
|
||||
for cat in all_categories:
|
||||
cat_tier = getattr(cat, "min_tier", "free") or "free"
|
||||
cat_level = SubscriptionService._tier_to_int(cat_tier)
|
||||
if user_level >= cat_level:
|
||||
visible.append(cat)
|
||||
|
||||
return visible
|
||||
|
||||
@staticmethod
|
||||
def require_tier(user_tier: str, required_feature_or_min_tier: str) -> bool:
|
||||
"""
|
||||
Egyszerű tier ellenőrzés — dependency check-hez.
|
||||
|
||||
Args:
|
||||
user_tier: A felhasználó tier szintje
|
||||
required_feature_or_min_tier: Vagy egy feature kulcs, vagy egy tier név
|
||||
|
||||
Returns:
|
||||
bool: True ha megfelel, False ha nem
|
||||
"""
|
||||
# Először feature kulcsként próbáljuk
|
||||
if required_feature_or_min_tier in SUBSCRIPTION_FEATURES:
|
||||
return SubscriptionService.can_access_feature(
|
||||
user_tier, required_feature_or_min_tier
|
||||
)
|
||||
|
||||
# Ha nem feature kulcs, akkor tier névként kezeljük
|
||||
user_level = SubscriptionService._tier_to_int(user_tier)
|
||||
required_level = SubscriptionService._tier_to_int(required_feature_or_min_tier)
|
||||
return user_level >= required_level
|
||||
Reference in New Issue
Block a user