import os from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Form, Body from fastapi.responses import JSONResponse from sqlalchemy.orm import Session from typing import List, Optional from database import get_db import models from auth import require_admin, get_current_user from utils import generate_qr router = APIRouter(prefix="/api/chargers", tags=["chargers"]) # ── 충전기 종류 ────────────────────────────────────── @router.get("/types") def list_types(db: Session = Depends(get_db)): types = db.query(models.ChargerType).order_by(models.ChargerType.id).all() return [{"id": t.id, "name": t.name, "description": t.description, "charger_count": len(t.chargers)} for t in types] @router.post("/types") def create_type(name: str = Form(...), description: str = Form(""), db: Session = Depends(get_db), _=Depends(require_admin)): t = models.ChargerType(name=name, description=description) db.add(t); db.commit(); db.refresh(t) return {"id": t.id, "name": t.name} @router.put("/types/{type_id}") def update_type(type_id: int, name: str = Form(...), description: str = Form(""), db: Session = Depends(get_db), _=Depends(require_admin)): t = db.query(models.ChargerType).filter_by(id=type_id).first() if not t: raise HTTPException(404, "종류를 찾을 수 없습니다.") t.name = name; t.description = description db.commit() return {"id": t.id, "name": t.name} @router.delete("/types/{type_id}") def delete_type(type_id: int, db: Session = Depends(get_db), _=Depends(require_admin)): t = db.query(models.ChargerType).filter_by(id=type_id).first() if not t: raise HTTPException(404) if t.chargers: raise HTTPException(400, "해당 종류로 등록된 충전기가 있어 삭제할 수 없습니다.") db.delete(t); db.commit() return {"ok": True} # ── 충전기 종류별 에러 코드 ────────────────────────── @router.get("/types/{type_id}/errors") def list_type_errors(type_id: int, db: Session = Depends(get_db)): errors = (db.query(models.ChargerTypeError) .filter_by(charger_type_id=type_id) .order_by(models.ChargerTypeError.display_order) .all()) return [{"id": e.id, "error_code": e.error_code, "error_name": e.error_name, "range_condition": e.range_condition, "description": e.description, "auto_recovery": e.auto_recovery, "display_order": e.display_order} for e in errors] @router.post("/types/{type_id}/errors") def create_type_error( type_id: int, error_code: str = Form(...), error_name: str = Form(...), range_condition: str = Form(""), description: str = Form(""), auto_recovery: bool = Form(True), display_order: int = Form(0), db: Session = Depends(get_db), _=Depends(require_admin) ): if not db.query(models.ChargerType).filter_by(id=type_id).first(): raise HTTPException(404) e = models.ChargerTypeError( charger_type_id=type_id, error_code=error_code, error_name=error_name, range_condition=range_condition or None, description=description or None, auto_recovery=auto_recovery, display_order=display_order ) db.add(e); db.commit(); db.refresh(e) return {"id": e.id} @router.put("/types/{type_id}/errors/{error_id}") def update_type_error( type_id: int, error_id: int, error_code: str = Form(...), error_name: str = Form(...), range_condition: str = Form(""), description: str = Form(""), auto_recovery: bool = Form(True), display_order: int = Form(0), db: Session = Depends(get_db), _=Depends(require_admin) ): e = db.query(models.ChargerTypeError).filter_by(id=error_id, charger_type_id=type_id).first() if not e: raise HTTPException(404) e.error_code = error_code; e.error_name = error_name e.range_condition = range_condition or None; e.description = description or None e.auto_recovery = auto_recovery; e.display_order = display_order db.commit() return {"ok": True} @router.delete("/types/{type_id}/errors/{error_id}") def delete_type_error( type_id: int, error_id: int, db: Session = Depends(get_db), _=Depends(require_admin) ): e = db.query(models.ChargerTypeError).filter_by(id=error_id, charger_type_id=type_id).first() if not e: raise HTTPException(404) db.delete(e); db.commit() return {"ok": True} # ── 충전기 ────────────────────────────────────────── @router.delete("/bulk") def bulk_delete_chargers( ids: List[str] = Body(...), db: Session = Depends(get_db), _=Depends(require_admin) ): if not ids: raise HTTPException(400, "삭제할 항목을 선택하세요.") has_reports = db.query(models.Report.charger_id).filter( models.Report.charger_id.in_(ids)).distinct().all() if has_reports: blocked = [r[0] for r in has_reports] raise HTTPException(400, f"신고 내역이 있는 충전기는 삭제할 수 없습니다: {', '.join(blocked)}") result = db.query(models.Charger).filter(models.Charger.id.in_(ids)).delete(synchronize_session=False) db.commit() return {"deleted": result} @router.get("") def list_chargers(db: Session = Depends(get_db)): chargers = db.query(models.Charger).order_by(models.Charger.id).all() result = [] for c in chargers: pending = db.query(models.Report).filter( models.Report.charger_id == c.id, models.Report.status.in_(["pending", "in_progress"]) ).count() result.append({ "id": c.id, "name": c.name, "station_name": c.station_name, "cpo_name": c.cpo_name, "location_detail": c.location_detail, "installed_at": str(c.installed_at) if c.installed_at else None, "gps_lat": c.gps_lat, "gps_lng": c.gps_lng, "is_active": c.is_active, "charger_type": c.charger_type.name if c.charger_type else None, "charger_type_id": c.charger_type_id, "pending_reports": pending, }) return result @router.get("/{charger_id}/errors") def get_charger_errors(charger_id: str, db: Session = Depends(get_db)): c = db.query(models.Charger).filter_by(id=charger_id).first() if not c: raise HTTPException(404) if not c.charger_type_id: return [] errors = (db.query(models.ChargerTypeError) .filter_by(charger_type_id=c.charger_type_id) .order_by(models.ChargerTypeError.display_order) .all()) return [{"id": e.id, "error_code": e.error_code, "error_name": e.error_name, "range_condition": e.range_condition, "auto_recovery": e.auto_recovery} for e in errors] @router.get("/{charger_id}") def get_charger(charger_id: str, db: Session = Depends(get_db)): c = db.query(models.Charger).filter_by(id=charger_id).first() if not c: raise HTTPException(404, "충전기를 찾을 수 없습니다.") return { "id": c.id, "name": c.name, "station_name": c.station_name, "cpo_name": c.cpo_name, "location_detail": c.location_detail, "installed_at": str(c.installed_at) if c.installed_at else None, "gps_lat": c.gps_lat, "gps_lng": c.gps_lng, "is_active": c.is_active, "charger_type": c.charger_type.name if c.charger_type else None, "charger_type_id": c.charger_type_id, } @router.post("") def create_charger( id: str = Form(...), charger_type_id: int = Form(...), name: str = Form(...), station_name: str = Form(...), location_detail: str = Form(""), cpo_name: str = Form(""), installed_at: Optional[str] = Form(None), gps_lat: Optional[float] = Form(None), gps_lng: Optional[float] = Form(None), db: Session = Depends(get_db), _=Depends(require_admin) ): if db.query(models.Charger).filter_by(id=id).first(): raise HTTPException(400, "이미 존재하는 충전기 ID입니다.") c = models.Charger( id=id, charger_type_id=charger_type_id, name=name, station_name=station_name, location_detail=location_detail, cpo_name=cpo_name, installed_at=installed_at or None, gps_lat=gps_lat, gps_lng=gps_lng ) db.add(c); db.commit() domain = os.getenv("DOMAIN", "localhost") qr_path = generate_qr(id, domain) return {"id": c.id, "qr_path": qr_path} @router.put("/{charger_id}") def update_charger( charger_id: str, charger_type_id: int = Form(...), name: str = Form(...), station_name: str = Form(...), location_detail: str = Form(""), cpo_name: str = Form(""), installed_at: Optional[str] = Form(None), gps_lat: Optional[float] = Form(None), gps_lng: Optional[float] = Form(None), db: Session = Depends(get_db), _=Depends(require_admin) ): c = db.query(models.Charger).filter_by(id=charger_id).first() if not c: raise HTTPException(404) c.charger_type_id = charger_type_id; c.name = name c.station_name = station_name; c.location_detail = location_detail c.cpo_name = cpo_name; c.installed_at = installed_at or None c.gps_lat = gps_lat; c.gps_lng = gps_lng db.commit() domain = os.getenv("DOMAIN", "localhost") qr_path = generate_qr(charger_id, domain) return {"id": c.id, "qr_path": qr_path} @router.post("/{charger_id}/qr") def regenerate_qr(charger_id: str, db: Session = Depends(get_db), _=Depends(require_admin)): c = db.query(models.Charger).filter_by(id=charger_id).first() if not c: raise HTTPException(404) domain = os.getenv("DOMAIN", "localhost") qr_path = generate_qr(charger_id, domain) return {"qr_path": qr_path}