Initial commit: Robot ökoszisztéma v2.0 - Stabilizált jármű és szerviz robotok

This commit is contained in:
Kincses
2026-03-04 02:03:03 +01:00
commit 250f4f4b8f
7942 changed files with 449625 additions and 0 deletions

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