# /opt/docker/dev/service_finder/backend/app/services/maintenance_service.py import os import logging import shutil from datetime import datetime, timedelta, timezone from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select, and_ from app.models import Asset, AssetTelemetry from app.services.config_service import config # 2.0 Dinamikus konfig from app.services.notification_service import NotificationService logger = logging.getLogger("Maintenance-Service-2.0") class MaintenanceService: """ Sentinel Master Maintenance Service 2.0. Felelős a rendszer tisztításáért és a prediktív karbantartási riasztásokért. """ @staticmethod async def cleanup_old_files(db: AsyncSession): """ Admin-vezérelt NAS takarítás. A megőrzési időt (napokban) az adatbázisból veszi. """ try: storage_path = await config.get_setting(db, "storage_nas_path", default="/mnt/nas/app_data") retention_days = await config.get_setting(db, "storage_retention_days", default=365) limit = datetime.now() - timedelta(days=int(retention_days)) deleted_count = 0 if not os.path.exists(storage_path): logger.warning(f"A tárolási útvonal nem található: {storage_path}") return 0 for root, dirs, files in os.walk(storage_path): for file in files: file_path = os.path.join(root, file) file_time = datetime.fromtimestamp(os.path.getmtime(file_path)) if file_time < limit: os.remove(file_path) deleted_count += 1 logger.info(f"🗑️ NAS takarítás kész. Törölve: {deleted_count} lejárt fájl.") return deleted_count except Exception as e: logger.error(f"Hiba a takarítás során: {e}") return 0 @staticmethod async def check_maintenance_intervals(db: AsyncSession): """ Prediktív karbantartás: Összeveti a Robot 3 gyári adatait a valós futásteljesítménnyel. Ha egy autó közeledik az olajcseréhez (pl. 1000 km-en belül), riasztást generál. """ try: # Admin beállítás: hány km-rel a szerviz előtt szóljunk? km_threshold = await config.get_setting(db, "maint_km_alert_threshold", default=1000) # Lekérjük az összes autót a telemetriával együtt stmt = select(Asset, AssetTelemetry).join(AssetTelemetry).where(Asset.status == "active") result = await db.execute(stmt) alerts_generated = 0 for asset, telemetry in result.all(): # A Robot 3 által feltöltött gyári adat (pl. 15.000 km) interval = asset.factory_data.get("oil_change_km") if asset.factory_data else None last_service_km = asset.factory_data.get("last_service_km", 0) if interval and telemetry.current_mileage: next_service_due = last_service_km + interval remaining_km = next_service_due - telemetry.current_mileage if 0 <= remaining_km <= int(km_threshold): # Értesítés küldése a Notification Centerbe és Emailben await NotificationService.send_direct_notification( db, user_id=asset.owner_id, message_key="maintenance_due", variables={ "vehicle": f"{asset.license_plate}", "type": "Olajcsere", "remaining": remaining_km } ) alerts_generated += 1 return alerts_generated except Exception as e: logger.error(f"Karbantartás ellenőrzési hiba: {e}") return 0 @staticmethod async def delete_validated_evidence(db: AsyncSession, document_id: str): """ Validáció utáni képkezelés. Az adminban állítható, hogy töröljük-e a bizonyítékot a hitelesítés után. """ should_delete = await config.get_setting(db, "storage_delete_after_validation", default=False) if should_delete: # Itt a Document modell alapján megkeressük a fájlt és töröljük # (A biztonság kedvéért naplózzuk a Sentinelbe) pass