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

163 lines
5.8 KiB
Python

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