# /opt/docker/dev/service_finder/backend/app/api/v1/endpoints/services.py from fastapi import APIRouter, Depends, Form, Query, HTTPException, status from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select, and_, text from typing import List, Optional from app.db.session import get_db from app.services.gamification_service import GamificationService from app.services.config_service import ConfigService from app.services.security_auditor import SecurityAuditorService from app.models.marketplace.service import ServiceProfile, ExpertiseTag, ServiceExpertise from app.services.marketplace_service import ( create_verified_review, get_service_reviews, can_user_review_service ) from app.schemas.social import ServiceReviewCreate, ServiceReviewResponse from app.api.deps import get_current_user from app.models.identity import User router = APIRouter() # --- 🎯 SZERVIZ VADÁSZAT (Service Hunt) --- @router.post("/hunt") async def register_service_hunt( name: str = Form(...), lat: float = Form(...), lng: float = Form(...), current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db) ): """ Új szerviz-jelölt rögzítése a staging táblába jutalompontért. """ # Új szerviz-jelölt rögzítése await db.execute(text(""" INSERT INTO marketplace.service_staging (name, fingerprint, status, city, submitted_by, raw_data) VALUES (:n, :f, 'pending', 'Unknown', :user_id, jsonb_build_object('lat', CAST(:lat AS double precision), 'lng', CAST(:lng AS double precision))) """), {"n": name, "f": f"{name}-{lat}-{lng}", "lat": lat, "lng": lng, "user_id": current_user.id}) # MB 2.0 Gamification: Dinamikus pontszám a felfedezésért reward_points = await ConfigService.get_int(db, "GAMIFICATION_HUNT_REWARD", 50) await GamificationService.award_points(db, current_user.id, reward_points, f"Service Hunt: {name}") await db.commit() return {"status": "success", "message": "Discovery registered and points awarded."} # --- ✅ SZERVIZ VALIDÁLÁS (Service Validation) --- @router.post("/hunt/{staging_id}/validate") async def validate_staged_service( staging_id: int, current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db) ): """ Validálja egy másik felhasználó által beküldött szerviz-jelöltet. Növeli a validation_level-t 10-zel (max 80), adományoz 10 XP-t, és növeli a places_validated számlálót a felhasználó statisztikáiban. """ # Anti-Cheat: Rapid Fire ellenőrzés await SecurityAuditorService.check_rapid_fire_validation(db, current_user.id) # 1. Keresd meg a staging rekordot result = await db.execute( text("SELECT id, submitted_by, validation_level FROM marketplace.service_staging WHERE id = :id"), {"id": staging_id} ) staging = result.fetchone() if not staging: raise HTTPException(status_code=404, detail="Staging record not found") # 2. Ha a saját beküldését validálná, hiba if staging.submitted_by == current_user.id: raise HTTPException(status_code=400, detail="Cannot validate your own submission") # 3. Növeld a validation_level-t 10-zel (max 80) new_level = staging.validation_level + 10 if new_level > 80: new_level = 80 # 4. UPDATE a validation_level és a status (ha elérte a 80-at, akkor "verified"?) # Jelenleg csak a validation_level frissítése await db.execute( text(""" UPDATE marketplace.service_staging SET validation_level = :new_level WHERE id = :id """), {"new_level": new_level, "id": staging_id} ) # 5. Adományozz dinamikus XP-t a current_user-nek a GamificationService-en keresztül validation_reward = await ConfigService.get_int(db, "GAMIFICATION_VALIDATE_REWARD", 10) await GamificationService.award_points(db, current_user.id, validation_reward, f"Service Validation: staging #{staging_id}") # 6. Növeld a current_user places_validated értékét a UserStats-ban await db.execute( text(""" UPDATE gamification.user_stats SET places_validated = places_validated + 1 WHERE user_id = :user_id """), {"user_id": current_user.id} ) await db.commit() return { "status": "success", "message": "Validation successful", "validation_level": new_level, "places_validated_incremented": True } # --- 🔍 SZERVIZ KERESŐ (Service Search) --- @router.get("/search") async def search_services( expertise_key: Optional[str] = Query(None, description="Szakmai címke (pl. brake_service)"), city: Optional[str] = Query(None, description="Város szűrés"), min_confidence: int = Query(0, description="Minimum hitelességi szint (0-2)"), db: AsyncSession = Depends(get_db) ): """ Szakmai szempontú keresőmotor, ami a validált címkék alapján szűr. """ # Alap lekérdezés: Szervizek, akiknek van szakértelmük query = select(ServiceProfile).join(ServiceProfile.expertises).join(ServiceExpertise.tag) filters = [] if expertise_key: filters.append(ExpertiseTag.key == expertise_key) if city: filters.append(ServiceProfile.city.ilike(f"%{city}%")) if min_confidence > 0: filters.append(ServiceExpertise.confidence_level >= min_confidence) if filters: query = query.where(and_(*filters)) result = await db.execute(query.distinct()) services = result.scalars().all() return services # --- ⭐ VERIFIED SERVICE REVIEWS (Social 3 - #66) --- @router.post("/{service_id}/reviews", response_model=ServiceReviewResponse, status_code=status.HTTP_201_CREATED) async def create_service_review( service_id: int, review_data: ServiceReviewCreate, current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db) ): """ Verifikált szerviz értékelés beküldése. Csak igazolt pénzügyi tranzakció után lehetséges (transaction_id kötelező). """ try: review = await create_verified_review( db=db, service_id=service_id, user_id=current_user.id, transaction_id=review_data.transaction_id, review_data=review_data ) return review except ValueError as e: raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) except IntegrityError as e: raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(e)) @router.get("/{service_id}/reviews", response_model=dict) async def list_service_reviews( service_id: int, skip: int = Query(0, ge=0), limit: int = Query(20, ge=1, le=100), verified_only: bool = Query(True), db: AsyncSession = Depends(get_db) ): """ Szerviz értékeléseinek lapozható listázása. """ reviews, total = await get_service_reviews( db=db, service_id=service_id, skip=skip, limit=limit, verified_only=verified_only ) return { "reviews": reviews, "total": total, "skip": skip, "limit": limit } @router.get("/{service_id}/reviews/check") async def check_review_eligibility( service_id: int, current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db) ): """ Ellenőrzi, hogy a felhasználó értékelheti‑e a szervizt. """ can_review, reason = await can_user_review_service(db, current_user.id, service_id) return { "can_review": can_review, "reason": reason, "user_id": current_user.id, "service_id": service_id }