141 lines
3.7 KiB
Python
141 lines
3.7 KiB
Python
import json
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
from pydantic import BaseModel
|
|
from sqlalchemy.orm import Session
|
|
|
|
from core.db import get_db
|
|
from core.redis_client import redis_client
|
|
from core.security import get_current_user_id
|
|
|
|
from .models import Cart, CartItem
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
class AddItemIn(BaseModel):
|
|
product_id: int
|
|
qty: int = 1
|
|
|
|
|
|
class UpdateItemIn(BaseModel):
|
|
qty: int
|
|
|
|
|
|
class CartItemOut(BaseModel):
|
|
product_id: int
|
|
qty: int
|
|
name: dict = {}
|
|
price: float = 0.0
|
|
image_url: str = ""
|
|
line_total: float = 0.0
|
|
|
|
|
|
class CartOut(BaseModel):
|
|
items: list[CartItemOut]
|
|
subtotal: float
|
|
|
|
|
|
def _get_or_create_cart(db: Session, user_id: int) -> Cart:
|
|
cart = db.query(Cart).filter_by(user_id=user_id).first()
|
|
if not cart:
|
|
cart = Cart(user_id=user_id)
|
|
db.add(cart)
|
|
db.commit()
|
|
db.refresh(cart)
|
|
return cart
|
|
|
|
|
|
def _cart_to_out(cart: Cart) -> CartOut:
|
|
items: list[CartItemOut] = []
|
|
subtotal = 0.0
|
|
for it in cart.items:
|
|
raw = redis_client.get(f"product:{it.product_id}")
|
|
if not raw:
|
|
continue
|
|
p = json.loads(raw)
|
|
line = round(float(p["price"]) * it.qty, 2)
|
|
subtotal += line
|
|
items.append(
|
|
CartItemOut(
|
|
product_id=it.product_id,
|
|
qty=it.qty,
|
|
name=p.get("name", {}),
|
|
price=float(p["price"]),
|
|
image_url=p.get("image_url", ""),
|
|
line_total=line,
|
|
)
|
|
)
|
|
return CartOut(items=items, subtotal=round(subtotal, 2))
|
|
|
|
|
|
@router.get("", response_model=CartOut)
|
|
def get_cart(user_id: int = Depends(get_current_user_id), db: Session = Depends(get_db)):
|
|
return _cart_to_out(_get_or_create_cart(db, user_id))
|
|
|
|
|
|
@router.post("/items", response_model=CartOut)
|
|
def add_item(
|
|
body: AddItemIn,
|
|
user_id: int = Depends(get_current_user_id),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
if body.qty < 1:
|
|
raise HTTPException(400, "qty must be >= 1")
|
|
if not redis_client.get(f"product:{body.product_id}"):
|
|
raise HTTPException(404, "Product not found or inactive")
|
|
cart = _get_or_create_cart(db, user_id)
|
|
existing = db.query(CartItem).filter_by(cart_id=cart.id, product_id=body.product_id).first()
|
|
if existing:
|
|
existing.qty += body.qty
|
|
else:
|
|
db.add(CartItem(cart_id=cart.id, product_id=body.product_id, qty=body.qty))
|
|
db.commit()
|
|
db.refresh(cart)
|
|
return _cart_to_out(cart)
|
|
|
|
|
|
@router.put("/items/{product_id}", response_model=CartOut)
|
|
def update_item(
|
|
product_id: int,
|
|
body: UpdateItemIn,
|
|
user_id: int = Depends(get_current_user_id),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
cart = _get_or_create_cart(db, user_id)
|
|
item = db.query(CartItem).filter_by(cart_id=cart.id, product_id=product_id).first()
|
|
if not item:
|
|
raise HTTPException(404, "Not in cart")
|
|
if body.qty < 1:
|
|
db.delete(item)
|
|
else:
|
|
item.qty = body.qty
|
|
db.commit()
|
|
db.refresh(cart)
|
|
return _cart_to_out(cart)
|
|
|
|
|
|
@router.delete("/items/{product_id}", response_model=CartOut)
|
|
def remove_item(
|
|
product_id: int,
|
|
user_id: int = Depends(get_current_user_id),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
cart = _get_or_create_cart(db, user_id)
|
|
item = db.query(CartItem).filter_by(cart_id=cart.id, product_id=product_id).first()
|
|
if item:
|
|
db.delete(item)
|
|
db.commit()
|
|
db.refresh(cart)
|
|
return _cart_to_out(cart)
|
|
|
|
|
|
@router.delete("", response_model=CartOut)
|
|
def clear_cart(user_id: int = Depends(get_current_user_id), db: Session = Depends(get_db)):
|
|
cart = _get_or_create_cart(db, user_id)
|
|
for it in list(cart.items):
|
|
db.delete(it)
|
|
db.commit()
|
|
db.refresh(cart)
|
|
return _cart_to_out(cart)
|