Implementación inicial del sistema de automatización de redes sociales
- Estructura completa del proyecto con FastAPI - Modelos de base de datos (productos, servicios, posts, calendario, interacciones) - Publishers para X, Threads, Instagram, Facebook - Generador de contenido con DeepSeek API - Worker de Celery con tareas programadas - Dashboard básico con templates HTML - Docker Compose para despliegue - Documentación completa Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
180
app/api/routes/products.py
Normal file
180
app/api/routes/products.py
Normal file
@@ -0,0 +1,180 @@
|
||||
"""
|
||||
API Routes para gestión de Productos.
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from typing import List, Optional
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy.orm import Session
|
||||
from pydantic import BaseModel
|
||||
|
||||
from app.core.database import get_db
|
||||
from app.models.product import Product
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
# ===========================================
|
||||
# SCHEMAS
|
||||
# ===========================================
|
||||
|
||||
class ProductCreate(BaseModel):
|
||||
name: str
|
||||
description: Optional[str] = None
|
||||
short_description: Optional[str] = None
|
||||
category: str
|
||||
subcategory: Optional[str] = None
|
||||
brand: Optional[str] = None
|
||||
model: Optional[str] = None
|
||||
price: float
|
||||
price_usd: Optional[float] = None
|
||||
stock: int = 0
|
||||
specs: Optional[dict] = None
|
||||
images: Optional[List[str]] = None
|
||||
main_image: Optional[str] = None
|
||||
tags: Optional[List[str]] = None
|
||||
highlights: Optional[List[str]] = None
|
||||
is_featured: bool = False
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class ProductUpdate(BaseModel):
|
||||
name: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
short_description: Optional[str] = None
|
||||
category: Optional[str] = None
|
||||
subcategory: Optional[str] = None
|
||||
brand: Optional[str] = None
|
||||
model: Optional[str] = None
|
||||
price: Optional[float] = None
|
||||
price_usd: Optional[float] = None
|
||||
stock: Optional[int] = None
|
||||
is_available: Optional[bool] = None
|
||||
specs: Optional[dict] = None
|
||||
images: Optional[List[str]] = None
|
||||
main_image: Optional[str] = None
|
||||
tags: Optional[List[str]] = None
|
||||
highlights: Optional[List[str]] = None
|
||||
is_featured: Optional[bool] = None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
# ===========================================
|
||||
# ENDPOINTS
|
||||
# ===========================================
|
||||
|
||||
@router.get("/")
|
||||
async def list_products(
|
||||
category: Optional[str] = Query(None),
|
||||
brand: Optional[str] = Query(None),
|
||||
is_available: Optional[bool] = Query(None),
|
||||
is_featured: Optional[bool] = Query(None),
|
||||
min_price: Optional[float] = Query(None),
|
||||
max_price: Optional[float] = Query(None),
|
||||
limit: int = Query(50, le=100),
|
||||
offset: int = Query(0),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Listar productos con filtros."""
|
||||
query = db.query(Product)
|
||||
|
||||
if category:
|
||||
query = query.filter(Product.category == category)
|
||||
if brand:
|
||||
query = query.filter(Product.brand == brand)
|
||||
if is_available is not None:
|
||||
query = query.filter(Product.is_available == is_available)
|
||||
if is_featured is not None:
|
||||
query = query.filter(Product.is_featured == is_featured)
|
||||
if min_price is not None:
|
||||
query = query.filter(Product.price >= min_price)
|
||||
if max_price is not None:
|
||||
query = query.filter(Product.price <= max_price)
|
||||
|
||||
products = query.order_by(Product.created_at.desc()).offset(offset).limit(limit).all()
|
||||
return [p.to_dict() for p in products]
|
||||
|
||||
|
||||
@router.get("/categories")
|
||||
async def list_categories(db: Session = Depends(get_db)):
|
||||
"""Listar categorías únicas de productos."""
|
||||
categories = db.query(Product.category).distinct().all()
|
||||
return [c[0] for c in categories if c[0]]
|
||||
|
||||
|
||||
@router.get("/featured")
|
||||
async def list_featured_products(db: Session = Depends(get_db)):
|
||||
"""Listar productos destacados."""
|
||||
products = db.query(Product).filter(
|
||||
Product.is_featured == True,
|
||||
Product.is_available == True
|
||||
).all()
|
||||
return [p.to_dict() for p in products]
|
||||
|
||||
|
||||
@router.get("/{product_id}")
|
||||
async def get_product(product_id: int, db: Session = Depends(get_db)):
|
||||
"""Obtener un producto por ID."""
|
||||
product = db.query(Product).filter(Product.id == product_id).first()
|
||||
if not product:
|
||||
raise HTTPException(status_code=404, detail="Producto no encontrado")
|
||||
return product.to_dict()
|
||||
|
||||
|
||||
@router.post("/")
|
||||
async def create_product(product_data: ProductCreate, db: Session = Depends(get_db)):
|
||||
"""Crear un nuevo producto."""
|
||||
product = Product(**product_data.dict())
|
||||
db.add(product)
|
||||
db.commit()
|
||||
db.refresh(product)
|
||||
return product.to_dict()
|
||||
|
||||
|
||||
@router.put("/{product_id}")
|
||||
async def update_product(product_id: int, product_data: ProductUpdate, db: Session = Depends(get_db)):
|
||||
"""Actualizar un producto."""
|
||||
product = db.query(Product).filter(Product.id == product_id).first()
|
||||
if not product:
|
||||
raise HTTPException(status_code=404, detail="Producto no encontrado")
|
||||
|
||||
update_data = product_data.dict(exclude_unset=True)
|
||||
for field, value in update_data.items():
|
||||
setattr(product, field, value)
|
||||
|
||||
product.updated_at = datetime.utcnow()
|
||||
db.commit()
|
||||
db.refresh(product)
|
||||
return product.to_dict()
|
||||
|
||||
|
||||
@router.delete("/{product_id}")
|
||||
async def delete_product(product_id: int, db: Session = Depends(get_db)):
|
||||
"""Eliminar un producto."""
|
||||
product = db.query(Product).filter(Product.id == product_id).first()
|
||||
if not product:
|
||||
raise HTTPException(status_code=404, detail="Producto no encontrado")
|
||||
|
||||
db.delete(product)
|
||||
db.commit()
|
||||
return {"message": "Producto eliminado", "product_id": product_id}
|
||||
|
||||
|
||||
@router.post("/{product_id}/toggle-featured")
|
||||
async def toggle_featured(product_id: int, db: Session = Depends(get_db)):
|
||||
"""Alternar estado de producto destacado."""
|
||||
product = db.query(Product).filter(Product.id == product_id).first()
|
||||
if not product:
|
||||
raise HTTPException(status_code=404, detail="Producto no encontrado")
|
||||
|
||||
product.is_featured = not product.is_featured
|
||||
db.commit()
|
||||
|
||||
return {
|
||||
"message": f"Producto {'destacado' if product.is_featured else 'no destacado'}",
|
||||
"is_featured": product.is_featured
|
||||
}
|
||||
Reference in New Issue
Block a user