Initial commit: Robot ökoszisztéma v2.0 - Stabilizált jármű és szerviz robotok
This commit is contained in:
141
backend/app/services/search_service.py
Executable file
141
backend/app/services/search_service.py
Executable file
@@ -0,0 +1,141 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/services/search_service.py
|
||||
import logging
|
||||
from typing import List, Optional, Dict, Any
|
||||
from uuid import UUID
|
||||
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select, func, and_
|
||||
from geoalchemy2.functions import ST_Distance, ST_MakePoint, ST_DWithin
|
||||
|
||||
from app.models.service import ServiceProfile, ExpertiseTag
|
||||
from app.models.organization import Organization
|
||||
from app.models.identity import User
|
||||
from app.services.config_service import config
|
||||
|
||||
logger = logging.getLogger("Search-Service-2.4-Agnostic")
|
||||
|
||||
class SearchService:
|
||||
"""
|
||||
Sentinel Master Search Service 2.4.
|
||||
Csomag-agnosztikus rangsoroló motor.
|
||||
Minden üzleti logika (súlyozás, prioritás, láthatóság) az adatbázisból jön.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
async def find_nearby_services(
|
||||
db: AsyncSession,
|
||||
lat: float,
|
||||
lon: float,
|
||||
current_user: User,
|
||||
expertise_key: str = None
|
||||
):
|
||||
try:
|
||||
# 1. HIERARCHIKUS RANGSOROLÁSI SZABÁLYOK LEKÉRÉSE
|
||||
# A config_service automatikusan a legspecifikusabbat adja vissza:
|
||||
# 1. User-specifikus (Céges egyedi beállítás)
|
||||
# 2. Package-specifikus (pl. 'free', 'premium', 'ultra_gold')
|
||||
# 3. Global (Alapértelmezett)
|
||||
|
||||
user_tier = current_user.tier_name
|
||||
|
||||
if current_user.role in [UserRole.superadmin, UserRole.admin]:
|
||||
user_tier = "vip"
|
||||
|
||||
ranking_rules = await config.get_setting(
|
||||
db,
|
||||
"RANKING_RULES",
|
||||
user_id=current_user.id, # Ez a belső config_service-ben kezeli a hierarchiát
|
||||
package_slug=user_tier,
|
||||
default={
|
||||
"ad_weight": 5000,
|
||||
"partner_weight": 1000,
|
||||
"trust_weight": 10,
|
||||
"dist_penalty": 20,
|
||||
"pref_weight": 0,
|
||||
"can_use_prefs": False,
|
||||
"search_radius_km": 30
|
||||
}
|
||||
)
|
||||
|
||||
# 2. PREFERENCIÁK (Ha a szabályrendszer engedi)
|
||||
user_prefs = {"networks": [], "ids": []}
|
||||
if ranking_rules.get("can_use_prefs", False):
|
||||
user_prefs = await config.get_setting(
|
||||
db, "USER_SEARCH_PREFERENCES",
|
||||
scope_level="user", scope_id=str(current_user.id),
|
||||
default={"networks": [], "ids": []}
|
||||
)
|
||||
|
||||
# 3. TÉRBELI LEKÉRDEZÉS
|
||||
user_point = ST_MakePoint(lon, lat)
|
||||
radius_m = ranking_rules.get("search_radius_km", 30) * 1000
|
||||
|
||||
distance_col = ST_Distance(ServiceProfile.location, user_point).label("distance_meters")
|
||||
|
||||
stmt = (
|
||||
select(ServiceProfile, Organization, distance_col)
|
||||
.join(Organization, ServiceProfile.organization_id == Organization.id)
|
||||
.where(
|
||||
and_(
|
||||
ST_DWithin(ServiceProfile.location, user_point, radius_m),
|
||||
ServiceProfile.is_active == True
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
if expertise_key:
|
||||
stmt = stmt.join(ServiceProfile.expertises).join(ExpertiseTag).where(ExpertiseTag.key == expertise_key)
|
||||
|
||||
result = await db.execute(stmt)
|
||||
rows = result.all()
|
||||
|
||||
# 4. UNIVERZÁLIS PONTOZÁS (Súlyozott mátrix alapján)
|
||||
final_results = []
|
||||
r = ranking_rules # Rövidítés a számításhoz
|
||||
|
||||
for s_prof, org, dist_m in rows:
|
||||
dist_km = dist_m / 1000.0
|
||||
score = 0
|
||||
|
||||
# --- PONTOZÁSI LOGIKA (Nincsenek fix csomagnevek!) ---
|
||||
|
||||
# A. Hirdetési súly
|
||||
if s_prof.is_advertiser:
|
||||
score += r.get("ad_weight", 0)
|
||||
|
||||
# B. Partner (Minősített) súly
|
||||
if s_prof.is_verified_partner:
|
||||
score += r.get("partner_weight", 0)
|
||||
|
||||
# C. Minőség (Trust Score) súly
|
||||
score += (s_prof.trust_score * r.get("trust_weight", 0))
|
||||
|
||||
# D. Egyéni/Céges preferencia súly (Csak ha engedélyezett)
|
||||
if r.get("can_use_prefs"):
|
||||
if s_prof.network_slug in user_prefs.get("networks", []):
|
||||
score += r.get("pref_weight", 0)
|
||||
if str(org.id) in user_prefs.get("ids", []):
|
||||
score += r.get("pref_weight", 0) * 1.2
|
||||
|
||||
# E. Távolság büntetés
|
||||
score -= (dist_km * r.get("dist_penalty", 0))
|
||||
|
||||
final_results.append({
|
||||
"id": org.id,
|
||||
"name": org.full_name,
|
||||
"trust_score": s_prof.trust_score,
|
||||
"distance_km": round(dist_km, 2),
|
||||
"rank_score": round(score, 2),
|
||||
"flags": {
|
||||
"is_ad": s_prof.is_advertiser,
|
||||
"is_partner": s_prof.is_verified_partner,
|
||||
"is_favorite": str(org.id) in user_prefs.get("ids", [])
|
||||
}
|
||||
})
|
||||
|
||||
# 5. RENDEZÉS
|
||||
return sorted(final_results, key=lambda x: x['rank_score'], reverse=True)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Search Service 2.4 Critical Error: {e}")
|
||||
raise e
|
||||
Reference in New Issue
Block a user