Initial commit: Robot ökoszisztéma v2.0 - Stabilizált jármű és szerviz robotok
This commit is contained in:
150
backend/app/services/notification_service.py
Executable file
150
backend/app/services/notification_service.py
Executable file
@@ -0,0 +1,150 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/services/notification_service.py
|
||||
import logging
|
||||
import uuid
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import Optional, List, Dict, Any
|
||||
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import joinedload
|
||||
|
||||
from app.models.identity import User
|
||||
from app.models.asset import Asset
|
||||
from app.models.organization import Organization
|
||||
from app.models.system import InternalNotification
|
||||
from app.services.email_manager import email_manager
|
||||
from app.services.config_service import config
|
||||
|
||||
logger = logging.getLogger("Notification-Service-2.2")
|
||||
|
||||
class NotificationService:
|
||||
"""
|
||||
Sentinel Master Notification Service 2.2 - Fully Admin-Configurable.
|
||||
Nincs fix kód: minden típus (biztosítás, műszaki, okmány) a DB-ből vezérelt.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
async def send_notification(
|
||||
db: AsyncSession,
|
||||
user_id: int,
|
||||
title: str,
|
||||
message: str,
|
||||
category: str = "info",
|
||||
priority: str = "medium",
|
||||
data: dict = None,
|
||||
send_email: bool = True,
|
||||
email_template: str = None,
|
||||
email_vars: dict = None
|
||||
):
|
||||
""" Univerzális küldő: Belső Dashboard + Email. """
|
||||
new_notif = InternalNotification(
|
||||
user_id=user_id,
|
||||
title=title,
|
||||
message=message,
|
||||
category=category,
|
||||
priority=priority,
|
||||
data=data or {}
|
||||
)
|
||||
db.add(new_notif)
|
||||
|
||||
if send_email and email_template and email_vars:
|
||||
await email_manager.send_email(
|
||||
db=db,
|
||||
recipient=email_vars.get("recipient"),
|
||||
template_key=email_template,
|
||||
variables=email_vars,
|
||||
lang=email_vars.get("lang", "hu")
|
||||
)
|
||||
await db.commit()
|
||||
|
||||
@staticmethod
|
||||
async def check_expiring_documents(db: AsyncSession):
|
||||
"""
|
||||
Dinamikus lejárat-figyelő: Típusonkénti naptárak az adminból.
|
||||
"""
|
||||
try:
|
||||
today = datetime.now(timezone.utc).date()
|
||||
|
||||
# 1. Lekérjük az összes aktív járművet és a tulajdonosaikat
|
||||
stmt = (
|
||||
select(Asset, User)
|
||||
.join(Organization, Asset.current_organization_id == Organization.id)
|
||||
.join(User, Organization.owner_id == User.id)
|
||||
.where(Asset.status == "active")
|
||||
.options(joinedload(Asset.catalog))
|
||||
)
|
||||
result = await db.execute(stmt)
|
||||
notifications_sent = 0
|
||||
|
||||
for asset, user in result.all():
|
||||
# 2. DINAMIKUS MÁTRIX LEKÉRÉSE (Hierarchikus: User > Region > Global)
|
||||
# Az adminban így van tárolva: {"insurance": [45, 30, 7, 0], "mot": [30, 7, 0], ...}
|
||||
alert_matrix = await config.get_setting(
|
||||
db,
|
||||
"NOTIFICATION_TYPE_MATRIX",
|
||||
scope_level="user",
|
||||
scope_id=str(user.id),
|
||||
default={
|
||||
"insurance": [45, 30, 15, 7, 1, 0],
|
||||
"mot": [30, 7, 1, 0],
|
||||
"personal_id": [30, 15, 0],
|
||||
"default": [30, 7, 1]
|
||||
}
|
||||
)
|
||||
|
||||
# 3. Ellenőrizendő dátumok (factory_data a Robotoktól)
|
||||
# Kulcsok: insurance_expiry_date, mot_expiry_date, id_card_expiry stb.
|
||||
check_map = {
|
||||
"insurance": asset.factory_data.get("insurance_expiry_date"),
|
||||
"mot": asset.factory_data.get("mot_expiry_date"),
|
||||
"personal_id": user.person.identity_docs.get("expiry_date") if user.person else None
|
||||
}
|
||||
|
||||
for doc_type, expiry_str in check_map.items():
|
||||
if not expiry_str: continue
|
||||
|
||||
try:
|
||||
expiry_dt = datetime.strptime(expiry_str, "%Y-%m-%d").date()
|
||||
days_until = (expiry_dt - today).days
|
||||
|
||||
# Megnézzük a típushoz tartozó admin-beállítást (pl. a 45 napot)
|
||||
alert_steps = alert_matrix.get(doc_type, alert_matrix["default"])
|
||||
|
||||
if days_until in alert_steps:
|
||||
# Prioritás meghatározása (Adminból is jöhetne, de itt kategória alapú)
|
||||
priority = "critical" if days_until <= 1 or (doc_type == "insurance" and days_until == 45) else "high"
|
||||
|
||||
title = f"Riasztás: {asset.license_plate} - {doc_type.upper()}"
|
||||
msg = f"A(z) {doc_type} dokumentum {days_until} nap múlva lejár ({expiry_str})."
|
||||
|
||||
if days_until == 45 and doc_type == "insurance":
|
||||
msg = f"🚨 BIZTOSÍTÁSI FORDULÓ! (45 nap). Most van időd felmondani a régit!"
|
||||
|
||||
await NotificationService.send_notification(
|
||||
db=db,
|
||||
user_id=user.id,
|
||||
title=title,
|
||||
message=msg,
|
||||
category=doc_type,
|
||||
priority=priority,
|
||||
data={"asset_id": str(asset.id), "vin": asset.vin, "days": days_until},
|
||||
email_template="expiry_alert",
|
||||
email_vars={
|
||||
"recipient": user.email,
|
||||
"first_name": user.person.first_name if user.person else "Partnerünk",
|
||||
"license_plate": asset.license_plate,
|
||||
"expiry_date": expiry_str,
|
||||
"days_left": days_until,
|
||||
"lang": user.preferred_language
|
||||
}
|
||||
)
|
||||
notifications_sent += 1
|
||||
|
||||
except (ValueError, TypeError):
|
||||
continue
|
||||
|
||||
return {"status": "success", "count": notifications_sent}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Notification System Error: {e}")
|
||||
raise e
|
||||
Reference in New Issue
Block a user