Initial commit: Robot ökoszisztéma v2.0 - Stabilizált jármű és szerviz robotok
This commit is contained in:
135
backend/app/services/fleet_service.py
Executable file
135
backend/app/services/fleet_service.py
Executable file
@@ -0,0 +1,135 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/services/fleet_service.py
|
||||
import logging
|
||||
from uuid import UUID
|
||||
from typing import Optional, Dict, Any
|
||||
from decimal import Decimal
|
||||
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select, func
|
||||
from sqlalchemy.orm import selectinload
|
||||
|
||||
from app.models.asset import Asset, AssetEvent, AssetCost, AssetTelemetry
|
||||
from app.models.social import ServiceProvider, ModerationStatus
|
||||
from app.schemas.fleet import EventCreate, TCOStats
|
||||
from app.services.gamification_service import gamification_service
|
||||
from app.services.config_service import config # 2.0 Dinamikus konfig
|
||||
|
||||
logger = logging.getLogger("Fleet-Service-2.2")
|
||||
|
||||
class FleetService:
|
||||
"""
|
||||
Sentinel Master Fleet Service 2.2.
|
||||
Kezeli a járműflotta eseményeit és a TCO elemzéseket admin-vezérelt szabályokkal.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
async def add_vehicle_event(db: AsyncSession, asset_id: UUID, event_data: EventCreate, user_id: int):
|
||||
"""
|
||||
Esemény rögzítése dinamikus jutalmazással és anomália figyeléssel.
|
||||
"""
|
||||
try:
|
||||
# 1. Asset és Telemetria betöltése
|
||||
stmt = select(Asset).where(Asset.id == asset_id).options(selectinload(Asset.telemetry))
|
||||
res = await db.execute(stmt)
|
||||
asset = res.scalar_one_or_none()
|
||||
if not asset: return None
|
||||
|
||||
# 2. ADMIN KONFIGURÁCIÓ LEKÉRÉSE (Hierarchikus: User > Region > Global)
|
||||
# Lekérjük az eseménytípushoz tartozó jutalmakat
|
||||
event_rewards = await config.get_setting(
|
||||
db,
|
||||
"FLEET_EVENT_REWARDS",
|
||||
scope_level="user",
|
||||
scope_id=str(user_id),
|
||||
default={
|
||||
"refuel": {"xp": 30, "social": 5},
|
||||
"service": {"xp": 100, "social": 20},
|
||||
"inspection": {"xp": 50, "social": 10},
|
||||
"default": {"xp": 20, "social": 2}
|
||||
}
|
||||
)
|
||||
|
||||
# 3. SZOLGÁLTATÓ KEZELÉSE
|
||||
provider_id = event_data.provider_id
|
||||
if not event_data.is_diy and event_data.provider_name and not provider_id:
|
||||
p_stmt = select(ServiceProvider).where(func.lower(ServiceProvider.name) == event_data.provider_name.lower())
|
||||
existing = (await db.execute(p_stmt)).scalar_one_or_none()
|
||||
if existing:
|
||||
provider_id = existing.id
|
||||
else:
|
||||
new_p = ServiceProvider(
|
||||
name=event_data.provider_name,
|
||||
added_by_user_id=user_id,
|
||||
status=ModerationStatus.pending
|
||||
)
|
||||
db.add(new_p)
|
||||
await db.flush()
|
||||
provider_id = new_p.id
|
||||
|
||||
# 4. ANOMÁLIA DETEKCIÓ (Admin-vezérelt küszöbökkel)
|
||||
current_mileage = asset.telemetry.current_mileage if asset.telemetry else 0
|
||||
is_odometer_anomaly = event_data.odometer_value < current_mileage
|
||||
|
||||
# 5. ESEMÉNY RÖGZÍTÉSE
|
||||
new_event = AssetEvent(
|
||||
asset_id=asset_id,
|
||||
event_type=event_data.event_type,
|
||||
recorded_mileage=event_data.odometer_value,
|
||||
provider_id=provider_id,
|
||||
is_anomaly=is_odometer_anomaly,
|
||||
data=event_data.model_dump(exclude={"provider_id", "provider_name"})
|
||||
)
|
||||
db.add(new_event)
|
||||
|
||||
# 6. DINAMIKUS GAMIFIKÁCIÓ
|
||||
# Kikeresjük a konkrét eseménytípushoz tartozó pontokat
|
||||
rewards = event_rewards.get(event_data.event_type, event_rewards["default"])
|
||||
|
||||
await gamification_service.process_activity(
|
||||
db,
|
||||
user_id,
|
||||
xp_amount=rewards["xp"],
|
||||
social_amount=rewards["social"],
|
||||
reason=f"FLEET_EVENT_{event_data.event_type.upper()}"
|
||||
)
|
||||
|
||||
await db.commit()
|
||||
return new_event
|
||||
|
||||
except Exception as e:
|
||||
await db.rollback()
|
||||
logger.error(f"Fleet Event Error: {e}")
|
||||
raise e
|
||||
|
||||
@staticmethod
|
||||
async def calculate_tco(db: AsyncSession, asset_id: UUID) -> TCOStats:
|
||||
"""
|
||||
TCO számítás dinamikus pénznemkezeléssel és KM-alapú költséganalízissel.
|
||||
"""
|
||||
# 1. Admin beállítások (Pl. alapértelmezett pénznem a riportokhoz)
|
||||
report_currency = await config.get_setting(db, "finance_base_currency", default="EUR")
|
||||
|
||||
# 2. Költségek összesítése kategóriánként
|
||||
result = await db.execute(
|
||||
select(AssetCost.cost_type, func.sum(AssetCost.amount_eur))
|
||||
.where(AssetCost.asset_id == asset_id)
|
||||
.group_by(AssetCost.cost_type)
|
||||
)
|
||||
|
||||
breakdown = {row[0]: float(row[1]) for row in result.all()}
|
||||
total_eur = sum(breakdown.values())
|
||||
|
||||
# 3. KM alapú költség (Telemetria bevonása)
|
||||
telemetry_stmt = select(AssetTelemetry).where(AssetTelemetry.asset_id == asset_id)
|
||||
telemetry = (await db.execute(telemetry_stmt)).scalar_one_or_none()
|
||||
|
||||
mileage = telemetry.current_mileage if telemetry and telemetry.current_mileage > 0 else 1
|
||||
cost_per_km = total_eur / mileage
|
||||
|
||||
return TCOStats(
|
||||
asset_id=asset_id,
|
||||
total_cost_eur=total_eur,
|
||||
breakdown=breakdown,
|
||||
cost_per_km=round(cost_per_km, 4),
|
||||
currency=report_currency
|
||||
)
|
||||
Reference in New Issue
Block a user