- Added centralized, self-learning GeoService (ZIP, City, Street) - Implemented Hybrid Address Management (Centralized table + Denormalized fields) - Fixed Gamification logic (PointsLedger field names & filtering) - Added address autocomplete and two-tier (Free/Premium) search API - Synchronized UserStats and PointsLedger schemas
86 lines
3.4 KiB
Python
86 lines
3.4 KiB
Python
from fastapi import APIRouter, Depends, Form, Query, UploadFile, File
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from sqlalchemy import text
|
|
from typing import Optional, List
|
|
from app.db.session import get_db
|
|
from app.services.geo_service import GeoService
|
|
from app.services.gamification_service import GamificationService
|
|
from app.services.config_service import config
|
|
|
|
router = APIRouter()
|
|
|
|
@router.get("/suggest-street")
|
|
async def suggest_street(zip_code: str, q: str, db: AsyncSession = Depends(get_db)):
|
|
"""Azonnali utca javaslatok gépelés közben."""
|
|
return await GeoService.get_street_suggestions(db, zip_code, q)
|
|
|
|
@router.post("/hunt")
|
|
async def register_service_hunt(
|
|
name: str = Form(...),
|
|
zip_code: str = Form(...),
|
|
city: str = Form(...),
|
|
street_name: str = Form(...),
|
|
street_type: str = Form(...),
|
|
house_number: str = Form(...),
|
|
parcel_id: Optional[str] = Form(None),
|
|
latitude: float = Form(...),
|
|
longitude: float = Form(...),
|
|
user_latitude: float = Form(...),
|
|
user_longitude: float = Form(...),
|
|
current_user_id: int = 1,
|
|
db: AsyncSession = Depends(get_db)
|
|
):
|
|
# 1. Hibrid címrögzítés
|
|
addr_id = await GeoService.get_or_create_full_address(
|
|
db, zip_code, city, street_name, street_type, house_number, parcel_id
|
|
)
|
|
|
|
# 2. Távolságmérés
|
|
dist_query = text("""
|
|
SELECT ST_Distance(
|
|
ST_SetSRID(ST_MakePoint(:u_lon, :u_lat), 4326)::geography,
|
|
ST_SetSRID(ST_MakePoint(:s_lon, :s_lat), 4326)::geography
|
|
)
|
|
""")
|
|
distance = (await db.execute(dist_query, {
|
|
"u_lon": user_longitude, "u_lat": user_latitude,
|
|
"s_lon": longitude, "s_lat": latitude
|
|
})).scalar() or 0.0
|
|
|
|
# 3. Mentés (Denormalizált adatokkal a sebességért)
|
|
await db.execute(text("""
|
|
INSERT INTO data.organization_locations
|
|
(name, address_id, coordinates, proposed_by, zip_code, city, street, house_number, sources, confidence_score)
|
|
VALUES (:n, :aid, ST_SetSRID(ST_MakePoint(:lon, :lat), 4326)::geography, :uid, :z, :c, :s, :hn, jsonb_build_array(CAST('user_hunt' AS TEXT)), 1)
|
|
"""), {
|
|
"n": name, "aid": addr_id, "lon": longitude, "lat": latitude,
|
|
"uid": current_user_id, "z": zip_code, "c": city, "s": f"{street_name} {street_type}", "hn": house_number
|
|
})
|
|
|
|
# 4. Jutalmazás
|
|
await GamificationService.award_points(db, current_user_id, 50, f"Service Hunt: {city}")
|
|
await db.commit()
|
|
|
|
return {"status": "success", "address_id": str(addr_id), "distance_meters": round(distance, 2)}
|
|
|
|
@router.get("/search")
|
|
async def search_services(
|
|
lat: float, lng: float,
|
|
is_premium: bool = False,
|
|
db: AsyncSession = Depends(get_db)
|
|
):
|
|
"""Kétlépcsős keresés: Free (Légvonal) vs Premium (Útvonal/Idő)"""
|
|
query = text("""
|
|
SELECT name, city, ST_Distance(coordinates, ST_SetSRID(ST_MakePoint(:lng, :lat), 4326)::geography) as dist
|
|
FROM data.organization_locations WHERE is_verified = TRUE ORDER BY dist LIMIT 10
|
|
""")
|
|
res = (await db.execute(query, {"lat": lat, "lng": lng})).fetchall()
|
|
|
|
results = []
|
|
for row in res:
|
|
item = {"name": row[0], "city": row[1], "distance_km": round(row[2]/1000, 2)}
|
|
if is_premium:
|
|
# PRÉMIUM: Itt jönne az útvonaltervező API integráció
|
|
item["estimated_travel_time_min"] = round(row[2] / 700) # Becsült
|
|
results.append(item)
|
|
return results |