from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.orm import Session from sqlalchemy import func from typing import List, Optional from datetime import datetime from ..database import get_db from ..models import User, Car, SnsShareSubmission, SystemSettings from ..schemas.sns_share import ( SnsShareSubmit, SnsShareResponse, SnsShareListResponse, SnsShareVerify, SnsShareStats ) from .auth import get_current_user, get_current_admin_user router = APIRouter(prefix="/sns-share", tags=["SNS Share"]) def get_car_info(car: Car) -> dict: """차량 정보 추출""" car_name = f"{car.maker_name or ''} {car.model_name or ''} {car.grade_name or ''}".strip() car_image = None if car.images and len(car.images) > 0: car_image = car.images[0].image_url return {"car_name": car_name, "car_image": car_image} def submission_to_response(submission: SnsShareSubmission) -> SnsShareResponse: """SnsShareSubmission을 Response로 변환""" car_info = get_car_info(submission.car) if submission.car else {} return SnsShareResponse( id=submission.id, user_id=submission.user_id, car_id=submission.car_id, platform=submission.platform, sns_url=submission.sns_url, status=submission.status, rejected_reason=submission.rejected_reason, reward_cc=submission.reward_cc, rewarded_at=submission.rewarded_at, submitted_at=submission.submitted_at, verified_at=submission.verified_at, verified_by=submission.verified_by, car_name=car_info.get("car_name"), car_image=car_info.get("car_image"), ) @router.post("/submit", response_model=SnsShareResponse) async def submit_sns_share( data: SnsShareSubmit, db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """SNS 공유 URL 제출""" # 마케팅 캠페인 활성화 확인 settings = db.query(SystemSettings).first() if settings and not settings.marketing_enabled: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Marketing campaign is not active" ) # 캠페인 기간 확인 now = datetime.utcnow() if settings: if settings.marketing_start_date and now < settings.marketing_start_date: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Marketing campaign has not started yet" ) if settings.marketing_end_date and now > settings.marketing_end_date: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Marketing campaign has ended" ) # 차량 존재 확인 car = db.query(Car).filter(Car.id == data.car_id).first() if not car: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Car not found" ) # 플랫폼 검증 valid_platforms = ["twitter", "instagram", "facebook"] if data.platform.lower() not in valid_platforms: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=f"Invalid platform. Must be one of: {', '.join(valid_platforms)}" ) # 중복 제출 확인 (같은 차량, 같은 URL) existing = db.query(SnsShareSubmission).filter( SnsShareSubmission.user_id == current_user.id, SnsShareSubmission.car_id == data.car_id, SnsShareSubmission.sns_url == data.sns_url ).first() if existing: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="You have already submitted this URL for this car" ) # 보상 CC 설정 reward_cc = settings.sns_share_reward_cc if settings else 3.0 # 제출 생성 submission = SnsShareSubmission( user_id=current_user.id, car_id=data.car_id, platform=data.platform.lower(), sns_url=data.sns_url, status="pending", reward_cc=reward_cc, ) db.add(submission) db.commit() db.refresh(submission) return submission_to_response(submission) @router.get("/my-submissions", response_model=SnsShareListResponse) async def get_my_submissions( db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """내 SNS 공유 제출 목록""" submissions = db.query(SnsShareSubmission).filter( SnsShareSubmission.user_id == current_user.id ).order_by(SnsShareSubmission.submitted_at.desc()).all() pending_count = len([s for s in submissions if s.status == "pending"]) approved_count = len([s for s in submissions if s.status == "approved"]) rejected_count = len([s for s in submissions if s.status == "rejected"]) return SnsShareListResponse( submissions=[submission_to_response(s) for s in submissions], total=len(submissions), pending_count=pending_count, approved_count=approved_count, rejected_count=rejected_count, ) @router.get("/admin/list", response_model=SnsShareListResponse) async def admin_get_submissions( status_filter: Optional[str] = None, skip: int = 0, limit: int = 50, db: Session = Depends(get_db), admin: User = Depends(get_current_admin_user) ): """관리자: 전체 SNS 공유 제출 목록""" query = db.query(SnsShareSubmission) if status_filter: query = query.filter(SnsShareSubmission.status == status_filter) total = query.count() submissions = query.order_by(SnsShareSubmission.submitted_at.desc()).offset(skip).limit(limit).all() # 전체 통계 pending_count = db.query(SnsShareSubmission).filter(SnsShareSubmission.status == "pending").count() approved_count = db.query(SnsShareSubmission).filter(SnsShareSubmission.status == "approved").count() rejected_count = db.query(SnsShareSubmission).filter(SnsShareSubmission.status == "rejected").count() return SnsShareListResponse( submissions=[submission_to_response(s) for s in submissions], total=total, pending_count=pending_count, approved_count=approved_count, rejected_count=rejected_count, ) @router.get("/admin/stats", response_model=SnsShareStats) async def admin_get_stats( db: Session = Depends(get_db), admin: User = Depends(get_current_admin_user) ): """관리자: SNS 공유 통계""" total = db.query(SnsShareSubmission).count() pending = db.query(SnsShareSubmission).filter(SnsShareSubmission.status == "pending").count() approved = db.query(SnsShareSubmission).filter(SnsShareSubmission.status == "approved").count() rejected = db.query(SnsShareSubmission).filter(SnsShareSubmission.status == "rejected").count() total_rewarded = db.query(func.sum(SnsShareSubmission.reward_cc)).filter( SnsShareSubmission.status == "approved" ).scalar() or 0.0 return SnsShareStats( total_submissions=total, pending_submissions=pending, approved_submissions=approved, rejected_submissions=rejected, total_rewarded_cc=total_rewarded, ) @router.put("/admin/{submission_id}/verify", response_model=SnsShareResponse) async def admin_verify_submission( submission_id: int, data: SnsShareVerify, db: Session = Depends(get_db), admin: User = Depends(get_current_admin_user) ): """관리자: SNS 공유 검증 (승인/거부)""" submission = db.query(SnsShareSubmission).filter( SnsShareSubmission.id == submission_id ).first() if not submission: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Submission not found" ) if submission.status != "pending": raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Submission has already been verified" ) now = datetime.utcnow() if data.approved: # 승인: CC 지급 submission.status = "approved" submission.verified_at = now submission.verified_by = admin.id submission.rewarded_at = now # 사용자 CC 잔액 업데이트 (프로모션 CC - 출금 불가) user = db.query(User).filter(User.id == submission.user_id).first() if user: user.cc_balance += submission.reward_cc else: # 거부 submission.status = "rejected" submission.verified_at = now submission.verified_by = admin.id submission.rejected_reason = data.rejected_reason db.commit() db.refresh(submission) return submission_to_response(submission) @router.get("/campaign-status") async def get_campaign_status(db: Session = Depends(get_db)): """마케팅 캠페인 상태 조회 (공개)""" settings = db.query(SystemSettings).first() if not settings: return { "enabled": False, "message": "Campaign not configured" } now = datetime.utcnow() is_active = settings.marketing_enabled if settings.marketing_start_date and now < settings.marketing_start_date: is_active = False if settings.marketing_end_date and now > settings.marketing_end_date: is_active = False return { "enabled": is_active, "start_date": settings.marketing_start_date, "end_date": settings.marketing_end_date, "sns_share_reward_cc": settings.sns_share_reward_cc, "referral_signup_reward_cc": settings.referral_signup_reward_cc, }