- Frontend: Next.js 14 with TypeScript - Backend: FastAPI with SQLAlchemy - Agent: Carmodoo sync agent - Deployment: Docker Compose based staging/production setup - Scripts: Automated deployment with rollback support 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
193 lines
6.6 KiB
Python
193 lines
6.6 KiB
Python
from fastapi import APIRouter, Depends, HTTPException
|
|
from sqlalchemy.orm import Session
|
|
from sqlalchemy import func as sql_func
|
|
from datetime import datetime
|
|
from typing import List
|
|
from ..database import get_db
|
|
from ..models import User, ReferralReward, SystemSettings
|
|
from ..schemas import (
|
|
ReferralRewardResponse, ReferralStats,
|
|
ReferralSettingsResponse, ReferralSettingsUpdate,
|
|
)
|
|
from .auth import get_current_user
|
|
from .notification import notify_referral_reward
|
|
|
|
router = APIRouter(prefix="/referral", tags=["referral"])
|
|
|
|
|
|
def get_referral_settings(db: Session) -> SystemSettings:
|
|
"""Get or create system settings"""
|
|
settings = db.query(SystemSettings).first()
|
|
if not settings:
|
|
settings = SystemSettings()
|
|
db.add(settings)
|
|
db.commit()
|
|
db.refresh(settings)
|
|
return settings
|
|
|
|
|
|
def create_referral_reward(
|
|
referrer_id: int,
|
|
referred_user_id: int,
|
|
payment_amount: float,
|
|
db: Session
|
|
):
|
|
"""Create a referral reward when a referred user makes a payment"""
|
|
settings = get_referral_settings(db)
|
|
|
|
# Check if referral rewards are enabled
|
|
if not settings.referral_reward_enabled:
|
|
return None
|
|
|
|
# Check if this is a one_time reward and already exists
|
|
if settings.referral_reward_type == "one_time":
|
|
existing = db.query(ReferralReward).filter(
|
|
ReferralReward.referrer_id == referrer_id,
|
|
ReferralReward.referred_user_id == referred_user_id
|
|
).first()
|
|
if existing:
|
|
return None # Already gave reward for this referral
|
|
|
|
# Calculate reward amount
|
|
reward_amount = payment_amount * (settings.referral_reward_percent / 100)
|
|
|
|
# Create reward record
|
|
reward = ReferralReward(
|
|
referrer_id=referrer_id,
|
|
referred_user_id=referred_user_id,
|
|
payment_amount=payment_amount,
|
|
reward_amount=reward_amount,
|
|
status="credited", # Auto-credit for simplicity
|
|
credited_at=datetime.utcnow()
|
|
)
|
|
|
|
db.add(reward)
|
|
db.commit()
|
|
db.refresh(reward)
|
|
|
|
# Send notification to referrer
|
|
referred_user = db.query(User).filter(User.id == referred_user_id).first()
|
|
referred_name = referred_user.name or referred_user.email if referred_user else "회원"
|
|
notify_referral_reward(db, referrer_id, reward_amount, referred_name)
|
|
|
|
return reward
|
|
|
|
|
|
@router.get("/my-link")
|
|
def get_my_referral_link(current_user: User = Depends(get_current_user)):
|
|
"""Get current user's referral link/code"""
|
|
return {
|
|
"referral_code": current_user.referral_code,
|
|
"referral_link": f"/register?ref={current_user.referral_code}"
|
|
}
|
|
|
|
|
|
@router.get("/my-rewards", response_model=List[ReferralRewardResponse])
|
|
def get_my_rewards(
|
|
current_user: User = Depends(get_current_user),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""Get current user's referral rewards"""
|
|
rewards = db.query(ReferralReward).filter(
|
|
ReferralReward.referrer_id == current_user.id
|
|
).order_by(ReferralReward.created_at.desc()).all()
|
|
|
|
return rewards
|
|
|
|
|
|
@router.get("/stats", response_model=ReferralStats)
|
|
def get_referral_stats(
|
|
current_user: User = Depends(get_current_user),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""Get referral statistics for current user"""
|
|
# Get all rewards where user is the referrer
|
|
rewards = db.query(ReferralReward).filter(
|
|
ReferralReward.referrer_id == current_user.id
|
|
).all()
|
|
|
|
# Count unique referred users
|
|
referred_users = db.query(sql_func.count(sql_func.distinct(ReferralReward.referred_user_id))).filter(
|
|
ReferralReward.referrer_id == current_user.id
|
|
).scalar() or 0
|
|
|
|
total_rewards_earned = sum(r.reward_amount for r in rewards)
|
|
total_rewards_credited = sum(r.reward_amount for r in rewards if r.status == "credited")
|
|
total_rewards_pending = sum(r.reward_amount for r in rewards if r.status == "pending")
|
|
total_withdrawn = sum(r.reward_amount for r in rewards if r.status == "withdrawn")
|
|
|
|
return ReferralStats(
|
|
total_referrals=referred_users,
|
|
total_rewards_earned=total_rewards_earned,
|
|
total_rewards_credited=total_rewards_credited,
|
|
total_rewards_pending=total_rewards_pending,
|
|
available_for_withdrawal=total_rewards_credited - total_withdrawn
|
|
)
|
|
|
|
|
|
@router.get("/settings", response_model=ReferralSettingsResponse)
|
|
def get_settings(
|
|
current_user: User = Depends(get_current_user),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""Get referral settings (public endpoint)"""
|
|
settings = get_referral_settings(db)
|
|
|
|
return ReferralSettingsResponse(
|
|
referral_reward_enabled=settings.referral_reward_enabled,
|
|
referral_reward_percent=settings.referral_reward_percent,
|
|
referral_reward_type=settings.referral_reward_type
|
|
)
|
|
|
|
|
|
# Admin endpoints
|
|
@router.put("/admin/settings", response_model=ReferralSettingsResponse)
|
|
def update_settings(
|
|
update_data: ReferralSettingsUpdate,
|
|
current_user: User = Depends(get_current_user),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""[Admin] Update referral settings"""
|
|
if not current_user.is_admin:
|
|
raise HTTPException(status_code=403, detail="Admin access required")
|
|
|
|
settings = get_referral_settings(db)
|
|
|
|
if update_data.referral_reward_enabled is not None:
|
|
settings.referral_reward_enabled = update_data.referral_reward_enabled
|
|
|
|
if update_data.referral_reward_percent is not None:
|
|
if update_data.referral_reward_percent < 0 or update_data.referral_reward_percent > 100:
|
|
raise HTTPException(status_code=400, detail="Reward percent must be between 0 and 100")
|
|
settings.referral_reward_percent = update_data.referral_reward_percent
|
|
|
|
if update_data.referral_reward_type is not None:
|
|
if update_data.referral_reward_type not in ["one_time", "recurring"]:
|
|
raise HTTPException(status_code=400, detail="Reward type must be 'one_time' or 'recurring'")
|
|
settings.referral_reward_type = update_data.referral_reward_type
|
|
|
|
db.commit()
|
|
db.refresh(settings)
|
|
|
|
return ReferralSettingsResponse(
|
|
referral_reward_enabled=settings.referral_reward_enabled,
|
|
referral_reward_percent=settings.referral_reward_percent,
|
|
referral_reward_type=settings.referral_reward_type
|
|
)
|
|
|
|
|
|
@router.get("/admin/all-rewards", response_model=List[ReferralRewardResponse])
|
|
def admin_get_all_rewards(
|
|
current_user: User = Depends(get_current_user),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""[Admin] Get all referral rewards"""
|
|
if not current_user.is_admin:
|
|
raise HTTPException(status_code=403, detail="Admin access required")
|
|
|
|
rewards = db.query(ReferralReward).order_by(
|
|
ReferralReward.created_at.desc()
|
|
).limit(100).all()
|
|
|
|
return rewards
|